create-tsrouter-app 0.8.0 → 0.9.0
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 +1 -1
- package/package.json +1 -1
- package/src/create-app.ts +1 -1
- 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
|
@@ -307,7 +307,7 @@ export async function createApp(options, { silent = false, environment, }) {
|
|
|
307
307
|
if (shadcnComponents.size > 0) {
|
|
308
308
|
s?.start(`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`);
|
|
309
309
|
await environment.execute('npx', ['shadcn@canary', 'add', ...shadcnComponents], targetDir);
|
|
310
|
-
s?.stop(`Installed
|
|
310
|
+
s?.stop(`Installed additional shadcn components`);
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
const integrations = [];
|
package/package.json
CHANGED
package/src/create-app.ts
CHANGED
|
@@ -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
|
-
}
|