@tanstack/form-core 1.19.5 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/EventClient.cjs +13 -0
- package/dist/cjs/EventClient.cjs.map +1 -0
- package/dist/cjs/EventClient.d.cts +52 -0
- package/dist/cjs/FieldGroupApi.cjs +20 -0
- package/dist/cjs/FieldGroupApi.cjs.map +1 -1
- package/dist/cjs/FieldGroupApi.d.cts +7 -2
- package/dist/cjs/FormApi.cjs +68 -3
- package/dist/cjs/FormApi.cjs.map +1 -1
- package/dist/cjs/FormApi.d.cts +11 -2
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/esm/EventClient.d.ts +52 -0
- package/dist/esm/EventClient.js +13 -0
- package/dist/esm/EventClient.js.map +1 -0
- package/dist/esm/FieldGroupApi.d.ts +7 -2
- package/dist/esm/FieldGroupApi.js +20 -0
- package/dist/esm/FieldGroupApi.js.map +1 -1
- package/dist/esm/FormApi.d.ts +11 -2
- package/dist/esm/FormApi.js +68 -3
- package/dist/esm/FormApi.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -2
- package/src/EventClient.ts +73 -0
- package/src/FieldGroupApi.ts +52 -1
- package/src/FormApi.ts +123 -5
- package/src/index.ts +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EventClient } from "@tanstack/devtools-event-client";
|
|
2
|
+
class FormEventClient extends EventClient {
|
|
3
|
+
constructor() {
|
|
4
|
+
super({
|
|
5
|
+
pluginId: "form-devtools"
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const formEventClient = new FormEventClient();
|
|
10
|
+
export {
|
|
11
|
+
formEventClient
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=EventClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventClient.js","sources":["../../src/EventClient.ts"],"sourcesContent":["import { EventClient } from '@tanstack/devtools-event-client'\n\nimport type { AnyFormOptions, AnyFormState } from './FormApi'\n\ntype ExtractEventNames<T> = T extends `${string}:${infer EventName}`\n ? EventName\n : never\n\nexport type BroadcastFormState = {\n id: string\n state: AnyFormState\n options: AnyFormOptions\n}\n\nexport type BroadcastFormSubmissionState =\n | {\n id: string\n submissionAttempt: number\n successful: false\n stage: 'validateAllFields' | 'validate'\n errors: any[]\n }\n | {\n id: string\n submissionAttempt: number\n successful: false\n stage: 'inflight'\n onError: unknown\n }\n | {\n id: string\n submissionAttempt: number\n successful: true\n }\n\nexport type BroadcastFormUnmounted = {\n id: string\n}\n\nexport type RequestFormState = {\n id: string\n}\n\nexport type RequestFormReset = {\n id: string\n}\n\nexport type RequestFormForceReset = {\n id: string\n}\n\ntype EventMap = {\n 'form-devtools:form-state-change': BroadcastFormState\n 'form-devtools:form-submission-state-change': BroadcastFormSubmissionState\n 'form-devtools:form-unmounted': BroadcastFormUnmounted\n 'form-devtools:request-form-state': RequestFormState\n 'form-devtools:request-form-reset': RequestFormReset\n 'form-devtools:request-form-force-submit': RequestFormForceReset\n}\n\nexport type EventClientEventMap = keyof EventMap\n\nexport type EventClientEventNames = ExtractEventNames<EventClientEventMap>\n\nclass FormEventClient extends EventClient<EventMap> {\n constructor() {\n super({\n pluginId: 'form-devtools',\n })\n }\n}\n\nexport const formEventClient = new FormEventClient()\n"],"names":[],"mappings":";AAgEA,MAAM,wBAAwB,YAAsB;AAAA,EAClD,cAAc;AACZ,UAAM;AAAA,MACJ,UAAU;AAAA,IAAA,CACX;AAAA,EACH;AACF;AAEO,MAAM,kBAAkB,IAAI,gBAAA;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Derived } from '@tanstack/store';
|
|
2
2
|
import { Updater } from './utils.js';
|
|
3
3
|
import { FormApi, FormAsyncValidateOrFn, FormValidateOrFn } from './FormApi.js';
|
|
4
|
-
import {
|
|
4
|
+
import { AnyFieldMetaBase, FieldOptions } from './FieldApi.js';
|
|
5
5
|
import { DeepKeys, DeepKeysOfType, DeepValue, FieldsMap } from './util-types.js';
|
|
6
6
|
import { FieldManipulator, UpdateMetaOptions, ValidationCause } from './types.js';
|
|
7
7
|
export type AnyFieldGroupApi = FieldGroupApi<any, any, any, any, any, any, any, any, any, any, any, any, any, any>;
|
|
@@ -40,6 +40,11 @@ export declare class FieldGroupApi<in out TFormData, in out TFieldGroupData, in
|
|
|
40
40
|
* @private
|
|
41
41
|
*/
|
|
42
42
|
getFormFieldName: <TField extends DeepKeys<TFieldGroupData>>(subfield: TField) => DeepKeys<TFormData>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the field options with the true form DeepKeys for validators
|
|
45
|
+
* @private
|
|
46
|
+
*/
|
|
47
|
+
getFormFieldOptions: <TOptions extends FieldOptions<any, any, any, any, any, any, any, any, any, any, any, any>>(props: TOptions) => TOptions;
|
|
43
48
|
store: Derived<FieldGroupState<TFieldGroupData>>;
|
|
44
49
|
get state(): FieldGroupState<TFieldGroupData>;
|
|
45
50
|
/**
|
|
@@ -70,7 +75,7 @@ export declare class FieldGroupApi<in out TFormData, in out TFieldGroupData, in
|
|
|
70
75
|
/**
|
|
71
76
|
* Gets the metadata of the specified field.
|
|
72
77
|
*/
|
|
73
|
-
getFieldMeta: <TField extends DeepKeys<TFieldGroupData>>(field: TField) => AnyFieldMeta | undefined;
|
|
78
|
+
getFieldMeta: <TField extends DeepKeys<TFieldGroupData>>(field: TField) => import('./FieldApi.js').AnyFieldMeta | undefined;
|
|
74
79
|
/**
|
|
75
80
|
* Updates the metadata of the specified field.
|
|
76
81
|
*/
|
|
@@ -20,6 +20,26 @@ class FieldGroupApi {
|
|
|
20
20
|
);
|
|
21
21
|
return concatenatePaths(formMappedPath, restOfPath);
|
|
22
22
|
};
|
|
23
|
+
this.getFormFieldOptions = (props) => {
|
|
24
|
+
const newProps = { ...props };
|
|
25
|
+
const validators = newProps.validators;
|
|
26
|
+
newProps.name = this.getFormFieldName(props.name);
|
|
27
|
+
if (validators && (validators.onChangeListenTo || validators.onBlurListenTo)) {
|
|
28
|
+
const newValidators = { ...validators };
|
|
29
|
+
const remapListenTo = (listenTo) => {
|
|
30
|
+
if (!listenTo) return void 0;
|
|
31
|
+
return listenTo.map(
|
|
32
|
+
(localFieldName) => this.getFormFieldName(localFieldName)
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
newValidators.onChangeListenTo = remapListenTo(
|
|
36
|
+
validators.onChangeListenTo
|
|
37
|
+
);
|
|
38
|
+
newValidators.onBlurListenTo = remapListenTo(validators.onBlurListenTo);
|
|
39
|
+
newProps.validators = newValidators;
|
|
40
|
+
}
|
|
41
|
+
return newProps;
|
|
42
|
+
};
|
|
23
43
|
this.mount = () => {
|
|
24
44
|
const cleanup = this.store.mount();
|
|
25
45
|
return cleanup;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldGroupApi.js","sources":["../../src/FieldGroupApi.ts"],"sourcesContent":["import { Derived } from '@tanstack/store'\nimport { concatenatePaths, getBy, makePathArray } from './utils'\nimport type { Updater } from './utils'\nimport type {\n FormApi,\n FormAsyncValidateOrFn,\n FormValidateOrFn,\n} from './FormApi'\nimport type { AnyFieldMeta, AnyFieldMetaBase } from './FieldApi'\nimport type {\n DeepKeys,\n DeepKeysOfType,\n DeepValue,\n FieldsMap,\n} from './util-types'\nimport type {\n FieldManipulator,\n UpdateMetaOptions,\n ValidationCause,\n} from './types'\n\nexport type AnyFieldGroupApi = FieldGroupApi<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>\n\nexport interface FieldGroupState<in out TFieldGroupData> {\n /**\n * The current values of the field group\n */\n values: TFieldGroupData\n}\n\n/**\n * An object representing the options for a field group.\n */\nexport interface FieldGroupOptions<\n in out TFormData,\n in out TFieldGroupData,\n in out TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta = never,\n> {\n form:\n | FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n | FieldGroupApi<\n any,\n TFormData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n TSubmitMeta\n >\n /**\n * The path to the field group data.\n */\n fields: TFields\n /**\n * The expected subsetValues that the form must provide.\n */\n defaultValues?: TFieldGroupData\n /**\n * onSubmitMeta, the data passed from the handleSubmit handler, to the onSubmit function props\n */\n onSubmitMeta?: TSubmitMeta\n}\n\nexport class FieldGroupApi<\n in out TFormData,\n in out TFieldGroupData,\n in out TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta = never,\n> implements FieldManipulator<TFieldGroupData, TSubmitMeta>\n{\n /**\n * The form that called this field group.\n */\n readonly form: FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\n readonly fieldsMap: TFields\n\n /**\n * Get the true name of the field. Not required within `Field` or `AppField`.\n * @private\n */\n getFormFieldName = <TField extends DeepKeys<TFieldGroupData>>(\n subfield: TField,\n ): DeepKeys<TFormData> => {\n if (typeof this.fieldsMap === 'string') {\n return concatenatePaths(this.fieldsMap, subfield)\n }\n\n const firstAccessor = makePathArray(subfield)[0]\n if (typeof firstAccessor !== 'string') {\n // top-level arrays cannot be mapped\n return ''\n }\n\n const restOfPath = subfield.slice(firstAccessor.length)\n const formMappedPath =\n // TFields is either a string or this. See guard above.\n (this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>)[\n firstAccessor as keyof TFieldGroupData\n ]\n\n return concatenatePaths(formMappedPath, restOfPath)\n }\n\n store: Derived<FieldGroupState<TFieldGroupData>>\n\n get state() {\n return this.store.state\n }\n\n /**\n * Constructs a new `FieldGroupApi` instance with the given form options.\n */\n constructor(\n opts: FieldGroupOptions<\n TFormData,\n TFieldGroupData,\n TFields,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ) {\n if (opts.form instanceof FieldGroupApi) {\n const group = opts.form\n this.form = group.form as never\n\n // the DeepKey is already namespaced, so we need to ensure that we reference\n // the form and not the group\n if (typeof opts.fields === 'string') {\n this.fieldsMap = group.getFormFieldName(opts.fields) as TFields\n } else {\n // TypeScript has a tough time with generics being a union for some reason\n const fields = {\n ...(opts.fields as FieldsMap<TFormData, TFieldGroupData>),\n }\n for (const key in fields) {\n fields[key] = group.getFormFieldName(fields[key]) as never\n }\n this.fieldsMap = fields as never\n }\n } else {\n this.form = opts.form\n this.fieldsMap = opts.fields\n }\n\n this.store = new Derived({\n deps: [this.form.store],\n fn: ({ currDepVals }) => {\n const currFormStore = currDepVals[0]\n let values: TFieldGroupData\n if (typeof this.fieldsMap === 'string') {\n // all values live at that name, so we can directly fetch it\n values = getBy(currFormStore.values, this.fieldsMap)\n } else {\n // we need to fetch the values from all places where they were mapped from\n values = {} as never\n const fields: Record<keyof TFieldGroupData, string> = this\n .fieldsMap as never\n for (const key in fields) {\n values[key] = getBy(currFormStore.values, fields[key])\n }\n }\n\n return {\n values,\n }\n },\n })\n }\n\n /**\n * Mounts the field group instance to listen to value changes.\n */\n mount = () => {\n const cleanup = this.store.mount()\n\n return cleanup\n }\n\n /**\n * Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.\n */\n validateArrayFieldsStartingFrom = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n cause: ValidationCause,\n ) => {\n return this.form.validateArrayFieldsStartingFrom(\n this.getFormFieldName(field),\n index,\n cause,\n )\n }\n\n /**\n * Validates a specified field in the form using the correct handlers for a given validation type.\n */\n validateField = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n cause: ValidationCause,\n ) => {\n return this.form.validateField(this.getFormFieldName(field), cause)\n }\n\n /**\n * Handles the form submission, performs validation, and calls the appropriate onSubmit or onSubmitInvalid callbacks.\n */\n handleSubmit(): Promise<void>\n handleSubmit(submitMeta: TSubmitMeta): Promise<void>\n async handleSubmit(submitMeta?: TSubmitMeta): Promise<void> {\n // cast is required since the implementation isn't one of the two overloads\n return this.form.handleSubmit(submitMeta as any)\n }\n\n /**\n * Gets the value of the specified field.\n */\n getFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n ): DeepValue<TFieldGroupData, TField> => {\n return this.form.getFieldValue(this.getFormFieldName(field)) as DeepValue<\n TFieldGroupData,\n TField\n >\n }\n\n /**\n * Gets the metadata of the specified field.\n */\n getFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.getFieldMeta(this.getFormFieldName(field))\n }\n\n /**\n * Updates the metadata of the specified field.\n */\n setFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n updater: Updater<AnyFieldMetaBase>,\n ) => {\n return this.form.setFieldMeta(this.getFormFieldName(field), updater)\n }\n\n /**\n * Sets the value of the specified field and optionally updates the touched state.\n */\n setFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n updater: Updater<DeepValue<TFieldGroupData, TField>>,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.setFieldValue(\n this.getFormFieldName(field) as never,\n updater as never,\n opts,\n )\n }\n\n /**\n * Delete a field and its subfields.\n */\n deleteField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.deleteField(this.getFormFieldName(field))\n }\n\n /**\n * Pushes a value into an array field.\n */\n pushFieldValue = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.pushFieldValue(\n this.getFormFieldName(field),\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Insert a value into an array field at the specified index.\n */\n insertFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.insertFieldValue(\n this.getFormFieldName(field),\n index,\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Replaces a value into an array field at the specified index.\n */\n replaceFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.replaceFieldValue(\n this.getFormFieldName(field),\n index,\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Removes a value from an array field at the specified index.\n */\n removeFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.removeFieldValue(this.getFormFieldName(field), index, opts)\n }\n\n /**\n * Swaps the values at the specified indices within an array field.\n */\n swapFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n index1: number,\n index2: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.swapFieldValues(\n this.getFormFieldName(field),\n index1,\n index2,\n opts,\n )\n }\n\n /**\n * Moves the value at the first specified index to the second specified index within an array field.\n */\n moveFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n index1: number,\n index2: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.moveFieldValues(\n this.getFormFieldName(field),\n index1,\n index2,\n opts,\n )\n }\n\n clearFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.clearFieldValues(this.getFormFieldName(field), opts)\n }\n\n /**\n * Resets the field value and meta to default state\n */\n resetField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.resetField(this.getFormFieldName(field))\n }\n\n validateAllFields = (cause: ValidationCause) =>\n this.form.validateAllFields(cause)\n}\n"],"names":["opts"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FieldGroupApi.js","sources":["../../src/FieldGroupApi.ts"],"sourcesContent":["import { Derived } from '@tanstack/store'\nimport { concatenatePaths, getBy, makePathArray } from './utils'\nimport type { Updater } from './utils'\nimport type {\n FormApi,\n FormAsyncValidateOrFn,\n FormValidateOrFn,\n} from './FormApi'\nimport type { AnyFieldMetaBase, FieldOptions } from './FieldApi'\nimport type {\n DeepKeys,\n DeepKeysOfType,\n DeepValue,\n FieldsMap,\n} from './util-types'\nimport type {\n FieldManipulator,\n UpdateMetaOptions,\n ValidationCause,\n} from './types'\n\nexport type AnyFieldGroupApi = FieldGroupApi<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n>\n\nexport interface FieldGroupState<in out TFieldGroupData> {\n /**\n * The current values of the field group\n */\n values: TFieldGroupData\n}\n\n/**\n * An object representing the options for a field group.\n */\nexport interface FieldGroupOptions<\n in out TFormData,\n in out TFieldGroupData,\n in out TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta = never,\n> {\n form:\n | FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n | FieldGroupApi<\n any,\n TFormData,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n TSubmitMeta\n >\n /**\n * The path to the field group data.\n */\n fields: TFields\n /**\n * The expected subsetValues that the form must provide.\n */\n defaultValues?: TFieldGroupData\n /**\n * onSubmitMeta, the data passed from the handleSubmit handler, to the onSubmit function props\n */\n onSubmitMeta?: TSubmitMeta\n}\n\nexport class FieldGroupApi<\n in out TFormData,\n in out TFieldGroupData,\n in out TFields extends\n | DeepKeysOfType<TFormData, TFieldGroupData | null | undefined>\n | FieldsMap<TFormData, TFieldGroupData>,\n in out TOnMount extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChange extends undefined | FormValidateOrFn<TFormData>,\n in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnDynamic extends undefined | FormValidateOrFn<TFormData>,\n in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n in out TSubmitMeta = never,\n> implements FieldManipulator<TFieldGroupData, TSubmitMeta>\n{\n /**\n * The form that called this field group.\n */\n readonly form: FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >\n\n readonly fieldsMap: TFields\n\n /**\n * Get the true name of the field. Not required within `Field` or `AppField`.\n * @private\n */\n getFormFieldName = <TField extends DeepKeys<TFieldGroupData>>(\n subfield: TField,\n ): DeepKeys<TFormData> => {\n if (typeof this.fieldsMap === 'string') {\n return concatenatePaths(this.fieldsMap, subfield)\n }\n\n const firstAccessor = makePathArray(subfield)[0]\n if (typeof firstAccessor !== 'string') {\n // top-level arrays cannot be mapped\n return ''\n }\n\n const restOfPath = subfield.slice(firstAccessor.length)\n const formMappedPath =\n // TFields is either a string or this. See guard above.\n (this.fieldsMap as FieldsMap<TFormData, TFieldGroupData>)[\n firstAccessor as keyof TFieldGroupData\n ]\n\n return concatenatePaths(formMappedPath, restOfPath)\n }\n\n /**\n * Get the field options with the true form DeepKeys for validators\n * @private\n */\n getFormFieldOptions = <\n TOptions extends FieldOptions<\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n >(\n props: TOptions,\n ): TOptions => {\n const newProps = { ...props }\n const validators = newProps.validators\n\n newProps.name = this.getFormFieldName(props.name)\n\n if (\n validators &&\n (validators.onChangeListenTo || validators.onBlurListenTo)\n ) {\n const newValidators = { ...validators }\n\n const remapListenTo = (listenTo: DeepKeys<any>[] | undefined) => {\n if (!listenTo) return undefined\n return listenTo.map((localFieldName) =>\n this.getFormFieldName(localFieldName),\n )\n }\n\n newValidators.onChangeListenTo = remapListenTo(\n validators.onChangeListenTo,\n )\n newValidators.onBlurListenTo = remapListenTo(validators.onBlurListenTo)\n\n newProps.validators = newValidators\n }\n\n return newProps\n }\n\n store: Derived<FieldGroupState<TFieldGroupData>>\n\n get state() {\n return this.store.state\n }\n\n /**\n * Constructs a new `FieldGroupApi` instance with the given form options.\n */\n constructor(\n opts: FieldGroupOptions<\n TFormData,\n TFieldGroupData,\n TFields,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnDynamic,\n TOnDynamicAsync,\n TOnServer,\n TSubmitMeta\n >,\n ) {\n if (opts.form instanceof FieldGroupApi) {\n const group = opts.form\n this.form = group.form as never\n\n // the DeepKey is already namespaced, so we need to ensure that we reference\n // the form and not the group\n if (typeof opts.fields === 'string') {\n this.fieldsMap = group.getFormFieldName(opts.fields) as TFields\n } else {\n // TypeScript has a tough time with generics being a union for some reason\n const fields = {\n ...(opts.fields as FieldsMap<TFormData, TFieldGroupData>),\n }\n for (const key in fields) {\n fields[key] = group.getFormFieldName(fields[key]) as never\n }\n this.fieldsMap = fields as never\n }\n } else {\n this.form = opts.form\n this.fieldsMap = opts.fields\n }\n\n this.store = new Derived({\n deps: [this.form.store],\n fn: ({ currDepVals }) => {\n const currFormStore = currDepVals[0]\n let values: TFieldGroupData\n if (typeof this.fieldsMap === 'string') {\n // all values live at that name, so we can directly fetch it\n values = getBy(currFormStore.values, this.fieldsMap)\n } else {\n // we need to fetch the values from all places where they were mapped from\n values = {} as never\n const fields: Record<keyof TFieldGroupData, string> = this\n .fieldsMap as never\n for (const key in fields) {\n values[key] = getBy(currFormStore.values, fields[key])\n }\n }\n\n return {\n values,\n }\n },\n })\n }\n\n /**\n * Mounts the field group instance to listen to value changes.\n */\n mount = () => {\n const cleanup = this.store.mount()\n\n return cleanup\n }\n\n /**\n * Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.\n */\n validateArrayFieldsStartingFrom = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n cause: ValidationCause,\n ) => {\n return this.form.validateArrayFieldsStartingFrom(\n this.getFormFieldName(field),\n index,\n cause,\n )\n }\n\n /**\n * Validates a specified field in the form using the correct handlers for a given validation type.\n */\n validateField = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n cause: ValidationCause,\n ) => {\n return this.form.validateField(this.getFormFieldName(field), cause)\n }\n\n /**\n * Handles the form submission, performs validation, and calls the appropriate onSubmit or onSubmitInvalid callbacks.\n */\n handleSubmit(): Promise<void>\n handleSubmit(submitMeta: TSubmitMeta): Promise<void>\n async handleSubmit(submitMeta?: TSubmitMeta): Promise<void> {\n // cast is required since the implementation isn't one of the two overloads\n return this.form.handleSubmit(submitMeta as any)\n }\n\n /**\n * Gets the value of the specified field.\n */\n getFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n ): DeepValue<TFieldGroupData, TField> => {\n return this.form.getFieldValue(this.getFormFieldName(field)) as DeepValue<\n TFieldGroupData,\n TField\n >\n }\n\n /**\n * Gets the metadata of the specified field.\n */\n getFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.getFieldMeta(this.getFormFieldName(field))\n }\n\n /**\n * Updates the metadata of the specified field.\n */\n setFieldMeta = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n updater: Updater<AnyFieldMetaBase>,\n ) => {\n return this.form.setFieldMeta(this.getFormFieldName(field), updater)\n }\n\n /**\n * Sets the value of the specified field and optionally updates the touched state.\n */\n setFieldValue = <TField extends DeepKeys<TFieldGroupData>>(\n field: TField,\n updater: Updater<DeepValue<TFieldGroupData, TField>>,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.setFieldValue(\n this.getFormFieldName(field) as never,\n updater as never,\n opts,\n )\n }\n\n /**\n * Delete a field and its subfields.\n */\n deleteField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.deleteField(this.getFormFieldName(field))\n }\n\n /**\n * Pushes a value into an array field.\n */\n pushFieldValue = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.pushFieldValue(\n this.getFormFieldName(field),\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Insert a value into an array field at the specified index.\n */\n insertFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.insertFieldValue(\n this.getFormFieldName(field),\n index,\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Replaces a value into an array field at the specified index.\n */\n replaceFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n value: DeepValue<TFieldGroupData, TField> extends any[]\n ? DeepValue<TFieldGroupData, TField>[number]\n : never,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.replaceFieldValue(\n this.getFormFieldName(field),\n index,\n // since unknown doesn't extend an array, it types `value` as never.\n value as never,\n opts,\n )\n }\n\n /**\n * Removes a value from an array field at the specified index.\n */\n removeFieldValue = async <\n TField extends DeepKeysOfType<TFieldGroupData, any[]>,\n >(\n field: TField,\n index: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.removeFieldValue(this.getFormFieldName(field), index, opts)\n }\n\n /**\n * Swaps the values at the specified indices within an array field.\n */\n swapFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n index1: number,\n index2: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.swapFieldValues(\n this.getFormFieldName(field),\n index1,\n index2,\n opts,\n )\n }\n\n /**\n * Moves the value at the first specified index to the second specified index within an array field.\n */\n moveFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n index1: number,\n index2: number,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.moveFieldValues(\n this.getFormFieldName(field),\n index1,\n index2,\n opts,\n )\n }\n\n clearFieldValues = <TField extends DeepKeysOfType<TFieldGroupData, any[]>>(\n field: TField,\n opts?: UpdateMetaOptions,\n ) => {\n return this.form.clearFieldValues(this.getFormFieldName(field), opts)\n }\n\n /**\n * Resets the field value and meta to default state\n */\n resetField = <TField extends DeepKeys<TFieldGroupData>>(field: TField) => {\n return this.form.resetField(this.getFormFieldName(field))\n }\n\n validateAllFields = (cause: ValidationCause) =>\n this.form.validateAllFields(cause)\n}\n"],"names":["opts"],"mappings":"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"}
|
package/dist/esm/FormApi.d.ts
CHANGED
|
@@ -164,6 +164,7 @@ export interface FormOptions<in out TFormData, in out TOnMount extends undefined
|
|
|
164
164
|
}) => void;
|
|
165
165
|
transform?: FormTransform<NoInfer<TFormData>, NoInfer<TOnMount>, NoInfer<TOnChange>, NoInfer<TOnChangeAsync>, NoInfer<TOnBlur>, NoInfer<TOnBlurAsync>, NoInfer<TOnSubmit>, NoInfer<TOnSubmitAsync>, NoInfer<TOnDynamic>, NoInfer<TOnDynamicAsync>, NoInfer<TOnServer>, NoInfer<TSubmitMeta>>;
|
|
166
166
|
}
|
|
167
|
+
export type AnyFormOptions = FormOptions<any, any, any, any, any, any, any, any, any, any, any, any>;
|
|
167
168
|
/**
|
|
168
169
|
* An object representing the validation metadata for a field. Not intended for public usage.
|
|
169
170
|
*/
|
|
@@ -332,18 +333,26 @@ export declare class FormApi<in out TFormData, in out TOnMount extends undefined
|
|
|
332
333
|
*/
|
|
333
334
|
prevTransformArray: unknown[];
|
|
334
335
|
/**
|
|
335
|
-
*
|
|
336
|
+
* @private
|
|
336
337
|
*/
|
|
337
338
|
timeoutIds: {
|
|
338
339
|
validations: Record<ValidationCause, ReturnType<typeof setTimeout> | null>;
|
|
339
340
|
listeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>;
|
|
340
341
|
formListeners: Record<ListenerCause, ReturnType<typeof setTimeout> | null>;
|
|
341
342
|
};
|
|
343
|
+
/**
|
|
344
|
+
* @private
|
|
345
|
+
*/
|
|
346
|
+
private _formId;
|
|
347
|
+
/**
|
|
348
|
+
* @private
|
|
349
|
+
*/
|
|
350
|
+
private _devtoolsSubmissionOverride;
|
|
342
351
|
/**
|
|
343
352
|
* Constructs a new `FormApi` instance with the given form options.
|
|
344
353
|
*/
|
|
345
354
|
constructor(opts?: FormOptions<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>);
|
|
346
|
-
|
|
355
|
+
formId(): string | undefined;
|
|
347
356
|
/**
|
|
348
357
|
* @private
|
|
349
358
|
*/
|
package/dist/esm/FormApi.js
CHANGED
|
@@ -3,6 +3,7 @@ import { evaluate, getSyncValidatorArray, determineFormLevelErrorSourceAndValue,
|
|
|
3
3
|
import { defaultValidationLogic } from "./ValidationLogic.js";
|
|
4
4
|
import { standardSchemaValidators, isStandardSchemaValidator } from "./standardSchemaValidator.js";
|
|
5
5
|
import { defaultFieldMeta, metaHelper } from "./metaHelper.js";
|
|
6
|
+
import { formEventClient } from "./EventClient.js";
|
|
6
7
|
function getDefaultFormState(defaultState) {
|
|
7
8
|
return {
|
|
8
9
|
values: defaultState.values ?? {},
|
|
@@ -37,9 +38,17 @@ class FormApi {
|
|
|
37
38
|
const cleanup = () => {
|
|
38
39
|
cleanupFieldMetaDerived();
|
|
39
40
|
cleanupStoreDerived();
|
|
41
|
+
formEventClient.emit("form-unmounted", {
|
|
42
|
+
id: this._formId
|
|
43
|
+
});
|
|
40
44
|
};
|
|
41
45
|
this.options.listeners?.onMount?.({ formApi: this });
|
|
42
46
|
const { onMount } = this.options.validators || {};
|
|
47
|
+
formEventClient.emit("form-state-change", {
|
|
48
|
+
id: this._formId,
|
|
49
|
+
state: this.store.state,
|
|
50
|
+
options: this.options
|
|
51
|
+
});
|
|
43
52
|
if (!onMount) return cleanup;
|
|
44
53
|
this.validateSync("mount");
|
|
45
54
|
return cleanup;
|
|
@@ -621,6 +630,8 @@ class FormApi {
|
|
|
621
630
|
listeners: {},
|
|
622
631
|
formListeners: {}
|
|
623
632
|
};
|
|
633
|
+
this._formId = opts?.formId ?? crypto.randomUUID();
|
|
634
|
+
this._devtoolsSubmissionOverride = false;
|
|
624
635
|
this.baseStore = new Store(
|
|
625
636
|
getDefaultFormState({
|
|
626
637
|
...opts?.defaultState,
|
|
@@ -770,12 +781,40 @@ class FormApi {
|
|
|
770
781
|
});
|
|
771
782
|
this.handleSubmit = this.handleSubmit.bind(this);
|
|
772
783
|
this.update(opts || {});
|
|
784
|
+
this.store.subscribe(() => {
|
|
785
|
+
formEventClient.emit("form-state-change", {
|
|
786
|
+
id: this._formId,
|
|
787
|
+
state: this.store.state,
|
|
788
|
+
options: this.options
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
formEventClient.on("request-form-state", (e) => {
|
|
792
|
+
if (e.payload.id === this._formId) {
|
|
793
|
+
formEventClient.emit("form-state-change", {
|
|
794
|
+
id: this._formId,
|
|
795
|
+
state: this.store.state,
|
|
796
|
+
options: this.options
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
formEventClient.on("request-form-reset", (e) => {
|
|
801
|
+
if (e.payload.id === this._formId) {
|
|
802
|
+
this.reset();
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
formEventClient.on("request-form-force-submit", (e) => {
|
|
806
|
+
if (e.payload.id === this._formId) {
|
|
807
|
+
this._devtoolsSubmissionOverride = true;
|
|
808
|
+
this.handleSubmit();
|
|
809
|
+
this._devtoolsSubmissionOverride = false;
|
|
810
|
+
}
|
|
811
|
+
});
|
|
773
812
|
}
|
|
774
813
|
get state() {
|
|
775
814
|
return this.store.state;
|
|
776
815
|
}
|
|
777
|
-
|
|
778
|
-
return this.
|
|
816
|
+
formId() {
|
|
817
|
+
return this._formId;
|
|
779
818
|
}
|
|
780
819
|
/**
|
|
781
820
|
* @private
|
|
@@ -809,7 +848,7 @@ class FormApi {
|
|
|
809
848
|
}
|
|
810
849
|
);
|
|
811
850
|
});
|
|
812
|
-
if (!this.state.canSubmit) return;
|
|
851
|
+
if (!this.state.canSubmit && !this._devtoolsSubmissionOverride) return;
|
|
813
852
|
const submitMetaArg = submitMeta ?? this.options.onSubmitMeta;
|
|
814
853
|
this.baseStore.setState((d) => ({ ...d, isSubmitting: true }));
|
|
815
854
|
const done = () => {
|
|
@@ -823,6 +862,13 @@ class FormApi {
|
|
|
823
862
|
formApi: this,
|
|
824
863
|
meta: submitMetaArg
|
|
825
864
|
});
|
|
865
|
+
formEventClient.emit("form-submission-state-change", {
|
|
866
|
+
id: this._formId,
|
|
867
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
868
|
+
successful: false,
|
|
869
|
+
stage: "validateAllFields",
|
|
870
|
+
errors: Object.values(this.state.fieldMeta).map((meta) => meta.errors).flat()
|
|
871
|
+
});
|
|
826
872
|
return;
|
|
827
873
|
}
|
|
828
874
|
await this.validate("submit");
|
|
@@ -833,6 +879,13 @@ class FormApi {
|
|
|
833
879
|
formApi: this,
|
|
834
880
|
meta: submitMetaArg
|
|
835
881
|
});
|
|
882
|
+
formEventClient.emit("form-submission-state-change", {
|
|
883
|
+
id: this._formId,
|
|
884
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
885
|
+
successful: false,
|
|
886
|
+
stage: "validate",
|
|
887
|
+
errors: this.state.errors
|
|
888
|
+
});
|
|
836
889
|
return;
|
|
837
890
|
}
|
|
838
891
|
batch(() => {
|
|
@@ -859,6 +912,11 @@ class FormApi {
|
|
|
859
912
|
isSubmitSuccessful: true
|
|
860
913
|
// Set isSubmitSuccessful to true on successful submission
|
|
861
914
|
}));
|
|
915
|
+
formEventClient.emit("form-submission-state-change", {
|
|
916
|
+
id: this._formId,
|
|
917
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
918
|
+
successful: true
|
|
919
|
+
});
|
|
862
920
|
done();
|
|
863
921
|
});
|
|
864
922
|
} catch (err) {
|
|
@@ -867,6 +925,13 @@ class FormApi {
|
|
|
867
925
|
isSubmitSuccessful: false
|
|
868
926
|
// Ensure isSubmitSuccessful is false if an error occurs
|
|
869
927
|
}));
|
|
928
|
+
formEventClient.emit("form-submission-state-change", {
|
|
929
|
+
id: this._formId,
|
|
930
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
931
|
+
successful: false,
|
|
932
|
+
stage: "inflight",
|
|
933
|
+
onError: err
|
|
934
|
+
});
|
|
870
935
|
done();
|
|
871
936
|
throw err;
|
|
872
937
|
}
|