Skip to content

Commit 0482832

Browse files
committed
fix: updating form example
1 parent f3f3a96 commit 0482832

File tree

2 files changed

+642
-139
lines changed

2 files changed

+642
-139
lines changed

templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs

Lines changed: 332 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'
22
import { useForm } from '@tanstack/react-form'
3+
import type { ValidationError } from "@tanstack/react-form";
34

45
<% if (codeRouter) { %>
56
import type { RootRoute } from '@tanstack/react-router'
@@ -9,48 +10,346 @@ export const Route = createFileRoute('/demo/form')({
910
})
1011
<% } %>
1112

13+
type FormSchema = {
14+
fullName: string;
15+
email: string;
16+
address: {
17+
street: string;
18+
city: string;
19+
state: string;
20+
zipCode: string;
21+
country: string;
22+
};
23+
phone: string;
24+
};
25+
26+
function FieldWrapper({
27+
children,
28+
errors,
29+
label,
30+
}: {
31+
children: React.ReactNode;
32+
errors: ValidationError[];
33+
label: string;
34+
}) {
35+
return (
36+
<div>
37+
<label htmlFor={label} className="block font-bold mb-1 text-xl">
38+
{label}
39+
</label>
40+
{children}
41+
{errors.length ? (
42+
<div className="text-red-500 mt-1 font-bold">{errors.join(", ")}</div>
43+
) : null}
44+
</div>
45+
);
46+
}
47+
1248
function FormDemo() {
13-
const form = useForm({
49+
const form = useForm<FormSchema>({
1450
defaultValues: {
15-
fullName: '',
51+
fullName: "",
52+
email: "",
53+
address: {
54+
street: "",
55+
city: "",
56+
state: "",
57+
zipCode: "",
58+
country: "",
59+
},
60+
phone: "",
1661
},
1762
onSubmit: async ({ value }) => {
18-
console.log(value)
63+
console.log(value);
64+
// Show success message
65+
alert("Form submitted successfully!");
1966
},
20-
})
67+
});
2168

2269
return (
23-
<div className="p-4">
24-
<form
25-
onSubmit={(e) => {
26-
e.preventDefault()
27-
e.stopPropagation()
28-
form.handleSubmit()
29-
}}
30-
>
31-
<div>
32-
<form.Field
33-
name="fullName"
34-
children={(field) => (
35-
<input
36-
name={field.name}
37-
value={field.state.value}
38-
onBlur={field.handleBlur}
39-
onChange={(e) => field.handleChange(e.target.value)}
40-
className="block w-full px-4 py-2 text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500"
41-
/>
42-
)}
43-
/>
44-
</div>
45-
<button
46-
type="submit"
47-
className="mt-4 inline-flex items-center px-6 py-3 border border-transparent shadow-sm text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
70+
<div
71+
className="flex items-center justify-center min-h-screen bg-gradient-to-br from-purple-100 to-blue-100 p-4 text-white"
72+
style={{
73+
backgroundImage:
74+
"radial-gradient(50% 50% at 5% 40%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)",
75+
}}
76+
>
77+
<div className="w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10">
78+
<form
79+
onSubmit={(e) => {
80+
e.preventDefault();
81+
e.stopPropagation();
82+
form.handleSubmit();
83+
}}
84+
className="space-y-6"
4885
>
49-
Submit
50-
</button>
51-
</form>
86+
{/* Full Name Field */}
87+
<div>
88+
<form.Field
89+
name="fullName"
90+
validators={{
91+
onBlur: ({ value }) => {
92+
if (value.trim().length === 0) {
93+
return "Full name is required";
94+
}
95+
if (value.length < 3) {
96+
return "Name must be at least 3 characters";
97+
}
98+
return undefined;
99+
},
100+
}}
101+
children={(field) => (
102+
<FieldWrapper
103+
label="Full Name"
104+
errors={field.state.meta.errors}
105+
>
106+
<input
107+
id="fullName"
108+
name="fullName"
109+
value={field.state.value}
110+
onBlur={field.handleBlur}
111+
onChange={(e) => field.handleChange(e.target.value)}
112+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
113+
/>
114+
</FieldWrapper>
115+
)}
116+
/>
117+
</div>
118+
119+
{/* Email Field */}
120+
<div>
121+
<form.Field
122+
name="email"
123+
validators={{
124+
onBlur: ({ value }) => {
125+
if (!value || value.trim().length === 0) {
126+
return "Email is required";
127+
}
128+
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
129+
return "Invalid email address";
130+
}
131+
return undefined;
132+
},
133+
}}
134+
children={(field) => (
135+
<FieldWrapper
136+
label="Email Address"
137+
errors={field.state.meta.errors}
138+
>
139+
<input
140+
id="email"
141+
name="email"
142+
type="email"
143+
value={field.state.value}
144+
onBlur={field.handleBlur}
145+
onChange={(e) => field.handleChange(e.target.value)}
146+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
147+
/>
148+
</FieldWrapper>
149+
)}
150+
/>
151+
</div>
152+
153+
{/* Street Address */}
154+
<div>
155+
<form.Field
156+
name="address.street"
157+
validators={{
158+
onBlur: ({ value }) => {
159+
if (!value || value.trim().length === 0) {
160+
return "Street address is required";
161+
}
162+
return undefined;
163+
},
164+
}}
165+
children={(field) => (
166+
<FieldWrapper
167+
label="Street Address"
168+
errors={field.state.meta.errors}
169+
>
170+
<input
171+
id="street"
172+
name="street"
173+
value={field.state.value}
174+
onBlur={field.handleBlur}
175+
onChange={(e) => field.handleChange(e.target.value)}
176+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
177+
/>
178+
</FieldWrapper>
179+
)}
180+
/>
181+
</div>
182+
183+
{/* City, State, Zip in a row */}
184+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
185+
{/* City */}
186+
<form.Field
187+
name="address.city"
188+
validators={{
189+
onBlur: ({ value }) => {
190+
if (!value || value.trim().length === 0) {
191+
return "City is required";
192+
}
193+
return undefined;
194+
},
195+
}}
196+
children={(field) => (
197+
<FieldWrapper label="City" errors={field.state.meta.errors}>
198+
<input
199+
id="city"
200+
name="city"
201+
value={field.state.value}
202+
onBlur={field.handleBlur}
203+
onChange={(e) => field.handleChange(e.target.value)}
204+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
205+
/>
206+
</FieldWrapper>
207+
)}
208+
/>
209+
210+
{/* State */}
211+
<form.Field
212+
name="address.state"
213+
validators={{
214+
onBlur: ({ value }) => {
215+
if (!value || value.trim().length === 0) {
216+
return "State is required";
217+
}
218+
return undefined;
219+
},
220+
}}
221+
children={(field) => (
222+
<FieldWrapper label="State" errors={field.state.meta.errors}>
223+
<input
224+
id="state"
225+
name="state"
226+
value={field.state.value}
227+
onBlur={field.handleBlur}
228+
onChange={(e) => field.handleChange(e.target.value)}
229+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
230+
/>
231+
</FieldWrapper>
232+
)}
233+
/>
234+
235+
{/* Zip Code */}
236+
<form.Field
237+
name="address.zipCode"
238+
validators={{
239+
onBlur: ({ value }) => {
240+
if (!value || value.trim().length === 0) {
241+
return "Zip code is required";
242+
}
243+
if (!/^\d{5}(-\d{4})?$/.test(value)) {
244+
return "Invalid zip code format";
245+
}
246+
return undefined;
247+
},
248+
}}
249+
children={(field) => (
250+
<FieldWrapper label="Zip Code" errors={field.state.meta.errors}>
251+
<input
252+
id="zipCode"
253+
name="zipCode"
254+
value={field.state.value}
255+
onBlur={field.handleBlur}
256+
onChange={(e) => field.handleChange(e.target.value)}
257+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
258+
/>
259+
</FieldWrapper>
260+
)}
261+
/>
262+
</div>
263+
264+
{/* Country */}
265+
<div>
266+
<form.Field
267+
name="address.country"
268+
validators={{
269+
onBlur: ({ value }) => {
270+
if (!value || value.trim().length === 0) {
271+
return "Country is required";
272+
}
273+
return undefined;
274+
},
275+
}}
276+
children={(field) => (
277+
<FieldWrapper label="Country" errors={field.state.meta.errors}>
278+
<select
279+
id="country"
280+
name="country"
281+
value={field.state.value}
282+
onBlur={field.handleBlur}
283+
onChange={(e) => field.handleChange(e.target.value)}
284+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
285+
>
286+
<option value="">Select a country</option>
287+
<option value="US">United States</option>
288+
<option value="CA">Canada</option>
289+
<option value="UK">United Kingdom</option>
290+
<option value="AU">Australia</option>
291+
<option value="DE">Germany</option>
292+
<option value="FR">France</option>
293+
<option value="JP">Japan</option>
294+
</select>
295+
</FieldWrapper>
296+
)}
297+
/>
298+
</div>
299+
300+
{/* Phone Number */}
301+
<div>
302+
<form.Field
303+
name="phone"
304+
validators={{
305+
onBlur: ({ value }) => {
306+
if (!value || value.trim().length === 0) {
307+
return "Phone number is required";
308+
}
309+
if (
310+
!/^(\+\d{1,3})?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/.test(
311+
value
312+
)
313+
) {
314+
return "Invalid phone number format";
315+
}
316+
return undefined;
317+
},
318+
}}
319+
children={(field) => (
320+
<FieldWrapper
321+
label="Phone Number"
322+
errors={field.state.meta.errors}
323+
>
324+
<input
325+
id="phone"
326+
name="phone"
327+
type="tel"
328+
value={field.state.value}
329+
onBlur={field.handleBlur}
330+
onChange={(e) => field.handleChange(e.target.value)}
331+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
332+
placeholder="(123) 456-7890"
333+
/>
334+
</FieldWrapper>
335+
)}
336+
/>
337+
</div>
338+
339+
{/* Submit Button */}
340+
<div className="flex justify-end">
341+
<button
342+
type="submit"
343+
disabled={form.state.isSubmitting}
344+
className="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors disabled:opacity-50"
345+
>
346+
{form.state.isSubmitting ? "Submitting..." : "Submit"}
347+
</button>
348+
</div>
349+
</form>
350+
</div>
52351
</div>
53-
)
352+
);
54353
}
55354

56355
<% if (codeRouter) { %>

0 commit comments

Comments
 (0)