@reckona/mreact-forms 0.0.180 → 0.0.182
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/README.md +2 -0
- package/dist/index.d.ts +26 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +234 -11
package/README.md
CHANGED
|
@@ -108,7 +108,9 @@ await inviteForm.submit((values) => {
|
|
|
108
108
|
|
|
109
109
|
- `createForm()` creates reactive form state.
|
|
110
110
|
- `form.field(name).bind()` returns `{ value, onInput, onChange, onBlur }` and handles string values and boolean checkbox-style values. Pass `{ event: "change" }` to update from `onChange`; the default updates from `onInput`.
|
|
111
|
+
- `form.fieldArray(name)` returns stable keyed rows and `append`, `insert`, `move`, `remove`, and `swap` helpers for array-valued form fields.
|
|
111
112
|
- Field state includes `validating`, which is true while the latest async field validator is pending. Slower stale validator results are ignored.
|
|
113
|
+
- Field validators can use `{ validate, deps }` when one field should be revalidated after another field changes or blurs.
|
|
112
114
|
- `setServerErrors()` applies errors returned by a route handler or server action.
|
|
113
115
|
- `form.submit()` dedupes concurrent submissions and keeps `submitting` true until the active validation and handler finish.
|
|
114
116
|
- `form.reset()` restores the initial values.
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export type FieldName<TValues extends FormValues> = Extract<keyof TValues, strin
|
|
|
8
8
|
export type FormErrors<TValues extends FormValues> = Partial<Record<FieldName<TValues> | "root", string[]>>;
|
|
9
9
|
/** Validates one field value against the full form values object. */
|
|
10
10
|
export type FieldValidator<TValue, TValues extends FormValues> = (value: TValue, values: TValues) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;
|
|
11
|
+
/** Configures one field validator and which other fields should re-trigger it. */
|
|
12
|
+
export interface FieldValidationConfig<TValue, TValues extends FormValues> {
|
|
13
|
+
deps?: readonly FieldName<TValues>[] | undefined;
|
|
14
|
+
validate: FieldValidator<TValue, TValues>;
|
|
15
|
+
}
|
|
16
|
+
/** Provides either a validator function or a validator descriptor with dependencies. */
|
|
17
|
+
export type FieldValidationEntry<TValue, TValues extends FormValues> = FieldValidator<TValue, TValues> | FieldValidationConfig<TValue, TValues>;
|
|
11
18
|
/** Names the form events that can trigger validation. */
|
|
12
19
|
export type FormValidateMode = "change" | "blur" | "submit";
|
|
13
20
|
/** Describes the complete reactive state tracked by a form instance. */
|
|
@@ -48,10 +55,27 @@ export interface FieldApi<TValues extends FormValues, Name extends FieldName<TVa
|
|
|
48
55
|
blur(): Promise<void>;
|
|
49
56
|
setValue(value: TValues[Name]): Promise<void>;
|
|
50
57
|
}
|
|
58
|
+
/** Extracts an array field's item type. */
|
|
59
|
+
export type ArrayFieldValue<TValues extends FormValues, Name extends FieldName<TValues>> = TValues[Name] extends readonly (infer Item)[] ? Item : never;
|
|
60
|
+
/** Describes one rendered row in a form array field. */
|
|
61
|
+
export interface FieldArrayRow<TValue> {
|
|
62
|
+
index: number;
|
|
63
|
+
key: string;
|
|
64
|
+
value: TValue;
|
|
65
|
+
}
|
|
66
|
+
/** Exposes stable keyed row state and mutation helpers for an array field. */
|
|
67
|
+
export interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {
|
|
68
|
+
readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;
|
|
69
|
+
append(value: ArrayFieldValue<TValues, Name>): Promise<void>;
|
|
70
|
+
insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;
|
|
71
|
+
move(from: number, to: number): Promise<void>;
|
|
72
|
+
remove(index: number): Promise<void>;
|
|
73
|
+
swap(first: number, second: number): Promise<void>;
|
|
74
|
+
}
|
|
51
75
|
interface BaseCreateFormOptions<TValues extends FormValues> {
|
|
52
76
|
initialValues: TValues;
|
|
53
77
|
validate?: Partial<{
|
|
54
|
-
[Name in FieldName<TValues>]:
|
|
78
|
+
[Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;
|
|
55
79
|
}> | undefined;
|
|
56
80
|
validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;
|
|
57
81
|
}
|
|
@@ -94,6 +118,7 @@ export interface ServerActionErrors<TValues extends FormValues> {
|
|
|
94
118
|
/** Provides reactive form state, field access, validation, submit, reset, and error controls. */
|
|
95
119
|
export interface FormApi<TValues extends FormValues, TSubmitValues> {
|
|
96
120
|
readonly state: ReadonlyCell<FormState<TValues>>;
|
|
121
|
+
fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;
|
|
97
122
|
field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;
|
|
98
123
|
getValues(): TValues;
|
|
99
124
|
reset(values?: TValues): void;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,sBAAsB,CAAC;AAErF,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,MAAM,SAAS,CAAC,OAAO,SAAS,UAAU,IAAI,OAAO,CAAC,MAAM,OAAO,EAAE,MAAM,CAAC,CAAC;AAEnF,2EAA2E;AAC3E,MAAM,MAAM,UAAU,CAAC,OAAO,SAAS,UAAU,IAAI,OAAO,CAC1D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,CAC9C,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,SAAS,UAAU,IAAI,CAC/D,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,KACZ,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9F,kFAAkF;AAClF,MAAM,WAAW,qBAAqB,CAAC,MAAM,EAAE,OAAO,SAAS,UAAU;IACvE,IAAI,CAAC,EAAE,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;IACjD,QAAQ,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,wFAAwF;AACxF,MAAM,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,SAAS,UAAU,IAC/D,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE3C,yDAAyD;AACzD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5D,wEAAwE;AACxE,MAAM,WAAW,SAAS,CAAC,OAAO,SAAS,UAAU;IACnD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,sDAAsD;AACtD,MAAM,WAAW,UAAU,CAAC,MAAM;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,YAAY,CAAC,MAAM;IAClC,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;CACxC;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ,CAAC,OAAO,SAAS,UAAU,EAAE,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC;IACnF,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,2CAA2C;AAC3C,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,UAAU,EAC1B,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC,IAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,GAAG,IAAI,GAAG,KAAK,CAAC;AAEjE,wDAAwD;AACxD,MAAM,WAAW,aAAa,CAAC,MAAM;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa,CAAC,OAAO,SAAS,UAAU,EAAE,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC;IACxF,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,UAAU,qBAAqB,CAAC,OAAO,SAAS,UAAU;IACxD,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EACL,OAAO,CAAC;SACL,IAAI,IAAI,SAAS,CAAC,OAAO,CAAC,GAAG,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;KAC3E,CAAC,GACF,SAAS,CAAC;IACd,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,GAAG,SAAS,CAAC;CACzE;AAED,iFAAiF;AACjF,MAAM,WAAW,8BAA8B,CAC7C,OAAO,SAAS,UAAU,CAC1B,SAAQ,qBAAqB,CAAC,OAAO,CAAC;IACtC,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,kGAAkG;AAClG,MAAM,WAAW,2BAA2B,CAC1C,OAAO,SAAS,UAAU,EAC1B,aAAa,CACb,SAAQ,qBAAqB,CAAC,OAAO,CAAC;IACtC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;CAClD;AAED,uGAAuG;AACvG,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,UAAU,EAAE,aAAa,GAAG,OAAO,IAC7E,8BAA8B,CAAC,OAAO,CAAC,GACvC,2BAA2B,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AAExD,oFAAoF;AACpF,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,UAAU,EAAE,aAAa,IACtE;IACE,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;CACtB,GACD;IACE,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,EAAE,KAAK,CAAC;CAChB,CAAC;AAEN,sFAAsF;AACtF,MAAM,MAAM,gBAAgB,CAAC,OAAO,SAAS,UAAU,EAAE,OAAO,IAC5D;IACE,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,MAAM,EAAE,WAAW,CAAC;CACrB,GACD;IACE,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEN,oEAAoE;AACpE,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,UAAU;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACjF,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC5C;AAED,iGAAiG;AACjG,MAAM,WAAW,OAAO,CAAC,OAAO,SAAS,UAAU,EAAE,aAAa;IAChE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,UAAU,CAAC,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACtF,KAAK,CAAC,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC5E,SAAS,IAAI,OAAO,CAAC;IACrB,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,IAAI,SAAS,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3F,MAAM,CAAC,OAAO,EACZ,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GAC7D,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,QAAQ,IAAI,OAAO,CAAC,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;CACnE;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,OAAO,SAAS,UAAU,EACnD,OAAO,EAAE,8BAA8B,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC7B,wBAAgB,UAAU,CAAC,OAAO,SAAS,UAAU,EAAE,aAAa,EAClE,OAAO,EAAE,2BAA2B,CAAC,OAAO,EAAE,aAAa,CAAC,GAC3D,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AA8jBnC,oEAAoE;AACpE,YAAY,EACV,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,yFAAyF;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,9 @@ export function createForm(options) {
|
|
|
16
16
|
});
|
|
17
17
|
const validationGenerations = new Map();
|
|
18
18
|
const dirtyFields = new Set();
|
|
19
|
+
const dependentFields = buildDependentFields(options.validate);
|
|
20
|
+
const fieldArrayKeys = new Map();
|
|
21
|
+
let nextFieldArrayKey = 0;
|
|
19
22
|
let activeSubmit;
|
|
20
23
|
function commit(patch, dirty = dirtyFields.size > 0) {
|
|
21
24
|
const previous = state.get();
|
|
@@ -30,8 +33,19 @@ export function createForm(options) {
|
|
|
30
33
|
function setErrors(errors) {
|
|
31
34
|
commit({ errors: normalizeErrors(errors) });
|
|
32
35
|
}
|
|
36
|
+
function invalidateFieldValidations(names) {
|
|
37
|
+
if (names.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const validating = { ...state.get().validating };
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);
|
|
43
|
+
validating[name] = false;
|
|
44
|
+
}
|
|
45
|
+
commit({ validating });
|
|
46
|
+
}
|
|
33
47
|
async function validateField(name) {
|
|
34
|
-
const validator = options.validate?.[name];
|
|
48
|
+
const validator = validatorForField(options.validate?.[name]);
|
|
35
49
|
const generation = (validationGenerations.get(name) ?? 0) + 1;
|
|
36
50
|
validationGenerations.set(name, generation);
|
|
37
51
|
if (validator === undefined) {
|
|
@@ -74,7 +88,7 @@ export function createForm(options) {
|
|
|
74
88
|
},
|
|
75
89
|
});
|
|
76
90
|
if (validateOn.has("change")) {
|
|
77
|
-
await
|
|
91
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
async function blurField(name) {
|
|
@@ -85,18 +99,21 @@ export function createForm(options) {
|
|
|
85
99
|
},
|
|
86
100
|
});
|
|
87
101
|
if (validateOn.has("blur")) {
|
|
88
|
-
await
|
|
102
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
89
103
|
}
|
|
90
104
|
}
|
|
91
105
|
async function validateForm() {
|
|
92
106
|
const values = state.get().values;
|
|
93
107
|
const errors = {};
|
|
94
|
-
|
|
108
|
+
const fieldNames = Object.keys(options.validate ?? {});
|
|
109
|
+
invalidateFieldValidations(fieldNames);
|
|
110
|
+
for (const name of fieldNames) {
|
|
95
111
|
const validator = options.validate?.[name];
|
|
96
|
-
|
|
112
|
+
const validate = validatorForField(validator);
|
|
113
|
+
if (validate === undefined) {
|
|
97
114
|
continue;
|
|
98
115
|
}
|
|
99
|
-
const fieldErrors = normalizeFieldErrors(await
|
|
116
|
+
const fieldErrors = normalizeFieldErrors(await validate(values[name], values));
|
|
100
117
|
if (fieldErrors.length > 0) {
|
|
101
118
|
errors[name] = fieldErrors;
|
|
102
119
|
}
|
|
@@ -127,8 +144,113 @@ export function createForm(options) {
|
|
|
127
144
|
value: values,
|
|
128
145
|
};
|
|
129
146
|
}
|
|
147
|
+
async function validateFields(names) {
|
|
148
|
+
const uniqueNames = [...new Set(names)];
|
|
149
|
+
await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));
|
|
150
|
+
}
|
|
151
|
+
function dependentFieldsFor(name) {
|
|
152
|
+
return dependentFields.get(name) ?? [];
|
|
153
|
+
}
|
|
154
|
+
function arrayValues(name) {
|
|
155
|
+
const value = state.get().values[name];
|
|
156
|
+
return Array.isArray(value) ? [...value] : [];
|
|
157
|
+
}
|
|
158
|
+
function createFieldArrayKey(name) {
|
|
159
|
+
const key = `${name}:${nextFieldArrayKey}`;
|
|
160
|
+
nextFieldArrayKey += 1;
|
|
161
|
+
return key;
|
|
162
|
+
}
|
|
163
|
+
function ensureFieldArrayKeys(name, length) {
|
|
164
|
+
const keys = fieldArrayKeys.get(name) ?? [];
|
|
165
|
+
if (keys.length > length) {
|
|
166
|
+
keys.length = length;
|
|
167
|
+
}
|
|
168
|
+
while (keys.length < length) {
|
|
169
|
+
keys.push(createFieldArrayKey(name));
|
|
170
|
+
}
|
|
171
|
+
fieldArrayKeys.set(name, keys);
|
|
172
|
+
return keys;
|
|
173
|
+
}
|
|
174
|
+
function fieldArrayRows(name) {
|
|
175
|
+
const values = arrayValues(name);
|
|
176
|
+
const keys = ensureFieldArrayKeys(name, values.length);
|
|
177
|
+
return values.map((value, index) => ({
|
|
178
|
+
index,
|
|
179
|
+
key: keys[index] ?? createFieldArrayKey(name),
|
|
180
|
+
value,
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
async function setArrayValue(name, values, keys) {
|
|
184
|
+
fieldArrayKeys.set(name, keys);
|
|
185
|
+
await setValue(name, values);
|
|
186
|
+
}
|
|
187
|
+
async function validateArrayField(name) {
|
|
188
|
+
if (validateOn.has("change")) {
|
|
189
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
130
192
|
return {
|
|
131
193
|
state,
|
|
194
|
+
fieldArray(name) {
|
|
195
|
+
return {
|
|
196
|
+
fields: {
|
|
197
|
+
get: () => fieldArrayRows(name),
|
|
198
|
+
},
|
|
199
|
+
async append(value) {
|
|
200
|
+
const values = arrayValues(name);
|
|
201
|
+
const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];
|
|
202
|
+
values.push(value);
|
|
203
|
+
await setArrayValue(name, values, keys);
|
|
204
|
+
},
|
|
205
|
+
async insert(index, value) {
|
|
206
|
+
const values = arrayValues(name);
|
|
207
|
+
const insertIndex = clampIndex(index, 0, values.length);
|
|
208
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
209
|
+
values.splice(insertIndex, 0, value);
|
|
210
|
+
keys.splice(insertIndex, 0, createFieldArrayKey(name));
|
|
211
|
+
await setArrayValue(name, values, keys);
|
|
212
|
+
},
|
|
213
|
+
async move(from, to) {
|
|
214
|
+
const values = arrayValues(name);
|
|
215
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
216
|
+
if (!isArrayIndex(from, values.length)) {
|
|
217
|
+
await validateArrayField(name);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const [value] = values.splice(from, 1);
|
|
221
|
+
const [key] = keys.splice(from, 1);
|
|
222
|
+
const insertIndex = clampIndex(to, 0, values.length);
|
|
223
|
+
values.splice(insertIndex, 0, value);
|
|
224
|
+
keys.splice(insertIndex, 0, key);
|
|
225
|
+
await setArrayValue(name, values, keys);
|
|
226
|
+
},
|
|
227
|
+
async remove(index) {
|
|
228
|
+
const values = arrayValues(name);
|
|
229
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
230
|
+
if (!isArrayIndex(index, values.length)) {
|
|
231
|
+
await validateArrayField(name);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
values.splice(index, 1);
|
|
235
|
+
keys.splice(index, 1);
|
|
236
|
+
await setArrayValue(name, values, keys);
|
|
237
|
+
},
|
|
238
|
+
async swap(first, second) {
|
|
239
|
+
const values = arrayValues(name);
|
|
240
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
241
|
+
if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {
|
|
242
|
+
await validateArrayField(name);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
[values[first], values[second]] = [
|
|
246
|
+
values[second],
|
|
247
|
+
values[first],
|
|
248
|
+
];
|
|
249
|
+
[keys[first], keys[second]] = [keys[second], keys[first]];
|
|
250
|
+
await setArrayValue(name, values, keys);
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
},
|
|
132
254
|
field(name) {
|
|
133
255
|
return {
|
|
134
256
|
state: {
|
|
@@ -173,6 +295,7 @@ export function createForm(options) {
|
|
|
173
295
|
}
|
|
174
296
|
initialValues = cloneValues(values);
|
|
175
297
|
dirtyFields.clear();
|
|
298
|
+
fieldArrayKeys.clear();
|
|
176
299
|
commit({
|
|
177
300
|
errors: {},
|
|
178
301
|
initialValues,
|
|
@@ -303,6 +426,32 @@ function normalizeFieldErrors(errors) {
|
|
|
303
426
|
}
|
|
304
427
|
return typeof errors === "string" ? [errors] : [...errors];
|
|
305
428
|
}
|
|
429
|
+
function clampIndex(index, min, max) {
|
|
430
|
+
return Math.min(Math.max(Math.trunc(index), min), max);
|
|
431
|
+
}
|
|
432
|
+
function isArrayIndex(index, length) {
|
|
433
|
+
return Number.isInteger(index) && index >= 0 && index < length;
|
|
434
|
+
}
|
|
435
|
+
function validatorForField(entry) {
|
|
436
|
+
return typeof entry === "function" ? entry : entry?.validate;
|
|
437
|
+
}
|
|
438
|
+
function buildDependentFields(validate) {
|
|
439
|
+
const dependents = new Map();
|
|
440
|
+
for (const [fieldName, entry] of Object.entries(validate ?? {})) {
|
|
441
|
+
if (entry === undefined || typeof entry === "function") {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
for (const dep of entry.deps ?? []) {
|
|
445
|
+
const fields = dependents.get(dep);
|
|
446
|
+
if (fields === undefined) {
|
|
447
|
+
dependents.set(dep, [fieldName]);
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
fields.push(fieldName);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return dependents;
|
|
454
|
+
}
|
|
306
455
|
function cloneValues(values) {
|
|
307
456
|
return { ...values };
|
|
308
457
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { cell, type ReadonlyCell } from \"@reckona/mreact-reactive-core\";\nimport { type StandardSchemaV1, validateStandardSchema } from \"./standard-schema.js\";\n\n/** Represents the object shape managed by a form instance. */\nexport type FormValues = Record<string, unknown>;\n\n/** Extracts string field names from a form value object. */\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\n\n/** Maps field names and the root form key to validation error messages. */\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\n\n/** Validates one field value against the full form values object. */\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\n\n/** Names the form events that can trigger validation. */\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\n/** Describes the complete reactive state tracked by a form instance. */\nexport interface FormState<TValues extends FormValues> {\n dirty: boolean;\n errors: FormErrors<TValues>;\n initialValues: TValues;\n submitCount: number;\n submitting: boolean;\n touched: Partial<Record<FieldName<TValues>, boolean>>;\n valid: boolean;\n validating: Partial<Record<FieldName<TValues>, boolean>>;\n values: TValues;\n}\n\n/** Describes the derived state for one form field. */\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\n/** Provides DOM event handlers and current value for binding a field to an input. */\nexport interface FieldBinding<TValue> {\n onBlur(event: Event): Promise<void>;\n onChange(event: Event): Promise<void>;\n onInput(event: Event): Promise<void>;\n value: TValue;\n}\n\n/** Configures which DOM event updates a field binding. */\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\n/** Exposes state, binding, blur, and value update controls for one field. */\nexport interface FieldApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly state: ReadonlyCell<FieldState<TValues[Name]>>;\n bind(options?: FieldBindingOptions): FieldBinding<TValues[Name]>;\n blur(): Promise<void>;\n setValue(value: TValues[Name]): Promise<void>;\n}\n\ninterface BaseCreateFormOptions<TValues extends FormValues> {\n initialValues: TValues;\n validate?:\n | Partial<{\n [Name in FieldName<TValues>]: FieldValidator<TValues[Name], TValues>;\n }>\n | undefined;\n validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;\n}\n\n/** Configures form creation without schema-level submit value transformation. */\nexport interface CreateFormOptionsWithoutSchema<TValues extends FormValues>\n extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\n/** Configures form creation with a Standard Schema validator that may transform submit values. */\nexport interface CreateFormOptionsWithSchema<TValues extends FormValues, TSubmitValues>\n extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\n/** Configures form creation with optional field validators and optional Standard Schema validation. */\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\n/** Reports either successful normalized submit values or form validation errors. */\nexport type FormValidationResult<TValues extends FormValues, TSubmitValues> =\n | {\n success: true;\n value: TSubmitValues;\n }\n | {\n errors: FormErrors<TValues>;\n success: false;\n };\n\n/** Reports the result of a form submit handler after validation and error capture. */\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n status: \"duplicate\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\n/** Represents field and form errors returned by a server action. */\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\n/** Provides reactive form state, field access, validation, submit, reset, and error controls. */\nexport interface FormApi<TValues extends FormValues, TSubmitValues> {\n readonly state: ReadonlyCell<FormState<TValues>>;\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;\n getValues(): TValues;\n reset(values?: TValues): void;\n setErrors(errors: FormErrors<TValues>): void;\n setServerErrors(errors: ServerActionErrors<TValues>): void;\n setValue<Name extends FieldName<TValues>>(name: Name, value: TValues[Name]): Promise<void>;\n submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>>;\n validate(): Promise<FormValidationResult<TValues, TSubmitValues>>;\n}\n\n/** Creates a reactive form API from initial values, validators, and optional schema validation. */\nexport function createForm<TValues extends FormValues>(\n options: CreateFormOptionsWithoutSchema<TValues>,\n): FormApi<TValues, TValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues>(\n options: CreateFormOptionsWithSchema<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues = TValues>(\n options: CreateFormOptions<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues> {\n const validateOn = new Set(\n Array.isArray(options.validateOn) ? options.validateOn : [options.validateOn ?? \"submit\"],\n );\n let initialValues = cloneValues(options.initialValues);\n const state = cell<FormState<TValues>>({\n dirty: false,\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n valid: true,\n validating: {},\n values: cloneValues(options.initialValues),\n });\n const validationGenerations = new Map<FieldName<TValues>, number>();\n const dirtyFields = new Set<FieldName<TValues>>();\n let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;\n\n function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = dirty;\n next.valid = hasNoErrors(next.errors);\n state.set(next);\n }\n\n function setErrors(errors: FormErrors<TValues>): void {\n commit({ errors: normalizeErrors(errors) });\n }\n\n async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n const validator = options.validate?.[name];\n const generation = (validationGenerations.get(name) ?? 0) + 1;\n validationGenerations.set(name, generation);\n\n if (validator === undefined) {\n setFieldErrors(name, []);\n setFieldValidating(name, false);\n return;\n }\n\n const values = state.get().values;\n setFieldValidating(name, true);\n const errors = await validator(values[name], values);\n\n if (validationGenerations.get(name) !== generation) {\n return;\n }\n\n setFieldErrors(name, normalizeFieldErrors(errors));\n setFieldValidating(name, false);\n }\n\n function setFieldErrors<Name extends FieldName<TValues>>(\n name: Name,\n errors: readonly string[],\n ): void {\n const current = state.get().errors;\n setErrors({\n ...current,\n [name]: [...errors],\n } as FormErrors<TValues>);\n }\n\n function setFieldValidating<Name extends FieldName<TValues>>(\n name: Name,\n validating: boolean,\n ): void {\n const current = state.get().validating;\n commit({\n validating: {\n ...current,\n [name]: validating,\n },\n });\n }\n\n async function setValue<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): Promise<void> {\n const previous = state.get();\n updateDirtyField(name, value);\n commit({\n values: {\n ...previous.values,\n [name]: value,\n },\n });\n\n if (validateOn.has(\"change\")) {\n await validateField(name);\n }\n }\n\n async function blurField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n commit({\n touched: {\n ...state.get().touched,\n [name]: true,\n },\n });\n\n if (validateOn.has(\"blur\")) {\n await validateField(name);\n }\n }\n\n async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {\n const values = state.get().values;\n const errors: FormErrors<TValues> = {};\n\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n const validator = options.validate?.[name];\n if (validator === undefined) {\n continue;\n }\n const fieldErrors = normalizeFieldErrors(await validator(values[name], values));\n if (fieldErrors.length > 0) {\n errors[name] = fieldErrors;\n }\n }\n\n if (options.schema !== undefined) {\n const result = await validateStandardSchema(options.schema, values);\n\n if (!result.success) {\n mergeIssueErrors(errors, result.issues);\n } else if (Object.keys(errors).length === 0) {\n setErrors({});\n return {\n success: true,\n value: result.value as TSubmitValues,\n };\n }\n }\n\n if (Object.keys(errors).length > 0) {\n setErrors(errors);\n return {\n errors,\n success: false,\n };\n }\n\n setErrors({});\n return {\n success: true,\n value: values as TValues & TSubmitValues,\n };\n }\n\n return {\n state,\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {\n return {\n state: {\n get: () => fieldState(state.get(), name),\n },\n bind(bindingOptions = {}) {\n const bindingEvent = bindingOptions.event ?? \"input\";\n const updateValue = async (inputEvent: Event) => {\n await setValue(name, eventValue(inputEvent, state.get().values[name]) as TValues[Name]);\n };\n\n return {\n onBlur: async () => {\n await blurField(name);\n },\n onChange: async (event) => {\n if (bindingEvent === \"change\") {\n await updateValue(event);\n }\n },\n onInput: async (event) => {\n if (bindingEvent === \"input\") {\n await updateValue(event);\n }\n },\n get value() {\n return state.get().values[name];\n },\n };\n },\n async blur() {\n await blurField(name);\n },\n setValue: (value) => setValue(name, value),\n };\n },\n getValues() {\n return state.get().values;\n },\n reset(values = initialValues): void {\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n }\n\n initialValues = cloneValues(values);\n dirtyFields.clear();\n commit({\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n validating: {},\n values: cloneValues(values),\n });\n },\n setErrors,\n setServerErrors(errors): void {\n const next: FormErrors<TValues> = {};\n\n for (const [name, messages] of Object.entries(errors.fieldErrors ?? {})) {\n if (isDangerousObjectKey(name)) {\n continue;\n }\n\n if (messages !== undefined && messages.length > 0) {\n next[name as FieldName<TValues>] = [...messages];\n }\n }\n\n if (errors.formErrors !== undefined && errors.formErrors.length > 0) {\n next.root = [...errors.formErrors];\n }\n\n setErrors(next);\n },\n setValue,\n async submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>> {\n if (activeSubmit !== undefined) {\n return { status: \"duplicate\" };\n }\n\n const task = (async (): Promise<FormSubmitResult<TValues, TResult>> => {\n commit({\n submitCount: state.get().submitCount + 1,\n submitting: true,\n });\n\n try {\n const validation = await validateForm();\n\n if (!validation.success) {\n return {\n errors: validation.errors,\n status: \"invalid\",\n };\n }\n\n try {\n const data = await handler(validation.value);\n return {\n data,\n status: \"success\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n } finally {\n activeSubmit = undefined;\n commit({ submitting: false });\n }\n })();\n\n activeSubmit = task as Promise<FormSubmitResult<TValues, unknown>>;\n return task;\n },\n validate: validateForm,\n };\n\n function updateDirtyField<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): void {\n if (Object.is(value, initialValues[name])) {\n dirtyFields.delete(name);\n return;\n }\n\n dirtyFields.add(name);\n }\n}\n\nfunction fieldState<TValues extends FormValues, Name extends FieldName<TValues>>(\n formState: FormState<TValues>,\n name: Name,\n): FieldState<TValues[Name]> {\n return {\n dirty: !Object.is(formState.values[name], formState.initialValues[name]),\n errors: [...(formState.errors[name] ?? [])],\n touched: formState.touched[name] === true,\n validating: formState.validating[name] === true,\n value: formState.values[name],\n };\n}\n\nfunction eventValue(event: Event, currentValue: unknown): unknown {\n const target = event.currentTarget ?? event.target;\n\n if (target !== null && typeof target === \"object\") {\n if (typeof currentValue === \"boolean\" && \"checked\" in target) {\n return Boolean((target as { checked: unknown }).checked);\n }\n\n if (\"value\" in target) {\n return (target as { value: unknown }).value;\n }\n }\n\n return currentValue;\n}\n\nfunction mergeIssueErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): void {\n for (const issue of issues) {\n const key = issuePathKey(issue.path);\n const name = (key === undefined || isDangerousObjectKey(key) ? \"root\" : key) as\n | FieldName<TValues>\n | \"root\";\n const messages = errors[name] ?? [];\n messages.push(issue.message);\n errors[name] = messages;\n }\n}\n\nfunction issuePathKey(\n path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment> | undefined,\n): string | undefined {\n const first = path?.[0];\n\n if (first === undefined) {\n return undefined;\n }\n\n return typeof first === \"object\" && first !== null && \"key\" in first\n ? String(first.key)\n : String(first);\n}\n\nfunction normalizeErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n): FormErrors<TValues> {\n return Object.fromEntries(\n Object.entries(errors).flatMap(([name, messages]) =>\n messages === undefined || messages.length === 0 ? [] : [[name, [...messages]]],\n ),\n ) as FormErrors<TValues>;\n}\n\nfunction isDangerousObjectKey(key: PropertyKey): boolean {\n return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n}\n\nfunction hasNoErrors<TValues extends FormValues>(errors: FormErrors<TValues>): boolean {\n return (Object.values(errors) as Array<string[] | undefined>).every(\n (messages) => messages === undefined || messages.length === 0,\n );\n}\n\nfunction normalizeFieldErrors(errors: readonly string[] | string | undefined): string[] {\n if (errors === undefined) {\n return [];\n }\n\n return typeof errors === \"string\" ? [errors] : [...errors];\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\n/** Re-exports Standard Schema types used by form schema options. */\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\n/** Validates an unknown value with a Standard Schema and normalizes the result shape. */\nexport { validateStandardSchema } from \"./standard-schema.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { cell, type ReadonlyCell } from \"@reckona/mreact-reactive-core\";\nimport { type StandardSchemaV1, validateStandardSchema } from \"./standard-schema.js\";\n\n/** Represents the object shape managed by a form instance. */\nexport type FormValues = Record<string, unknown>;\n\n/** Extracts string field names from a form value object. */\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\n\n/** Maps field names and the root form key to validation error messages. */\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\n\n/** Validates one field value against the full form values object. */\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\n\n/** Configures one field validator and which other fields should re-trigger it. */\nexport interface FieldValidationConfig<TValue, TValues extends FormValues> {\n deps?: readonly FieldName<TValues>[] | undefined;\n validate: FieldValidator<TValue, TValues>;\n}\n\n/** Provides either a validator function or a validator descriptor with dependencies. */\nexport type FieldValidationEntry<TValue, TValues extends FormValues> =\n | FieldValidator<TValue, TValues>\n | FieldValidationConfig<TValue, TValues>;\n\n/** Names the form events that can trigger validation. */\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\n/** Describes the complete reactive state tracked by a form instance. */\nexport interface FormState<TValues extends FormValues> {\n dirty: boolean;\n errors: FormErrors<TValues>;\n initialValues: TValues;\n submitCount: number;\n submitting: boolean;\n touched: Partial<Record<FieldName<TValues>, boolean>>;\n valid: boolean;\n validating: Partial<Record<FieldName<TValues>, boolean>>;\n values: TValues;\n}\n\n/** Describes the derived state for one form field. */\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\n/** Provides DOM event handlers and current value for binding a field to an input. */\nexport interface FieldBinding<TValue> {\n onBlur(event: Event): Promise<void>;\n onChange(event: Event): Promise<void>;\n onInput(event: Event): Promise<void>;\n value: TValue;\n}\n\n/** Configures which DOM event updates a field binding. */\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\n/** Exposes state, binding, blur, and value update controls for one field. */\nexport interface FieldApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly state: ReadonlyCell<FieldState<TValues[Name]>>;\n bind(options?: FieldBindingOptions): FieldBinding<TValues[Name]>;\n blur(): Promise<void>;\n setValue(value: TValues[Name]): Promise<void>;\n}\n\n/** Extracts an array field's item type. */\nexport type ArrayFieldValue<\n TValues extends FormValues,\n Name extends FieldName<TValues>,\n> = TValues[Name] extends readonly (infer Item)[] ? Item : never;\n\n/** Describes one rendered row in a form array field. */\nexport interface FieldArrayRow<TValue> {\n index: number;\n key: string;\n value: TValue;\n}\n\n/** Exposes stable keyed row state and mutation helpers for an array field. */\nexport interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {\n readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;\n append(value: ArrayFieldValue<TValues, Name>): Promise<void>;\n insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;\n move(from: number, to: number): Promise<void>;\n remove(index: number): Promise<void>;\n swap(first: number, second: number): Promise<void>;\n}\n\ninterface BaseCreateFormOptions<TValues extends FormValues> {\n initialValues: TValues;\n validate?:\n | Partial<{\n [Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;\n }>\n | undefined;\n validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;\n}\n\n/** Configures form creation without schema-level submit value transformation. */\nexport interface CreateFormOptionsWithoutSchema<\n TValues extends FormValues,\n> extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\n/** Configures form creation with a Standard Schema validator that may transform submit values. */\nexport interface CreateFormOptionsWithSchema<\n TValues extends FormValues,\n TSubmitValues,\n> extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\n/** Configures form creation with optional field validators and optional Standard Schema validation. */\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\n/** Reports either successful normalized submit values or form validation errors. */\nexport type FormValidationResult<TValues extends FormValues, TSubmitValues> =\n | {\n success: true;\n value: TSubmitValues;\n }\n | {\n errors: FormErrors<TValues>;\n success: false;\n };\n\n/** Reports the result of a form submit handler after validation and error capture. */\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n status: \"duplicate\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\n/** Represents field and form errors returned by a server action. */\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\n/** Provides reactive form state, field access, validation, submit, reset, and error controls. */\nexport interface FormApi<TValues extends FormValues, TSubmitValues> {\n readonly state: ReadonlyCell<FormState<TValues>>;\n fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;\n getValues(): TValues;\n reset(values?: TValues): void;\n setErrors(errors: FormErrors<TValues>): void;\n setServerErrors(errors: ServerActionErrors<TValues>): void;\n setValue<Name extends FieldName<TValues>>(name: Name, value: TValues[Name]): Promise<void>;\n submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>>;\n validate(): Promise<FormValidationResult<TValues, TSubmitValues>>;\n}\n\n/** Creates a reactive form API from initial values, validators, and optional schema validation. */\nexport function createForm<TValues extends FormValues>(\n options: CreateFormOptionsWithoutSchema<TValues>,\n): FormApi<TValues, TValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues>(\n options: CreateFormOptionsWithSchema<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues>;\nexport function createForm<TValues extends FormValues, TSubmitValues = TValues>(\n options: CreateFormOptions<TValues, TSubmitValues>,\n): FormApi<TValues, TSubmitValues> {\n const validateOn = new Set(\n Array.isArray(options.validateOn) ? options.validateOn : [options.validateOn ?? \"submit\"],\n );\n let initialValues = cloneValues(options.initialValues);\n const state = cell<FormState<TValues>>({\n dirty: false,\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n valid: true,\n validating: {},\n values: cloneValues(options.initialValues),\n });\n const validationGenerations = new Map<FieldName<TValues>, number>();\n const dirtyFields = new Set<FieldName<TValues>>();\n const dependentFields = buildDependentFields(options.validate);\n const fieldArrayKeys = new Map<FieldName<TValues>, string[]>();\n let nextFieldArrayKey = 0;\n let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;\n\n function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = dirty;\n next.valid = hasNoErrors(next.errors);\n state.set(next);\n }\n\n function setErrors(errors: FormErrors<TValues>): void {\n commit({ errors: normalizeErrors(errors) });\n }\n\n function invalidateFieldValidations(names: readonly FieldName<TValues>[]): void {\n if (names.length === 0) {\n return;\n }\n\n const validating = { ...state.get().validating };\n for (const name of names) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n validating[name] = false;\n }\n commit({ validating });\n }\n\n async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n const validator = validatorForField(options.validate?.[name]);\n const generation = (validationGenerations.get(name) ?? 0) + 1;\n validationGenerations.set(name, generation);\n\n if (validator === undefined) {\n setFieldErrors(name, []);\n setFieldValidating(name, false);\n return;\n }\n\n const values = state.get().values;\n setFieldValidating(name, true);\n const errors = await validator(values[name], values);\n\n if (validationGenerations.get(name) !== generation) {\n return;\n }\n\n setFieldErrors(name, normalizeFieldErrors(errors));\n setFieldValidating(name, false);\n }\n\n function setFieldErrors<Name extends FieldName<TValues>>(\n name: Name,\n errors: readonly string[],\n ): void {\n const current = state.get().errors;\n setErrors({\n ...current,\n [name]: [...errors],\n } as FormErrors<TValues>);\n }\n\n function setFieldValidating<Name extends FieldName<TValues>>(\n name: Name,\n validating: boolean,\n ): void {\n const current = state.get().validating;\n commit({\n validating: {\n ...current,\n [name]: validating,\n },\n });\n }\n\n async function setValue<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): Promise<void> {\n const previous = state.get();\n updateDirtyField(name, value);\n commit({\n values: {\n ...previous.values,\n [name]: value,\n },\n });\n\n if (validateOn.has(\"change\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n async function blurField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n commit({\n touched: {\n ...state.get().touched,\n [name]: true,\n },\n });\n\n if (validateOn.has(\"blur\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {\n const values = state.get().values;\n const errors: FormErrors<TValues> = {};\n const fieldNames = Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>;\n invalidateFieldValidations(fieldNames);\n\n for (const name of fieldNames) {\n const validator = options.validate?.[name];\n const validate = validatorForField(validator);\n if (validate === undefined) {\n continue;\n }\n const fieldErrors = normalizeFieldErrors(await validate(values[name], values));\n if (fieldErrors.length > 0) {\n errors[name] = fieldErrors;\n }\n }\n\n if (options.schema !== undefined) {\n const result = await validateStandardSchema(options.schema, values);\n\n if (!result.success) {\n mergeIssueErrors(errors, result.issues);\n } else if (Object.keys(errors).length === 0) {\n setErrors({});\n return {\n success: true,\n value: result.value as TSubmitValues,\n };\n }\n }\n\n if (Object.keys(errors).length > 0) {\n setErrors(errors);\n return {\n errors,\n success: false,\n };\n }\n\n setErrors({});\n return {\n success: true,\n value: values as TValues & TSubmitValues,\n };\n }\n\n async function validateFields(names: readonly FieldName<TValues>[]): Promise<void> {\n const uniqueNames = [...new Set(names)];\n await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));\n }\n\n function dependentFieldsFor(name: FieldName<TValues>): readonly FieldName<TValues>[] {\n return dependentFields.get(name) ?? [];\n }\n\n function arrayValues<Name extends FieldName<TValues>>(\n name: Name,\n ): Array<ArrayFieldValue<TValues, Name>> {\n const value = state.get().values[name];\n return Array.isArray(value) ? ([...value] as Array<ArrayFieldValue<TValues, Name>>) : [];\n }\n\n function createFieldArrayKey(name: FieldName<TValues>): string {\n const key = `${name}:${nextFieldArrayKey}`;\n nextFieldArrayKey += 1;\n return key;\n }\n\n function ensureFieldArrayKeys(name: FieldName<TValues>, length: number): string[] {\n const keys = fieldArrayKeys.get(name) ?? [];\n if (keys.length > length) {\n keys.length = length;\n }\n\n while (keys.length < length) {\n keys.push(createFieldArrayKey(name));\n }\n\n fieldArrayKeys.set(name, keys);\n return keys;\n }\n\n function fieldArrayRows<Name extends FieldName<TValues>>(\n name: Name,\n ): Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>> {\n const values = arrayValues(name);\n const keys = ensureFieldArrayKeys(name, values.length);\n return values.map((value, index) => ({\n index,\n key: keys[index] ?? createFieldArrayKey(name),\n value,\n }));\n }\n\n async function setArrayValue<Name extends FieldName<TValues>>(\n name: Name,\n values: Array<ArrayFieldValue<TValues, Name>>,\n keys: string[],\n ): Promise<void> {\n fieldArrayKeys.set(name, keys);\n await setValue(name, values as TValues[Name]);\n }\n\n async function validateArrayField<Name extends FieldName<TValues>>(name: Name): Promise<void> {\n if (validateOn.has(\"change\")) {\n await validateFields([name, ...dependentFieldsFor(name)]);\n }\n }\n\n return {\n state,\n fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name> {\n return {\n fields: {\n get: () => fieldArrayRows(name),\n },\n async append(value) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];\n values.push(value);\n await setArrayValue(name, values, keys);\n },\n async insert(index, value) {\n const values = arrayValues(name);\n const insertIndex = clampIndex(index, 0, values.length);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n values.splice(insertIndex, 0, value);\n keys.splice(insertIndex, 0, createFieldArrayKey(name));\n await setArrayValue(name, values, keys);\n },\n async move(from, to) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(from, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n const [value] = values.splice(from, 1);\n const [key] = keys.splice(from, 1);\n const insertIndex = clampIndex(to, 0, values.length);\n values.splice(insertIndex, 0, value as ArrayFieldValue<TValues, Name>);\n keys.splice(insertIndex, 0, key as string);\n await setArrayValue(name, values, keys);\n },\n async remove(index) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(index, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n values.splice(index, 1);\n keys.splice(index, 1);\n await setArrayValue(name, values, keys);\n },\n async swap(first, second) {\n const values = arrayValues(name);\n const keys = [...ensureFieldArrayKeys(name, values.length)];\n if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {\n await validateArrayField(name);\n return;\n }\n\n [values[first], values[second]] = [\n values[second] as ArrayFieldValue<TValues, Name>,\n values[first] as ArrayFieldValue<TValues, Name>,\n ];\n [keys[first], keys[second]] = [keys[second] as string, keys[first] as string];\n await setArrayValue(name, values, keys);\n },\n };\n },\n field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {\n return {\n state: {\n get: () => fieldState(state.get(), name),\n },\n bind(bindingOptions = {}) {\n const bindingEvent = bindingOptions.event ?? \"input\";\n const updateValue = async (inputEvent: Event) => {\n await setValue(name, eventValue(inputEvent, state.get().values[name]) as TValues[Name]);\n };\n\n return {\n onBlur: async () => {\n await blurField(name);\n },\n onChange: async (event) => {\n if (bindingEvent === \"change\") {\n await updateValue(event);\n }\n },\n onInput: async (event) => {\n if (bindingEvent === \"input\") {\n await updateValue(event);\n }\n },\n get value() {\n return state.get().values[name];\n },\n };\n },\n async blur() {\n await blurField(name);\n },\n setValue: (value) => setValue(name, value),\n };\n },\n getValues() {\n return state.get().values;\n },\n reset(values = initialValues): void {\n for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {\n validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);\n }\n\n initialValues = cloneValues(values);\n dirtyFields.clear();\n fieldArrayKeys.clear();\n commit({\n errors: {},\n initialValues,\n submitCount: 0,\n submitting: false,\n touched: {},\n validating: {},\n values: cloneValues(values),\n });\n },\n setErrors,\n setServerErrors(errors): void {\n const next: FormErrors<TValues> = {};\n\n for (const [name, messages] of Object.entries(errors.fieldErrors ?? {})) {\n if (isDangerousObjectKey(name)) {\n continue;\n }\n\n if (messages !== undefined && messages.length > 0) {\n next[name as FieldName<TValues>] = [...messages];\n }\n }\n\n if (errors.formErrors !== undefined && errors.formErrors.length > 0) {\n next.root = [...errors.formErrors];\n }\n\n setErrors(next);\n },\n setValue,\n async submit<TResult>(\n handler: (values: TSubmitValues) => Promise<TResult> | TResult,\n ): Promise<FormSubmitResult<TValues, TResult>> {\n if (activeSubmit !== undefined) {\n return { status: \"duplicate\" };\n }\n\n const task = (async (): Promise<FormSubmitResult<TValues, TResult>> => {\n commit({\n submitCount: state.get().submitCount + 1,\n submitting: true,\n });\n\n try {\n const validation = await validateForm();\n\n if (!validation.success) {\n return {\n errors: validation.errors,\n status: \"invalid\",\n };\n }\n\n try {\n const data = await handler(validation.value);\n return {\n data,\n status: \"success\",\n };\n } catch (error) {\n return {\n error,\n status: \"error\",\n };\n }\n } finally {\n activeSubmit = undefined;\n commit({ submitting: false });\n }\n })();\n\n activeSubmit = task as Promise<FormSubmitResult<TValues, unknown>>;\n return task;\n },\n validate: validateForm,\n };\n\n function updateDirtyField<Name extends FieldName<TValues>>(\n name: Name,\n value: TValues[Name],\n ): void {\n if (Object.is(value, initialValues[name])) {\n dirtyFields.delete(name);\n return;\n }\n\n dirtyFields.add(name);\n }\n}\n\nfunction fieldState<TValues extends FormValues, Name extends FieldName<TValues>>(\n formState: FormState<TValues>,\n name: Name,\n): FieldState<TValues[Name]> {\n return {\n dirty: !Object.is(formState.values[name], formState.initialValues[name]),\n errors: [...(formState.errors[name] ?? [])],\n touched: formState.touched[name] === true,\n validating: formState.validating[name] === true,\n value: formState.values[name],\n };\n}\n\nfunction eventValue(event: Event, currentValue: unknown): unknown {\n const target = event.currentTarget ?? event.target;\n\n if (target !== null && typeof target === \"object\") {\n if (typeof currentValue === \"boolean\" && \"checked\" in target) {\n return Boolean((target as { checked: unknown }).checked);\n }\n\n if (\"value\" in target) {\n return (target as { value: unknown }).value;\n }\n }\n\n return currentValue;\n}\n\nfunction mergeIssueErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): void {\n for (const issue of issues) {\n const key = issuePathKey(issue.path);\n const name = (key === undefined || isDangerousObjectKey(key) ? \"root\" : key) as\n | FieldName<TValues>\n | \"root\";\n const messages = errors[name] ?? [];\n messages.push(issue.message);\n errors[name] = messages;\n }\n}\n\nfunction issuePathKey(\n path: ReadonlyArray<PropertyKey | StandardSchemaV1.PathSegment> | undefined,\n): string | undefined {\n const first = path?.[0];\n\n if (first === undefined) {\n return undefined;\n }\n\n return typeof first === \"object\" && first !== null && \"key\" in first\n ? String(first.key)\n : String(first);\n}\n\nfunction normalizeErrors<TValues extends FormValues>(\n errors: FormErrors<TValues>,\n): FormErrors<TValues> {\n return Object.fromEntries(\n Object.entries(errors).flatMap(([name, messages]) =>\n messages === undefined || messages.length === 0 ? [] : [[name, [...messages]]],\n ),\n ) as FormErrors<TValues>;\n}\n\nfunction isDangerousObjectKey(key: PropertyKey): boolean {\n return key === \"__proto__\" || key === \"constructor\" || key === \"prototype\";\n}\n\nfunction hasNoErrors<TValues extends FormValues>(errors: FormErrors<TValues>): boolean {\n return (Object.values(errors) as Array<string[] | undefined>).every(\n (messages) => messages === undefined || messages.length === 0,\n );\n}\n\nfunction normalizeFieldErrors(errors: readonly string[] | string | undefined): string[] {\n if (errors === undefined) {\n return [];\n }\n\n return typeof errors === \"string\" ? [errors] : [...errors];\n}\n\nfunction clampIndex(index: number, min: number, max: number): number {\n return Math.min(Math.max(Math.trunc(index), min), max);\n}\n\nfunction isArrayIndex(index: number, length: number): boolean {\n return Number.isInteger(index) && index >= 0 && index < length;\n}\n\nfunction validatorForField<TValue, TValues extends FormValues>(\n entry: FieldValidationEntry<TValue, TValues> | undefined,\n): FieldValidator<TValue, TValues> | undefined {\n return typeof entry === \"function\" ? entry : entry?.validate;\n}\n\nfunction buildDependentFields<TValues extends FormValues>(\n validate: BaseCreateFormOptions<TValues>[\"validate\"],\n): Map<FieldName<TValues>, FieldName<TValues>[]> {\n const dependents = new Map<FieldName<TValues>, FieldName<TValues>[]>();\n\n for (const [fieldName, entry] of Object.entries(validate ?? {}) as Array<\n [FieldName<TValues>, FieldValidationEntry<TValues[FieldName<TValues>], TValues> | undefined]\n >) {\n if (entry === undefined || typeof entry === \"function\") {\n continue;\n }\n\n for (const dep of entry.deps ?? []) {\n const fields = dependents.get(dep);\n if (fields === undefined) {\n dependents.set(dep, [fieldName]);\n continue;\n }\n fields.push(fieldName);\n }\n }\n\n return dependents;\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\n/** Re-exports Standard Schema types used by form schema options. */\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\n/** Validates an unknown value with a Standard Schema and normalizes the result shape. */\nexport { validateStandardSchema } from \"./standard-schema.js\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-forms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.182",
|
|
4
4
|
"description": "Reactive form state and validation utilities for mreact.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"forms",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@reckona/mreact-reactive-core": "0.0.
|
|
47
|
+
"@reckona/mreact-reactive-core": "0.0.182"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,17 @@ export type FieldValidator<TValue, TValues extends FormValues> = (
|
|
|
18
18
|
values: TValues,
|
|
19
19
|
) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;
|
|
20
20
|
|
|
21
|
+
/** Configures one field validator and which other fields should re-trigger it. */
|
|
22
|
+
export interface FieldValidationConfig<TValue, TValues extends FormValues> {
|
|
23
|
+
deps?: readonly FieldName<TValues>[] | undefined;
|
|
24
|
+
validate: FieldValidator<TValue, TValues>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Provides either a validator function or a validator descriptor with dependencies. */
|
|
28
|
+
export type FieldValidationEntry<TValue, TValues extends FormValues> =
|
|
29
|
+
| FieldValidator<TValue, TValues>
|
|
30
|
+
| FieldValidationConfig<TValue, TValues>;
|
|
31
|
+
|
|
21
32
|
/** Names the form events that can trigger validation. */
|
|
22
33
|
export type FormValidateMode = "change" | "blur" | "submit";
|
|
23
34
|
|
|
@@ -64,25 +75,51 @@ export interface FieldApi<TValues extends FormValues, Name extends FieldName<TVa
|
|
|
64
75
|
setValue(value: TValues[Name]): Promise<void>;
|
|
65
76
|
}
|
|
66
77
|
|
|
78
|
+
/** Extracts an array field's item type. */
|
|
79
|
+
export type ArrayFieldValue<
|
|
80
|
+
TValues extends FormValues,
|
|
81
|
+
Name extends FieldName<TValues>,
|
|
82
|
+
> = TValues[Name] extends readonly (infer Item)[] ? Item : never;
|
|
83
|
+
|
|
84
|
+
/** Describes one rendered row in a form array field. */
|
|
85
|
+
export interface FieldArrayRow<TValue> {
|
|
86
|
+
index: number;
|
|
87
|
+
key: string;
|
|
88
|
+
value: TValue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Exposes stable keyed row state and mutation helpers for an array field. */
|
|
92
|
+
export interface FieldArrayApi<TValues extends FormValues, Name extends FieldName<TValues>> {
|
|
93
|
+
readonly fields: ReadonlyCell<Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>>>;
|
|
94
|
+
append(value: ArrayFieldValue<TValues, Name>): Promise<void>;
|
|
95
|
+
insert(index: number, value: ArrayFieldValue<TValues, Name>): Promise<void>;
|
|
96
|
+
move(from: number, to: number): Promise<void>;
|
|
97
|
+
remove(index: number): Promise<void>;
|
|
98
|
+
swap(first: number, second: number): Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
|
|
67
101
|
interface BaseCreateFormOptions<TValues extends FormValues> {
|
|
68
102
|
initialValues: TValues;
|
|
69
103
|
validate?:
|
|
70
104
|
| Partial<{
|
|
71
|
-
[Name in FieldName<TValues>]:
|
|
105
|
+
[Name in FieldName<TValues>]: FieldValidationEntry<TValues[Name], TValues>;
|
|
72
106
|
}>
|
|
73
107
|
| undefined;
|
|
74
108
|
validateOn?: FormValidateMode | readonly FormValidateMode[] | undefined;
|
|
75
109
|
}
|
|
76
110
|
|
|
77
111
|
/** Configures form creation without schema-level submit value transformation. */
|
|
78
|
-
export interface CreateFormOptionsWithoutSchema<
|
|
79
|
-
extends
|
|
112
|
+
export interface CreateFormOptionsWithoutSchema<
|
|
113
|
+
TValues extends FormValues,
|
|
114
|
+
> extends BaseCreateFormOptions<TValues> {
|
|
80
115
|
schema?: undefined;
|
|
81
116
|
}
|
|
82
117
|
|
|
83
118
|
/** Configures form creation with a Standard Schema validator that may transform submit values. */
|
|
84
|
-
export interface CreateFormOptionsWithSchema<
|
|
85
|
-
extends
|
|
119
|
+
export interface CreateFormOptionsWithSchema<
|
|
120
|
+
TValues extends FormValues,
|
|
121
|
+
TSubmitValues,
|
|
122
|
+
> extends BaseCreateFormOptions<TValues> {
|
|
86
123
|
schema: StandardSchemaV1<TValues, TSubmitValues>;
|
|
87
124
|
}
|
|
88
125
|
|
|
@@ -129,6 +166,7 @@ export interface ServerActionErrors<TValues extends FormValues> {
|
|
|
129
166
|
/** Provides reactive form state, field access, validation, submit, reset, and error controls. */
|
|
130
167
|
export interface FormApi<TValues extends FormValues, TSubmitValues> {
|
|
131
168
|
readonly state: ReadonlyCell<FormState<TValues>>;
|
|
169
|
+
fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name>;
|
|
132
170
|
field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name>;
|
|
133
171
|
getValues(): TValues;
|
|
134
172
|
reset(values?: TValues): void;
|
|
@@ -168,6 +206,9 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
168
206
|
});
|
|
169
207
|
const validationGenerations = new Map<FieldName<TValues>, number>();
|
|
170
208
|
const dirtyFields = new Set<FieldName<TValues>>();
|
|
209
|
+
const dependentFields = buildDependentFields(options.validate);
|
|
210
|
+
const fieldArrayKeys = new Map<FieldName<TValues>, string[]>();
|
|
211
|
+
let nextFieldArrayKey = 0;
|
|
171
212
|
let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;
|
|
172
213
|
|
|
173
214
|
function commit(patch: Partial<FormState<TValues>>, dirty = dirtyFields.size > 0): void {
|
|
@@ -185,8 +226,21 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
185
226
|
commit({ errors: normalizeErrors(errors) });
|
|
186
227
|
}
|
|
187
228
|
|
|
229
|
+
function invalidateFieldValidations(names: readonly FieldName<TValues>[]): void {
|
|
230
|
+
if (names.length === 0) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const validating = { ...state.get().validating };
|
|
235
|
+
for (const name of names) {
|
|
236
|
+
validationGenerations.set(name, (validationGenerations.get(name) ?? 0) + 1);
|
|
237
|
+
validating[name] = false;
|
|
238
|
+
}
|
|
239
|
+
commit({ validating });
|
|
240
|
+
}
|
|
241
|
+
|
|
188
242
|
async function validateField<Name extends FieldName<TValues>>(name: Name): Promise<void> {
|
|
189
|
-
const validator = options.validate?.[name];
|
|
243
|
+
const validator = validatorForField(options.validate?.[name]);
|
|
190
244
|
const generation = (validationGenerations.get(name) ?? 0) + 1;
|
|
191
245
|
validationGenerations.set(name, generation);
|
|
192
246
|
|
|
@@ -246,7 +300,7 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
246
300
|
});
|
|
247
301
|
|
|
248
302
|
if (validateOn.has("change")) {
|
|
249
|
-
await
|
|
303
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
250
304
|
}
|
|
251
305
|
}
|
|
252
306
|
|
|
@@ -259,20 +313,23 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
259
313
|
});
|
|
260
314
|
|
|
261
315
|
if (validateOn.has("blur")) {
|
|
262
|
-
await
|
|
316
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
263
317
|
}
|
|
264
318
|
}
|
|
265
319
|
|
|
266
320
|
async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {
|
|
267
321
|
const values = state.get().values;
|
|
268
322
|
const errors: FormErrors<TValues> = {};
|
|
323
|
+
const fieldNames = Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>;
|
|
324
|
+
invalidateFieldValidations(fieldNames);
|
|
269
325
|
|
|
270
|
-
for (const name of
|
|
326
|
+
for (const name of fieldNames) {
|
|
271
327
|
const validator = options.validate?.[name];
|
|
272
|
-
|
|
328
|
+
const validate = validatorForField(validator);
|
|
329
|
+
if (validate === undefined) {
|
|
273
330
|
continue;
|
|
274
331
|
}
|
|
275
|
-
const fieldErrors = normalizeFieldErrors(await
|
|
332
|
+
const fieldErrors = normalizeFieldErrors(await validate(values[name], values));
|
|
276
333
|
if (fieldErrors.length > 0) {
|
|
277
334
|
errors[name] = fieldErrors;
|
|
278
335
|
}
|
|
@@ -307,8 +364,134 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
307
364
|
};
|
|
308
365
|
}
|
|
309
366
|
|
|
367
|
+
async function validateFields(names: readonly FieldName<TValues>[]): Promise<void> {
|
|
368
|
+
const uniqueNames = [...new Set(names)];
|
|
369
|
+
await Promise.all(uniqueNames.map((fieldName) => validateField(fieldName)));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function dependentFieldsFor(name: FieldName<TValues>): readonly FieldName<TValues>[] {
|
|
373
|
+
return dependentFields.get(name) ?? [];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function arrayValues<Name extends FieldName<TValues>>(
|
|
377
|
+
name: Name,
|
|
378
|
+
): Array<ArrayFieldValue<TValues, Name>> {
|
|
379
|
+
const value = state.get().values[name];
|
|
380
|
+
return Array.isArray(value) ? ([...value] as Array<ArrayFieldValue<TValues, Name>>) : [];
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function createFieldArrayKey(name: FieldName<TValues>): string {
|
|
384
|
+
const key = `${name}:${nextFieldArrayKey}`;
|
|
385
|
+
nextFieldArrayKey += 1;
|
|
386
|
+
return key;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function ensureFieldArrayKeys(name: FieldName<TValues>, length: number): string[] {
|
|
390
|
+
const keys = fieldArrayKeys.get(name) ?? [];
|
|
391
|
+
if (keys.length > length) {
|
|
392
|
+
keys.length = length;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
while (keys.length < length) {
|
|
396
|
+
keys.push(createFieldArrayKey(name));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
fieldArrayKeys.set(name, keys);
|
|
400
|
+
return keys;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function fieldArrayRows<Name extends FieldName<TValues>>(
|
|
404
|
+
name: Name,
|
|
405
|
+
): Array<FieldArrayRow<ArrayFieldValue<TValues, Name>>> {
|
|
406
|
+
const values = arrayValues(name);
|
|
407
|
+
const keys = ensureFieldArrayKeys(name, values.length);
|
|
408
|
+
return values.map((value, index) => ({
|
|
409
|
+
index,
|
|
410
|
+
key: keys[index] ?? createFieldArrayKey(name),
|
|
411
|
+
value,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
async function setArrayValue<Name extends FieldName<TValues>>(
|
|
416
|
+
name: Name,
|
|
417
|
+
values: Array<ArrayFieldValue<TValues, Name>>,
|
|
418
|
+
keys: string[],
|
|
419
|
+
): Promise<void> {
|
|
420
|
+
fieldArrayKeys.set(name, keys);
|
|
421
|
+
await setValue(name, values as TValues[Name]);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function validateArrayField<Name extends FieldName<TValues>>(name: Name): Promise<void> {
|
|
425
|
+
if (validateOn.has("change")) {
|
|
426
|
+
await validateFields([name, ...dependentFieldsFor(name)]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
310
430
|
return {
|
|
311
431
|
state,
|
|
432
|
+
fieldArray<Name extends FieldName<TValues>>(name: Name): FieldArrayApi<TValues, Name> {
|
|
433
|
+
return {
|
|
434
|
+
fields: {
|
|
435
|
+
get: () => fieldArrayRows(name),
|
|
436
|
+
},
|
|
437
|
+
async append(value) {
|
|
438
|
+
const values = arrayValues(name);
|
|
439
|
+
const keys = [...ensureFieldArrayKeys(name, values.length), createFieldArrayKey(name)];
|
|
440
|
+
values.push(value);
|
|
441
|
+
await setArrayValue(name, values, keys);
|
|
442
|
+
},
|
|
443
|
+
async insert(index, value) {
|
|
444
|
+
const values = arrayValues(name);
|
|
445
|
+
const insertIndex = clampIndex(index, 0, values.length);
|
|
446
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
447
|
+
values.splice(insertIndex, 0, value);
|
|
448
|
+
keys.splice(insertIndex, 0, createFieldArrayKey(name));
|
|
449
|
+
await setArrayValue(name, values, keys);
|
|
450
|
+
},
|
|
451
|
+
async move(from, to) {
|
|
452
|
+
const values = arrayValues(name);
|
|
453
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
454
|
+
if (!isArrayIndex(from, values.length)) {
|
|
455
|
+
await validateArrayField(name);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const [value] = values.splice(from, 1);
|
|
460
|
+
const [key] = keys.splice(from, 1);
|
|
461
|
+
const insertIndex = clampIndex(to, 0, values.length);
|
|
462
|
+
values.splice(insertIndex, 0, value as ArrayFieldValue<TValues, Name>);
|
|
463
|
+
keys.splice(insertIndex, 0, key as string);
|
|
464
|
+
await setArrayValue(name, values, keys);
|
|
465
|
+
},
|
|
466
|
+
async remove(index) {
|
|
467
|
+
const values = arrayValues(name);
|
|
468
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
469
|
+
if (!isArrayIndex(index, values.length)) {
|
|
470
|
+
await validateArrayField(name);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
values.splice(index, 1);
|
|
475
|
+
keys.splice(index, 1);
|
|
476
|
+
await setArrayValue(name, values, keys);
|
|
477
|
+
},
|
|
478
|
+
async swap(first, second) {
|
|
479
|
+
const values = arrayValues(name);
|
|
480
|
+
const keys = [...ensureFieldArrayKeys(name, values.length)];
|
|
481
|
+
if (!isArrayIndex(first, values.length) || !isArrayIndex(second, values.length)) {
|
|
482
|
+
await validateArrayField(name);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
[values[first], values[second]] = [
|
|
487
|
+
values[second] as ArrayFieldValue<TValues, Name>,
|
|
488
|
+
values[first] as ArrayFieldValue<TValues, Name>,
|
|
489
|
+
];
|
|
490
|
+
[keys[first], keys[second]] = [keys[second] as string, keys[first] as string];
|
|
491
|
+
await setArrayValue(name, values, keys);
|
|
492
|
+
},
|
|
493
|
+
};
|
|
494
|
+
},
|
|
312
495
|
field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {
|
|
313
496
|
return {
|
|
314
497
|
state: {
|
|
@@ -355,6 +538,7 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
|
|
|
355
538
|
|
|
356
539
|
initialValues = cloneValues(values);
|
|
357
540
|
dirtyFields.clear();
|
|
541
|
+
fieldArrayKeys.clear();
|
|
358
542
|
commit({
|
|
359
543
|
errors: {},
|
|
360
544
|
initialValues,
|
|
@@ -532,6 +716,45 @@ function normalizeFieldErrors(errors: readonly string[] | string | undefined): s
|
|
|
532
716
|
return typeof errors === "string" ? [errors] : [...errors];
|
|
533
717
|
}
|
|
534
718
|
|
|
719
|
+
function clampIndex(index: number, min: number, max: number): number {
|
|
720
|
+
return Math.min(Math.max(Math.trunc(index), min), max);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function isArrayIndex(index: number, length: number): boolean {
|
|
724
|
+
return Number.isInteger(index) && index >= 0 && index < length;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function validatorForField<TValue, TValues extends FormValues>(
|
|
728
|
+
entry: FieldValidationEntry<TValue, TValues> | undefined,
|
|
729
|
+
): FieldValidator<TValue, TValues> | undefined {
|
|
730
|
+
return typeof entry === "function" ? entry : entry?.validate;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function buildDependentFields<TValues extends FormValues>(
|
|
734
|
+
validate: BaseCreateFormOptions<TValues>["validate"],
|
|
735
|
+
): Map<FieldName<TValues>, FieldName<TValues>[]> {
|
|
736
|
+
const dependents = new Map<FieldName<TValues>, FieldName<TValues>[]>();
|
|
737
|
+
|
|
738
|
+
for (const [fieldName, entry] of Object.entries(validate ?? {}) as Array<
|
|
739
|
+
[FieldName<TValues>, FieldValidationEntry<TValues[FieldName<TValues>], TValues> | undefined]
|
|
740
|
+
>) {
|
|
741
|
+
if (entry === undefined || typeof entry === "function") {
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
for (const dep of entry.deps ?? []) {
|
|
746
|
+
const fields = dependents.get(dep);
|
|
747
|
+
if (fields === undefined) {
|
|
748
|
+
dependents.set(dep, [fieldName]);
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
fields.push(fieldName);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return dependents;
|
|
756
|
+
}
|
|
757
|
+
|
|
535
758
|
function cloneValues<TValues extends FormValues>(values: TValues): TValues {
|
|
536
759
|
return { ...values };
|
|
537
760
|
}
|