@tanstack/form-core 0.44.1 → 0.45.1
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/FieldApi.cjs.map +1 -1
- package/dist/cjs/FieldApi.d.cts +11 -11
- package/dist/cjs/FormApi.cjs +88 -68
- package/dist/cjs/FormApi.cjs.map +1 -1
- package/dist/cjs/FormApi.d.cts +25 -15
- package/dist/cjs/FormApi.test-d.d.cts +1 -0
- package/dist/cjs/formOptions.cjs.map +1 -1
- package/dist/cjs/formOptions.d.cts +1 -1
- package/dist/cjs/mergeForm.cjs.map +1 -1
- package/dist/cjs/mergeForm.d.cts +1 -1
- package/dist/cjs/metaHelper.cjs.map +1 -1
- package/dist/cjs/metaHelper.d.cts +1 -1
- package/dist/esm/FieldApi.d.ts +11 -11
- package/dist/esm/FieldApi.js.map +1 -1
- package/dist/esm/FormApi.d.ts +25 -15
- package/dist/esm/FormApi.js +88 -68
- package/dist/esm/FormApi.js.map +1 -1
- package/dist/esm/FormApi.test-d.d.ts +1 -0
- package/dist/esm/formOptions.d.ts +1 -1
- package/dist/esm/formOptions.js.map +1 -1
- package/dist/esm/mergeForm.d.ts +1 -1
- package/dist/esm/mergeForm.js.map +1 -1
- package/dist/esm/metaHelper.d.ts +1 -1
- package/dist/esm/metaHelper.js.map +1 -1
- package/package.json +1 -1
- package/src/FieldApi.ts +16 -5
- package/src/FormApi.test-d.ts +31 -0
- package/src/FormApi.ts +108 -35
- package/src/formOptions.ts +3 -1
- package/src/mergeForm.ts +13 -2
- package/src/metaHelper.ts +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeForm.js","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { NoInfer } from './util-types'\n\nfunction isValidKey(key: string | number | symbol): boolean {\n const dangerousProps = ['__proto__', 'constructor', 'prototype']\n return !dangerousProps.includes(String(key))\n}\n\n/**\n * @private\n */\nexport function mutateMergeDeep(\n target: object | null | undefined,\n source: object | null | undefined,\n): object {\n // Early return if either is not an object\n if (target === null || target === undefined || typeof target !== 'object')\n return {} as object\n if (source === null || source === undefined || typeof source !== 'object')\n return target\n\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n\n for (const key of keySet) {\n if (!isValidKey(key)) continue\n\n const targetKey = key as keyof typeof target\n const sourceKey = key as keyof typeof source\n\n if (!Object.hasOwn(source, sourceKey)) continue\n\n const sourceValue = source[sourceKey] as unknown\n const targetValue = target[targetKey] as unknown\n\n // Handle arrays\n if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {\n Object.defineProperty(target, key, {\n value: [...sourceValue],\n enumerable: true,\n writable: true,\n configurable: true,\n })\n continue\n }\n\n // Handle nested objects (type assertion to satisfy ESLint)\n const isTargetObj = typeof targetValue === 'object' && targetValue !== null\n const isSourceObj = typeof sourceValue === 'object' && sourceValue !== null\n const areObjects =\n isTargetObj &&\n isSourceObj &&\n !Array.isArray(targetValue) &&\n !Array.isArray(sourceValue)\n\n if (areObjects) {\n mutateMergeDeep(targetValue as object, sourceValue as object)\n continue\n }\n\n // Handle all other cases\n Object.defineProperty(target, key, {\n value: sourceValue,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n\n return target\n}\n\nexport function mergeForm<TFormData>(\n baseForm: FormApi
|
|
1
|
+
{"version":3,"file":"mergeForm.js","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { NoInfer } from './util-types'\n\nfunction isValidKey(key: string | number | symbol): boolean {\n const dangerousProps = ['__proto__', 'constructor', 'prototype']\n return !dangerousProps.includes(String(key))\n}\n\n/**\n * @private\n */\nexport function mutateMergeDeep(\n target: object | null | undefined,\n source: object | null | undefined,\n): object {\n // Early return if either is not an object\n if (target === null || target === undefined || typeof target !== 'object')\n return {} as object\n if (source === null || source === undefined || typeof source !== 'object')\n return target\n\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n\n for (const key of keySet) {\n if (!isValidKey(key)) continue\n\n const targetKey = key as keyof typeof target\n const sourceKey = key as keyof typeof source\n\n if (!Object.hasOwn(source, sourceKey)) continue\n\n const sourceValue = source[sourceKey] as unknown\n const targetValue = target[targetKey] as unknown\n\n // Handle arrays\n if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {\n Object.defineProperty(target, key, {\n value: [...sourceValue],\n enumerable: true,\n writable: true,\n configurable: true,\n })\n continue\n }\n\n // Handle nested objects (type assertion to satisfy ESLint)\n const isTargetObj = typeof targetValue === 'object' && targetValue !== null\n const isSourceObj = typeof sourceValue === 'object' && sourceValue !== null\n const areObjects =\n isTargetObj &&\n isSourceObj &&\n !Array.isArray(targetValue) &&\n !Array.isArray(sourceValue)\n\n if (areObjects) {\n mutateMergeDeep(targetValue as object, sourceValue as object)\n continue\n }\n\n // Handle all other cases\n Object.defineProperty(target, key, {\n value: sourceValue,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n\n return target\n}\n\nexport function mergeForm<TFormData>(\n baseForm: FormApi<\n NoInfer<TFormData>,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n state: Partial<\n FormApi<TFormData, any, any, any, any, any, any, any, any, any>['state']\n >,\n) {\n mutateMergeDeep(baseForm.state, state)\n return baseForm\n}\n"],"names":[],"mappings":"AAGA,SAAS,WAAW,KAAwC;AAC1D,QAAM,iBAAiB,CAAC,aAAa,eAAe,WAAW;AAC/D,SAAO,CAAC,eAAe,SAAS,OAAO,GAAG,CAAC;AAC7C;AAKgB,SAAA,gBACd,QACA,QACQ;AAER,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AAC/D,WAAO,CAAC;AACV,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AACxD,WAAA;AAEH,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,6BAAa,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD,aAAW,OAAO,QAAQ;AACpB,QAAA,CAAC,WAAW,GAAG,EAAG;AAEtB,UAAM,YAAY;AAClB,UAAM,YAAY;AAElB,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAS,EAAG;AAEjC,UAAA,cAAc,OAAO,SAAS;AAC9B,UAAA,cAAc,OAAO,SAAS;AAGpC,QAAI,MAAM,QAAQ,WAAW,KAAK,MAAM,QAAQ,WAAW,GAAG;AACrD,aAAA,eAAe,QAAQ,KAAK;AAAA,QACjC,OAAO,CAAC,GAAG,WAAW;AAAA,QACtB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,cAAc;AAAA,MAAA,CACf;AACD;AAAA,IAAA;AAIF,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACvE,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACjE,UAAA,aACJ,eACA,eACA,CAAC,MAAM,QAAQ,WAAW,KAC1B,CAAC,MAAM,QAAQ,WAAW;AAE5B,QAAI,YAAY;AACd,sBAAgB,aAAuB,WAAqB;AAC5D;AAAA,IAAA;AAIK,WAAA,eAAe,QAAQ,KAAK;AAAA,MACjC,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,cAAc;AAAA,IAAA,CACf;AAAA,EAAA;AAGI,SAAA;AACT;AAEgB,SAAA,UACd,UAYA,OAGA;AACgB,kBAAA,SAAS,OAAO,KAAK;AAC9B,SAAA;AACT;"}
|
package/dist/esm/metaHelper.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FormApi, FormAsyncValidateOrFn, FormValidateOrFn } from './FormApi.js';
|
|
2
2
|
import { DeepKeys } from './util-types.js';
|
|
3
3
|
type ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move';
|
|
4
|
-
export declare function metaHelper<TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData>, TOnChange extends undefined | FormValidateOrFn<TFormData>, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnBlur extends undefined | FormValidateOrFn<TFormData>, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnSubmit extends undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData
|
|
4
|
+
export declare function metaHelper<TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData>, TOnChange extends undefined | FormValidateOrFn<TFormData>, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnBlur extends undefined | FormValidateOrFn<TFormData>, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnSubmit extends undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>, TSubmitMeta>(formApi: FormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnServer, TSubmitMeta>): {
|
|
5
5
|
handleArrayFieldMetaShift: (field: DeepKeys<TFormData>, index: number, mode: ArrayFieldMode, secondIndex?: number) => void;
|
|
6
6
|
};
|
|
7
7
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metaHelper.js","sources":["../../src/metaHelper.ts"],"sourcesContent":["import type {\n FormApi,\n FormAsyncValidateOrFn,\n FormValidateOrFn,\n} from './FormApi'\nimport type { AnyFieldMeta } from './FieldApi'\nimport type { DeepKeys } from './util-types'\n\ntype ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move'\n\nexport function metaHelper<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n>(\n formApi: FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnServer\n >,\n) {\n function handleArrayFieldMetaShift(\n field: DeepKeys<TFormData>,\n index: number,\n mode: ArrayFieldMode,\n secondIndex?: number,\n ) {\n const affectedFields = getAffectedFields(field, index, mode, secondIndex)\n\n const handlers = {\n insert: () => handleInsertMode(affectedFields, field, index),\n remove: () => handleRemoveMode(affectedFields),\n swap: () =>\n secondIndex !== undefined &&\n handleSwapMode(affectedFields, field, index, secondIndex),\n move: () =>\n secondIndex !== undefined &&\n handleMoveMode(affectedFields, field, index, secondIndex),\n }\n\n handlers[mode]()\n }\n\n function getFieldPath(field: DeepKeys<TFormData>, index: number): string {\n return `${field}[${index}]`\n }\n\n function getAffectedFields(\n field: DeepKeys<TFormData>,\n index: number,\n mode: ArrayFieldMode,\n secondIndex?: number,\n ): DeepKeys<TFormData>[] {\n const affectedFieldKeys = [getFieldPath(field, index)]\n\n if (mode === 'swap') {\n affectedFieldKeys.push(getFieldPath(field, secondIndex!))\n } else if (mode === 'move') {\n const [startIndex, endIndex] = [\n Math.min(index, secondIndex!),\n Math.max(index, secondIndex!),\n ]\n for (let i = startIndex; i <= endIndex; i++) {\n affectedFieldKeys.push(getFieldPath(field, i))\n }\n } else {\n const currentValue = formApi.getFieldValue(field)\n const fieldItems = Array.isArray(currentValue) ? currentValue.length : 0\n for (let i = index + 1; i < fieldItems; i++) {\n affectedFieldKeys.push(getFieldPath(field, i))\n }\n }\n\n return Object.keys(formApi.fieldInfo).filter((fieldKey) =>\n affectedFieldKeys.some((key) => fieldKey.startsWith(key)),\n ) as DeepKeys<TFormData>[]\n }\n\n function updateIndex(\n fieldKey: string,\n direction: 'up' | 'down',\n ): DeepKeys<TFormData> {\n return fieldKey.replace(/\\[(\\d+)\\]/, (_, num) => {\n const currIndex = parseInt(num, 10)\n const newIndex =\n direction === 'up' ? currIndex + 1 : Math.max(0, currIndex - 1)\n return `[${newIndex}]`\n }) as DeepKeys<TFormData>\n }\n\n function shiftMeta(fields: DeepKeys<TFormData>[], direction: 'up' | 'down') {\n const sortedFields = direction === 'up' ? fields : [...fields].reverse()\n\n sortedFields.forEach((fieldKey) => {\n const nextFieldKey = updateIndex(fieldKey.toString(), direction)\n const nextFieldMeta = formApi.getFieldMeta(nextFieldKey)\n if (nextFieldMeta) {\n formApi.setFieldMeta(fieldKey, nextFieldMeta)\n }\n })\n }\n\n const getEmptyFieldMeta = (): AnyFieldMeta => ({\n isValidating: false,\n isTouched: false,\n isBlurred: false,\n isDirty: false,\n isPristine: true,\n errors: [],\n errorMap: {},\n })\n\n const handleInsertMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n insertIndex: number,\n ) => {\n shiftMeta(fields, 'down')\n\n fields.forEach((fieldKey) => {\n if (fieldKey.toString().startsWith(getFieldPath(field, insertIndex))) {\n formApi.setFieldMeta(fieldKey, getEmptyFieldMeta())\n }\n })\n }\n\n const handleRemoveMode = (fields: DeepKeys<TFormData>[]) => {\n shiftMeta(fields, 'up')\n }\n\n const handleMoveMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n fromIndex: number,\n toIndex: number,\n ) => {\n // Store the original field meta that will be reapplied at the destination index\n const fromFields = new Map(\n Object.keys(formApi.fieldInfo)\n .filter((fieldKey) =>\n fieldKey.startsWith(getFieldPath(field, fromIndex)),\n )\n .map((fieldKey) => [\n fieldKey as DeepKeys<TFormData>,\n formApi.getFieldMeta(fieldKey as DeepKeys<TFormData>),\n ]),\n )\n\n shiftMeta(fields, fromIndex < toIndex ? 'up' : 'down')\n\n // Reapply the stored field meta at the destination index\n Object.keys(formApi.fieldInfo)\n .filter((fieldKey) => fieldKey.startsWith(getFieldPath(field, toIndex)))\n .forEach((fieldKey) => {\n const fromKey = fieldKey.replace(\n getFieldPath(field, toIndex),\n getFieldPath(field, fromIndex),\n ) as DeepKeys<TFormData>\n\n const fromMeta = fromFields.get(fromKey)\n if (fromMeta) {\n formApi.setFieldMeta(fieldKey as DeepKeys<TFormData>, fromMeta)\n }\n })\n }\n\n const handleSwapMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n index: number,\n secondIndex: number,\n ) => {\n fields.forEach((fieldKey) => {\n if (!fieldKey.toString().startsWith(getFieldPath(field, index))) return\n\n const swappedKey = fieldKey\n .toString()\n .replace(\n getFieldPath(field, index),\n getFieldPath(field, secondIndex),\n ) as DeepKeys<TFormData>\n\n const [meta1, meta2] = [\n formApi.getFieldMeta(fieldKey),\n formApi.getFieldMeta(swappedKey),\n ]\n\n if (meta1) formApi.setFieldMeta(swappedKey, meta1)\n if (meta2) formApi.setFieldMeta(fieldKey, meta2)\n })\n }\n\n return { handleArrayFieldMetaShift }\n}\n"],"names":[],"mappings":"AAUO,SAAS,
|
|
1
|
+
{"version":3,"file":"metaHelper.js","sources":["../../src/metaHelper.ts"],"sourcesContent":["import type {\n FormApi,\n FormAsyncValidateOrFn,\n FormValidateOrFn,\n} from './FormApi'\nimport type { AnyFieldMeta } from './FieldApi'\nimport type { DeepKeys } from './util-types'\n\ntype ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move'\n\nexport function metaHelper<\n TFormData,\n TOnMount extends undefined | FormValidateOrFn<TFormData>,\n TOnChange extends undefined | FormValidateOrFn<TFormData>,\n TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnBlur extends undefined | FormValidateOrFn<TFormData>,\n TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnSubmit extends undefined | FormValidateOrFn<TFormData>,\n TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,\n TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,\n TSubmitMeta,\n>(\n formApi: FormApi<\n TFormData,\n TOnMount,\n TOnChange,\n TOnChangeAsync,\n TOnBlur,\n TOnBlurAsync,\n TOnSubmit,\n TOnSubmitAsync,\n TOnServer,\n TSubmitMeta\n >,\n) {\n function handleArrayFieldMetaShift(\n field: DeepKeys<TFormData>,\n index: number,\n mode: ArrayFieldMode,\n secondIndex?: number,\n ) {\n const affectedFields = getAffectedFields(field, index, mode, secondIndex)\n\n const handlers = {\n insert: () => handleInsertMode(affectedFields, field, index),\n remove: () => handleRemoveMode(affectedFields),\n swap: () =>\n secondIndex !== undefined &&\n handleSwapMode(affectedFields, field, index, secondIndex),\n move: () =>\n secondIndex !== undefined &&\n handleMoveMode(affectedFields, field, index, secondIndex),\n }\n\n handlers[mode]()\n }\n\n function getFieldPath(field: DeepKeys<TFormData>, index: number): string {\n return `${field}[${index}]`\n }\n\n function getAffectedFields(\n field: DeepKeys<TFormData>,\n index: number,\n mode: ArrayFieldMode,\n secondIndex?: number,\n ): DeepKeys<TFormData>[] {\n const affectedFieldKeys = [getFieldPath(field, index)]\n\n if (mode === 'swap') {\n affectedFieldKeys.push(getFieldPath(field, secondIndex!))\n } else if (mode === 'move') {\n const [startIndex, endIndex] = [\n Math.min(index, secondIndex!),\n Math.max(index, secondIndex!),\n ]\n for (let i = startIndex; i <= endIndex; i++) {\n affectedFieldKeys.push(getFieldPath(field, i))\n }\n } else {\n const currentValue = formApi.getFieldValue(field)\n const fieldItems = Array.isArray(currentValue) ? currentValue.length : 0\n for (let i = index + 1; i < fieldItems; i++) {\n affectedFieldKeys.push(getFieldPath(field, i))\n }\n }\n\n return Object.keys(formApi.fieldInfo).filter((fieldKey) =>\n affectedFieldKeys.some((key) => fieldKey.startsWith(key)),\n ) as DeepKeys<TFormData>[]\n }\n\n function updateIndex(\n fieldKey: string,\n direction: 'up' | 'down',\n ): DeepKeys<TFormData> {\n return fieldKey.replace(/\\[(\\d+)\\]/, (_, num) => {\n const currIndex = parseInt(num, 10)\n const newIndex =\n direction === 'up' ? currIndex + 1 : Math.max(0, currIndex - 1)\n return `[${newIndex}]`\n }) as DeepKeys<TFormData>\n }\n\n function shiftMeta(fields: DeepKeys<TFormData>[], direction: 'up' | 'down') {\n const sortedFields = direction === 'up' ? fields : [...fields].reverse()\n\n sortedFields.forEach((fieldKey) => {\n const nextFieldKey = updateIndex(fieldKey.toString(), direction)\n const nextFieldMeta = formApi.getFieldMeta(nextFieldKey)\n if (nextFieldMeta) {\n formApi.setFieldMeta(fieldKey, nextFieldMeta)\n }\n })\n }\n\n const getEmptyFieldMeta = (): AnyFieldMeta => ({\n isValidating: false,\n isTouched: false,\n isBlurred: false,\n isDirty: false,\n isPristine: true,\n errors: [],\n errorMap: {},\n })\n\n const handleInsertMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n insertIndex: number,\n ) => {\n shiftMeta(fields, 'down')\n\n fields.forEach((fieldKey) => {\n if (fieldKey.toString().startsWith(getFieldPath(field, insertIndex))) {\n formApi.setFieldMeta(fieldKey, getEmptyFieldMeta())\n }\n })\n }\n\n const handleRemoveMode = (fields: DeepKeys<TFormData>[]) => {\n shiftMeta(fields, 'up')\n }\n\n const handleMoveMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n fromIndex: number,\n toIndex: number,\n ) => {\n // Store the original field meta that will be reapplied at the destination index\n const fromFields = new Map(\n Object.keys(formApi.fieldInfo)\n .filter((fieldKey) =>\n fieldKey.startsWith(getFieldPath(field, fromIndex)),\n )\n .map((fieldKey) => [\n fieldKey as DeepKeys<TFormData>,\n formApi.getFieldMeta(fieldKey as DeepKeys<TFormData>),\n ]),\n )\n\n shiftMeta(fields, fromIndex < toIndex ? 'up' : 'down')\n\n // Reapply the stored field meta at the destination index\n Object.keys(formApi.fieldInfo)\n .filter((fieldKey) => fieldKey.startsWith(getFieldPath(field, toIndex)))\n .forEach((fieldKey) => {\n const fromKey = fieldKey.replace(\n getFieldPath(field, toIndex),\n getFieldPath(field, fromIndex),\n ) as DeepKeys<TFormData>\n\n const fromMeta = fromFields.get(fromKey)\n if (fromMeta) {\n formApi.setFieldMeta(fieldKey as DeepKeys<TFormData>, fromMeta)\n }\n })\n }\n\n const handleSwapMode = (\n fields: DeepKeys<TFormData>[],\n field: DeepKeys<TFormData>,\n index: number,\n secondIndex: number,\n ) => {\n fields.forEach((fieldKey) => {\n if (!fieldKey.toString().startsWith(getFieldPath(field, index))) return\n\n const swappedKey = fieldKey\n .toString()\n .replace(\n getFieldPath(field, index),\n getFieldPath(field, secondIndex),\n ) as DeepKeys<TFormData>\n\n const [meta1, meta2] = [\n formApi.getFieldMeta(fieldKey),\n formApi.getFieldMeta(swappedKey),\n ]\n\n if (meta1) formApi.setFieldMeta(swappedKey, meta1)\n if (meta2) formApi.setFieldMeta(fieldKey, meta2)\n })\n }\n\n return { handleArrayFieldMetaShift }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/FieldApi.ts
CHANGED
|
@@ -96,6 +96,7 @@ export type FieldValidateFn<
|
|
|
96
96
|
any,
|
|
97
97
|
any,
|
|
98
98
|
any,
|
|
99
|
+
any,
|
|
99
100
|
any
|
|
100
101
|
>
|
|
101
102
|
}) => unknown
|
|
@@ -179,6 +180,7 @@ export type FieldValidateAsyncFn<
|
|
|
179
180
|
any,
|
|
180
181
|
any,
|
|
181
182
|
any,
|
|
183
|
+
any,
|
|
182
184
|
any
|
|
183
185
|
>
|
|
184
186
|
signal: AbortSignal
|
|
@@ -261,6 +263,7 @@ export type FieldListenerFn<
|
|
|
261
263
|
any,
|
|
262
264
|
any,
|
|
263
265
|
any,
|
|
266
|
+
any,
|
|
264
267
|
any
|
|
265
268
|
>
|
|
266
269
|
}) => void
|
|
@@ -471,6 +474,7 @@ export interface FieldApiOptions<
|
|
|
471
474
|
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
|
|
472
475
|
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
|
|
473
476
|
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
|
|
477
|
+
TParentSubmitMeta,
|
|
474
478
|
> extends FieldOptions<
|
|
475
479
|
TParentData,
|
|
476
480
|
TName,
|
|
@@ -492,7 +496,8 @@ export interface FieldApiOptions<
|
|
|
492
496
|
TFormOnBlurAsync,
|
|
493
497
|
TFormOnSubmit,
|
|
494
498
|
TFormOnSubmitAsync,
|
|
495
|
-
TFormOnServer
|
|
499
|
+
TFormOnServer,
|
|
500
|
+
TParentSubmitMeta
|
|
496
501
|
>
|
|
497
502
|
}
|
|
498
503
|
|
|
@@ -843,6 +848,7 @@ export type AnyFieldApi = FieldApi<
|
|
|
843
848
|
any,
|
|
844
849
|
any,
|
|
845
850
|
any,
|
|
851
|
+
any,
|
|
846
852
|
any
|
|
847
853
|
>
|
|
848
854
|
|
|
@@ -880,6 +886,7 @@ export class FieldApi<
|
|
|
880
886
|
TFormOnSubmit extends undefined | FormValidateOrFn<TParentData>,
|
|
881
887
|
TFormOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TParentData>,
|
|
882
888
|
TFormOnServer extends undefined | FormAsyncValidateOrFn<TParentData>,
|
|
889
|
+
TParentSubmitMeta,
|
|
883
890
|
> {
|
|
884
891
|
/**
|
|
885
892
|
* A reference to the form API instance.
|
|
@@ -902,7 +909,8 @@ export class FieldApi<
|
|
|
902
909
|
TFormOnBlurAsync,
|
|
903
910
|
TFormOnSubmit,
|
|
904
911
|
TFormOnSubmitAsync,
|
|
905
|
-
TFormOnServer
|
|
912
|
+
TFormOnServer,
|
|
913
|
+
TParentSubmitMeta
|
|
906
914
|
>['form']
|
|
907
915
|
/**
|
|
908
916
|
* The field name.
|
|
@@ -929,7 +937,8 @@ export class FieldApi<
|
|
|
929
937
|
TFormOnBlurAsync,
|
|
930
938
|
TFormOnSubmit,
|
|
931
939
|
TFormOnSubmitAsync,
|
|
932
|
-
TFormOnServer
|
|
940
|
+
TFormOnServer,
|
|
941
|
+
TParentSubmitMeta
|
|
933
942
|
> = {} as any
|
|
934
943
|
/**
|
|
935
944
|
* The field state store.
|
|
@@ -985,7 +994,8 @@ export class FieldApi<
|
|
|
985
994
|
TFormOnBlurAsync,
|
|
986
995
|
TFormOnSubmit,
|
|
987
996
|
TFormOnSubmitAsync,
|
|
988
|
-
TFormOnServer
|
|
997
|
+
TFormOnServer,
|
|
998
|
+
TParentSubmitMeta
|
|
989
999
|
>,
|
|
990
1000
|
) {
|
|
991
1001
|
this.form = opts.form as never
|
|
@@ -1130,7 +1140,8 @@ export class FieldApi<
|
|
|
1130
1140
|
TFormOnBlurAsync,
|
|
1131
1141
|
TFormOnSubmit,
|
|
1132
1142
|
TFormOnSubmitAsync,
|
|
1133
|
-
TFormOnServer
|
|
1143
|
+
TFormOnServer,
|
|
1144
|
+
TParentSubmitMeta
|
|
1134
1145
|
>,
|
|
1135
1146
|
) => {
|
|
1136
1147
|
// Default Value
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { assertType, it } from 'vitest'
|
|
2
|
+
import { FormApi } from '../src/index'
|
|
3
|
+
|
|
4
|
+
it('should type handleSubmit as never when onSubmitMeta is not passed', () => {
|
|
5
|
+
const form = new FormApi({
|
|
6
|
+
defaultValues: {
|
|
7
|
+
name: 'test',
|
|
8
|
+
},
|
|
9
|
+
} as const)
|
|
10
|
+
|
|
11
|
+
assertType<() => Promise<void>>(form.handleSubmit)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
type OnSubmitMeta = {
|
|
15
|
+
group: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
it('should type handleChange correctly', () => {
|
|
19
|
+
const form = new FormApi({
|
|
20
|
+
defaultValues: {
|
|
21
|
+
name: 'test',
|
|
22
|
+
},
|
|
23
|
+
onSubmitMeta: {} as OnSubmitMeta,
|
|
24
|
+
} as const)
|
|
25
|
+
|
|
26
|
+
form.handleSubmit({ group: 'track' })
|
|
27
|
+
|
|
28
|
+
assertType<(submitMeta: { group: string }) => Promise<void>>(
|
|
29
|
+
form.handleSubmit,
|
|
30
|
+
)
|
|
31
|
+
})
|
package/src/FormApi.ts
CHANGED
|
@@ -74,6 +74,7 @@ export type FormValidateFn<TFormData> = (props: {
|
|
|
74
74
|
any,
|
|
75
75
|
any,
|
|
76
76
|
any,
|
|
77
|
+
any,
|
|
77
78
|
any
|
|
78
79
|
>
|
|
79
80
|
}) => unknown
|
|
@@ -109,6 +110,7 @@ export type FormValidateAsyncFn<TFormData> = (props: {
|
|
|
109
110
|
any,
|
|
110
111
|
any,
|
|
111
112
|
any,
|
|
113
|
+
any,
|
|
112
114
|
any
|
|
113
115
|
>
|
|
114
116
|
signal: AbortSignal
|
|
@@ -199,6 +201,7 @@ export interface FormTransform<
|
|
|
199
201
|
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
|
|
200
202
|
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
201
203
|
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
204
|
+
TSubmitMeta = never,
|
|
202
205
|
> {
|
|
203
206
|
fn: (
|
|
204
207
|
formBase: FormApi<
|
|
@@ -210,7 +213,8 @@ export interface FormTransform<
|
|
|
210
213
|
TOnBlurAsync,
|
|
211
214
|
TOnSubmit,
|
|
212
215
|
TOnSubmitAsync,
|
|
213
|
-
TOnServer
|
|
216
|
+
TOnServer,
|
|
217
|
+
TSubmitMeta
|
|
214
218
|
>,
|
|
215
219
|
) => FormApi<
|
|
216
220
|
TFormData,
|
|
@@ -221,7 +225,8 @@ export interface FormTransform<
|
|
|
221
225
|
TOnBlurAsync,
|
|
222
226
|
TOnSubmit,
|
|
223
227
|
TOnSubmitAsync,
|
|
224
|
-
TOnServer
|
|
228
|
+
TOnServer,
|
|
229
|
+
TSubmitMeta
|
|
225
230
|
>
|
|
226
231
|
deps: unknown[]
|
|
227
232
|
}
|
|
@@ -239,6 +244,7 @@ export interface FormOptions<
|
|
|
239
244
|
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
|
|
240
245
|
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
241
246
|
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
247
|
+
TSubmitMeta = never,
|
|
242
248
|
> {
|
|
243
249
|
/**
|
|
244
250
|
* Set initial values for your form.
|
|
@@ -281,6 +287,12 @@ export interface FormOptions<
|
|
|
281
287
|
TOnSubmit,
|
|
282
288
|
TOnSubmitAsync
|
|
283
289
|
>
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* onSubmitMeta, the data passed from the handleSubmit handler, to the onSubmit function props
|
|
293
|
+
*/
|
|
294
|
+
onSubmitMeta?: TSubmitMeta
|
|
295
|
+
|
|
284
296
|
/**
|
|
285
297
|
* A function to be called when the form is submitted, what should happen once the user submits a valid form returns `any` or a promise `Promise<any>`
|
|
286
298
|
*/
|
|
@@ -295,8 +307,10 @@ export interface FormOptions<
|
|
|
295
307
|
TOnBlurAsync,
|
|
296
308
|
TOnSubmit,
|
|
297
309
|
TOnSubmitAsync,
|
|
298
|
-
TOnServer
|
|
310
|
+
TOnServer,
|
|
311
|
+
TSubmitMeta
|
|
299
312
|
>
|
|
313
|
+
meta: TSubmitMeta
|
|
300
314
|
}) => any | Promise<any>
|
|
301
315
|
/**
|
|
302
316
|
* Specify an action for scenarios where the user tries to submit an invalid form.
|
|
@@ -312,7 +326,8 @@ export interface FormOptions<
|
|
|
312
326
|
TOnBlurAsync,
|
|
313
327
|
TOnSubmit,
|
|
314
328
|
TOnSubmitAsync,
|
|
315
|
-
TOnServer
|
|
329
|
+
TOnServer,
|
|
330
|
+
TSubmitMeta
|
|
316
331
|
>
|
|
317
332
|
}) => void
|
|
318
333
|
transform?: FormTransform<
|
|
@@ -324,7 +339,8 @@ export interface FormOptions<
|
|
|
324
339
|
TOnBlurAsync,
|
|
325
340
|
TOnSubmit,
|
|
326
341
|
TOnSubmitAsync,
|
|
327
|
-
TOnServer
|
|
342
|
+
TOnServer,
|
|
343
|
+
TSubmitMeta
|
|
328
344
|
>
|
|
329
345
|
}
|
|
330
346
|
|
|
@@ -363,6 +379,7 @@ export type FieldInfo<TFormData> = {
|
|
|
363
379
|
any,
|
|
364
380
|
any,
|
|
365
381
|
any,
|
|
382
|
+
any,
|
|
366
383
|
any
|
|
367
384
|
> | null
|
|
368
385
|
/**
|
|
@@ -610,7 +627,18 @@ function getDefaultFormState<
|
|
|
610
627
|
*
|
|
611
628
|
* A type representing the Form API with all generics set to `any` for convenience.
|
|
612
629
|
*/
|
|
613
|
-
export type AnyFormApi = FormApi<
|
|
630
|
+
export type AnyFormApi = FormApi<
|
|
631
|
+
any,
|
|
632
|
+
any,
|
|
633
|
+
any,
|
|
634
|
+
any,
|
|
635
|
+
any,
|
|
636
|
+
any,
|
|
637
|
+
any,
|
|
638
|
+
any,
|
|
639
|
+
any,
|
|
640
|
+
any
|
|
641
|
+
>
|
|
614
642
|
|
|
615
643
|
/**
|
|
616
644
|
* A class representing the Form API. It handles the logic and interactions with the form state.
|
|
@@ -629,6 +657,7 @@ export class FormApi<
|
|
|
629
657
|
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
|
|
630
658
|
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
631
659
|
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
660
|
+
TSubmitMeta = never,
|
|
632
661
|
> {
|
|
633
662
|
/**
|
|
634
663
|
* The options for the form.
|
|
@@ -642,7 +671,8 @@ export class FormApi<
|
|
|
642
671
|
TOnBlurAsync,
|
|
643
672
|
TOnSubmit,
|
|
644
673
|
TOnSubmitAsync,
|
|
645
|
-
TOnServer
|
|
674
|
+
TOnServer,
|
|
675
|
+
TSubmitMeta
|
|
646
676
|
> = {}
|
|
647
677
|
baseStore!: Store<
|
|
648
678
|
BaseFormState<
|
|
@@ -685,6 +715,20 @@ export class FormApi<
|
|
|
685
715
|
*/
|
|
686
716
|
prevTransformArray: unknown[] = []
|
|
687
717
|
|
|
718
|
+
/**
|
|
719
|
+
* @private map of errors originated from form level validators
|
|
720
|
+
*/
|
|
721
|
+
prevFieldsErrorMap: FormErrorMapFromValidator<
|
|
722
|
+
TFormData,
|
|
723
|
+
TOnMount,
|
|
724
|
+
TOnChange,
|
|
725
|
+
TOnChangeAsync,
|
|
726
|
+
TOnBlur,
|
|
727
|
+
TOnBlurAsync,
|
|
728
|
+
TOnSubmit,
|
|
729
|
+
TOnSubmitAsync
|
|
730
|
+
> = {}
|
|
731
|
+
|
|
688
732
|
/**
|
|
689
733
|
* Constructs a new `FormApi` instance with the given form options.
|
|
690
734
|
*/
|
|
@@ -698,7 +742,8 @@ export class FormApi<
|
|
|
698
742
|
TOnBlurAsync,
|
|
699
743
|
TOnSubmit,
|
|
700
744
|
TOnSubmitAsync,
|
|
701
|
-
TOnServer
|
|
745
|
+
TOnServer,
|
|
746
|
+
TSubmitMeta
|
|
702
747
|
>,
|
|
703
748
|
) {
|
|
704
749
|
this.baseStore = new Store(
|
|
@@ -868,7 +913,7 @@ export class FormApi<
|
|
|
868
913
|
>
|
|
869
914
|
>((prev, curr) => {
|
|
870
915
|
if (curr === undefined) return prev
|
|
871
|
-
|
|
916
|
+
|
|
872
917
|
if (curr && isGlobalFormValidationError(curr)) {
|
|
873
918
|
prev.push(curr.form as never)
|
|
874
919
|
return prev
|
|
@@ -958,6 +1003,8 @@ export class FormApi<
|
|
|
958
1003
|
},
|
|
959
1004
|
})
|
|
960
1005
|
|
|
1006
|
+
this.handleSubmit = this.handleSubmit.bind(this)
|
|
1007
|
+
|
|
961
1008
|
this.update(opts || {})
|
|
962
1009
|
}
|
|
963
1010
|
|
|
@@ -1013,7 +1060,8 @@ export class FormApi<
|
|
|
1013
1060
|
TOnBlurAsync,
|
|
1014
1061
|
TOnSubmit,
|
|
1015
1062
|
TOnSubmitAsync,
|
|
1016
|
-
TOnServer
|
|
1063
|
+
TOnServer,
|
|
1064
|
+
TSubmitMeta
|
|
1017
1065
|
>,
|
|
1018
1066
|
) => {
|
|
1019
1067
|
if (!options) return
|
|
@@ -1195,7 +1243,7 @@ export class FormApi<
|
|
|
1195
1243
|
const validates = getSyncValidatorArray(cause, this.options)
|
|
1196
1244
|
let hasErrored = false as boolean
|
|
1197
1245
|
|
|
1198
|
-
const
|
|
1246
|
+
const newFieldsErrorMap: FormErrorMapFromValidator<
|
|
1199
1247
|
TFormData,
|
|
1200
1248
|
TOnMount,
|
|
1201
1249
|
TOnChange,
|
|
@@ -1227,12 +1275,12 @@ export class FormApi<
|
|
|
1227
1275
|
if (fieldErrors) {
|
|
1228
1276
|
for (const [field, fieldError] of Object.entries(fieldErrors)) {
|
|
1229
1277
|
const oldErrorMap =
|
|
1230
|
-
|
|
1278
|
+
newFieldsErrorMap[field as DeepKeys<TFormData>] || {}
|
|
1231
1279
|
const newErrorMap = {
|
|
1232
1280
|
...oldErrorMap,
|
|
1233
1281
|
[errorMapKey]: fieldError,
|
|
1234
1282
|
}
|
|
1235
|
-
|
|
1283
|
+
newFieldsErrorMap[field as DeepKeys<TFormData>] = newErrorMap
|
|
1236
1284
|
|
|
1237
1285
|
const fieldMeta = this.getFieldMeta(field as DeepKeys<TFormData>)
|
|
1238
1286
|
if (fieldMeta && fieldMeta.errorMap[errorMapKey] !== fieldError) {
|
|
@@ -1247,6 +1295,24 @@ export class FormApi<
|
|
|
1247
1295
|
}
|
|
1248
1296
|
}
|
|
1249
1297
|
|
|
1298
|
+
for (const field of Object.keys(this.prevFieldsErrorMap) as Array<
|
|
1299
|
+
DeepKeys<TFormData>
|
|
1300
|
+
>) {
|
|
1301
|
+
const fieldMeta = this.getFieldMeta(field)
|
|
1302
|
+
if (
|
|
1303
|
+
fieldMeta?.errorMap[errorMapKey] &&
|
|
1304
|
+
!newFieldsErrorMap[field]?.[errorMapKey]
|
|
1305
|
+
) {
|
|
1306
|
+
this.setFieldMeta(field, (prev) => ({
|
|
1307
|
+
...prev,
|
|
1308
|
+
errorMap: {
|
|
1309
|
+
...prev.errorMap,
|
|
1310
|
+
[errorMapKey]: undefined,
|
|
1311
|
+
},
|
|
1312
|
+
}))
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1250
1316
|
if (this.state.errorMap[errorMapKey] !== formError) {
|
|
1251
1317
|
this.baseStore.setState((prev) => ({
|
|
1252
1318
|
...prev,
|
|
@@ -1261,28 +1327,30 @@ export class FormApi<
|
|
|
1261
1327
|
hasErrored = true
|
|
1262
1328
|
}
|
|
1263
1329
|
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* when we have an error for onSubmit in the state, we want
|
|
1333
|
+
* to clear the error as soon as the user enters a valid value in the field
|
|
1334
|
+
*/
|
|
1335
|
+
const submitErrKey = getErrorMapKey('submit')
|
|
1336
|
+
if (
|
|
1337
|
+
this.state.errorMap[submitErrKey] &&
|
|
1338
|
+
cause !== 'submit' &&
|
|
1339
|
+
!hasErrored
|
|
1340
|
+
) {
|
|
1341
|
+
this.baseStore.setState((prev) => ({
|
|
1342
|
+
...prev,
|
|
1343
|
+
errorMap: {
|
|
1344
|
+
...prev.errorMap,
|
|
1345
|
+
[submitErrKey]: undefined,
|
|
1346
|
+
},
|
|
1347
|
+
}))
|
|
1348
|
+
}
|
|
1264
1349
|
})
|
|
1265
1350
|
|
|
1266
|
-
|
|
1267
|
-
* when we have an error for onSubmit in the state, we want
|
|
1268
|
-
* to clear the error as soon as the user enters a valid value in the field
|
|
1269
|
-
*/
|
|
1270
|
-
const submitErrKey = getErrorMapKey('submit')
|
|
1271
|
-
if (
|
|
1272
|
-
this.state.errorMap[submitErrKey] &&
|
|
1273
|
-
cause !== 'submit' &&
|
|
1274
|
-
!hasErrored
|
|
1275
|
-
) {
|
|
1276
|
-
this.baseStore.setState((prev) => ({
|
|
1277
|
-
...prev,
|
|
1278
|
-
errorMap: {
|
|
1279
|
-
...prev.errorMap,
|
|
1280
|
-
[submitErrKey]: undefined,
|
|
1281
|
-
},
|
|
1282
|
-
}))
|
|
1283
|
-
}
|
|
1351
|
+
this.prevFieldsErrorMap = newFieldsErrorMap
|
|
1284
1352
|
|
|
1285
|
-
return { hasErrored, fieldsErrorMap }
|
|
1353
|
+
return { hasErrored, fieldsErrorMap: newFieldsErrorMap }
|
|
1286
1354
|
}
|
|
1287
1355
|
|
|
1288
1356
|
/**
|
|
@@ -1319,7 +1387,6 @@ export class FormApi<
|
|
|
1319
1387
|
| undefined
|
|
1320
1388
|
|
|
1321
1389
|
for (const validateObj of validates) {
|
|
1322
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1323
1390
|
if (!validateObj.validate) continue
|
|
1324
1391
|
const key = getErrorMapKey(validateObj.cause)
|
|
1325
1392
|
const fieldValidatorMeta = this.state.validationMetaMap[key]
|
|
@@ -1482,7 +1549,9 @@ export class FormApi<
|
|
|
1482
1549
|
/**
|
|
1483
1550
|
* Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
|
|
1484
1551
|
*/
|
|
1485
|
-
handleSubmit
|
|
1552
|
+
handleSubmit(): Promise<void>
|
|
1553
|
+
handleSubmit(submitMeta: TSubmitMeta): Promise<void>
|
|
1554
|
+
async handleSubmit(submitMeta?: TSubmitMeta): Promise<void> {
|
|
1486
1555
|
this.baseStore.setState((old) => ({
|
|
1487
1556
|
...old,
|
|
1488
1557
|
// Submission attempts mark the form as not submitted
|
|
@@ -1536,7 +1605,11 @@ export class FormApi<
|
|
|
1536
1605
|
|
|
1537
1606
|
try {
|
|
1538
1607
|
// Run the submit code
|
|
1539
|
-
await this.options.onSubmit?.({
|
|
1608
|
+
await this.options.onSubmit?.({
|
|
1609
|
+
value: this.state.values,
|
|
1610
|
+
formApi: this,
|
|
1611
|
+
meta: submitMeta ?? this.options.onSubmitMeta,
|
|
1612
|
+
} as any)
|
|
1540
1613
|
|
|
1541
1614
|
batch(() => {
|
|
1542
1615
|
this.baseStore.setState((prev) => ({ ...prev, isSubmitted: true }))
|
package/src/formOptions.ts
CHANGED
|
@@ -14,6 +14,7 @@ export function formOptions<
|
|
|
14
14
|
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
|
|
15
15
|
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
16
16
|
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
17
|
+
TSubmitMeta,
|
|
17
18
|
>(
|
|
18
19
|
defaultOpts?: FormOptions<
|
|
19
20
|
TFormData,
|
|
@@ -24,7 +25,8 @@ export function formOptions<
|
|
|
24
25
|
TOnBlurAsync,
|
|
25
26
|
TOnSubmit,
|
|
26
27
|
TOnSubmitAsync,
|
|
27
|
-
TOnServer
|
|
28
|
+
TOnServer,
|
|
29
|
+
TSubmitMeta
|
|
28
30
|
>,
|
|
29
31
|
) {
|
|
30
32
|
return defaultOpts
|
package/src/mergeForm.ts
CHANGED
|
@@ -72,9 +72,20 @@ export function mutateMergeDeep(
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export function mergeForm<TFormData>(
|
|
75
|
-
baseForm: FormApi<
|
|
75
|
+
baseForm: FormApi<
|
|
76
|
+
NoInfer<TFormData>,
|
|
77
|
+
any,
|
|
78
|
+
any,
|
|
79
|
+
any,
|
|
80
|
+
any,
|
|
81
|
+
any,
|
|
82
|
+
any,
|
|
83
|
+
any,
|
|
84
|
+
any,
|
|
85
|
+
any
|
|
86
|
+
>,
|
|
76
87
|
state: Partial<
|
|
77
|
-
FormApi<TFormData, any, any, any, any, any, any, any, any>['state']
|
|
88
|
+
FormApi<TFormData, any, any, any, any, any, any, any, any, any>['state']
|
|
78
89
|
>,
|
|
79
90
|
) {
|
|
80
91
|
mutateMergeDeep(baseForm.state, state)
|
package/src/metaHelper.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function metaHelper<
|
|
|
18
18
|
TOnSubmit extends undefined | FormValidateOrFn<TFormData>,
|
|
19
19
|
TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
20
20
|
TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>,
|
|
21
|
+
TSubmitMeta,
|
|
21
22
|
>(
|
|
22
23
|
formApi: FormApi<
|
|
23
24
|
TFormData,
|
|
@@ -28,7 +29,8 @@ export function metaHelper<
|
|
|
28
29
|
TOnBlurAsync,
|
|
29
30
|
TOnSubmit,
|
|
30
31
|
TOnSubmitAsync,
|
|
31
|
-
TOnServer
|
|
32
|
+
TOnServer,
|
|
33
|
+
TSubmitMeta
|
|
32
34
|
>,
|
|
33
35
|
) {
|
|
34
36
|
function handleArrayFieldMetaShift(
|