formstand 0.4.0 → 0.5.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 +163 -12
- package/dist/index.cjs +41 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +41 -25
- package/dist/index.js.map +1 -1
- package/package.json +7 -1
package/dist/index.d.cts
CHANGED
|
@@ -85,7 +85,7 @@ type SubmitResult<TOutput> = Readonly<{
|
|
|
85
85
|
kind: "error";
|
|
86
86
|
error: unknown;
|
|
87
87
|
}>;
|
|
88
|
-
type ArrayItemOf<T> = T extends readonly (infer U)[] ? U : never;
|
|
88
|
+
type ArrayItemOf$1<T> = T extends readonly (infer U)[] ? U : never;
|
|
89
89
|
type ErrorPath<TValues> = FieldPath<TValues> | "";
|
|
90
90
|
type ResetOptions = Readonly<{
|
|
91
91
|
keepErrors?: boolean;
|
|
@@ -129,9 +129,9 @@ type Form<TSchema extends z.ZodType> = Readonly<{
|
|
|
129
129
|
validateFieldAsync: (path: ErrorPath<z.input<TSchema>>) => Promise<FieldValidationResult>;
|
|
130
130
|
validateFieldsAsync: (paths: readonly FieldPath<z.input<TSchema>>[]) => Promise<boolean>;
|
|
131
131
|
submit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => Promise<SubmitResult<z.output<TSchema>>>;
|
|
132
|
-
arrayPush: <P extends FieldPath<z.input<TSchema>>>(path: P, item: ArrayItemOf<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
132
|
+
arrayPush: <P extends FieldPath<z.input<TSchema>>>(path: P, item: ArrayItemOf$1<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
133
133
|
arrayRemove: (path: FieldPath<z.input<TSchema>>, index: number) => void;
|
|
134
|
-
arrayInsert: <P extends FieldPath<z.input<TSchema>>>(path: P, index: number, item: ArrayItemOf<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
134
|
+
arrayInsert: <P extends FieldPath<z.input<TSchema>>>(path: P, index: number, item: ArrayItemOf$1<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
135
135
|
arrayMove: (path: FieldPath<z.input<TSchema>>, from: number, to: number) => void;
|
|
136
136
|
arraySwap: (path: FieldPath<z.input<TSchema>>, a: number, b: number) => void;
|
|
137
137
|
handleSubmit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => (event?: {
|
|
@@ -288,7 +288,12 @@ type UseFieldArrayReturn<TItem> = Readonly<{
|
|
|
288
288
|
move: (from: number, to: number) => void;
|
|
289
289
|
swap: (a: number, b: number) => void;
|
|
290
290
|
}>;
|
|
291
|
-
|
|
291
|
+
type ArrayItemOf<T> = [NonNullable<T>] extends [readonly (infer U)[]] ? U : never;
|
|
292
|
+
declare function useFieldArray<TSchema extends z.ZodType>(form: Form<TSchema>, pathSelector: (state: FormState<z.input<TSchema>>) => string): UseFieldArrayReturn<unknown>;
|
|
293
|
+
declare function useFieldArray<TItem = unknown>(form: FieldArrayFormApi & {
|
|
294
|
+
readonly schema?: undefined;
|
|
295
|
+
}, path: string | ((state: FormState<unknown>) => string)): UseFieldArrayReturn<TItem>;
|
|
296
|
+
declare function useFieldArray<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P): UseFieldArrayReturn<ArrayItemOf<FieldValue<z.input<TSchema>, P>>>;
|
|
292
297
|
|
|
293
298
|
type TextInputProps = Readonly<{
|
|
294
299
|
name: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -85,7 +85,7 @@ type SubmitResult<TOutput> = Readonly<{
|
|
|
85
85
|
kind: "error";
|
|
86
86
|
error: unknown;
|
|
87
87
|
}>;
|
|
88
|
-
type ArrayItemOf<T> = T extends readonly (infer U)[] ? U : never;
|
|
88
|
+
type ArrayItemOf$1<T> = T extends readonly (infer U)[] ? U : never;
|
|
89
89
|
type ErrorPath<TValues> = FieldPath<TValues> | "";
|
|
90
90
|
type ResetOptions = Readonly<{
|
|
91
91
|
keepErrors?: boolean;
|
|
@@ -129,9 +129,9 @@ type Form<TSchema extends z.ZodType> = Readonly<{
|
|
|
129
129
|
validateFieldAsync: (path: ErrorPath<z.input<TSchema>>) => Promise<FieldValidationResult>;
|
|
130
130
|
validateFieldsAsync: (paths: readonly FieldPath<z.input<TSchema>>[]) => Promise<boolean>;
|
|
131
131
|
submit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => Promise<SubmitResult<z.output<TSchema>>>;
|
|
132
|
-
arrayPush: <P extends FieldPath<z.input<TSchema>>>(path: P, item: ArrayItemOf<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
132
|
+
arrayPush: <P extends FieldPath<z.input<TSchema>>>(path: P, item: ArrayItemOf$1<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
133
133
|
arrayRemove: (path: FieldPath<z.input<TSchema>>, index: number) => void;
|
|
134
|
-
arrayInsert: <P extends FieldPath<z.input<TSchema>>>(path: P, index: number, item: ArrayItemOf<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
134
|
+
arrayInsert: <P extends FieldPath<z.input<TSchema>>>(path: P, index: number, item: ArrayItemOf$1<NonNullable<FieldValue<z.input<TSchema>, P>>>) => void;
|
|
135
135
|
arrayMove: (path: FieldPath<z.input<TSchema>>, from: number, to: number) => void;
|
|
136
136
|
arraySwap: (path: FieldPath<z.input<TSchema>>, a: number, b: number) => void;
|
|
137
137
|
handleSubmit: (onValid: SubmitHandler<TSchema>, onInvalid?: InvalidSubmitHandler, options?: SubmitOptions) => (event?: {
|
|
@@ -288,7 +288,12 @@ type UseFieldArrayReturn<TItem> = Readonly<{
|
|
|
288
288
|
move: (from: number, to: number) => void;
|
|
289
289
|
swap: (a: number, b: number) => void;
|
|
290
290
|
}>;
|
|
291
|
-
|
|
291
|
+
type ArrayItemOf<T> = [NonNullable<T>] extends [readonly (infer U)[]] ? U : never;
|
|
292
|
+
declare function useFieldArray<TSchema extends z.ZodType>(form: Form<TSchema>, pathSelector: (state: FormState<z.input<TSchema>>) => string): UseFieldArrayReturn<unknown>;
|
|
293
|
+
declare function useFieldArray<TItem = unknown>(form: FieldArrayFormApi & {
|
|
294
|
+
readonly schema?: undefined;
|
|
295
|
+
}, path: string | ((state: FormState<unknown>) => string)): UseFieldArrayReturn<TItem>;
|
|
296
|
+
declare function useFieldArray<TSchema extends z.ZodType, P extends FieldPath<z.input<TSchema>>>(form: Form<TSchema>, path: P): UseFieldArrayReturn<ArrayItemOf<FieldValue<z.input<TSchema>, P>>>;
|
|
292
297
|
|
|
293
298
|
type TextInputProps = Readonly<{
|
|
294
299
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -272,6 +272,7 @@ var dirtyPathsOf = (values, initial, prefix) => {
|
|
|
272
272
|
return [prefix];
|
|
273
273
|
};
|
|
274
274
|
var FORM_SEQ_KEY = "__form__";
|
|
275
|
+
var SUBMIT_SEQ_KEY = "__submit__";
|
|
275
276
|
var stringArraysEqual = (a, b) => a === b || a.length === b.length && a.every((v, i) => v === b[i]);
|
|
276
277
|
var reuseErrorRefs = (next, prev) => {
|
|
277
278
|
if (next === prev) return prev;
|
|
@@ -464,19 +465,18 @@ var createForm = (schema, options) => {
|
|
|
464
465
|
};
|
|
465
466
|
};
|
|
466
467
|
const sequences = /* @__PURE__ */ new Map();
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
return seqRef.current;
|
|
468
|
+
const claimPass = (key) => {
|
|
469
|
+
const pass = Symbol(key);
|
|
470
|
+
sequences.set(key, pass);
|
|
471
|
+
return pass;
|
|
472
472
|
};
|
|
473
473
|
const validateAsyncOnForm = async () => {
|
|
474
|
-
const
|
|
474
|
+
const pass = claimPass(FORM_SEQ_KEY);
|
|
475
475
|
const valuesAtStart = store.getState().values;
|
|
476
476
|
store.setState((state) => ({ ...state, isValidatingForm: true }));
|
|
477
477
|
const result = await validateAsync(schema, valuesAtStart);
|
|
478
478
|
const live = store.getState();
|
|
479
|
-
const stillOwns = sequences.get(FORM_SEQ_KEY) ===
|
|
479
|
+
const stillOwns = sequences.get(FORM_SEQ_KEY) === pass;
|
|
480
480
|
if (!stillOwns || live.values !== valuesAtStart) {
|
|
481
481
|
if (stillOwns) {
|
|
482
482
|
store.setState((state) => ({ ...state, isValidatingForm: false }));
|
|
@@ -499,7 +499,7 @@ var createForm = (schema, options) => {
|
|
|
499
499
|
const result2 = await validateAsyncOnForm();
|
|
500
500
|
return result2.kind === "valid";
|
|
501
501
|
}
|
|
502
|
-
const
|
|
502
|
+
const passes = new Map(paths.map((p) => [p, claimPass(p)]));
|
|
503
503
|
const valuesAtStart = store.getState().values;
|
|
504
504
|
store.setState((state) => ({
|
|
505
505
|
...state,
|
|
@@ -514,7 +514,9 @@ var createForm = (schema, options) => {
|
|
|
514
514
|
(k) => !inScopeOfAny(k, paths)
|
|
515
515
|
);
|
|
516
516
|
const live = store.getState();
|
|
517
|
-
const stillCurrent = paths.filter(
|
|
517
|
+
const stillCurrent = paths.filter(
|
|
518
|
+
(p) => sequences.get(p) === passes.get(p)
|
|
519
|
+
);
|
|
518
520
|
if (live.values !== valuesAtStart) {
|
|
519
521
|
if (stillCurrent.length > 0) {
|
|
520
522
|
store.setState((state) => ({
|
|
@@ -555,7 +557,7 @@ var createForm = (schema, options) => {
|
|
|
555
557
|
result.kind === "invalid" ? result.errors : emptyErrors
|
|
556
558
|
);
|
|
557
559
|
}
|
|
558
|
-
const
|
|
560
|
+
const pass = claimPass(path);
|
|
559
561
|
const valuesAtStart = store.getState().values;
|
|
560
562
|
store.setState((state) => ({
|
|
561
563
|
...state,
|
|
@@ -563,7 +565,7 @@ var createForm = (schema, options) => {
|
|
|
563
565
|
}));
|
|
564
566
|
const scoped = await scopedFieldErrorsAsync(path, valuesAtStart);
|
|
565
567
|
const live = store.getState();
|
|
566
|
-
const stillOwns = sequences.get(path) ===
|
|
568
|
+
const stillOwns = sequences.get(path) === pass;
|
|
567
569
|
if (!stillOwns || live.values !== valuesAtStart) {
|
|
568
570
|
if (stillOwns) {
|
|
569
571
|
store.setState((state) => ({
|
|
@@ -642,11 +644,12 @@ var createForm = (schema, options) => {
|
|
|
642
644
|
submitCount: state.submitCount + 1
|
|
643
645
|
}));
|
|
644
646
|
try {
|
|
647
|
+
const pass = claimPass(SUBMIT_SEQ_KEY);
|
|
645
648
|
const valuesAtStart = store.getState().values;
|
|
646
649
|
const result = await validateAsync(schema, valuesAtStart);
|
|
647
|
-
const
|
|
650
|
+
const stale = sequences.get(SUBMIT_SEQ_KEY) !== pass || store.getState().values !== valuesAtStart;
|
|
648
651
|
if (result.kind === "invalid") {
|
|
649
|
-
if (!
|
|
652
|
+
if (!stale) {
|
|
650
653
|
store.setState((state) => {
|
|
651
654
|
const committed = commitFullPass(state, result.errors);
|
|
652
655
|
return {
|
|
@@ -664,11 +667,11 @@ var createForm = (schema, options) => {
|
|
|
664
667
|
};
|
|
665
668
|
});
|
|
666
669
|
}
|
|
667
|
-
const merged =
|
|
670
|
+
const merged = stale ? result.errors : store.getState().errors;
|
|
668
671
|
onInvalid?.(merged);
|
|
669
672
|
return { kind: "invalid", errors: merged };
|
|
670
673
|
}
|
|
671
|
-
if (!
|
|
674
|
+
if (!stale) {
|
|
672
675
|
store.setState((state) => ({
|
|
673
676
|
...state,
|
|
674
677
|
...commitFullPass(state, emptyErrors)
|
|
@@ -921,7 +924,13 @@ var createForm = (schema, options) => {
|
|
|
921
924
|
errors: reuseErrorRefs(
|
|
922
925
|
mergeErrorChannels(schemaErrors, serverErrors),
|
|
923
926
|
snap.errors ?? emptyErrors
|
|
924
|
-
)
|
|
927
|
+
),
|
|
928
|
+
// In-flight state is owned by live passes, never by snapshots: a
|
|
929
|
+
// snapshotted flag has no pass left to clear it (the pass that set
|
|
930
|
+
// it cleared its ORIGINAL entry when it settled), so restoring one
|
|
931
|
+
// would stick it forever.
|
|
932
|
+
isValidating: emptyBools,
|
|
933
|
+
isValidatingForm: false
|
|
925
934
|
};
|
|
926
935
|
})
|
|
927
936
|
});
|
|
@@ -1077,7 +1086,10 @@ function useField(form, pathArg, options) {
|
|
|
1077
1086
|
[form, path]
|
|
1078
1087
|
);
|
|
1079
1088
|
const setError = useCallback(
|
|
1080
|
-
|
|
1089
|
+
// Normalize the string shorthand HERE, before forwarding: custom
|
|
1090
|
+
// FieldFormApi implementations written against the pre-0.4 surface are
|
|
1091
|
+
// typed for readonly string[] and must never receive a bare string.
|
|
1092
|
+
(errors) => form.setError(path, typeof errors === "string" ? [errors] : errors),
|
|
1081
1093
|
[form, path]
|
|
1082
1094
|
);
|
|
1083
1095
|
const clearError = useCallback(
|
|
@@ -1346,9 +1358,14 @@ var focusableControls = (scope) => [
|
|
|
1346
1358
|
].filter(
|
|
1347
1359
|
(el) => !el.matches(':disabled, [type="hidden"]') && el.closest("dialog:not([open])") === null
|
|
1348
1360
|
);
|
|
1361
|
+
var holdsFocus = (el) => {
|
|
1362
|
+
const rootNode = el.getRootNode();
|
|
1363
|
+
const active = rootNode instanceof Document || rootNode instanceof ShadowRoot ? rootNode.activeElement : document.activeElement;
|
|
1364
|
+
return active === el;
|
|
1365
|
+
};
|
|
1349
1366
|
var focusFirstOf = (candidates) => candidates.some((el) => {
|
|
1350
1367
|
el.focus();
|
|
1351
|
-
return
|
|
1368
|
+
return holdsFocus(el);
|
|
1352
1369
|
});
|
|
1353
1370
|
var nameMatchesAny = (el, paths) => {
|
|
1354
1371
|
const name = el.getAttribute("name");
|
|
@@ -1368,11 +1385,10 @@ var focusFirstError = (errors, root) => {
|
|
|
1368
1385
|
const candidates = fieldMatches.length > 0 ? fieldMatches : rootFallbackApplies ? controls : [];
|
|
1369
1386
|
return focusFirstOf(candidates);
|
|
1370
1387
|
};
|
|
1371
|
-
var focusField = (path, root) =>
|
|
1372
|
-
focusableControls(root ?? document)
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
);
|
|
1388
|
+
var focusField = (path, root) => {
|
|
1389
|
+
const controls = focusableControls(root ?? document);
|
|
1390
|
+
return path === "" ? (root !== void 0 || document.querySelectorAll("form").length <= 1) && focusFirstOf(controls) : focusFirstOf(controls.filter((el) => nameMatchesAny(el, [path])));
|
|
1391
|
+
};
|
|
1376
1392
|
var EMPTY_ID_STATE = { items: [], ids: [], counter: 0 };
|
|
1377
1393
|
var EMPTY_ITEMS = [];
|
|
1378
1394
|
var reconcileIds = (prev, nextItems) => {
|
|
@@ -1411,7 +1427,7 @@ var reconcileIds = (prev, nextItems) => {
|
|
|
1411
1427
|
counter: prev.counter + Math.max(0, unmatched - leftover.length)
|
|
1412
1428
|
};
|
|
1413
1429
|
};
|
|
1414
|
-
|
|
1430
|
+
function useFieldArray(form, pathArg) {
|
|
1415
1431
|
const path = useStore(
|
|
1416
1432
|
form.store,
|
|
1417
1433
|
(state) => typeof pathArg === "function" ? pathArg(state) : pathArg
|
|
@@ -1476,7 +1492,7 @@ var useFieldArray = (form, pathArg) => {
|
|
|
1476
1492
|
}),
|
|
1477
1493
|
[fields, items, error, push, remove, insert, move, swap]
|
|
1478
1494
|
);
|
|
1479
|
-
}
|
|
1495
|
+
}
|
|
1480
1496
|
|
|
1481
1497
|
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 };
|
|
1482
1498
|
//# sourceMappingURL=index.js.map
|