wcz-layout 7.6.1 → 7.6.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/components/core/navigation/NavigationList.d.ts +4 -4
- package/dist/components/core/navigation/NavigationListItem.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/dist/models/Navigation.d.ts +23 -11
- package/package.json +8 -2
- package/skills/api-routes/SKILL.md +251 -0
- package/skills/auth/SKILL.md +268 -0
- package/skills/data-grid/SKILL.md +229 -0
- package/skills/database-schema/SKILL.md +182 -0
- package/skills/dialogs-notifications/SKILL.md +241 -0
- package/skills/forms-validation/SKILL.md +331 -0
- package/skills/forms-validation/references/field-components.md +212 -0
- package/skills/layout-navigation/SKILL.md +259 -0
- package/skills/project-initialization/SKILL.md +181 -0
- package/skills/project-structure/SKILL.md +157 -0
- package/skills/tanstack-db-collections/SKILL.md +270 -0
- package/skills/ui-pages/SKILL.md +278 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forms-validation
|
|
3
|
+
description: >
|
|
4
|
+
Build forms with useLayoutForm hook (primary) and withLayoutForm for
|
|
5
|
+
composable sub-forms. 13 pre-registered MUI field components via
|
|
6
|
+
form.AppField: TextField, NumberField, Autocomplete, Checkbox, Switch,
|
|
7
|
+
RadioGroup, Slider, DatePicker, DateRangePicker, TimePicker,
|
|
8
|
+
TimeRangePicker, DateTimePicker, DateTimeRangePicker. SubmitButton in
|
|
9
|
+
form.AppForm. Zod onChange validators. FormOmittedProps type. Activate
|
|
10
|
+
when creating or modifying forms with validation.
|
|
11
|
+
type: core
|
|
12
|
+
library: wcz-layout
|
|
13
|
+
library_version: "7.6.1"
|
|
14
|
+
sources:
|
|
15
|
+
- "wcz-layout:src/hooks/FormHooks.ts"
|
|
16
|
+
- "wcz-layout:src/components/form/"
|
|
17
|
+
- "wcz-layout:src/lib/utils.ts"
|
|
18
|
+
references:
|
|
19
|
+
- "references/field-components.md"
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Forms & Validation
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
Import the form hook and Zod schema:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { useLayoutForm } from "wcz-layout/hooks";
|
|
30
|
+
import { TodoSchema } from "~/schemas/todo";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Core Patterns
|
|
34
|
+
|
|
35
|
+
### Basic form with useLayoutForm
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { useLayoutForm } from "wcz-layout/hooks";
|
|
39
|
+
import { TodoSchema } from "~/schemas/todo";
|
|
40
|
+
import type { Todo } from "~/schemas/todo";
|
|
41
|
+
import { todoCollection } from "~/lib/db/collections/todoCollection";
|
|
42
|
+
import { uuidv7 } from "uuidv7";
|
|
43
|
+
|
|
44
|
+
function TodoForm() {
|
|
45
|
+
const form = useLayoutForm({
|
|
46
|
+
defaultValues: {
|
|
47
|
+
id: uuidv7(),
|
|
48
|
+
name: "",
|
|
49
|
+
description: "",
|
|
50
|
+
isCompleted: false,
|
|
51
|
+
} as Todo,
|
|
52
|
+
validators: {
|
|
53
|
+
onChange: TodoSchema,
|
|
54
|
+
},
|
|
55
|
+
onSubmit: ({ value }) => {
|
|
56
|
+
todoCollection.insert(value);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<form.AppForm>
|
|
62
|
+
<form.AppField
|
|
63
|
+
name="name"
|
|
64
|
+
children={(field) => <field.TextField label="Name" required />}
|
|
65
|
+
/>
|
|
66
|
+
<form.AppField
|
|
67
|
+
name="description"
|
|
68
|
+
children={(field) => <field.TextField label="Description" multiline rows={3} />}
|
|
69
|
+
/>
|
|
70
|
+
<form.AppField
|
|
71
|
+
name="isCompleted"
|
|
72
|
+
children={(field) => <field.Checkbox label="Completed" />}
|
|
73
|
+
/>
|
|
74
|
+
<form.SubmitButton />
|
|
75
|
+
</form.AppForm>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Available field components
|
|
81
|
+
|
|
82
|
+
All 13 field components are accessed through `field.*` inside `form.AppField`:
|
|
83
|
+
|
|
84
|
+
| Component | MUI base | Use case |
|
|
85
|
+
| --------------------------- | --------------------------- | ----------------------------- |
|
|
86
|
+
| `field.TextField` | TextField | Text input, multiline |
|
|
87
|
+
| `field.NumberField` | TextField (number) | Numeric input |
|
|
88
|
+
| `field.Autocomplete` | Autocomplete | Search/select with options |
|
|
89
|
+
| `field.Checkbox` | FormControlLabel + Checkbox | Boolean toggle |
|
|
90
|
+
| `field.Switch` | FormControlLabel + Switch | Boolean toggle (switch) |
|
|
91
|
+
| `field.RadioGroup` | RadioGroup | Single selection from options |
|
|
92
|
+
| `field.Slider` | Slider | Range/value slider |
|
|
93
|
+
| `field.DatePicker` | DatePicker | Date only |
|
|
94
|
+
| `field.DateRangePicker` | DateRangePicker | Date range |
|
|
95
|
+
| `field.TimePicker` | TimePicker | Time only |
|
|
96
|
+
| `field.TimeRangePicker` | TimeRangePicker | Time range |
|
|
97
|
+
| `field.DateTimePicker` | DateTimePicker | Date + time |
|
|
98
|
+
| `field.DateTimeRangePicker` | DateTimeRangePicker | Date + time range |
|
|
99
|
+
|
|
100
|
+
See references/field-components.md for detailed prop surfaces.
|
|
101
|
+
|
|
102
|
+
### Edit form with existing data
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
function TodoEditForm({ todo }: { todo: Todo }) {
|
|
106
|
+
const form = useLayoutForm({
|
|
107
|
+
defaultValues: todo,
|
|
108
|
+
validators: {
|
|
109
|
+
onChange: TodoSchema,
|
|
110
|
+
},
|
|
111
|
+
onSubmit: ({ value }) => {
|
|
112
|
+
todoCollection.update(value);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<form.AppForm>
|
|
118
|
+
<form.AppField
|
|
119
|
+
name="name"
|
|
120
|
+
children={(field) => <field.TextField label="Name" required />}
|
|
121
|
+
/>
|
|
122
|
+
<form.SubmitButton />
|
|
123
|
+
</form.AppForm>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Sub-form composition with withLayoutForm
|
|
129
|
+
|
|
130
|
+
Use `withLayoutForm` when splitting a large form into reusable sub-form components:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { withLayoutForm } from "wcz-layout/hooks";
|
|
134
|
+
import { TodoSchema } from "~/schemas/todo";
|
|
135
|
+
|
|
136
|
+
const TodoDetailsSubForm = withLayoutForm({
|
|
137
|
+
defaultValues: { name: "", description: "" },
|
|
138
|
+
render: ({ form }) => (
|
|
139
|
+
<>
|
|
140
|
+
<form.AppField
|
|
141
|
+
name="name"
|
|
142
|
+
children={(field) => <field.TextField label="Name" required />}
|
|
143
|
+
/>
|
|
144
|
+
<form.AppField
|
|
145
|
+
name="description"
|
|
146
|
+
children={(field) => <field.TextField label="Description" multiline />}
|
|
147
|
+
/>
|
|
148
|
+
</>
|
|
149
|
+
),
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Autocomplete with API data
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
<form.AppField
|
|
157
|
+
name="assigneeId"
|
|
158
|
+
children={(field) => (
|
|
159
|
+
<field.Autocomplete
|
|
160
|
+
label="Assignee"
|
|
161
|
+
options={users}
|
|
162
|
+
getOptionLabel={(user) => user.displayName}
|
|
163
|
+
isOptionEqualToValue={(option, value) => option.id === value.id}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
166
|
+
/>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Date pickers
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
<form.AppField
|
|
173
|
+
name="dueDate"
|
|
174
|
+
children={(field) => <field.DatePicker label="Due Date" />}
|
|
175
|
+
/>
|
|
176
|
+
|
|
177
|
+
<form.AppField
|
|
178
|
+
name="dateRange"
|
|
179
|
+
children={(field) => (
|
|
180
|
+
<field.DateRangePicker
|
|
181
|
+
localeText={{ start: "Start Date", end: "End Date" }}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Common Mistakes
|
|
188
|
+
|
|
189
|
+
### CRITICAL Using raw MUI TextField instead of form.AppField
|
|
190
|
+
|
|
191
|
+
Wrong:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
<TextField
|
|
195
|
+
name="title"
|
|
196
|
+
value={title}
|
|
197
|
+
onChange={(e) => setTitle(e.target.value)}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Correct:
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
<form.AppField
|
|
205
|
+
name="title"
|
|
206
|
+
children={(field) => <field.TextField label="Title" />}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`useLayoutForm` pre-registers all field components. Using raw MUI inputs bypasses TanStack Form state management, validation, and error display.
|
|
211
|
+
|
|
212
|
+
Source: wcz-layout:src/hooks/FormHooks.ts
|
|
213
|
+
|
|
214
|
+
### HIGH Passing name/value/onChange to AppField components
|
|
215
|
+
|
|
216
|
+
Wrong:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
<form.AppField
|
|
220
|
+
name="title"
|
|
221
|
+
children={(field) => (
|
|
222
|
+
<field.TextField
|
|
223
|
+
label="Title"
|
|
224
|
+
name="title"
|
|
225
|
+
value={field.state.value}
|
|
226
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
227
|
+
/>
|
|
228
|
+
)}
|
|
229
|
+
/>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Correct:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
<form.AppField
|
|
236
|
+
name="title"
|
|
237
|
+
children={(field) => <field.TextField label="Title" />}
|
|
238
|
+
/>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`FormOmittedProps` explicitly strips `name`, `value`, `onChange`, `onBlur`, `error`, `helperText`, `renderInput`, `type`, and `aria-label`. These are managed internally by `useFieldContext`. Passing them causes conflicts or is silently ignored.
|
|
242
|
+
|
|
243
|
+
Source: wcz-layout:src/lib/utils.ts
|
|
244
|
+
|
|
245
|
+
### HIGH Not wrapping SubmitButton in form.AppForm
|
|
246
|
+
|
|
247
|
+
Wrong:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
<div>
|
|
251
|
+
<form.AppField name="name" children={(field) => <field.TextField label="Name" />} />
|
|
252
|
+
<form.SubmitButton /> {/* Outside AppForm — crashes */}
|
|
253
|
+
</div>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Correct:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
<form.AppForm>
|
|
260
|
+
<form.AppField name="name" children={(field) => <field.TextField label="Name" />} />
|
|
261
|
+
<form.SubmitButton />
|
|
262
|
+
</form.AppForm>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`SubmitButton` uses `useFormContext()` to read `canSubmit` and `isSubmitting` state. It must be rendered inside `form.AppForm`.
|
|
266
|
+
|
|
267
|
+
Source: wcz-layout:src/components/form/FormSubmitButton.tsx
|
|
268
|
+
|
|
269
|
+
### HIGH Using useMemo or useCallback in form components
|
|
270
|
+
|
|
271
|
+
Wrong:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const handleSubmit = useCallback(() => form.handleSubmit(), [form]);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Correct:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const handleSubmit = () => form.handleSubmit();
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
React Compiler handles memoization. Manual `useMemo` / `useCallback` is forbidden per project conventions.
|
|
284
|
+
|
|
285
|
+
Source: copilot-instructions.md
|
|
286
|
+
|
|
287
|
+
Cross-skill: See also skills/ui-pages/SKILL.md § Common Mistakes
|
|
288
|
+
|
|
289
|
+
### MEDIUM FormRadioGroup numeric values become strings
|
|
290
|
+
|
|
291
|
+
Wrong:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
<field.RadioGroup
|
|
295
|
+
options={[
|
|
296
|
+
{ label: "Low", value: 1 },
|
|
297
|
+
{ label: "High", value: 2 },
|
|
298
|
+
]}
|
|
299
|
+
/>
|
|
300
|
+
// field.state.value is "1" not 1
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Correct:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
<field.RadioGroup
|
|
307
|
+
options={[
|
|
308
|
+
{ label: "Low", value: "1" },
|
|
309
|
+
{ label: "High", value: "2" },
|
|
310
|
+
]}
|
|
311
|
+
/>
|
|
312
|
+
// Use string values consistently, or convert in onSubmit
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Radio group `onChange` always returns `event.target.value` as a string. Numeric option values round-trip as strings unless explicitly converted.
|
|
316
|
+
|
|
317
|
+
Source: wcz-layout:src/components/form/FormRadioGroup.tsx
|
|
318
|
+
|
|
319
|
+
### HIGH Tension: Type safety vs. rapid prototyping
|
|
320
|
+
|
|
321
|
+
Quick forms using `useState` + manual validation bypass the enforced pattern. Always use `useLayoutForm` + Zod schema derived from Drizzle — even for simple forms. The boilerplate pays off in type safety and consistency.
|
|
322
|
+
|
|
323
|
+
See also: skills/database-schema/SKILL.md § Common Mistakes
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
See also:
|
|
328
|
+
|
|
329
|
+
- skills/database-schema/SKILL.md — Zod schemas used as form validators.
|
|
330
|
+
- skills/dialogs-notifications/SKILL.md — Form submissions typically show notifications.
|
|
331
|
+
- skills/ui-pages/SKILL.md — Create/edit pages embed forms.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Field Components Reference
|
|
2
|
+
|
|
3
|
+
All field components are accessed via `field.*` inside a `form.AppField` children render prop. Props listed below are the MUI props you can pass — `FormOmittedProps` (`name`, `value`, `onChange`, `onBlur`, `error`, `helperText`, `renderInput`, `type`, `aria-label`) are automatically stripped and managed by TanStack Form.
|
|
4
|
+
|
|
5
|
+
## TextField
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
<form.AppField
|
|
9
|
+
name="title"
|
|
10
|
+
children={(field) => (
|
|
11
|
+
<field.TextField
|
|
12
|
+
label="Title"
|
|
13
|
+
required
|
|
14
|
+
multiline // optional: enables textarea
|
|
15
|
+
rows={3} // optional: fixed row count for multiline
|
|
16
|
+
placeholder="..." // optional
|
|
17
|
+
disabled // optional
|
|
18
|
+
slotProps={{ // optional: MUI slot props
|
|
19
|
+
input: { startAdornment: <InputAdornment position="start">$</InputAdornment> }
|
|
20
|
+
}}
|
|
21
|
+
/>
|
|
22
|
+
)}
|
|
23
|
+
/>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## NumberField
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
<form.AppField
|
|
30
|
+
name="quantity"
|
|
31
|
+
children={(field) => (
|
|
32
|
+
<field.NumberField
|
|
33
|
+
label="Quantity"
|
|
34
|
+
required
|
|
35
|
+
slotProps={{
|
|
36
|
+
input: { inputProps: { min: 0, max: 100, step: 1 } }
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
)}
|
|
40
|
+
/>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Autocomplete
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
<form.AppField
|
|
47
|
+
name="assignee"
|
|
48
|
+
children={(field) => (
|
|
49
|
+
<field.Autocomplete
|
|
50
|
+
label="Assignee"
|
|
51
|
+
options={users}
|
|
52
|
+
getOptionLabel={(user) => user.displayName}
|
|
53
|
+
isOptionEqualToValue={(option, value) => option.id === value.id}
|
|
54
|
+
multiple // optional: multi-select
|
|
55
|
+
freeSolo // optional: allow custom input
|
|
56
|
+
loading={isLoading} // optional: show loading indicator
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
/>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Checkbox
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
<form.AppField
|
|
66
|
+
name="isActive"
|
|
67
|
+
children={(field) => (
|
|
68
|
+
<field.Checkbox
|
|
69
|
+
label="Active"
|
|
70
|
+
disabled // optional
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Renders `FormControlLabel` wrapping MUI `Checkbox`.
|
|
77
|
+
|
|
78
|
+
## Switch
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
<form.AppField
|
|
82
|
+
name="notifications"
|
|
83
|
+
children={(field) => (
|
|
84
|
+
<field.Switch
|
|
85
|
+
label="Enable Notifications"
|
|
86
|
+
disabled // optional
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
/>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Renders `FormControlLabel` wrapping MUI `Switch`.
|
|
93
|
+
|
|
94
|
+
## RadioGroup
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
<form.AppField
|
|
98
|
+
name="priority"
|
|
99
|
+
children={(field) => (
|
|
100
|
+
<field.RadioGroup
|
|
101
|
+
label="Priority"
|
|
102
|
+
options={[
|
|
103
|
+
{ label: "Low", value: "low" },
|
|
104
|
+
{ label: "Medium", value: "medium" },
|
|
105
|
+
{ label: "High", value: "high" },
|
|
106
|
+
]}
|
|
107
|
+
row // optional: horizontal layout
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Warning:** `onChange` returns `event.target.value` as a string. Use string values.
|
|
114
|
+
|
|
115
|
+
## Slider
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
<form.AppField
|
|
119
|
+
name="rating"
|
|
120
|
+
children={(field) => (
|
|
121
|
+
<field.Slider
|
|
122
|
+
min={0}
|
|
123
|
+
max={10}
|
|
124
|
+
step={1}
|
|
125
|
+
marks
|
|
126
|
+
valueLabelDisplay="auto"
|
|
127
|
+
/>
|
|
128
|
+
)}
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## DatePicker
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
<form.AppField
|
|
136
|
+
name="dueDate"
|
|
137
|
+
children={(field) => (
|
|
138
|
+
<field.DatePicker
|
|
139
|
+
label="Due Date"
|
|
140
|
+
minDate={dayjs()} // optional
|
|
141
|
+
maxDate={dayjs().add(1, "year")} // optional
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## DateRangePicker
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
<form.AppField
|
|
151
|
+
name="dateRange"
|
|
152
|
+
children={(field) => (
|
|
153
|
+
<field.DateRangePicker
|
|
154
|
+
localeText={{ start: "Start", end: "End" }}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
/>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## TimePicker
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
<form.AppField
|
|
164
|
+
name="startTime"
|
|
165
|
+
children={(field) => (
|
|
166
|
+
<field.TimePicker
|
|
167
|
+
label="Start Time"
|
|
168
|
+
ampm={false} // optional: 24-hour format
|
|
169
|
+
/>
|
|
170
|
+
)}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## TimeRangePicker
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
<form.AppField
|
|
178
|
+
name="timeRange"
|
|
179
|
+
children={(field) => (
|
|
180
|
+
<field.TimeRangePicker
|
|
181
|
+
localeText={{ start: "From", end: "To" }}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## DateTimePicker
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
<form.AppField
|
|
191
|
+
name="scheduledAt"
|
|
192
|
+
children={(field) => (
|
|
193
|
+
<field.DateTimePicker
|
|
194
|
+
label="Scheduled At"
|
|
195
|
+
ampm={false}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## DateTimeRangePicker
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
<form.AppField
|
|
205
|
+
name="availability"
|
|
206
|
+
children={(field) => (
|
|
207
|
+
<field.DateTimeRangePicker
|
|
208
|
+
localeText={{ start: "From", end: "Until" }}
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
/>
|
|
212
|
+
```
|