@reckona/mreact-forms 0.0.97 → 0.0.98

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 CHANGED
@@ -35,6 +35,8 @@ input({
35
35
  });
36
36
  ```
37
37
 
38
+ Use `field.bind({ event: "change" })` for controls such as `<select>` that should update on change instead of input. `binding.value` is read live from form state, so repeated reads after validation or reset return the current value.
39
+
38
40
  ## Valibot And Standard Schema
39
41
 
40
42
  `createForm` accepts Standard Schema compatible validators through the
@@ -105,9 +107,10 @@ await inviteForm.submit((values) => {
105
107
  ## Core APIs
106
108
 
107
109
  - `createForm()` creates reactive form state.
108
- - `form.field(name).bind()` returns `{ value, onInput, onChange, onBlur }` and handles string values and boolean checkbox-style values.
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`.
109
111
  - Field state includes `validating`, which is true while the latest async field validator is pending. Slower stale validator results are ignored.
110
112
  - `setServerErrors()` applies errors returned by a route handler or server action.
113
+ - `form.submit()` dedupes concurrent submissions and keeps `submitting` true until the active validation and handler finish.
111
114
  - `form.reset()` restores the initial values.
112
115
  - The `schema` option connects forms to Standard Schema compatible validators such as zod and valibot.
113
116
 
@@ -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":"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"}
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export function createForm(options) {
15
15
  values: cloneValues(options.initialValues),
16
16
  });
17
17
  const validationGenerations = new Map();
