A daily game
at main 159 lines 4.6 kB view raw
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}