Forms

React example: Log pages form

This selection is invalid
1import { Input, Select } from '@components/Form'
2import { FormProvider, useForm } from 'react-hook-form'
3import { zodResolver } from '@hookform/resolvers/zod';
4import { z } from 'zod'
5
6const LogPagesFormSchema = z.object({
7  languageCode: z.string(),
8  activity: z.number(),
9  amount: z.number().positive(),
10  unit: z.number(),
11  tags: z
12    .array(z.string())
13    .min(1, 'Must select at least one tag')
14    .max(3, 'Must select three or fewer'),
15  description: z.string().optional(),
16})
17
18const LogPagesForm = () => {
19  const methods = useForm({
20    resolver: zodResolver(LogPagesFormSchema),
21  })
22  const onSubmit = (data: any) => console.log(data, 'submitted')
23
24  const languages = [
25    { value: 'jpa', label: 'Japanese' },
26    { value: 'zho', label: 'Chinese' },
27    { value: 'kor', label: 'Korean' },
28  ]
29  const units = [
30    { value: '1', label: 'Pages' },
31    { value: '2', label: 'Comic pages' },
32  ]
33  const tags = ['Book', 'Ebook', 'Fiction', 'Non-fiction', 'Web page', 'Lyric']
34  const activities = [
35    { value: '1', label: 'Reading' },
36    { value: '2', label: 'Listening' },
37    { value: '3', label: 'Speaking' },
38    { value: '4', label: 'Writing' },
39  ]
40
41  return (
42    <FormProvider {...methods}>
43      <form
44        onSubmit={methods.handleSubmit(onSubmit)}
45        className="v-stack spaced"
46      >
47        <Select name="languageCode" label="Language" values={languages} />
48        <Select
49          name="activity"
50          label="Activity"
51          values={activities}
52          options={{ valueAsNumber: true }}
53        />
54        <div className="h-stack spaced">
55          <div className="flex-grow">
56            <Input
57              name="amount"
58              label="Amount"
59              type="number"
60              defaultValue={0}
61              options={{ valueAsNumber: true }}
62              min={0}
63            />
64          </div>
65          <div className="min-w-[150px]">
66            <Select
67              name="unit"
68              label="Unit"
69              values={units}
70              options={{ valueAsNumber: true }}
71            />
72          </div>
73        </div>
74        <AutocompleteMultiInput
75          name="tags"
76          label="Tags"
77          options={tags}
78          match={(option, query) =>
79            option
80              .toLowerCase()
81              .replace(/[^a-zA-Z0-9]/g, '')
82              .includes(query.toLowerCase())
83          }
84          getIdForOption={option => option}
85          format={option => option}
86        />
87        <Input
88          name="description"
89          label="Description"
90          type="text"
91          placeholder="e.g. One Piece volume 45"
92        />
93        <button
94          type="submit"
95          className="btn primary"
96          disabled={methods.formState.isSubmitting}
97        >
98          Save changes
99        </button>
100      </form>
101    </FormProvider>
102  )
103}

React example: Autocomplete

1import { AutocompleteInput, AutocompleteMultiInput } from '@components/Form'
2import { FormProvider, useForm } from 'react-hook-form'
3
4
5const AutocompleteForm = () => {
6  const methods = useForm()
7  const onSubmit = (data: any) => console.log(data, 'submitted')
8
9  const tags = ['Book', 'Ebook', 'Fiction', 'Non-fiction', 'Web page', 'Lyric']
10  const activities = [
11    { id: 1, name: 'Reading' },
12    { id: 2, name: 'Listening' },
13    { id: 3, name: 'Speaking' },
14    { id: 4, name: 'Writing' },
15  ]
16
17  return (
18    <FormProvider {...methods}>
19      <form
20        onSubmit={methods.handleSubmit(onSubmit)}
21        className="v-stack spaced"
22      >
23        <AutocompleteInput
24          name="tags"
25          label="Tags"
26          options={tags}
27          match={(option, query) =>
28            option
29              .toLowerCase()
30              .replace(/[^a-zA-Z0-9]/g, '')
31              .includes(query.toLowerCase())
32          }
33          format={option => option}
34        />
35        <AutocompleteMultiInput
36          name="activities"
37          label="Activities"
38          options={activities}
39          match={(option, query) =>
40            option.name
41              .toLowerCase()
42              .replace(/[^a-zA-Z0-9]/g, '')
43              .includes(query.toLowerCase())
44          }
45          getIdForOption={option => option.id}
46          format={option => option.name}
47        />
48        <button
49          type="submit"
50          className="btn primary"
51          disabled={methods.formState.isSubmitting}
52        >
53          Submit
54        </button>
55      </form>
56    </FormProvider>
57  )
58}

React example: Compose blog post form

