create-start-app 0.8.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/create-app.js +7 -7
- package/package.json +1 -2
- package/src/create-app.ts +12 -8
- package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx.ejs +300 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +14 -11
- package/templates/react/add-on/form/info.json +9 -0
- package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx +0 -121
package/dist/create-app.js
CHANGED
|
@@ -290,7 +290,7 @@ export async function createApp(options, { silent = false, environment, }) {
|
|
|
290
290
|
await copyFilesRecursively(environment, addOnDir, targetDir, async (file, targetFileName) => templateFile(addOnDir, file, targetFileName));
|
|
291
291
|
}
|
|
292
292
|
if (addOn.command) {
|
|
293
|
-
await environment.execute(addOn.command.command, addOn.command.args || [], targetDir);
|
|
293
|
+
await environment.execute(addOn.command.command, addOn.command.args || [], resolve(targetDir));
|
|
294
294
|
}
|
|
295
295
|
s?.stop(`${addOn.name} setup complete`);
|
|
296
296
|
}
|
|
@@ -306,8 +306,8 @@ export async function createApp(options, { silent = false, environment, }) {
|
|
|
306
306
|
}
|
|
307
307
|
if (shadcnComponents.size > 0) {
|
|
308
308
|
s?.start(`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`);
|
|
309
|
-
await environment.execute('npx', ['shadcn@canary', 'add', ...shadcnComponents], targetDir);
|
|
310
|
-
s?.stop(`Installed
|
|
309
|
+
await environment.execute('npx', ['shadcn@canary', 'add', '--force', ...shadcnComponents], resolve(targetDir));
|
|
310
|
+
s?.stop(`Installed additional shadcn components`);
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
const integrations = [];
|
|
@@ -400,7 +400,7 @@ export async function createApp(options, { silent = false, environment, }) {
|
|
|
400
400
|
await templateFile(templateDirBase, 'README.md.ejs');
|
|
401
401
|
// Install dependencies
|
|
402
402
|
s?.start(`Installing dependencies via ${options.packageManager}...`);
|
|
403
|
-
await environment.execute(options.packageManager, ['install'], targetDir);
|
|
403
|
+
await environment.execute(options.packageManager, ['install'], resolve(targetDir));
|
|
404
404
|
s?.stop(`Installed dependencies`);
|
|
405
405
|
if (warnings.length > 0) {
|
|
406
406
|
if (!silent) {
|
|
@@ -412,17 +412,17 @@ export async function createApp(options, { silent = false, environment, }) {
|
|
|
412
412
|
switch (options.packageManager) {
|
|
413
413
|
case 'pnpm':
|
|
414
414
|
// pnpm automatically forwards extra arguments
|
|
415
|
-
await environment.execute(options.packageManager, ['run', 'check', '--fix'], targetDir);
|
|
415
|
+
await environment.execute(options.packageManager, ['run', 'check', '--fix'], resolve(targetDir));
|
|
416
416
|
break;
|
|
417
417
|
default:
|
|
418
|
-
await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], targetDir);
|
|
418
|
+
await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], resolve(targetDir));
|
|
419
419
|
break;
|
|
420
420
|
}
|
|
421
421
|
s?.stop(`Applied toolchain ${options.toolchain}...`);
|
|
422
422
|
}
|
|
423
423
|
if (options.git) {
|
|
424
424
|
s?.start(`Initializing git repository...`);
|
|
425
|
-
await environment.execute('git', ['init'], targetDir);
|
|
425
|
+
await environment.execute('git', ['init'], resolve(targetDir));
|
|
426
426
|
s?.stop(`Initialized git repository`);
|
|
427
427
|
}
|
|
428
428
|
environment.finishRun();
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-start-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Tanstack Application Builder",
|
|
5
5
|
"bin": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"scripts": {},
|
|
8
7
|
"repository": {
|
|
9
8
|
"type": "git",
|
|
10
9
|
"url": "https://github.com/TanStack/create-tsrouter-app.git"
|
package/src/create-app.ts
CHANGED
|
@@ -463,7 +463,7 @@ export async function createApp(
|
|
|
463
463
|
await environment.execute(
|
|
464
464
|
addOn.command.command,
|
|
465
465
|
addOn.command.args || [],
|
|
466
|
-
targetDir,
|
|
466
|
+
resolve(targetDir),
|
|
467
467
|
)
|
|
468
468
|
}
|
|
469
469
|
|
|
@@ -487,10 +487,10 @@ export async function createApp(
|
|
|
487
487
|
)
|
|
488
488
|
await environment.execute(
|
|
489
489
|
'npx',
|
|
490
|
-
['shadcn@canary', 'add', ...shadcnComponents],
|
|
491
|
-
targetDir,
|
|
490
|
+
['shadcn@canary', 'add', '--force', ...shadcnComponents],
|
|
491
|
+
resolve(targetDir),
|
|
492
492
|
)
|
|
493
|
-
s?.stop(`Installed
|
|
493
|
+
s?.stop(`Installed additional shadcn components`)
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
|
|
@@ -658,7 +658,11 @@ export async function createApp(
|
|
|
658
658
|
|
|
659
659
|
// Install dependencies
|
|
660
660
|
s?.start(`Installing dependencies via ${options.packageManager}...`)
|
|
661
|
-
await environment.execute(
|
|
661
|
+
await environment.execute(
|
|
662
|
+
options.packageManager,
|
|
663
|
+
['install'],
|
|
664
|
+
resolve(targetDir),
|
|
665
|
+
)
|
|
662
666
|
s?.stop(`Installed dependencies`)
|
|
663
667
|
|
|
664
668
|
if (warnings.length > 0) {
|
|
@@ -675,14 +679,14 @@ export async function createApp(
|
|
|
675
679
|
await environment.execute(
|
|
676
680
|
options.packageManager,
|
|
677
681
|
['run', 'check', '--fix'],
|
|
678
|
-
targetDir,
|
|
682
|
+
resolve(targetDir),
|
|
679
683
|
)
|
|
680
684
|
break
|
|
681
685
|
default:
|
|
682
686
|
await environment.execute(
|
|
683
687
|
options.packageManager,
|
|
684
688
|
['run', 'check', '--', '--fix'],
|
|
685
|
-
targetDir,
|
|
689
|
+
resolve(targetDir),
|
|
686
690
|
)
|
|
687
691
|
break
|
|
688
692
|
}
|
|
@@ -691,7 +695,7 @@ export async function createApp(
|
|
|
691
695
|
|
|
692
696
|
if (options.git) {
|
|
693
697
|
s?.start(`Initializing git repository...`)
|
|
694
|
-
await environment.execute('git', ['init'], targetDir)
|
|
698
|
+
await environment.execute('git', ['init'], resolve(targetDir))
|
|
695
699
|
s?.stop(`Initialized git repository`)
|
|
696
700
|
}
|
|
697
701
|
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { useStore } from '@tanstack/react-form'
|
|
2
|
+
|
|
3
|
+
import { useFieldContext, useFormContext } from '../hooks/demo.form-context'
|
|
4
|
+
<% if (addOnEnabled.shadcn) { %>
|
|
5
|
+
import { Button } from '@/components/ui/button'
|
|
6
|
+
import { Input } from '@/components/ui/input'
|
|
7
|
+
import { Textarea as ShadcnTextarea } from '@/components/ui/textarea'
|
|
8
|
+
import * as ShadcnSelect from '@/components/ui/select'
|
|
9
|
+
import { Slider as ShadcnSlider } from '@/components/ui/slider'
|
|
10
|
+
import { Switch as ShadcnSwitch } from '@/components/ui/switch'
|
|
11
|
+
import { Label } from '@/components/ui/label'
|
|
12
|
+
|
|
13
|
+
export function SubscribeButton({ label }: { label: string }) {
|
|
14
|
+
const form = useFormContext()
|
|
15
|
+
return (
|
|
16
|
+
<form.Subscribe selector={(state) => state.isSubmitting}>
|
|
17
|
+
{(isSubmitting) => (
|
|
18
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
19
|
+
{label}
|
|
20
|
+
</Button>
|
|
21
|
+
)}
|
|
22
|
+
</form.Subscribe>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ErrorMessages({
|
|
27
|
+
errors,
|
|
28
|
+
}: {
|
|
29
|
+
errors: Array<string | { message: string }>
|
|
30
|
+
}) {
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
{errors.map((error) => (
|
|
34
|
+
<div
|
|
35
|
+
key={typeof error === 'string' ? error : error.message}
|
|
36
|
+
className="text-red-500 mt-1 font-bold"
|
|
37
|
+
>
|
|
38
|
+
{typeof error === 'string' ? error : error.message}
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
41
|
+
</>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function TextField({
|
|
46
|
+
label,
|
|
47
|
+
placeholder,
|
|
48
|
+
}: {
|
|
49
|
+
label: string
|
|
50
|
+
placeholder?: string
|
|
51
|
+
}) {
|
|
52
|
+
const field = useFieldContext<string>()
|
|
53
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
<Label htmlFor={label} className="mb-2 text-xl font-bold">
|
|
58
|
+
{label}
|
|
59
|
+
</Label>
|
|
60
|
+
<Input
|
|
61
|
+
value={field.state.value}
|
|
62
|
+
placeholder={placeholder}
|
|
63
|
+
onBlur={field.handleBlur}
|
|
64
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
65
|
+
/>
|
|
66
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function TextArea({
|
|
72
|
+
label,
|
|
73
|
+
rows = 3,
|
|
74
|
+
}: {
|
|
75
|
+
label: string
|
|
76
|
+
rows?: number
|
|
77
|
+
}) {
|
|
78
|
+
const field = useFieldContext<string>()
|
|
79
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div>
|
|
83
|
+
<Label htmlFor={label} className="mb-2 text-xl font-bold">
|
|
84
|
+
{label}
|
|
85
|
+
</Label>
|
|
86
|
+
<ShadcnTextarea
|
|
87
|
+
id={label}
|
|
88
|
+
value={field.state.value}
|
|
89
|
+
onBlur={field.handleBlur}
|
|
90
|
+
rows={rows}
|
|
91
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
92
|
+
/>
|
|
93
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function Select({
|
|
99
|
+
label,
|
|
100
|
+
values,
|
|
101
|
+
placeholder,
|
|
102
|
+
}: {
|
|
103
|
+
label: string
|
|
104
|
+
values: Array<{ label: string; value: string }>
|
|
105
|
+
placeholder?: string
|
|
106
|
+
}) {
|
|
107
|
+
const field = useFieldContext<string>()
|
|
108
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div>
|
|
112
|
+
<ShadcnSelect.Select
|
|
113
|
+
name={field.name}
|
|
114
|
+
value={field.state.value}
|
|
115
|
+
onValueChange={(value) => field.handleChange(value)}
|
|
116
|
+
>
|
|
117
|
+
<ShadcnSelect.SelectTrigger className="w-full">
|
|
118
|
+
<ShadcnSelect.SelectValue placeholder={placeholder} />
|
|
119
|
+
</ShadcnSelect.SelectTrigger>
|
|
120
|
+
<ShadcnSelect.SelectContent>
|
|
121
|
+
<ShadcnSelect.SelectGroup>
|
|
122
|
+
<ShadcnSelect.SelectLabel>{label}</ShadcnSelect.SelectLabel>
|
|
123
|
+
{values.map((value) => (
|
|
124
|
+
<ShadcnSelect.SelectItem key={value.value} value={value.value}>
|
|
125
|
+
{value.label}
|
|
126
|
+
</ShadcnSelect.SelectItem>
|
|
127
|
+
))}
|
|
128
|
+
</ShadcnSelect.SelectGroup>
|
|
129
|
+
</ShadcnSelect.SelectContent>
|
|
130
|
+
</ShadcnSelect.Select>
|
|
131
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
132
|
+
</div>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function Slider({ label }: { label: string }) {
|
|
137
|
+
const field = useFieldContext<number>()
|
|
138
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div>
|
|
142
|
+
<Label htmlFor={label} className="mb-2 text-xl font-bold">
|
|
143
|
+
{label}
|
|
144
|
+
</Label>
|
|
145
|
+
<ShadcnSlider
|
|
146
|
+
id={label}
|
|
147
|
+
onBlur={field.handleBlur}
|
|
148
|
+
value={[field.state.value]}
|
|
149
|
+
onValueChange={(value) => field.handleChange(value[0])}
|
|
150
|
+
/>
|
|
151
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
152
|
+
</div>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function Switch({ label }: { label: string }) {
|
|
157
|
+
const field = useFieldContext<boolean>()
|
|
158
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
<div className="flex items-center gap-2">
|
|
163
|
+
<ShadcnSwitch
|
|
164
|
+
id={label}
|
|
165
|
+
onBlur={field.handleBlur}
|
|
166
|
+
checked={field.state.value}
|
|
167
|
+
onCheckedChange={(checked) => field.handleChange(checked)}
|
|
168
|
+
/>
|
|
169
|
+
<Label htmlFor={label}>{label}</Label>
|
|
170
|
+
</div>
|
|
171
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
172
|
+
</div>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
<% } else { %>
|
|
177
|
+
export function SubscribeButton({ label }: { label: string }) {
|
|
178
|
+
const form = useFormContext()
|
|
179
|
+
return (
|
|
180
|
+
<form.Subscribe selector={(state) => state.isSubmitting}>
|
|
181
|
+
{(isSubmitting) => (
|
|
182
|
+
<button
|
|
183
|
+
type="submit"
|
|
184
|
+
disabled={isSubmitting}
|
|
185
|
+
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"
|
|
186
|
+
>
|
|
187
|
+
{label}
|
|
188
|
+
</button>
|
|
189
|
+
)}
|
|
190
|
+
</form.Subscribe>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function ErrorMessages({
|
|
195
|
+
errors,
|
|
196
|
+
}: {
|
|
197
|
+
errors: Array<string | { message: string }>
|
|
198
|
+
}) {
|
|
199
|
+
return (
|
|
200
|
+
<>
|
|
201
|
+
{errors.map((error) => (
|
|
202
|
+
<div
|
|
203
|
+
key={typeof error === 'string' ? error : error.message}
|
|
204
|
+
className="text-red-500 mt-1 font-bold"
|
|
205
|
+
>
|
|
206
|
+
{typeof error === 'string' ? error : error.message}
|
|
207
|
+
</div>
|
|
208
|
+
))}
|
|
209
|
+
</>
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function TextField({
|
|
214
|
+
label,
|
|
215
|
+
placeholder,
|
|
216
|
+
}: {
|
|
217
|
+
label: string
|
|
218
|
+
placeholder?: string
|
|
219
|
+
}) {
|
|
220
|
+
const field = useFieldContext<string>()
|
|
221
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div>
|
|
225
|
+
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
226
|
+
{label}
|
|
227
|
+
<input
|
|
228
|
+
value={field.state.value}
|
|
229
|
+
placeholder={placeholder}
|
|
230
|
+
onBlur={field.handleBlur}
|
|
231
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
232
|
+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
233
|
+
/>
|
|
234
|
+
</label>
|
|
235
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
236
|
+
</div>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function TextArea({
|
|
241
|
+
label,
|
|
242
|
+
rows = 3,
|
|
243
|
+
}: {
|
|
244
|
+
label: string
|
|
245
|
+
rows?: number
|
|
246
|
+
}) {
|
|
247
|
+
const field = useFieldContext<string>()
|
|
248
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div>
|
|
252
|
+
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
253
|
+
{label}
|
|
254
|
+
<textarea
|
|
255
|
+
value={field.state.value}
|
|
256
|
+
onBlur={field.handleBlur}
|
|
257
|
+
rows={rows}
|
|
258
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
259
|
+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
260
|
+
/>
|
|
261
|
+
</label>
|
|
262
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
263
|
+
</div>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function Select({
|
|
268
|
+
label,
|
|
269
|
+
values,
|
|
270
|
+
}: {
|
|
271
|
+
label: string
|
|
272
|
+
values: Array<{ label: string; value: string }>
|
|
273
|
+
placeholder?: string
|
|
274
|
+
}) {
|
|
275
|
+
const field = useFieldContext<string>()
|
|
276
|
+
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div>
|
|
280
|
+
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
281
|
+
{label}
|
|
282
|
+
</label>
|
|
283
|
+
<select
|
|
284
|
+
name={field.name}
|
|
285
|
+
value={field.state.value}
|
|
286
|
+
onBlur={field.handleBlur}
|
|
287
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
288
|
+
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
289
|
+
>
|
|
290
|
+
{values.map((value) => (
|
|
291
|
+
<option key={value.value} value={value.value}>
|
|
292
|
+
{value.label}
|
|
293
|
+
</option>
|
|
294
|
+
))}
|
|
295
|
+
</select>
|
|
296
|
+
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
297
|
+
</div>
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
<% } %>
|
|
@@ -153,18 +153,21 @@ function AddressForm() {
|
|
|
153
153
|
}}
|
|
154
154
|
>
|
|
155
155
|
{(field) => (
|
|
156
|
-
<field.Select
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
<field.Select
|
|
157
|
+
label="Country"
|
|
158
|
+
values={[
|
|
159
|
+
{ label: 'United States', value: 'US' },
|
|
160
|
+
{ label: 'Canada', value: 'CA' },
|
|
161
|
+
{ label: 'United Kingdom', value: 'UK' },
|
|
162
|
+
{ label: 'Australia', value: 'AU' },
|
|
163
|
+
{ label: 'Germany', value: 'DE' },
|
|
164
|
+
{ label: 'France', value: 'FR' },
|
|
165
|
+
{ label: 'Japan', value: 'JP' },
|
|
166
|
+
]}
|
|
167
|
+
placeholder="Select a country"
|
|
168
|
+
/>
|
|
166
169
|
)}
|
|
167
|
-
|
|
170
|
+
</form.AppField>
|
|
168
171
|
|
|
169
172
|
<form.AppField
|
|
170
173
|
name="phone"
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { useStore } from '@tanstack/react-form'
|
|
2
|
-
import { useFieldContext, useFormContext } from '../hooks/demo.form-context'
|
|
3
|
-
|
|
4
|
-
export function SubscribeButton({ label }: { label: string }) {
|
|
5
|
-
const form = useFormContext()
|
|
6
|
-
return (
|
|
7
|
-
<form.Subscribe selector={(state) => state.isSubmitting}>
|
|
8
|
-
{(isSubmitting) => (
|
|
9
|
-
<button
|
|
10
|
-
type="submit"
|
|
11
|
-
disabled={isSubmitting}
|
|
12
|
-
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"
|
|
13
|
-
>
|
|
14
|
-
{label}
|
|
15
|
-
</button>
|
|
16
|
-
)}
|
|
17
|
-
</form.Subscribe>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function ErrorMessages({
|
|
22
|
-
errors,
|
|
23
|
-
}: {
|
|
24
|
-
errors: Array<string | { message: string }>
|
|
25
|
-
}) {
|
|
26
|
-
return (
|
|
27
|
-
<>
|
|
28
|
-
{errors.map((error) => (
|
|
29
|
-
<div
|
|
30
|
-
key={typeof error === 'string' ? error : error.message}
|
|
31
|
-
className="text-red-500 mt-1 font-bold"
|
|
32
|
-
>
|
|
33
|
-
{typeof error === 'string' ? error : error.message}
|
|
34
|
-
</div>
|
|
35
|
-
))}
|
|
36
|
-
</>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function TextField({
|
|
41
|
-
label,
|
|
42
|
-
placeholder,
|
|
43
|
-
}: {
|
|
44
|
-
label: string
|
|
45
|
-
placeholder?: string
|
|
46
|
-
}) {
|
|
47
|
-
const field = useFieldContext<string>()
|
|
48
|
-
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<div>
|
|
52
|
-
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
53
|
-
{label}
|
|
54
|
-
<input
|
|
55
|
-
value={field.state.value}
|
|
56
|
-
placeholder={placeholder}
|
|
57
|
-
onBlur={field.handleBlur}
|
|
58
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
59
|
-
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
60
|
-
/>
|
|
61
|
-
</label>
|
|
62
|
-
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function TextArea({
|
|
68
|
-
label,
|
|
69
|
-
rows = 3,
|
|
70
|
-
}: {
|
|
71
|
-
label: string
|
|
72
|
-
rows?: number
|
|
73
|
-
}) {
|
|
74
|
-
const field = useFieldContext<string>()
|
|
75
|
-
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div>
|
|
79
|
-
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
80
|
-
{label}
|
|
81
|
-
<textarea
|
|
82
|
-
value={field.state.value}
|
|
83
|
-
onBlur={field.handleBlur}
|
|
84
|
-
rows={rows}
|
|
85
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
86
|
-
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
87
|
-
/>
|
|
88
|
-
</label>
|
|
89
|
-
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
90
|
-
</div>
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function Select({
|
|
95
|
-
label,
|
|
96
|
-
children,
|
|
97
|
-
}: {
|
|
98
|
-
label: string
|
|
99
|
-
children: React.ReactNode
|
|
100
|
-
}) {
|
|
101
|
-
const field = useFieldContext<string>()
|
|
102
|
-
const errors = useStore(field.store, (state) => state.meta.errors)
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div>
|
|
106
|
-
<label htmlFor={label} className="block font-bold mb-1 text-xl">
|
|
107
|
-
{label}
|
|
108
|
-
</label>
|
|
109
|
-
<select
|
|
110
|
-
name={field.name}
|
|
111
|
-
value={field.state.value}
|
|
112
|
-
onBlur={field.handleBlur}
|
|
113
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
114
|
-
className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
|
|
115
|
-
>
|
|
116
|
-
{children}
|
|
117
|
-
</select>
|
|
118
|
-
{field.state.meta.isTouched && <ErrorMessages errors={errors} />}
|
|
119
|
-
</div>
|
|
120
|
-
)
|
|
121
|
-
}
|