18
+ let activeSubmit;
18
19
  function commit(patch) {
19
20
  const previous = state.get();
20
21
  const next = {
@@ -84,6 +85,45 @@ export function createForm(options) {
84
85
  await validateField(name);
85
86
  }
86
87
  }
88
+ async function validateForm() {
89
+ const values = state.get().values;
90
+ const errors = {};
91
+ for (const name of Object.keys(options.validate ?? {})) {
92
+ const validator = options.validate?.[name];
93
+ if (validator === undefined) {
94
+ continue;
95
+ }
96
+ const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
97
+ if (fieldErrors.length > 0) {
98
+ errors[name] = fieldErrors;
99
+ }
100
+ }
101
+ if (options.schema !== undefined) {
102
+ const result = await validateStandardSchema(options.schema, values);
103
+ if (!result.success) {
104
+ mergeIssueErrors(errors, result.issues);
105
+ }
106
+ else if (Object.keys(errors).length === 0) {
107
+ setErrors({});
108
+ return {
109
+ success: true,
110
+ value: result.value,
111
+ };
112
+ }
113
+ }
114
+ if (Object.keys(errors).length > 0) {
115
+ setErrors(errors);
116
+ return {
117
+ errors,
118
+ success: false,
119
+ };
120
+ }
121
+ setErrors({});
122
+ return {
123
+ success: true,
124
+ value: values,
125
+ };
126
+ }
87
127
  return {
88
128
  state,
89
129
  field(name) {
@@ -91,18 +131,28 @@ export function createForm(options) {
91
131
  state: {
92
132
  get: () => fieldState(state.get(), name),
93
133
  },
94
- bind() {
134
+ bind(bindingOptions = {}) {
135
+ const bindingEvent = bindingOptions.event ?? "input";
136
+ const updateValue = async (inputEvent) => {
137
+ await setValue(name, eventValue(inputEvent, state.get().values[name]));
138
+ };
95
139
  return {
96
140
  onBlur: async () => {
97
141
  await blurField(name);
98
142
  },
99
143
  onChange: async (event) => {
100
- await setValue(name, eventValue(event, state.get().values[name]));
144
+ if (bindingEvent === "change") {
145
+ await updateValue(event);
146
+ }
101
147
  },
102
148
  onInput: async (event) => {
103
- await setValue(name, eventValue(event, state.get().values[name]));
149
+ if (bindingEvent === "input") {
150
+ await updateValue(event);
151
+ }
152
+ },
153
+ get value() {
154
+ return state.get().values[name];
104
155
  },
105
- value: state.get().values[name],
106
156
  };
107
157
  },
108
158
  async blur() {
@@ -144,73 +194,45 @@ export function createForm(options) {
144
194
  },
145
195
  setValue,
146
196
  async submit(handler) {
147
- commit({
148
- submitCount: state.get().submitCount + 1,
149
- submitting: true,
150
- });
151
- const validation = await this.validate();
152
- if (!validation.success) {
153
- commit({ submitting: false });
154
- return {
155
- errors: validation.errors,
156
- status: "invalid",
157
- };
197
+ if (activeSubmit !== undefined) {
198
+ return activeSubmit;
158
199
  }
159
- try {
160
- const data = await handler(validation.value);
161
- commit({ submitting: false });
162
- return {
163
- data,
164
- status: "success",
165
- };
166
- }
167
- catch (error) {
168
- commit({ submitting: false });
169
- return {
170
- error,
171
- status: "error",
172
- };
173
- }
174
- },
175
- async validate() {
176
- const values = state.get().values;
177
- const errors = {};
178
- for (const name of Object.keys(options.validate ?? {})) {
179
- const validator = options.validate?.[name];
180
- if (validator === undefined) {
181
- continue;
200
+ const task = (async () => {
201
+ commit({
202
+ submitCount: state.get().submitCount + 1,
203
+ submitting: true,
204
+ });
205
+ try {
206
+ const validation = await validateForm();
207
+ if (!validation.success) {
208
+ return {
209
+ errors: validation.errors,
210
+ status: "invalid",
211
+ };
212
+ }
213
+ try {
214
+ const data = await handler(validation.value);
215
+ return {
216
+ data,
217
+ status: "success",
218
+ };
219
+ }
220
+ catch (error) {
221
+ return {
222
+ error,
223
+ status: "error",
224
+ };
225
+ }
182
226
  }
183
- const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
184
- if (fieldErrors.length > 0) {
185
- errors[name] = fieldErrors;
186
- }
187
- }
188
- if (options.schema !== undefined) {
189
- const result = await validateStandardSchema(options.schema, values);
190
- if (!result.success) {
191
- mergeIssueErrors(errors, result.issues);
227
+ finally {
228
+ activeSubmit = undefined;
229
+ commit({ submitting: false });
192
230
  }
193
- else if (Object.keys(errors).length === 0) {
194
- setErrors({});
195
- return {
196
- success: true,
197
- value: result.value,
198
- };
199
- }
200
- }
201
- if (Object.keys(errors).length > 0) {
202
- setErrors(errors);
203
- return {
204
- errors,
205
- success: false,
206
- };
207
- }
208
- setErrors({});
209
- return {
210
- success: true,
211
- value: values,
212
- };
231
+ })();
232
+ activeSubmit = task;
233
+ return task;
213
234
  },
235
+ validate: validateForm,
214
236
  };
215
237
  }
216
238
  function fieldState(formState, name) {
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\nexport type FormValues = Record<string, unknown>;\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\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\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\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\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\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\nexport interface CreateFormOptionsWithoutSchema<TValues extends FormValues>\n extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\nexport interface CreateFormOptionsWithSchema<TValues extends FormValues, TSubmitValues>\n extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\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\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\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\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\n function commit(patch: Partial<FormState<TValues>>): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = isDirty(next.values, next.initialValues);\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 commit({\n values: {\n ...state.get().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 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() {\n return {\n onBlur: async () => {\n await blurField(name);\n },\n onChange: async (event) => {\n await setValue(name, eventValue(event, state.get().values[name]) as TValues[Name]);\n },\n onInput: async (event) => {\n await setValue(name, eventValue(event, state.get().values[name]) as TValues[Name]);\n },\n value: state.get().values[name],\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 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 (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 commit({\n submitCount: state.get().submitCount + 1,\n submitting: true,\n });\n\n const validation = await this.validate();\n\n if (!validation.success) {\n commit({ submitting: false });\n return {\n errors: validation.errors,\n status: \"invalid\",\n };\n }\n\n try {\n const data = await handler(validation.value);\n commit({ submitting: false });\n return {\n data,\n status: \"success\",\n };\n } catch (error) {\n commit({ submitting: false });\n return {\n error,\n status: \"error\",\n };\n }\n },\n async validate(): 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}\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 ?? \"root\") as FieldName<TValues> | \"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 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 isDirty<TValues extends FormValues>(values: TValues, initialValues: TValues): boolean {\n return Object.keys(values).some((key) => !Object.is(values[key], initialValues[key]));\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\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\nexport type FormValues = Record<string, unknown>;\nexport type FieldName<TValues extends FormValues> = Extract<keyof TValues, string>;\nexport type FormErrors<TValues extends FormValues> = Partial<\n Record<FieldName<TValues> | \"root\", string[]>\n>;\nexport type FieldValidator<TValue, TValues extends FormValues> = (\n value: TValue,\n values: TValues,\n) => readonly string[] | string | undefined | Promise<readonly string[] | string | undefined>;\nexport type FormValidateMode = \"change\" | \"blur\" | \"submit\";\n\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\nexport interface FieldState<TValue> {\n dirty: boolean;\n errors: string[];\n touched: boolean;\n validating: boolean;\n value: TValue;\n}\n\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\nexport interface FieldBindingOptions {\n event?: \"change\" | \"input\" | undefined;\n}\n\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\nexport interface CreateFormOptionsWithoutSchema<TValues extends FormValues>\n extends BaseCreateFormOptions<TValues> {\n schema?: undefined;\n}\n\nexport interface CreateFormOptionsWithSchema<TValues extends FormValues, TSubmitValues>\n extends BaseCreateFormOptions<TValues> {\n schema: StandardSchemaV1<TValues, TSubmitValues>;\n}\n\nexport type CreateFormOptions<TValues extends FormValues, TSubmitValues = TValues> =\n | CreateFormOptionsWithoutSchema<TValues>\n | CreateFormOptionsWithSchema<TValues, TSubmitValues>;\n\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\nexport type FormSubmitResult<TValues extends FormValues, TResult> =\n | {\n data: TResult;\n status: \"success\";\n }\n | {\n errors: FormErrors<TValues>;\n status: \"invalid\";\n }\n | {\n error: unknown;\n status: \"error\";\n };\n\nexport interface ServerActionErrors<TValues extends FormValues> {\n fieldErrors?: Partial<Record<FieldName<TValues>, readonly string[]>> | undefined;\n formErrors?: readonly string[] | undefined;\n}\n\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\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 let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;\n\n function commit(patch: Partial<FormState<TValues>>): void {\n const previous = state.get();\n const next = {\n ...previous,\n ...patch,\n };\n next.dirty = isDirty(next.values, next.initialValues);\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 commit({\n values: {\n ...state.get().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 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 (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 activeSubmit as Promise<FormSubmitResult<TValues, TResult>>;\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\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 ?? \"root\") as FieldName<TValues> | \"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 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 isDirty<TValues extends FormValues>(values: TValues, initialValues: TValues): boolean {\n return Object.keys(values).some((key) => !Object.is(values[key], initialValues[key]));\n}\n\nfunction cloneValues<TValues extends FormValues>(values: TValues): TValues {\n return { ...values };\n}\n\nexport type {\n InferStandardSchemaInput,\n InferStandardSchemaOutput,\n StandardSchemaV1,\n} from \"./standard-schema.js\";\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.97",
3
+ "version": "0.0.98",
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.97"
47
+ "@reckona/mreact-reactive-core": "0.0.98"
48
48
  }
49
49
  }
package/src/index.ts CHANGED
@@ -142,6 +142,7 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
142
142
  values: cloneValues(options.initialValues),
143
143
  });
144
144
  const validationGenerations = new Map<FieldName<TValues>, number>();
145
+ let activeSubmit: Promise<FormSubmitResult<TValues, unknown>> | undefined;
145
146
 
146
147
  function commit(patch: Partial<FormState<TValues>>): void {
147
148
  const previous = state.get();
@@ -234,6 +235,50 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
234
235
  }
235
236
  }
236
237
 
238
+ async function validateForm(): Promise<FormValidationResult<TValues, TSubmitValues>> {
239
+ const values = state.get().values;
240
+ const errors: FormErrors<TValues> = {};
241
+
242
+ for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {
243
+ const validator = options.validate?.[name];
244
+ if (validator === undefined) {
245
+ continue;
246
+ }
247
+ const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
248
+ if (fieldErrors.length > 0) {
249
+ errors[name] = fieldErrors;
250
+ }
251
+ }
252
+
253
+ if (options.schema !== undefined) {
254
+ const result = await validateStandardSchema(options.schema, values);
255
+
256
+ if (!result.success) {
257
+ mergeIssueErrors(errors, result.issues);
258
+ } else if (Object.keys(errors).length === 0) {
259
+ setErrors({});
260
+ return {
261
+ success: true,
262
+ value: result.value as TSubmitValues,
263
+ };
264
+ }
265
+ }
266
+
267
+ if (Object.keys(errors).length > 0) {
268
+ setErrors(errors);
269
+ return {
270
+ errors,
271
+ success: false,
272
+ };
273
+ }
274
+
275
+ setErrors({});
276
+ return {
277
+ success: true,
278
+ value: values as TValues & TSubmitValues,
279
+ };
280
+ }
281
+
237
282
  return {
238
283
  state,
239
284
  field<Name extends FieldName<TValues>>(name: Name): FieldApi<TValues, Name> {
@@ -241,18 +286,29 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
241
286
  state: {
242
287
  get: () => fieldState(state.get(), name),
243
288
  },
244
- bind() {
289
+ bind(bindingOptions = {}) {
290
+ const bindingEvent = bindingOptions.event ?? "input";
291
+ const updateValue = async (inputEvent: Event) => {
292
+ await setValue(name, eventValue(inputEvent, state.get().values[name]) as TValues[Name]);
293
+ };
294
+
245
295
  return {
246
296
  onBlur: async () => {
247
297
  await blurField(name);
248
298
  },
249
299
  onChange: async (event) => {
250
- await setValue(name, eventValue(event, state.get().values[name]) as TValues[Name]);
300
+ if (bindingEvent === "change") {
301
+ await updateValue(event);
302
+ }
251
303
  },
252
304
  onInput: async (event) => {
253
- await setValue(name, eventValue(event, state.get().values[name]) as TValues[Name]);
305
+ if (bindingEvent === "input") {
306
+ await updateValue(event);
307
+ }
308
+ },
309
+ get value() {
310
+ return state.get().values[name];
254
311
  },
255
- value: state.get().values[name],
256
312
  };
257
313
  },
258
314
  async blur() {
@@ -300,79 +356,48 @@ export function createForm<TValues extends FormValues, TSubmitValues = TValues>(
300
356
  async submit<TResult>(
301
357
  handler: (values: TSubmitValues) => Promise<TResult> | TResult,
302
358
  ): Promise<FormSubmitResult<TValues, TResult>> {
303
- commit({
304
- submitCount: state.get().submitCount + 1,
305
- submitting: true,
306
- });
307
-
308
- const validation = await this.validate();
309
-
310
- if (!validation.success) {
311
- commit({ submitting: false });
312
- return {
313
- errors: validation.errors,
314
- status: "invalid",
315
- };
359
+ if (activeSubmit !== undefined) {
360
+ return activeSubmit as Promise<FormSubmitResult<TValues, TResult>>;
316
361
  }
317
362
 
318
- try {
319
- const data = await handler(validation.value);
320
- commit({ submitting: false });
321
- return {
322
- data,
323
- status: "success",
324
- };
325
- } catch (error) {
326
- commit({ submitting: false });
327
- return {
328
- error,
329
- status: "error",
330
- };
331
- }
332
- },
333
- async validate(): Promise<FormValidationResult<TValues, TSubmitValues>> {
334
- const values = state.get().values;
335
- const errors: FormErrors<TValues> = {};
336
-
337
- for (const name of Object.keys(options.validate ?? {}) as Array<FieldName<TValues>>) {
338
- const validator = options.validate?.[name];
339
- if (validator === undefined) {
340
- continue;
341
- }
342
- const fieldErrors = normalizeFieldErrors(await validator(values[name], values));
343
- if (fieldErrors.length > 0) {
344
- errors[name] = fieldErrors;
363
+ const task = (async (): Promise<FormSubmitResult<TValues, TResult>> => {
364
+ commit({
365
+ submitCount: state.get().submitCount + 1,
366
+ submitting: true,
367
+ });
368
+
369
+ try {
370
+ const validation = await validateForm();
371
+
372
+ if (!validation.success) {
373
+ return {
374
+ errors: validation.errors,
375
+ status: "invalid",
376
+ };
377
+ }
378
+
379
+ try {
380
+ const data = await handler(validation.value);
381
+ return {
382
+ data,
383
+ status: "success",
384
+ };
385
+ } catch (error) {
386
+ return {
387
+ error,
388
+ status: "error",
389
+ };
390
+ }
391
+ } finally {
392
+ activeSubmit = undefined;
393
+ commit({ submitting: false });
345
394
  }
346
- }
395
+ })();
347
396
 
348
- if (options.schema !== undefined) {
349
- const result = await validateStandardSchema(options.schema, values);
350
-
351
- if (!result.success) {
352
- mergeIssueErrors(errors, result.issues);
353
- } else if (Object.keys(errors).length === 0) {
354
- setErrors({});
355
- return {
356
- success: true,
357
- value: result.value as TSubmitValues,
358
- };
359
- }
360
- }
361
-
362
- if (Object.keys(errors).length > 0) {
363
- setErrors(errors);
364
- return {
365
- errors,
366
- success: false,
367
- };
368
- }
369
-
370
- setErrors({});
371
- return {
372
- success: true,
373
- value: values as TValues & TSubmitValues,
374
- };
397
+ activeSubmit = task as Promise<FormSubmitResult<TValues, unknown>>;
398
+ return task;
375
399
  },
400
+ validate: validateForm,
376
401
  };
377
402
  }
378
403