@zeno-lib/forms 0.0.1 → 0.2.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/package.json +23 -11
- package/src/addons/validation-spinner.tsx +3 -4
- package/src/create-form.tsx +59 -0
- package/src/create-zeno-form.tsx +531 -0
- package/src/fields/checkbox-field.tsx +15 -12
- package/src/fields/checkbox-group-field.tsx +160 -0
- package/src/fields/combobox-field.tsx +13 -14
- package/src/fields/date-picker-field.tsx +18 -15
- package/src/fields/index.ts +5 -0
- package/src/fields/input-field.tsx +12 -13
- package/src/fields/money-field.tsx +123 -0
- package/src/fields/multi-select-field.tsx +193 -0
- package/src/fields/number-field.tsx +1 -2
- package/src/fields/otp-field.tsx +15 -12
- package/src/fields/percentage-field.tsx +73 -0
- package/src/fields/radio-group-field.tsx +12 -13
- package/src/fields/reset-button.tsx +2 -3
- package/src/fields/select-field.tsx +12 -13
- package/src/fields/slider-field.tsx +11 -12
- package/src/fields/submit-button.tsx +3 -4
- package/src/fields/switch-field.tsx +15 -12
- package/src/fields/textarea-field.tsx +12 -13
- package/src/fields/year-field.tsx +56 -0
- package/src/form-dialog.test.tsx +161 -0
- package/src/form-dialog.tsx +253 -0
- package/src/form-element.tsx +51 -0
- package/src/formatted-number-fields.test.tsx +236 -0
- package/src/index.ts +27 -0
- package/src/lib/action-result.ts +61 -0
- package/src/lib/apply-validation-error.test.ts +16 -6
- package/src/lib/apply-validation-error.ts +38 -6
- package/src/lib/formatted-number.test.ts +157 -0
- package/src/lib/formatted-number.ts +307 -0
- package/src/lib/required-indicator.tsx +1 -1
- package/src/lib/schema-defaults.test.ts +1 -2
- package/src/lib/schema-required.test.ts +98 -6
- package/src/lib/schema-required.ts +126 -22
- package/src/lib/submit-action.test-d.ts +83 -0
- package/src/lib/submit-action.ts +178 -0
- package/src/lib/use-form-dialog.ts +184 -0
- package/src/lib/use-formatted-number.ts +235 -0
- package/src/lib/use-is-invalid.ts +4 -4
- package/src/lib/use-rebased-default-values.ts +42 -0
- package/src/lib/use-unsaved-changes-warning.ts +3 -3
- package/src/lib/validation-error.test.ts +1 -1
- package/src/lib/validation-logic.test.ts +1 -1
- package/src/lib/validation-modes.test-d.ts +1 -1
- package/src/lib/validation-modes.test.ts +12 -2
- package/src/lib/validation-modes.ts +3 -1
- package/src/multi-value-fields.test.tsx +116 -0
- package/src/submit-action.test.tsx +421 -0
- package/src/use-app-fields.test-d.ts +8 -25
- package/src/use-app-fields.test.tsx +8 -32
- package/src/use-form.cascade.test.tsx +4 -6
- package/src/use-form.focus-invalid.test.tsx +198 -0
- package/src/use-form.reset-defaults.test.tsx +125 -0
- package/src/use-form.test-d.ts +2 -3
- package/src/use-form.test.tsx +12 -14
- package/src/use-form.unsaved-warning.test.tsx +4 -13
- package/src/use-form.validation-modes.test.tsx +10 -8
- package/src/form.tsx +0 -105
- package/src/use-app-fields.tsx +0 -272
- package/src/use-form.tsx +0 -450
package/src/use-app-fields.tsx
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { DeepKeys } from "@tanstack/react-form"
|
|
4
|
-
import { type ComponentProps, useMemo } from "react"
|
|
5
|
-
|
|
6
|
-
import { CheckboxField as CheckboxFieldImpl } from "./fields/checkbox-field"
|
|
7
|
-
import { ComboboxField as ComboboxFieldImpl } from "./fields/combobox-field"
|
|
8
|
-
import { DatePickerField as DatePickerFieldImpl } from "./fields/date-picker-field"
|
|
9
|
-
import { EmailField as EmailFieldImpl } from "./fields/email-field"
|
|
10
|
-
import { InputField as InputFieldImpl } from "./fields/input-field"
|
|
11
|
-
import { NumberField as NumberFieldImpl } from "./fields/number-field"
|
|
12
|
-
import { OtpField as OtpFieldImpl } from "./fields/otp-field"
|
|
13
|
-
import { PasswordField as PasswordFieldImpl } from "./fields/password-field"
|
|
14
|
-
import { RadioGroupField as RadioGroupFieldImpl } from "./fields/radio-group-field"
|
|
15
|
-
import { SelectField as SelectFieldImpl } from "./fields/select-field"
|
|
16
|
-
import { SliderField as SliderFieldImpl } from "./fields/slider-field"
|
|
17
|
-
import { SwitchField as SwitchFieldImpl } from "./fields/switch-field"
|
|
18
|
-
import { TextAreaField as TextAreaFieldImpl } from "./fields/textarea-field"
|
|
19
|
-
|
|
20
|
-
type FormDataOf<F> = F extends { state: { values: infer V } } ? V : never
|
|
21
|
-
|
|
22
|
-
type AnyAppForm = {
|
|
23
|
-
AppField: unknown
|
|
24
|
-
state: { values: unknown }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type WithDefaultName<T, D extends string> =
|
|
28
|
-
D extends DeepKeys<T> ? { name?: DeepKeys<T> } : { name: DeepKeys<T> }
|
|
29
|
-
|
|
30
|
-
// Loose validators type for the typed wrappers — name-aware validator typing
|
|
31
|
-
// from TanStack Form would collide with our per-field generics. Callers pass
|
|
32
|
-
// standard schemas (Zod) or functions; both flow through to `<Field>` at
|
|
33
|
-
// runtime unchanged. The structural shape mirrors TanStack's `FieldValidators`
|
|
34
|
-
// slots so prop-name autocomplete still works.
|
|
35
|
-
type AnyFieldValidators = {
|
|
36
|
-
onMount?: unknown
|
|
37
|
-
onChange?: unknown
|
|
38
|
-
onChangeAsync?: unknown
|
|
39
|
-
onChangeAsyncDebounceMs?: number
|
|
40
|
-
onBlur?: unknown
|
|
41
|
-
onBlurAsync?: unknown
|
|
42
|
-
onBlurAsyncDebounceMs?: number
|
|
43
|
-
onSubmit?: unknown
|
|
44
|
-
onSubmitAsync?: unknown
|
|
45
|
-
onSubmitAsyncDebounceMs?: number
|
|
46
|
-
onDynamic?: unknown
|
|
47
|
-
onDynamicAsync?: unknown
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Mirrors TanStack's `FieldListeners`. Same loose-shape rationale as
|
|
51
|
-
// `AnyFieldValidators`: prop-name autocomplete with no per-field generic
|
|
52
|
-
// collisions. Use for cascade patterns where changing one field updates
|
|
53
|
-
// another (`form.setFieldValue(...)` inside `onChange`).
|
|
54
|
-
type AnyFieldListeners = {
|
|
55
|
-
onChange?: unknown
|
|
56
|
-
onChangeDebounceMs?: number
|
|
57
|
-
onBlur?: unknown
|
|
58
|
-
onBlurDebounceMs?: number
|
|
59
|
-
onMount?: unknown
|
|
60
|
-
onSubmit?: unknown
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
type WithValidators = {
|
|
64
|
-
validators?: AnyFieldValidators
|
|
65
|
-
listeners?: AnyFieldListeners
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function useAppFields<TForm extends AnyAppForm>(form: TForm) {
|
|
69
|
-
type T = FormDataOf<TForm>
|
|
70
|
-
|
|
71
|
-
return useMemo(() => {
|
|
72
|
-
const Field = form.AppField as React.ComponentType<{
|
|
73
|
-
name: string
|
|
74
|
-
validators?: AnyFieldValidators
|
|
75
|
-
listeners?: AnyFieldListeners
|
|
76
|
-
children: () => React.ReactNode
|
|
77
|
-
}>
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
CheckboxField<N extends DeepKeys<T>>({
|
|
81
|
-
name,
|
|
82
|
-
validators,
|
|
83
|
-
listeners,
|
|
84
|
-
...props
|
|
85
|
-
}: ComponentProps<typeof CheckboxFieldImpl> & {
|
|
86
|
-
name: N
|
|
87
|
-
} & WithValidators) {
|
|
88
|
-
return (
|
|
89
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
90
|
-
{() => <CheckboxFieldImpl {...props} />}
|
|
91
|
-
</Field>
|
|
92
|
-
)
|
|
93
|
-
},
|
|
94
|
-
ComboboxField<N extends DeepKeys<T>>({
|
|
95
|
-
name,
|
|
96
|
-
validators,
|
|
97
|
-
listeners,
|
|
98
|
-
...props
|
|
99
|
-
}: ComponentProps<typeof ComboboxFieldImpl> & {
|
|
100
|
-
name: N
|
|
101
|
-
} & WithValidators) {
|
|
102
|
-
return (
|
|
103
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
104
|
-
{() => <ComboboxFieldImpl {...props} />}
|
|
105
|
-
</Field>
|
|
106
|
-
)
|
|
107
|
-
},
|
|
108
|
-
DatePickerField<N extends DeepKeys<T>>({
|
|
109
|
-
name,
|
|
110
|
-
validators,
|
|
111
|
-
listeners,
|
|
112
|
-
...props
|
|
113
|
-
}: ComponentProps<typeof DatePickerFieldImpl> & {
|
|
114
|
-
name: N
|
|
115
|
-
} & WithValidators) {
|
|
116
|
-
return (
|
|
117
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
118
|
-
{() => <DatePickerFieldImpl {...props} />}
|
|
119
|
-
</Field>
|
|
120
|
-
)
|
|
121
|
-
},
|
|
122
|
-
EmailField({
|
|
123
|
-
name,
|
|
124
|
-
validators,
|
|
125
|
-
listeners,
|
|
126
|
-
...props
|
|
127
|
-
}: ComponentProps<typeof EmailFieldImpl> &
|
|
128
|
-
WithDefaultName<T, "email"> &
|
|
129
|
-
WithValidators) {
|
|
130
|
-
return (
|
|
131
|
-
<Field
|
|
132
|
-
listeners={listeners}
|
|
133
|
-
name={(name ?? "email") as string}
|
|
134
|
-
validators={validators}
|
|
135
|
-
>
|
|
136
|
-
{() => <EmailFieldImpl {...props} />}
|
|
137
|
-
</Field>
|
|
138
|
-
)
|
|
139
|
-
},
|
|
140
|
-
InputField<N extends DeepKeys<T>>({
|
|
141
|
-
name,
|
|
142
|
-
validators,
|
|
143
|
-
listeners,
|
|
144
|
-
...props
|
|
145
|
-
}: ComponentProps<typeof InputFieldImpl> & { name: N } & WithValidators) {
|
|
146
|
-
return (
|
|
147
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
148
|
-
{() => <InputFieldImpl {...props} />}
|
|
149
|
-
</Field>
|
|
150
|
-
)
|
|
151
|
-
},
|
|
152
|
-
NumberField<N extends DeepKeys<T>>({
|
|
153
|
-
name,
|
|
154
|
-
validators,
|
|
155
|
-
listeners,
|
|
156
|
-
...props
|
|
157
|
-
}: ComponentProps<typeof NumberFieldImpl> & {
|
|
158
|
-
name: N
|
|
159
|
-
} & WithValidators) {
|
|
160
|
-
return (
|
|
161
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
162
|
-
{() => <NumberFieldImpl {...props} />}
|
|
163
|
-
</Field>
|
|
164
|
-
)
|
|
165
|
-
},
|
|
166
|
-
OtpField<N extends DeepKeys<T>>({
|
|
167
|
-
name,
|
|
168
|
-
validators,
|
|
169
|
-
listeners,
|
|
170
|
-
...props
|
|
171
|
-
}: ComponentProps<typeof OtpFieldImpl> & {
|
|
172
|
-
name: N
|
|
173
|
-
} & WithValidators) {
|
|
174
|
-
return (
|
|
175
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
176
|
-
{() => <OtpFieldImpl {...props} />}
|
|
177
|
-
</Field>
|
|
178
|
-
)
|
|
179
|
-
},
|
|
180
|
-
PasswordField({
|
|
181
|
-
name,
|
|
182
|
-
validators,
|
|
183
|
-
listeners,
|
|
184
|
-
...props
|
|
185
|
-
}: ComponentProps<typeof PasswordFieldImpl> &
|
|
186
|
-
WithDefaultName<T, "password"> &
|
|
187
|
-
WithValidators) {
|
|
188
|
-
return (
|
|
189
|
-
<Field
|
|
190
|
-
listeners={listeners}
|
|
191
|
-
name={(name ?? "password") as string}
|
|
192
|
-
validators={validators}
|
|
193
|
-
>
|
|
194
|
-
{() => <PasswordFieldImpl {...props} />}
|
|
195
|
-
</Field>
|
|
196
|
-
)
|
|
197
|
-
},
|
|
198
|
-
RadioGroupField<N extends DeepKeys<T>>({
|
|
199
|
-
name,
|
|
200
|
-
validators,
|
|
201
|
-
listeners,
|
|
202
|
-
...props
|
|
203
|
-
}: ComponentProps<typeof RadioGroupFieldImpl> & {
|
|
204
|
-
name: N
|
|
205
|
-
} & WithValidators) {
|
|
206
|
-
return (
|
|
207
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
208
|
-
{() => <RadioGroupFieldImpl {...props} />}
|
|
209
|
-
</Field>
|
|
210
|
-
)
|
|
211
|
-
},
|
|
212
|
-
SelectField<N extends DeepKeys<T>>({
|
|
213
|
-
name,
|
|
214
|
-
validators,
|
|
215
|
-
listeners,
|
|
216
|
-
...props
|
|
217
|
-
}: ComponentProps<typeof SelectFieldImpl> & {
|
|
218
|
-
name: N
|
|
219
|
-
} & WithValidators) {
|
|
220
|
-
return (
|
|
221
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
222
|
-
{() => <SelectFieldImpl {...props} />}
|
|
223
|
-
</Field>
|
|
224
|
-
)
|
|
225
|
-
},
|
|
226
|
-
SliderField<N extends DeepKeys<T>>({
|
|
227
|
-
name,
|
|
228
|
-
validators,
|
|
229
|
-
listeners,
|
|
230
|
-
...props
|
|
231
|
-
}: ComponentProps<typeof SliderFieldImpl> & {
|
|
232
|
-
name: N
|
|
233
|
-
} & WithValidators) {
|
|
234
|
-
return (
|
|
235
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
236
|
-
{() => <SliderFieldImpl {...props} />}
|
|
237
|
-
</Field>
|
|
238
|
-
)
|
|
239
|
-
},
|
|
240
|
-
SwitchField<N extends DeepKeys<T>>({
|
|
241
|
-
name,
|
|
242
|
-
validators,
|
|
243
|
-
listeners,
|
|
244
|
-
...props
|
|
245
|
-
}: ComponentProps<typeof SwitchFieldImpl> & {
|
|
246
|
-
name: N
|
|
247
|
-
} & WithValidators) {
|
|
248
|
-
return (
|
|
249
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
250
|
-
{() => <SwitchFieldImpl {...props} />}
|
|
251
|
-
</Field>
|
|
252
|
-
)
|
|
253
|
-
},
|
|
254
|
-
TextAreaField<N extends DeepKeys<T>>({
|
|
255
|
-
name,
|
|
256
|
-
validators,
|
|
257
|
-
listeners,
|
|
258
|
-
...props
|
|
259
|
-
}: ComponentProps<typeof TextAreaFieldImpl> & {
|
|
260
|
-
name: N
|
|
261
|
-
} & WithValidators) {
|
|
262
|
-
return (
|
|
263
|
-
<Field listeners={listeners} name={name} validators={validators}>
|
|
264
|
-
{() => <TextAreaFieldImpl {...props} />}
|
|
265
|
-
</Field>
|
|
266
|
-
)
|
|
267
|
-
},
|
|
268
|
-
}
|
|
269
|
-
}, [form])
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export { useAppFields }
|