A daily game
1import { zodResolver } from "@hookform/resolvers/zod";
2import { SubmitHandler, useForm } from "react-hook-form";
3import z from "zod";
4import {
5 Form,
6 FormControl,
7 FormField,
8 FormItem,
9 FormLabel,
10 FormMessage,
11} from "../ui/form";
12import { Input } from "../ui/input";
13
14const oneEmoji = z
15 .string()
16 .refine((val) => val.length === 0 || [...val].length === 1, {
17 error: "Please enter a single emoji",
18 });
19
20const gridDimension = (dir: "row" | "column") =>
21 z.coerce.number<number>().min(2, { error: `Minimum 2 ${dir}s` });
22
23type FormData = z.infer<typeof formSchema>;
24const formSchema = z.object({
25 width: gridDimension("column"),
26 height: gridDimension("row"),
27 variants: z.array(oneEmoji).min(2).max(4),
28});
29
30interface Props {
31 defaultValues: FormData;
32 onSubmit: SubmitHandler<FormData>;
33 buttons?: () => React.ReactNode;
34}
35
36export function PreferencesForm({ defaultValues, onSubmit, buttons }: Props) {
37 const form = useForm<z.infer<typeof formSchema>>({
38 resolver: zodResolver(formSchema),
39 defaultValues,
40 });
41
42 return (
43 <Form {...form}>
44 <form
45 id="preferences-form"
46 onSubmit={form.handleSubmit((formData) => {
47 formData.variants = formData.variants.filter(Boolean);
48 return onSubmit(formData);
49 })}
50 >
51 <div className="grid gap-4 grid-cols-4">
52 <FormField
53 control={form.control}
54 name="width"
55 render={({ field }) => (
56 <FormItem className="col-span-2">
57 <FormLabel>Columns</FormLabel>
58 <FormControl>
59 <Input
60 type="number"
61 onFocus={(e) => e.target.select()}
62 {...field}
63 />
64 </FormControl>
65 <FormMessage />
66 </FormItem>
67 )}
68 />
69 <FormField
70 control={form.control}
71 name="height"
72 render={({ field }) => (
73 <FormItem className="col-span-2">
74 <FormLabel>Rows</FormLabel>
75 <FormControl>
76 <Input
77 type="number"
78 onFocus={(e) => e.target.select()}
79 {...field}
80 />
81 </FormControl>
82 <FormMessage />
83 </FormItem>
84 )}
85 />
86 <FormField
87 control={form.control}
88 name="variants.0"
89 render={({ field }) => (
90 <FormItem>
91 <FormLabel>Left</FormLabel>
92 <FormControl>
93 <Input
94 {...field}
95 onFocus={(e) => e.target.select()}
96 className="text-center text-2xl"
97 />
98 </FormControl>
99 <FormMessage />
100 </FormItem>
101 )}
102 />
103 <FormField
104 control={form.control}
105 name="variants.1"
106 render={({ field }) => (
107 <FormItem>
108 <FormLabel>Top</FormLabel>
109 <FormControl>
110 <Input
111 {...field}
112 onFocus={(e) => e.target.select()}
113 className="text-center text-2xl"
114 />
115 </FormControl>
116 <FormMessage />
117 </FormItem>
118 )}
119 />
120 <FormField
121 control={form.control}
122 name="variants.2"
123 render={({ field }) => (
124 <FormItem>
125 <FormLabel>Right</FormLabel>
126 <FormControl>
127 <Input
128 {...field}
129 onFocus={(e) => e.target.select()}
130 className="text-center text-2xl"
131 />
132 </FormControl>
133 <FormMessage />
134 </FormItem>
135 )}
136 />
137 <FormField
138 control={form.control}
139 name="variants.3"
140 render={({ field }) => (
141 <FormItem>
142 <FormLabel>Bottom</FormLabel>
143 <FormControl>
144 <Input
145 {...field}
146 onFocus={(e) => e.target.select()}
147 className="text-center text-2xl"
148 />
149 </FormControl>
150 <FormMessage />
151 </FormItem>
152 )}
153 />
154 <div className="col-span-4">{buttons?.()}</div>
155 </div>
156 </form>
157 </Form>
158 );
159}