-
Notifications
You must be signed in to change notification settings - Fork 85
/
nested-list.tsx
111 lines (103 loc) · 2.83 KB
/
nested-list.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import {
getFormProps,
getInputProps,
useForm,
FormProvider,
FormStateInput,
} from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';
import { json } from '@remix-run/node';
import { Form, useActionData, useLoaderData } from '@remix-run/react';
import { z } from 'zod';
import { Playground, Field } from '~/components';
const schema = z.object({
items: z
.object({
options: z.string().array().min(1),
})
.array(),
});
export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
return {
noClientValidate: url.searchParams.get('noClientValidate') === 'yes',
};
}
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const submission = parseWithZod(formData, { schema });
return json(submission.reply());
}
export default function App() {
const { noClientValidate } = useLoaderData<typeof loader>();
const lastResult = useActionData<typeof action>();
const [form, fields] = useForm({
lastResult,
onValidate: !noClientValidate
? ({ formData }) => parseWithZod(formData, { schema })
: undefined,
});
const items = fields.items.getFieldList();
return (
<FormProvider context={form.context}>
<Form method="post" {...getFormProps(form)}>
<FormStateInput formId={form.id} />
<Playground title="Nested list" result={lastResult}>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-y-6">
{items.map((item) => {
const fieldset = item.getFieldset();
const options = fieldset.options.getFieldList();
return (
<div key={item.key}>
<div className="mt-4">
Options
{options.map((option, index) => (
<div
key={option.key}
className="flex flex-nowrap gap-2"
>
<Field label={`Option #${index + 1}`} meta={option}>
<input
{...getInputProps(option, { type: 'text' })}
/>
</Field>
<button
{...form.remove.getButtonProps({
name: fieldset.options.name,
index,
})}
>
Delete option
</button>
</div>
))}
</div>
<button
{...form.insert.getButtonProps({
name: fieldset.options.name,
})}
>
Add option
</button>
</div>
);
})}
<button
{...form.insert.getButtonProps({
name: fields.items.name,
defaultValue: {
options: [''],
},
})}
>
Add item
</button>
</div>
</div>
</Playground>
</Form>
</FormProvider>
);
}