formstand 0.2.0 → 0.4.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/CHANGELOG.md +83 -1
- package/README.md +26 -4
- package/dist/index.cjs +146 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -28
- package/dist/index.d.ts +36 -28
- package/dist/index.js +143 -46
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.d.cts
CHANGED
|
@@ -59,6 +59,7 @@ declare const flattenIssues: (issues: readonly z.core.$ZodIssue[]) => ErrorMap;
|
|
|
59
59
|
declare const validateSync: <TSchema extends z.ZodType>(schema: TSchema, values: z.input<TSchema>) => SettledValidationResult<z.output<TSchema>>;
|
|
60
60
|
declare const validateAsync: <TSchema extends z.ZodType>(schema: TSchema, values: z.input<TSchema>) => Promise<SettledValidationResult<z.output<TSchema>>>;
|
|
61
61
|
declare const isAsyncRequiredError: (e: unknown) => boolean;
|
|
62
|
+
declare const emptyValueForSchema: (schema: z.ZodType) => null | undefined;
|
|
62
63
|
|
|
63
64
|
type ReadonlyStoreApi<T> = Pick<StoreApi<T>, "getState" | "getInitialState" | "subscribe">;
|
|
64
65
|
type CreateFormOptions<TSchema extends z.ZodType> = Readonly<{
|
|
@@ -80,6 +81,9 @@ type SubmitResult<TOutput> = Readonly<{
|
|
|
80
81
|
errors: ErrorMap;
|
|
81
82
|
}> | Readonly<{
|
|
82
83
|
kind: "skipped";
|
|
84
|
+
}> | Readonly<{
|
|
85
|
+
kind: "error";
|
|
86
|
+
error: unknown;
|
|
83
87
|
}>;
|
|
84
88
|
type ArrayItemOf<T> = T extends readonly (infer U)[] ? U : never;
|
|
85
89
|
type ErrorPath<TValues> = FieldPath<TValues> | "";
|
|
@@ -169,14 +173,6 @@ declare function useFormSelectorShallow<TSchema extends z.ZodType, U>(form: Form
|
|
|
169
173
|
declare function useFormSelectorShallow<U>(form: FormStateApi & {
|
|
170
174
|
readonly schema?: undefined;
|
|
171
175
|
}, selector: (state: FormState<unknown>) => U): U;
|
|
172
|
-
/**
|
|
173
|
-
* @deprecated Renamed to `useFormSelector` — React DOM ships its own
|
|
174
|
-
* (deprecated) `useFormState`, and auto-imports regularly grab the wrong one.
|
|
175
|
-
* This alias will be removed before 1.0.
|
|
176
|
-
*/
|
|
177
|
-
declare const useFormState: typeof useFormSelector;
|
|
178
|
-
/** @deprecated Renamed to `useFormSelectorShallow`; see `useFormState`. */
|
|
179
|
-
declare const useFormStateShallow: typeof useFormSelectorShallow;
|
|
180
176
|
|
|
181
177
|
declare const useFormError: (form: FormStateApi) => readonly string[] | undefined;
|
|
182
178
|
|
|
@@ -190,7 +186,7 @@ type FieldFormApi = Readonly<{
|
|
|
190
186
|
store: ReadonlyStore$1<FormState<unknown>>;
|
|
191
187
|
setValue(path: string, value: unknown): void;
|
|
192
188
|
setTouched(path: string, touched?: boolean): void;
|
|
193
|
-
setError(path: string, errors: readonly string[]): void;
|
|
189
|
+
setError(path: string, errors: string | readonly string[]): void;
|
|
194
190
|
clearErrors(path?: string): void;
|
|
195
191
|
validateField(path: string): FieldValidationResult;
|
|
196
192
|
validateFieldAsync(path: string): Promise<FieldValidationResult>;
|
|
@@ -210,61 +206,63 @@ type UseFieldReturn<TValue> = Readonly<{
|
|
|
210
206
|
isValidating: boolean;
|
|
211
207
|
setValue: (value: TValue) => void;
|
|
212
208
|
setTouched: (touched?: boolean) => void;
|
|
213
|
-
setError: (errors: readonly string[]) => void;
|
|
209
|
+
setError: (errors: string | readonly string[]) => void;
|
|
214
210
|
clearError: () => void;
|
|
215
211
|
validate: () => FieldValidationResult;
|
|
216
212
|
validateAsync: () => Promise<FieldValidationResult>;
|
|
217
213
|
onBlur: () => void;
|
|
218
214
|
}>;
|
|
219
|
-
declare function useField<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
|
|
220
215
|
declare function useField<TSchema extends z.ZodType>(form: Form<TSchema>, pathSelector: (state: FormState<z.input<TSchema>>) => string, options?: UseFieldOptions): UseFieldReturn<unknown>;
|
|
221
216
|
declare function useField<TValue = unknown>(form: FieldFormApi & {
|
|
222
217
|
readonly schema?: undefined;
|
|
223
218
|
}, path: string | ((state: FormState<unknown>) => string), options?: UseFieldOptions): UseFieldReturn<TValue>;
|
|
219
|
+
declare function useField<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
|
|
224
220
|
|
|
221
|
+
type PathsOf<F extends FieldFormApi> = F extends Form<infer TSchema extends z.ZodType> ? FieldPath<z.input<TSchema>> : string;
|
|
225
222
|
type FieldRef<T> = ((instance: T | null) => void) | Readonly<{
|
|
226
223
|
current: T | null;
|
|
227
224
|
}> | null;
|
|
228
|
-
type TextFieldProps = Readonly<{
|
|
229
|
-
form:
|
|
230
|
-
path:
|
|
225
|
+
type TextFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
226
|
+
form: F;
|
|
227
|
+
path: PathsOf<F>;
|
|
231
228
|
label?: ReactNode;
|
|
232
229
|
placeholder?: string;
|
|
233
230
|
type?: "text" | "password" | "email" | "url" | "tel";
|
|
234
231
|
autoComplete?: string;
|
|
235
232
|
ref?: FieldRef<HTMLInputElement>;
|
|
236
233
|
}>;
|
|
237
|
-
declare const TextField: ({ form, path, label, placeholder, type, autoComplete, ref, }: TextFieldProps) => react.JSX.Element;
|
|
238
|
-
type NumberFieldProps = Readonly<{
|
|
239
|
-
form:
|
|
240
|
-
path:
|
|
234
|
+
declare const TextField: <F extends FieldFormApi>({ form, path, label, placeholder, type, autoComplete, ref, }: TextFieldProps<F>) => react.JSX.Element;
|
|
235
|
+
type NumberFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
236
|
+
form: F;
|
|
237
|
+
path: PathsOf<F>;
|
|
241
238
|
label?: ReactNode;
|
|
242
239
|
placeholder?: string;
|
|
243
240
|
ref?: FieldRef<HTMLInputElement>;
|
|
244
241
|
}>;
|
|
245
|
-
declare const NumberField: ({ form, path, label, placeholder, ref, }: NumberFieldProps) => react.JSX.Element;
|
|
246
|
-
type CheckboxFieldProps = Readonly<{
|
|
247
|
-
form:
|
|
248
|
-
path:
|
|
242
|
+
declare const NumberField: <F extends FieldFormApi>({ form, path, label, placeholder, ref, }: NumberFieldProps<F>) => react.JSX.Element;
|
|
243
|
+
type CheckboxFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
244
|
+
form: F;
|
|
245
|
+
path: PathsOf<F>;
|
|
249
246
|
label?: ReactNode;
|
|
250
247
|
ref?: FieldRef<HTMLInputElement>;
|
|
251
248
|
}>;
|
|
252
|
-
declare const CheckboxField: ({ form, path, label, ref }: CheckboxFieldProps) => react.JSX.Element;
|
|
249
|
+
declare const CheckboxField: <F extends FieldFormApi>({ form, path, label, ref, }: CheckboxFieldProps<F>) => react.JSX.Element;
|
|
253
250
|
type SelectFieldOption<T extends string> = Readonly<{
|
|
254
251
|
value: T;
|
|
255
252
|
label: ReactNode;
|
|
256
253
|
}>;
|
|
257
|
-
type SelectFieldProps<T extends string> = Readonly<{
|
|
258
|
-
form:
|
|
259
|
-
path:
|
|
254
|
+
type SelectFieldProps<T extends string, F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
255
|
+
form: F;
|
|
256
|
+
path: PathsOf<F>;
|
|
260
257
|
label?: ReactNode;
|
|
261
258
|
options: readonly SelectFieldOption<T>[];
|
|
262
259
|
placeholder?: string;
|
|
263
260
|
ref?: FieldRef<HTMLSelectElement>;
|
|
264
261
|
}>;
|
|
265
|
-
declare const SelectField: <T extends string>({ form, path, label, options, placeholder, ref, }: SelectFieldProps<T>) => react.JSX.Element;
|
|
262
|
+
declare const SelectField: <T extends string, F extends FieldFormApi>({ form, path, label, options, placeholder, ref, }: SelectFieldProps<T, F>) => react.JSX.Element;
|
|
266
263
|
|
|
267
264
|
declare const focusFirstError: (errors: ErrorMap, root?: ParentNode) => boolean;
|
|
265
|
+
declare const focusField: (path: string, root?: ParentNode) => boolean;
|
|
268
266
|
|
|
269
267
|
type ReadonlyStore<T> = Pick<StoreApi<T>, "getState" | "getInitialState" | "subscribe">;
|
|
270
268
|
type FieldArrayFormApi = Readonly<{
|
|
@@ -322,9 +320,19 @@ type SelectProps = Readonly<{
|
|
|
322
320
|
onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
|
|
323
321
|
onBlur: () => void;
|
|
324
322
|
}>;
|
|
323
|
+
declare const numberToInputText: (value: number | null | undefined) => string;
|
|
324
|
+
type ParsedNumberText = Readonly<{
|
|
325
|
+
kind: "empty";
|
|
326
|
+
}> | Readonly<{
|
|
327
|
+
kind: "number";
|
|
328
|
+
value: number;
|
|
329
|
+
}> | Readonly<{
|
|
330
|
+
kind: "invalid";
|
|
331
|
+
}>;
|
|
332
|
+
declare const parseNumberText: (text: string) => ParsedNumberText;
|
|
325
333
|
declare const textInputProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => TextInputProps;
|
|
326
334
|
declare const numberInputProps: <T extends number | null | undefined>(field: UseFieldReturn<T>) => NumberInputProps;
|
|
327
335
|
declare const checkboxProps: <T extends boolean | null | undefined>(field: UseFieldReturn<T>) => CheckboxProps;
|
|
328
336
|
declare const selectProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => SelectProps;
|
|
329
337
|
|
|
330
|
-
export { type BoolMap, CheckboxField, type CheckboxFieldProps, type CheckboxProps, type CreateFormOptions, type ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, type FieldPath, type FieldPathArg, type FieldRef, type FieldSnapshot, type FieldValidationResult, type FieldValue, type Form, type FormContextApi, type FormProviderProps, type FormState, type FormStateApi, type InvalidSubmitHandler, NumberField, type NumberFieldProps, type NumberInputProps, type PathSegment, type ReadonlyStoreApi, type ResetOptions, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, type SettledFieldValidationResult, type SettledValidationResult, type SubmitHandler, type SubmitOptions, type SubmitResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type ValidationMode, type ValidationResult, type ValidationTrigger, checkboxProps, createForm, createFormContext, flattenIssues, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow,
|
|
338
|
+
export { type BoolMap, CheckboxField, type CheckboxFieldProps, type CheckboxProps, type CreateFormOptions, type ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, type FieldPath, type FieldPathArg, type FieldRef, type FieldSnapshot, type FieldValidationResult, type FieldValue, type Form, type FormContextApi, type FormProviderProps, type FormState, type FormStateApi, type InvalidSubmitHandler, NumberField, type NumberFieldProps, type NumberInputProps, type ParsedNumberText, type PathSegment, type PathsOf, type ReadonlyStoreApi, type ResetOptions, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, type SettledFieldValidationResult, type SettledValidationResult, type SubmitHandler, type SubmitOptions, type SubmitResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type ValidationMode, type ValidationResult, type ValidationTrigger, checkboxProps, createForm, createFormContext, emptyValueForSchema, flattenIssues, focusField, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, numberToInputText, parseNumberText, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow, useIsDirty, useIsSubmitting, useIsValid, useSubmitCount, validateAsync, validateSync };
|
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ declare const flattenIssues: (issues: readonly z.core.$ZodIssue[]) => ErrorMap;
|
|
|
59
59
|
declare const validateSync: <TSchema extends z.ZodType>(schema: TSchema, values: z.input<TSchema>) => SettledValidationResult<z.output<TSchema>>;
|
|
60
60
|
declare const validateAsync: <TSchema extends z.ZodType>(schema: TSchema, values: z.input<TSchema>) => Promise<SettledValidationResult<z.output<TSchema>>>;
|
|
61
61
|
declare const isAsyncRequiredError: (e: unknown) => boolean;
|
|
62
|
+
declare const emptyValueForSchema: (schema: z.ZodType) => null | undefined;
|
|
62
63
|
|
|
63
64
|
type ReadonlyStoreApi<T> = Pick<StoreApi<T>, "getState" | "getInitialState" | "subscribe">;
|
|
64
65
|
type CreateFormOptions<TSchema extends z.ZodType> = Readonly<{
|
|
@@ -80,6 +81,9 @@ type SubmitResult<TOutput> = Readonly<{
|
|
|
80
81
|
errors: ErrorMap;
|
|
81
82
|
}> | Readonly<{
|
|
82
83
|
kind: "skipped";
|
|
84
|
+
}> | Readonly<{
|
|
85
|
+
kind: "error";
|
|
86
|
+
error: unknown;
|
|
83
87
|
}>;
|
|
84
88
|
type ArrayItemOf<T> = T extends readonly (infer U)[] ? U : never;
|
|
85
89
|
type ErrorPath<TValues> = FieldPath<TValues> | "";
|
|
@@ -169,14 +173,6 @@ declare function useFormSelectorShallow<TSchema extends z.ZodType, U>(form: Form
|
|
|
169
173
|
declare function useFormSelectorShallow<U>(form: FormStateApi & {
|
|
170
174
|
readonly schema?: undefined;
|
|
171
175
|
}, selector: (state: FormState<unknown>) => U): U;
|
|
172
|
-
/**
|
|
173
|
-
* @deprecated Renamed to `useFormSelector` — React DOM ships its own
|
|
174
|
-
* (deprecated) `useFormState`, and auto-imports regularly grab the wrong one.
|
|
175
|
-
* This alias will be removed before 1.0.
|
|
176
|
-
*/
|
|
177
|
-
declare const useFormState: typeof useFormSelector;
|
|
178
|
-
/** @deprecated Renamed to `useFormSelectorShallow`; see `useFormState`. */
|
|
179
|
-
declare const useFormStateShallow: typeof useFormSelectorShallow;
|
|
180
176
|
|
|
181
177
|
declare const useFormError: (form: FormStateApi) => readonly string[] | undefined;
|
|
182
178
|
|
|
@@ -190,7 +186,7 @@ type FieldFormApi = Readonly<{
|
|
|
190
186
|
store: ReadonlyStore$1<FormState<unknown>>;
|
|
191
187
|
setValue(path: string, value: unknown): void;
|
|
192
188
|
setTouched(path: string, touched?: boolean): void;
|
|
193
|
-
setError(path: string, errors: readonly string[]): void;
|
|
189
|
+
setError(path: string, errors: string | readonly string[]): void;
|
|
194
190
|
clearErrors(path?: string): void;
|
|
195
191
|
validateField(path: string): FieldValidationResult;
|
|
196
192
|
validateFieldAsync(path: string): Promise<FieldValidationResult>;
|
|
@@ -210,61 +206,63 @@ type UseFieldReturn<TValue> = Readonly<{
|
|
|
210
206
|
isValidating: boolean;
|
|
211
207
|
setValue: (value: TValue) => void;
|
|
212
208
|
setTouched: (touched?: boolean) => void;
|
|
213
|
-
setError: (errors: readonly string[]) => void;
|
|
209
|
+
setError: (errors: string | readonly string[]) => void;
|
|
214
210
|
clearError: () => void;
|
|
215
211
|
validate: () => FieldValidationResult;
|
|
216
212
|
validateAsync: () => Promise<FieldValidationResult>;
|
|
217
213
|
onBlur: () => void;
|
|
218
214
|
}>;
|
|
219
|
-
declare function useField<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
|
|
220
215
|
declare function useField<TSchema extends z.ZodType>(form: Form<TSchema>, pathSelector: (state: FormState<z.input<TSchema>>) => string, options?: UseFieldOptions): UseFieldReturn<unknown>;
|
|
221
216
|
declare function useField<TValue = unknown>(form: FieldFormApi & {
|
|
222
217
|
readonly schema?: undefined;
|
|
223
218
|
}, path: string | ((state: FormState<unknown>) => string), options?: UseFieldOptions): UseFieldReturn<TValue>;
|
|
219
|
+
declare function useField<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P, options?: UseFieldOptions): UseFieldReturn<FieldValue<z.input<TSchema>, P>>;
|
|
224
220
|
|
|
221
|
+
type PathsOf<F extends FieldFormApi> = F extends Form<infer TSchema extends z.ZodType> ? FieldPath<z.input<TSchema>> : string;
|
|
225
222
|
type FieldRef<T> = ((instance: T | null) => void) | Readonly<{
|
|
226
223
|
current: T | null;
|
|
227
224
|
}> | null;
|
|
228
|
-
type TextFieldProps = Readonly<{
|
|
229
|
-
form:
|
|
230
|
-
path:
|
|
225
|
+
type TextFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
226
|
+
form: F;
|
|
227
|
+
path: PathsOf<F>;
|
|
231
228
|
label?: ReactNode;
|
|
232
229
|
placeholder?: string;
|
|
233
230
|
type?: "text" | "password" | "email" | "url" | "tel";
|
|
234
231
|
autoComplete?: string;
|
|
235
232
|
ref?: FieldRef<HTMLInputElement>;
|
|
236
233
|
}>;
|
|
237
|
-
declare const TextField: ({ form, path, label, placeholder, type, autoComplete, ref, }: TextFieldProps) => react.JSX.Element;
|
|
238
|
-
type NumberFieldProps = Readonly<{
|
|
239
|
-
form:
|
|
240
|
-
path:
|
|
234
|
+
declare const TextField: <F extends FieldFormApi>({ form, path, label, placeholder, type, autoComplete, ref, }: TextFieldProps<F>) => react.JSX.Element;
|
|
235
|
+
type NumberFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
236
|
+
form: F;
|
|
237
|
+
path: PathsOf<F>;
|
|
241
238
|
label?: ReactNode;
|
|
242
239
|
placeholder?: string;
|
|
243
240
|
ref?: FieldRef<HTMLInputElement>;
|
|
244
241
|
}>;
|
|
245
|
-
declare const NumberField: ({ form, path, label, placeholder, ref, }: NumberFieldProps) => react.JSX.Element;
|
|
246
|
-
type CheckboxFieldProps = Readonly<{
|
|
247
|
-
form:
|
|
248
|
-
path:
|
|
242
|
+
declare const NumberField: <F extends FieldFormApi>({ form, path, label, placeholder, ref, }: NumberFieldProps<F>) => react.JSX.Element;
|
|
243
|
+
type CheckboxFieldProps<F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
244
|
+
form: F;
|
|
245
|
+
path: PathsOf<F>;
|
|
249
246
|
label?: ReactNode;
|
|
250
247
|
ref?: FieldRef<HTMLInputElement>;
|
|
251
248
|
}>;
|
|
252
|
-
declare const CheckboxField: ({ form, path, label, ref }: CheckboxFieldProps) => react.JSX.Element;
|
|
249
|
+
declare const CheckboxField: <F extends FieldFormApi>({ form, path, label, ref, }: CheckboxFieldProps<F>) => react.JSX.Element;
|
|
253
250
|
type SelectFieldOption<T extends string> = Readonly<{
|
|
254
251
|
value: T;
|
|
255
252
|
label: ReactNode;
|
|
256
253
|
}>;
|
|
257
|
-
type SelectFieldProps<T extends string> = Readonly<{
|
|
258
|
-
form:
|
|
259
|
-
path:
|
|
254
|
+
type SelectFieldProps<T extends string, F extends FieldFormApi = FieldFormApi> = Readonly<{
|
|
255
|
+
form: F;
|
|
256
|
+
path: PathsOf<F>;
|
|
260
257
|
label?: ReactNode;
|
|
261
258
|
options: readonly SelectFieldOption<T>[];
|
|
262
259
|
placeholder?: string;
|
|
263
260
|
ref?: FieldRef<HTMLSelectElement>;
|
|
264
261
|
}>;
|
|
265
|
-
declare const SelectField: <T extends string>({ form, path, label, options, placeholder, ref, }: SelectFieldProps<T>) => react.JSX.Element;
|
|
262
|
+
declare const SelectField: <T extends string, F extends FieldFormApi>({ form, path, label, options, placeholder, ref, }: SelectFieldProps<T, F>) => react.JSX.Element;
|
|
266
263
|
|
|
267
264
|
declare const focusFirstError: (errors: ErrorMap, root?: ParentNode) => boolean;
|
|
265
|
+
declare const focusField: (path: string, root?: ParentNode) => boolean;
|
|
268
266
|
|
|
269
267
|
type ReadonlyStore<T> = Pick<StoreApi<T>, "getState" | "getInitialState" | "subscribe">;
|
|
270
268
|
type FieldArrayFormApi = Readonly<{
|
|
@@ -322,9 +320,19 @@ type SelectProps = Readonly<{
|
|
|
322
320
|
onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
|
|
323
321
|
onBlur: () => void;
|
|
324
322
|
}>;
|
|
323
|
+
declare const numberToInputText: (value: number | null | undefined) => string;
|
|
324
|
+
type ParsedNumberText = Readonly<{
|
|
325
|
+
kind: "empty";
|
|
326
|
+
}> | Readonly<{
|
|
327
|
+
kind: "number";
|
|
328
|
+
value: number;
|
|
329
|
+
}> | Readonly<{
|
|
330
|
+
kind: "invalid";
|
|
331
|
+
}>;
|
|
332
|
+
declare const parseNumberText: (text: string) => ParsedNumberText;
|
|
325
333
|
declare const textInputProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => TextInputProps;
|
|
326
334
|
declare const numberInputProps: <T extends number | null | undefined>(field: UseFieldReturn<T>) => NumberInputProps;
|
|
327
335
|
declare const checkboxProps: <T extends boolean | null | undefined>(field: UseFieldReturn<T>) => CheckboxProps;
|
|
328
336
|
declare const selectProps: <T extends string | null | undefined>(field: UseFieldReturn<T>) => SelectProps;
|
|
329
337
|
|
|
330
|
-
export { type BoolMap, CheckboxField, type CheckboxFieldProps, type CheckboxProps, type CreateFormOptions, type ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, type FieldPath, type FieldPathArg, type FieldRef, type FieldSnapshot, type FieldValidationResult, type FieldValue, type Form, type FormContextApi, type FormProviderProps, type FormState, type FormStateApi, type InvalidSubmitHandler, NumberField, type NumberFieldProps, type NumberInputProps, type PathSegment, type ReadonlyStoreApi, type ResetOptions, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, type SettledFieldValidationResult, type SettledValidationResult, type SubmitHandler, type SubmitOptions, type SubmitResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type ValidationMode, type ValidationResult, type ValidationTrigger, checkboxProps, createForm, createFormContext, flattenIssues, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow,
|
|
338
|
+
export { type BoolMap, CheckboxField, type CheckboxFieldProps, type CheckboxProps, type CreateFormOptions, type ErrorMap, type FieldArrayEntry, type FieldArrayFormApi, type FieldFormApi, type FieldPath, type FieldPathArg, type FieldRef, type FieldSnapshot, type FieldValidationResult, type FieldValue, type Form, type FormContextApi, type FormProviderProps, type FormState, type FormStateApi, type InvalidSubmitHandler, NumberField, type NumberFieldProps, type NumberInputProps, type ParsedNumberText, type PathSegment, type PathsOf, type ReadonlyStoreApi, type ResetOptions, SelectField, type SelectFieldOption, type SelectFieldProps, type SelectProps, type SettledFieldValidationResult, type SettledValidationResult, type SubmitHandler, type SubmitOptions, type SubmitResult, TextField, type TextFieldProps, type TextInputProps, type UseFieldArrayReturn, type UseFieldOptions, type UseFieldReturn, type ValidationMode, type ValidationResult, type ValidationTrigger, checkboxProps, createForm, createFormContext, emptyValueForSchema, flattenIssues, focusField, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, numberToInputText, parseNumberText, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow, useIsDirty, useIsSubmitting, useIsValid, useSubmitCount, validateAsync, validateSync };
|
package/dist/index.js
CHANGED
|
@@ -186,6 +186,39 @@ var emptyValueForSchema = (schema) => {
|
|
|
186
186
|
};
|
|
187
187
|
return walk(schema, false);
|
|
188
188
|
};
|
|
189
|
+
var unwrapForWalk = (s) => {
|
|
190
|
+
if (s instanceof z.ZodOptional || s instanceof z.ZodNullable) {
|
|
191
|
+
return s.unwrap();
|
|
192
|
+
}
|
|
193
|
+
if (s instanceof z.ZodDefault || s instanceof z.ZodPrefault || s instanceof z.ZodCatch || s instanceof z.ZodReadonly || s instanceof z.ZodNonOptional) {
|
|
194
|
+
return s.def.innerType;
|
|
195
|
+
}
|
|
196
|
+
if (s instanceof z.ZodPipe) return s.def.in;
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
var isLeafSchema = (s) => s instanceof z.ZodString || s instanceof z.ZodNumber || s instanceof z.ZodBoolean || s instanceof z.ZodBigInt || s instanceof z.ZodDate || s instanceof z.ZodEnum || s instanceof z.ZodLiteral || s instanceof z.ZodNull || s instanceof z.ZodUndefined || s instanceof z.ZodVoid || s instanceof z.ZodNaN || s instanceof z.ZodSymbol;
|
|
200
|
+
var SCHEMA_WALK_BUDGET = 64;
|
|
201
|
+
var schemaHasPath = (schema, path) => {
|
|
202
|
+
const walk = (s, segments, budget) => {
|
|
203
|
+
if (budget <= 0) return true;
|
|
204
|
+
const inner = unwrapForWalk(s);
|
|
205
|
+
if (inner !== null) return walk(inner, segments, budget - 1);
|
|
206
|
+
const [head, ...rest] = segments;
|
|
207
|
+
if (head === void 0) return true;
|
|
208
|
+
if (s instanceof z.ZodObject) {
|
|
209
|
+
const shape = s.shape;
|
|
210
|
+
const child = shape[String(head)];
|
|
211
|
+
if (child !== void 0) return walk(child, rest, budget - 1);
|
|
212
|
+
const catchall = s.def.catchall;
|
|
213
|
+
return catchall !== void 0 && !(catchall instanceof z.ZodNever);
|
|
214
|
+
}
|
|
215
|
+
if (s instanceof z.ZodArray) {
|
|
216
|
+
return typeof head === "number" ? walk(s.element, rest, budget - 1) : false;
|
|
217
|
+
}
|
|
218
|
+
return !isLeafSchema(s);
|
|
219
|
+
};
|
|
220
|
+
return walk(schema, parsePath(path), SCHEMA_WALK_BUDGET);
|
|
221
|
+
};
|
|
189
222
|
var hasChecks = (s) => {
|
|
190
223
|
const checks = s.def.checks;
|
|
191
224
|
return checks !== void 0 && checks.length > 0;
|
|
@@ -227,13 +260,14 @@ var dirtyPathsOf = (values, initial, prefix) => {
|
|
|
227
260
|
if (valuesEqual(values, initial)) return [];
|
|
228
261
|
if (isPlainObject(values) && isPlainObject(initial)) {
|
|
229
262
|
const keys = /* @__PURE__ */ new Set([...Object.keys(values), ...Object.keys(initial)]);
|
|
230
|
-
|
|
263
|
+
const childPaths = [...keys].flatMap(
|
|
231
264
|
(k) => dirtyPathsOf(
|
|
232
265
|
values[k],
|
|
233
266
|
initial[k],
|
|
234
267
|
prefix === "" ? k : `${prefix}.${k}`
|
|
235
268
|
)
|
|
236
269
|
);
|
|
270
|
+
return childPaths.length > 0 ? childPaths : [prefix];
|
|
237
271
|
}
|
|
238
272
|
return [prefix];
|
|
239
273
|
};
|
|
@@ -357,6 +391,16 @@ var createForm = (schema, options) => {
|
|
|
357
391
|
subschemaCache.set(path, sub);
|
|
358
392
|
return sub;
|
|
359
393
|
};
|
|
394
|
+
const checkedSchemaPaths = /* @__PURE__ */ new Set();
|
|
395
|
+
const warnMissingSchemaPath = (fn, path) => {
|
|
396
|
+
if (path === "" || checkedSchemaPaths.has(path)) return;
|
|
397
|
+
checkedSchemaPaths.add(path);
|
|
398
|
+
if (!schemaHasPath(schema, path)) {
|
|
399
|
+
console.warn(
|
|
400
|
+
`[formstand] ${fn}("${path}"): path does not exist in the schema \u2014 result will always be valid.`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
360
404
|
const fieldScopeFor = (path, values) => {
|
|
361
405
|
const sub = path === "" ? null : subschemaAt(path);
|
|
362
406
|
if (sub === null) {
|
|
@@ -394,6 +438,7 @@ var createForm = (schema, options) => {
|
|
|
394
438
|
serverErrors: omitScope(state.serverErrors, [path])
|
|
395
439
|
});
|
|
396
440
|
const validateField = (path) => {
|
|
441
|
+
warnMissingSchemaPath("validateField", path);
|
|
397
442
|
try {
|
|
398
443
|
const scoped = scopedFieldErrors(path);
|
|
399
444
|
store.setState((state) => ({
|
|
@@ -419,10 +464,11 @@ var createForm = (schema, options) => {
|
|
|
419
464
|
};
|
|
420
465
|
};
|
|
421
466
|
const sequences = /* @__PURE__ */ new Map();
|
|
467
|
+
const seqRef = { current: 0 };
|
|
422
468
|
const nextSeq = (key) => {
|
|
423
|
-
|
|
424
|
-
sequences.set(key,
|
|
425
|
-
return
|
|
469
|
+
seqRef.current += 1;
|
|
470
|
+
sequences.set(key, seqRef.current);
|
|
471
|
+
return seqRef.current;
|
|
426
472
|
};
|
|
427
473
|
const validateAsyncOnForm = async () => {
|
|
428
474
|
const seq = nextSeq(FORM_SEQ_KEY);
|
|
@@ -502,6 +548,13 @@ var createForm = (schema, options) => {
|
|
|
502
548
|
return requestedValid;
|
|
503
549
|
};
|
|
504
550
|
const validateFieldAsync = async (path) => {
|
|
551
|
+
warnMissingSchemaPath("validateFieldAsync", path);
|
|
552
|
+
if (path === "") {
|
|
553
|
+
const result = await validateAsyncOnForm();
|
|
554
|
+
return fieldResult(
|
|
555
|
+
result.kind === "invalid" ? result.errors : emptyErrors
|
|
556
|
+
);
|
|
557
|
+
}
|
|
505
558
|
const seq = nextSeq(path);
|
|
506
559
|
const valuesAtStart = store.getState().values;
|
|
507
560
|
store.setState((state) => ({
|
|
@@ -561,7 +614,13 @@ var createForm = (schema, options) => {
|
|
|
561
614
|
)
|
|
562
615
|
}),
|
|
563
616
|
touched: reKeyByArrayPath(state.touched, path, mapper),
|
|
564
|
-
|
|
617
|
+
// Drop (don't re-key) in-flight flags under the array: the op changes
|
|
618
|
+
// `values`, so every pass they belong to fails its values-changed
|
|
619
|
+
// guard and never commits — but each pass's cleanup omits its
|
|
620
|
+
// ORIGINAL key, so a re-keyed entry would be orphaned and stick
|
|
621
|
+
// forever. Dropped, the flag is gone now and the cleanup is a
|
|
622
|
+
// harmless no-op.
|
|
623
|
+
isValidating: omitScope(state.isValidating, [path])
|
|
565
624
|
};
|
|
566
625
|
});
|
|
567
626
|
};
|
|
@@ -583,33 +642,43 @@ var createForm = (schema, options) => {
|
|
|
583
642
|
submitCount: state.submitCount + 1
|
|
584
643
|
}));
|
|
585
644
|
try {
|
|
586
|
-
const
|
|
645
|
+
const valuesAtStart = store.getState().values;
|
|
646
|
+
const result = await validateAsync(schema, valuesAtStart);
|
|
647
|
+
const staleValues = store.getState().values !== valuesAtStart;
|
|
587
648
|
if (result.kind === "invalid") {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
Object.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
649
|
+
if (!staleValues) {
|
|
650
|
+
store.setState((state) => {
|
|
651
|
+
const committed = commitFullPass(state, result.errors);
|
|
652
|
+
return {
|
|
653
|
+
...state,
|
|
654
|
+
...committed,
|
|
655
|
+
// Mark every errored field touched so touched-gated error UIs
|
|
656
|
+
// show something after the canonical first failed submit. The
|
|
657
|
+
// "" key (schema-level refine) has no field to touch.
|
|
658
|
+
touched: {
|
|
659
|
+
...state.touched,
|
|
660
|
+
...Object.fromEntries(
|
|
661
|
+
Object.keys(committed.errors).filter((k) => k !== "").map((k) => [k, true])
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
const merged = staleValues ? result.errors : store.getState().errors;
|
|
605
668
|
onInvalid?.(merged);
|
|
606
669
|
return { kind: "invalid", errors: merged };
|
|
607
670
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
671
|
+
if (!staleValues) {
|
|
672
|
+
store.setState((state) => ({
|
|
673
|
+
...state,
|
|
674
|
+
...commitFullPass(state, emptyErrors)
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
try {
|
|
678
|
+
await onValid(result.data);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
return { kind: "error", error };
|
|
681
|
+
}
|
|
613
682
|
return { kind: "valid", data: result.data };
|
|
614
683
|
} finally {
|
|
615
684
|
inFlight.count -= 1;
|
|
@@ -741,7 +810,12 @@ var createForm = (schema, options) => {
|
|
|
741
810
|
...errorChannels(state, {
|
|
742
811
|
schemaErrors: emptyErrors,
|
|
743
812
|
serverErrors: emptyErrors
|
|
744
|
-
})
|
|
813
|
+
}),
|
|
814
|
+
// Clearing `sequences` disowns every in-flight pass, including its
|
|
815
|
+
// flag cleanup (the ownership check fails, so it never writes) —
|
|
816
|
+
// clear the flags here too, mirroring reset(), or they'd stick.
|
|
817
|
+
isValidating: emptyBools,
|
|
818
|
+
isValidatingForm: false
|
|
745
819
|
}));
|
|
746
820
|
},
|
|
747
821
|
reset: (nextInitial, resetOptions) => {
|
|
@@ -906,8 +980,6 @@ function useFormSelector(form, selector) {
|
|
|
906
980
|
function useFormSelectorShallow(form, selector) {
|
|
907
981
|
return useStore(form.store, useShallow(selector));
|
|
908
982
|
}
|
|
909
|
-
var useFormState = useFormSelector;
|
|
910
|
-
var useFormStateShallow = useFormSelectorShallow;
|
|
911
983
|
var useFormError = (form) => useStore(
|
|
912
984
|
form.store,
|
|
913
985
|
useShallow((state) => state.errors[""])
|
|
@@ -1204,7 +1276,12 @@ var NumberField = ({
|
|
|
1204
1276
|
/* @__PURE__ */ jsx(ErrorText, { id: errorId, error: field.error })
|
|
1205
1277
|
] });
|
|
1206
1278
|
};
|
|
1207
|
-
var CheckboxField = ({
|
|
1279
|
+
var CheckboxField = ({
|
|
1280
|
+
form,
|
|
1281
|
+
path,
|
|
1282
|
+
label,
|
|
1283
|
+
ref
|
|
1284
|
+
}) => {
|
|
1208
1285
|
const id = useId();
|
|
1209
1286
|
const errorId = `${id}-error`;
|
|
1210
1287
|
const field = useField(form, path);
|
|
@@ -1248,7 +1325,12 @@ var SelectField = ({
|
|
|
1248
1325
|
"aria-describedby": hasError(field.error) ? errorId : void 0,
|
|
1249
1326
|
children: [
|
|
1250
1327
|
showEmptyOption ? /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder ?? "" }) : null,
|
|
1251
|
-
options.map((opt) =>
|
|
1328
|
+
options.map((opt, index) => (
|
|
1329
|
+
// Index-qualified: option lists may legitimately repeat a value
|
|
1330
|
+
// (e.g. differently-labelled aliases), and bare value keys would
|
|
1331
|
+
// collide.
|
|
1332
|
+
/* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, `${index}-${opt.value}`)
|
|
1333
|
+
))
|
|
1252
1334
|
]
|
|
1253
1335
|
}
|
|
1254
1336
|
),
|
|
@@ -1257,25 +1339,40 @@ var SelectField = ({
|
|
|
1257
1339
|
};
|
|
1258
1340
|
|
|
1259
1341
|
// src/react/focusError.ts
|
|
1342
|
+
var focusableControls = (scope) => [
|
|
1343
|
+
...scope.querySelectorAll(
|
|
1344
|
+
"input[name], select[name], textarea[name]"
|
|
1345
|
+
)
|
|
1346
|
+
].filter(
|
|
1347
|
+
(el) => !el.matches(':disabled, [type="hidden"]') && el.closest("dialog:not([open])") === null
|
|
1348
|
+
);
|
|
1349
|
+
var focusFirstOf = (candidates) => candidates.some((el) => {
|
|
1350
|
+
el.focus();
|
|
1351
|
+
return document.activeElement === el;
|
|
1352
|
+
});
|
|
1353
|
+
var nameMatchesAny = (el, paths) => {
|
|
1354
|
+
const name = el.getAttribute("name");
|
|
1355
|
+
return name !== null && paths.some((p) => isPathOrChild(name, p));
|
|
1356
|
+
};
|
|
1260
1357
|
var focusFirstError = (errors, root) => {
|
|
1261
1358
|
const scope = root ?? document;
|
|
1262
1359
|
const erroredPaths = Object.keys(errors).filter(
|
|
1263
1360
|
(k) => k !== "" && (errors[k]?.length ?? 0) > 0
|
|
1264
1361
|
);
|
|
1265
1362
|
const hasRootError = (errors[""]?.length ?? 0) > 0;
|
|
1266
|
-
const controls =
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
return name !== null && erroredPaths.some((k) => isPathOrChild(name, k));
|
|
1274
|
-
});
|
|
1275
|
-
const target = fieldMatch ?? (hasRootError ? controls[0] : void 0);
|
|
1276
|
-
target?.focus();
|
|
1277
|
-
return target !== void 0;
|
|
1363
|
+
const controls = focusableControls(scope);
|
|
1364
|
+
const fieldMatches = controls.filter(
|
|
1365
|
+
(el) => nameMatchesAny(el, erroredPaths)
|
|
1366
|
+
);
|
|
1367
|
+
const rootFallbackApplies = hasRootError && (root !== void 0 || document.querySelectorAll("form").length <= 1);
|
|
1368
|
+
const candidates = fieldMatches.length > 0 ? fieldMatches : rootFallbackApplies ? controls : [];
|
|
1369
|
+
return focusFirstOf(candidates);
|
|
1278
1370
|
};
|
|
1371
|
+
var focusField = (path, root) => focusFirstOf(
|
|
1372
|
+
focusableControls(root ?? document).filter(
|
|
1373
|
+
(el) => nameMatchesAny(el, [path])
|
|
1374
|
+
)
|
|
1375
|
+
);
|
|
1279
1376
|
var EMPTY_ID_STATE = { items: [], ids: [], counter: 0 };
|
|
1280
1377
|
var EMPTY_ITEMS = [];
|
|
1281
1378
|
var reconcileIds = (prev, nextItems) => {
|
|
@@ -1381,6 +1478,6 @@ var useFieldArray = (form, pathArg) => {
|
|
|
1381
1478
|
);
|
|
1382
1479
|
};
|
|
1383
1480
|
|
|
1384
|
-
export { CheckboxField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, flattenIssues, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow,
|
|
1481
|
+
export { CheckboxField, NumberField, SelectField, TextField, checkboxProps, createForm, createFormContext, emptyValueForSchema, flattenIssues, focusField, focusFirstError, getAtPath, isAsyncRequiredError, numberInputProps, numberToInputText, parseNumberText, parsePath, selectProps, setAtPath, shouldValidateOn, textInputProps, useField, useFieldArray, useForm, useFormError, useFormSelector, useFormSelectorShallow, useIsDirty, useIsSubmitting, useIsValid, useSubmitCount, validateAsync, validateSync };
|
|
1385
1482
|
//# sourceMappingURL=index.js.map
|
|
1386
1483
|
//# sourceMappingURL=index.js.map
|