This input is invalid
1import { Checkbox, Input, TextArea } from '@components/Form'
2import { FormProvider, useForm } from 'react-hook-form'
3
4const ComposeBlogPostForm = () => {
5  const methods = useForm()
6  const onSubmit = (data: any) => console.log(data, 'submitted')
7
8  return (
9    <FormProvider {...methods}>
10      <form
11        onSubmit={methods.handleSubmit(onSubmit)}
12        className="v-stack spaced"
13      >
14        <Input
15          name="title"
16          label="Title"
17          type="text"
18          options={{
19            required: true,
20          }}
21        />
22        <TextArea
23          name="content"
24          label="Content"
25          options={{
26            required: true,
27          }}
28        />
29        <Input
30          name="publishedAt"
31          label="Published at"
32          type="date"
33          options={{
34            required: true,
35            valueAsDate: true,
36          }}
37        />
38        <Checkbox name="isPublished" label="Published" />
39        <button
40          type="submit"
41          className="btn primary"
42          disabled={methods.formState.isSubmitting}
43        >
44          Save
45        </button>
46      </form>
47    </FormProvider>
48  )
49}

React example: other elements

Card typeThis selection is invalid
1import { RadioSelect } from '@components/Form'
2import { FormProvider, useForm } from 'react-hook-form'
3
4const MiscForm = () => {
5  const methods = useForm()
6  const onSubmit = (data: any) => console.log(data, 'submitted')
7
8  return (
9    <FormProvider {...methods}>
10      <form
11        onSubmit={methods.handleSubmit(onSubmit)}
12        className="v-stack spaced"
13      >
14        <RadioSelect
15          name="cardType"
16          label="Card type"
17          options={{
18            required: true,
19            valueAsNumber: true,
20          }}
21          values={[
22            { value: '1', label: 'Sentence card' },
23            { value: '2', label: 'Vocab card' },
24          ]}
25        />
26        <Input
27          name="datetime"
28          label="Datetime"
29          type="datetime-local"
30          options={{
31            required: true,
32            valueAsDate: true,
33          }}
34        />
35        <Input
36          name="time"
37          label="Time"
38          type="time"
39          options={{
40            required: true,
41          }}
42        />
43        <Input
44          name="week"
45          label="Week"
46          type="week"
47          options={{
48            required: true,
49          }}
50        />
51        <Input
52          name="month"
53          label="Month"
54          type="month"
55          options={{
56            required: true,
57          }}
58        />
59        <Input
60          name="color"
61          label="Color"
62          type="color"
63          options={{
64            required: true,
65          }}
66        />
67        <Input
68          name="email"
69          label="Email"
70          type="email"
71          options={{
72            required: true,
73          }}
74        />
75        <Input
76          name="file"
77          label="File"
78          type="file"
79          options={{
80            required: true,
81          }}
82        />
83        <Input
84          name="range"
85          label="Range"
86          type="range"
87          options={{
88            required: true,
89          }}
90        />
91        <Input
92          name="search"
93          label="Search"
94          type="search"
95          options={{
96            required: true,
97          }}
98        />
99        <Input
100          name="tel"
101          label="tel"
102          type="tel"
103          options={{
104            required: true,
105          }}
106        />
107        <Input
108          name="url"
109          label="url"
110          type="url"
111          options={{
112            required: true,
113          }}
114        />
115        <button
116          type="submit"
117          className="btn primary"
118          disabled={methods.formState.isSubmitting}
119        >
120          Submit
121        </button>
122      </form>
123    </FormProvider>
124  )
125}

Basic elements

Choose a color
1<form className="v-stack spaced">
2<label className="label">
3  <span className="label-text">First name</span>
4  <input className="input" type="text" placeholder="John Doe" />
5</label>
6<label className="label">
7  <span className="label-text">Message</span>
8  <textarea className="input" placeholder="Dolor sit amet..." />
9</label>
10<label className="label">
11  <span className="label-text">Choose a color</span>
12  <select className="input">
13    <option value="#ff0000">Red</option>
14    <option value="#00ff00">Green</option>
15    <option value="#0000ff">Blue</option>
16  </select>
17</label>
18<div>
19  <span className="label-text">Choose a color</span>
20  <div className="v-stack">
21    <label className="label-inline">
22      <input type="radio" name="color-radio" className="input" />
23      <span>Red</span>
24    </label>
25    <label className="label-inline">
26      <input type="radio" name="color-radio" />
27      <span>Green</span>
28    </label>
29    <label className="label-inline">
30      <input type="radio" name="color-radio" />
31      <span>Blue</span>
32    </label>
33  </div>
34</div>
35<label className="label error">
36  <span className="label-text">First name</span>
37  <input type="text" placeholder="John Doe" className="input" />
38  <span className="error">
39    Should be at least 1 character long
40  </span>
41</label>
42</form>