@tanstack/form-core 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,9 +47,12 @@ var FieldApi = class _FieldApi {
47
47
  this.update(this.options);
48
48
  (_b = (_a = this.options).onMount) == null ? void 0 : _b.call(_a, this);
49
49
  return () => {
50
+ const preserveValue = this.options.preserveValue;
50
51
  unsubscribe();
51
- delete info.instances[this.uid];
52
- if (!Object.keys(info.instances).length) {
52
+ if (!preserveValue) {
53
+ delete info.instances[this.uid];
54
+ }
55
+ if (!Object.keys(info.instances).length && !preserveValue) {
53
56
  delete this.form.fieldInfo[this.name];
54
57
  }
55
58
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name as never]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n preserveValue?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n const preserveValue = this.options.preserveValue\n unsubscribe()\n if (!preserveValue) {\n delete info.instances[this.uid]\n }\n\n if (!Object.keys(info.instances).length && !preserveValue) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
@@ -23,9 +23,12 @@ var FieldApi = class _FieldApi {
23
23
  this.update(this.options);
24
24
  (_b = (_a = this.options).onMount) == null ? void 0 : _b.call(_a, this);
25
25
  return () => {
26
+ const preserveValue = this.options.preserveValue;
26
27
  unsubscribe();
27
- delete info.instances[this.uid];
28
- if (!Object.keys(info.instances).length) {
28
+ if (!preserveValue) {
29
+ delete info.instances[this.uid];
30
+ }
31
+ if (!Object.keys(info.instances).length && !preserveValue) {
29
32
  delete this.form.fieldInfo[this.name];
30
33
  }
31
34
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name as never]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n preserveValue?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n const preserveValue = this.options.preserveValue\n unsubscribe()\n if (!preserveValue) {\n delete info.instances[this.uid]\n }\n\n if (!Object.keys(info.instances).length && !preserveValue) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
@@ -172,6 +172,10 @@ var FormApi = class {
172
172
  });
173
173
  });
174
174
  };
175
+ this.deleteField = (field) => {
176
+ delete this.state.values[field];
177
+ delete this.state.fieldMeta[field];
178
+ };
175
179
  this.pushFieldValue = (field, value, opts) => {
176
180
  return this.setFieldValue(
177
181
  field,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {\n delete this.state.values[field as keyof TFormData]\n delete this.state.fieldMeta[field]\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -148,6 +148,10 @@ var FormApi = class {
148
148
  });
149
149
  });
150
150
  };
151
+ this.deleteField = (field) => {
152
+ delete this.state.values[field];
153
+ delete this.state.fieldMeta[field];
154
+ };
151
155
  this.pushFieldValue = (field, value, opts) => {
152
156
  return this.setFieldValue(
153
157
  field,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {\n delete this.state.values[field as keyof TFormData]\n delete this.state.fieldMeta[field]\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -73,6 +73,7 @@ declare class FormApi<TFormData, ValidatorType> {
73
73
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
74
74
  touch?: boolean;
75
75
  }) => void;
76
+ deleteField: <TField extends DeepKeys<TFormData>>(field: TField) => void;
76
77
  pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
77
78
  touch?: boolean;
78
79
  }) => void;
@@ -96,6 +97,7 @@ interface FieldOptions<TParentData, TName extends DeepKeys<TParentData>, Validat
96
97
  defaultValue?: TData;
97
98
  asyncDebounceMs?: number;
98
99
  asyncAlways?: boolean;
100
+ preserveValue?: boolean;
99
101
  validator?: ValidatorType;
100
102
  onMount?: (formApi: FieldApi<TParentData, TName, ValidatorType, TData>) => void;
101
103
  onChange?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>;
@@ -73,6 +73,7 @@ declare class FormApi<TFormData, ValidatorType> {
73
73
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
74
74
  touch?: boolean;
75
75
  }) => void;
76
+ deleteField: <TField extends DeepKeys<TFormData>>(field: TField) => void;
76
77
  pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
77
78
  touch?: boolean;
78
79
  }) => void;
@@ -96,6 +97,7 @@ interface FieldOptions<TParentData, TName extends DeepKeys<TParentData>, Validat
96
97
  defaultValue?: TData;
97
98
  asyncDebounceMs?: number;
98
99
  asyncAlways?: boolean;
100
+ preserveValue?: boolean;
99
101
  validator?: ValidatorType;
100
102
  onMount?: (formApi: FieldApi<TParentData, TName, ValidatorType, TData>) => void;
101
103
  onChange?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>;
@@ -46,9 +46,12 @@ var FieldApi = class _FieldApi {
46
46
  this.update(this.options);
47
47
  this.options.onMount?.(this);
48
48
  return () => {
49
+ const preserveValue = this.options.preserveValue;
49
50
  unsubscribe();
50
- delete info.instances[this.uid];
51
- if (!Object.keys(info.instances).length) {
51
+ if (!preserveValue) {
52
+ delete info.instances[this.uid];
53
+ }
54
+ if (!Object.keys(info.instances).length && !preserveValue) {
52
55
  delete this.form.fieldInfo[this.name];
53
56
  }
54
57
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name as never]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n preserveValue?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n const preserveValue = this.options.preserveValue\n unsubscribe()\n if (!preserveValue) {\n delete info.instances[this.uid]\n }\n\n if (!Object.keys(info.instances).length && !preserveValue) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
@@ -22,9 +22,12 @@ var FieldApi = class _FieldApi {
22
22
  this.update(this.options);
23
23
  this.options.onMount?.(this);
24
24
  return () => {
25
+ const preserveValue = this.options.preserveValue;
25
26
  unsubscribe();
26
- delete info.instances[this.uid];
27
- if (!Object.keys(info.instances).length) {
27
+ if (!preserveValue) {
28
+ delete info.instances[this.uid];
29
+ }
30
+ if (!Object.keys(info.instances).length && !preserveValue) {
28
31
  delete this.form.fieldInfo[this.name];
29
32
  }
30
33
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n unsubscribe()\n delete info.instances[this.uid]\n if (!Object.keys(info.instances).length) {\n delete this.form.fieldInfo[this.name as never]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FieldApi.ts"],"sourcesContent":["import { type DeepKeys, type DeepValue, type Updater } from './utils'\nimport type { FormApi, ValidationErrorMap } from './FormApi'\nimport { Store } from '@tanstack/store'\nimport type { Validator, ValidationError } from './types'\n\nexport type ValidationCause = 'change' | 'blur' | 'submit' | 'mount'\n\ntype ValidateFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError\n\ntype ValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateFn<TParentData, TName, ValidatorType, TData>\n : ValidateFn<TParentData, TName, ValidatorType, TData>\n\ntype ValidateAsyncFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = (\n value: TData,\n fieldApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n) => ValidationError | Promise<ValidationError>\n\ntype AsyncValidateOrFn<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> = ValidatorType extends Validator<TData>\n ?\n | Parameters<ReturnType<ValidatorType>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : FormValidator extends Validator<TData>\n ?\n | Parameters<ReturnType<FormValidator>['validate']>[1]\n | ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n : ValidateAsyncFn<TParentData, TName, ValidatorType, TData>\n\nexport interface FieldOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n name: TName\n index?: TData extends any[] ? number : never\n defaultValue?: TData\n asyncDebounceMs?: number\n asyncAlways?: boolean\n preserveValue?: boolean\n validator?: ValidatorType\n onMount?: (\n formApi: FieldApi<TParentData, TName, ValidatorType, TData>,\n ) => void\n onChange?: ValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>\n onBlurAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n onBlurAsyncDebounceMs?: number\n onSubmitAsync?: AsyncValidateOrFn<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >\n defaultMeta?: Partial<FieldMeta>\n}\n\nexport interface FieldApiOptions<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> extends FieldOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n > {\n form: FormApi<TParentData, FormValidator>\n}\n\nexport type FieldMeta = {\n isTouched: boolean\n touchedErrors: ValidationError[]\n errors: ValidationError[]\n errorMap: ValidationErrorMap\n isValidating: boolean\n}\n\nlet uid = 0\n\nexport type FieldState<TData> = {\n value: TData\n meta: FieldMeta\n}\n\nexport type ResolveName<TParentData> = unknown extends TParentData\n ? string\n : DeepKeys<TParentData>\n\nexport class FieldApi<\n TParentData,\n TName extends DeepKeys<TParentData>,\n ValidatorType,\n FormValidator,\n TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,\n> {\n uid: number\n form: FieldApiOptions<TParentData, TName, ValidatorType, TData>['form']\n name!: DeepKeys<TParentData>\n options: FieldApiOptions<TParentData, TName, ValidatorType, TData> = {} as any\n store!: Store<FieldState<TData>>\n state!: FieldState<TData>\n prevState!: FieldState<TData>\n\n constructor(\n opts: FieldApiOptions<\n TParentData,\n TName,\n ValidatorType,\n FormValidator,\n TData\n >,\n ) {\n this.form = opts.form as never\n this.uid = uid++\n // Support field prefixing from FieldScope\n // let fieldPrefix = ''\n // if (this.form.fieldName) {\n // fieldPrefix = `${this.form.fieldName}.`\n // }\n\n this.name = opts.name as never\n\n if (opts.defaultValue !== undefined) {\n this.form.setFieldValue(this.name, opts.defaultValue as never)\n }\n\n this.store = new Store<FieldState<TData>>(\n {\n value: this.getValue(),\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n meta: this._getMeta() ?? {\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...opts.defaultMeta,\n },\n },\n {\n onUpdate: () => {\n const state = this.store.state\n\n state.meta.errors = Object.values(state.meta.errorMap).filter(\n (val: unknown) => val !== undefined,\n )\n\n state.meta.touchedErrors = state.meta.isTouched\n ? state.meta.errors\n : []\n\n this.prevState = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n this.prevState = this.state\n this.options = opts as never\n }\n\n mount = () => {\n const info = this.getInfo()\n info.instances[this.uid] = this as never\n\n const unsubscribe = this.form.store.subscribe(() => {\n this.store.batch(() => {\n const nextValue = this.getValue()\n const nextMeta = this.getMeta()\n\n if (nextValue !== this.state.value) {\n this.store.setState((prev) => ({ ...prev, value: nextValue }))\n }\n\n if (nextMeta !== this.state.meta) {\n this.store.setState((prev) => ({ ...prev, meta: nextMeta }))\n }\n })\n })\n\n this.update(this.options as never)\n this.options.onMount?.(this as never)\n\n return () => {\n const preserveValue = this.options.preserveValue\n unsubscribe()\n if (!preserveValue) {\n delete info.instances[this.uid]\n }\n\n if (!Object.keys(info.instances).length && !preserveValue) {\n delete this.form.fieldInfo[this.name]\n }\n }\n }\n\n update = (\n opts: FieldApiOptions<TParentData, TName, ValidatorType, TData>,\n ) => {\n // Default Value\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.state.value === undefined) {\n const formDefault =\n opts.form.options.defaultValues?.[opts.name as keyof TParentData]\n\n if (opts.defaultValue !== undefined) {\n this.setValue(opts.defaultValue as never)\n } else if (formDefault !== undefined) {\n this.setValue(formDefault as never)\n }\n }\n\n // Default Meta\n if (this._getMeta() === undefined) {\n this.setMeta(this.state.meta)\n }\n\n this.options = opts as never\n }\n\n getValue = (): TData => {\n return this.form.getFieldValue(this.name) as any\n }\n\n setValue = (\n updater: Updater<TData>,\n options?: { touch?: boolean; notify?: boolean },\n ) => {\n this.form.setFieldValue(this.name, updater as never, options)\n this.validate('change', this.state.value)\n }\n\n _getMeta = () => this.form.getFieldMeta(this.name)\n getMeta = () =>\n this._getMeta() ??\n ({\n isValidating: false,\n isTouched: false,\n touchedErrors: [],\n errors: [],\n errorMap: {},\n ...this.options.defaultMeta,\n } as FieldMeta)\n\n setMeta = (updater: Updater<FieldMeta>) =>\n this.form.setFieldMeta(this.name, updater)\n\n getInfo = () => this.form.getFieldInfo(this.name)\n\n pushValue = (value: TData extends any[] ? TData[number] : never) =>\n this.form.pushFieldValue(this.name, value as any)\n\n insertValue = (\n index: number,\n value: TData extends any[] ? TData[number] : never,\n ) => this.form.insertFieldValue(this.name, index, value as any)\n\n removeValue = (index: number) => this.form.removeFieldValue(this.name, index)\n\n swapValues = (aIndex: number, bIndex: number) =>\n this.form.swapFieldValues(this.name, aIndex, bIndex)\n\n getSubField = <\n TSubName extends DeepKeys<TData>,\n TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,\n >(\n name: TSubName,\n ): FieldApi<TData, TSubName, ValidatorType, TSubData> =>\n new FieldApi({\n name: `${this.name}.${name}` as never,\n form: this.form,\n }) as any\n\n validateSync = (value = this.state.value, cause: ValidationCause) => {\n const { onChange, onBlur } = this.options\n const validate =\n cause === 'submit' ? undefined : cause === 'change' ? onChange : onBlur\n\n if (!validate) return\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationCount = (this.getInfo().validationCount || 0) + 1\n this.getInfo().validationCount = validationCount\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (this.form.options.validator as Validator<TData>)().validate(\n value,\n validate,\n )\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n const error = normalizeError(doValidate())\n const errorMapKey = getErrorMapKey(cause)\n if (this.state.meta.errorMap[errorMapKey] !== error) {\n this.setMeta((prev) => ({\n ...prev,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n }\n\n // If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation\n if (this.state.meta.errorMap[errorMapKey]) {\n this.cancelValidateAsync()\n }\n }\n\n __leaseValidateAsync = () => {\n const count = (this.getInfo().validationAsyncCount || 0) + 1\n this.getInfo().validationAsyncCount = count\n return count\n }\n\n cancelValidateAsync = () => {\n // Lease a new validation count to ignore any pending validations\n this.__leaseValidateAsync()\n // Cancel any pending validation state\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n }))\n }\n\n validateAsync = async (value = this.state.value, cause: ValidationCause) => {\n const {\n onChangeAsync,\n onBlurAsync,\n onSubmitAsync,\n asyncDebounceMs,\n onBlurAsyncDebounceMs,\n onChangeAsyncDebounceMs,\n } = this.options\n\n const validate =\n cause === 'change'\n ? onChangeAsync\n : cause === 'submit'\n ? onSubmitAsync\n : onBlurAsync\n if (!validate) return []\n const debounceMs =\n cause === 'submit'\n ? 0\n : (cause === 'change'\n ? onChangeAsyncDebounceMs\n : onBlurAsyncDebounceMs) ??\n asyncDebounceMs ??\n 0\n\n if (this.state.meta.isValidating !== true) {\n this.setMeta((prev) => ({ ...prev, isValidating: true }))\n }\n\n // Use the validationCount for all field instances to\n // track freshness of the validation\n const validationAsyncCount = this.__leaseValidateAsync()\n\n const checkLatest = () =>\n validationAsyncCount === this.getInfo().validationAsyncCount\n\n if (!this.getInfo().validationPromise) {\n this.getInfo().validationPromise = new Promise((resolve, reject) => {\n this.getInfo().validationResolve = resolve\n this.getInfo().validationReject = reject\n })\n }\n\n if (debounceMs > 0) {\n await new Promise((r) => setTimeout(r, debounceMs))\n }\n\n const doValidate = () => {\n if (this.options.validator && typeof validate !== 'function') {\n return (this.options.validator as Validator<TData>)().validateAsync(\n value,\n validate,\n )\n }\n\n if (this.form.options.validator && typeof validate !== 'function') {\n return (\n this.form.options.validator as Validator<TData>\n )().validateAsync(value, validate)\n }\n\n return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(\n value,\n this as never,\n )\n }\n\n // Only kick off validation if this validation is the latest attempt\n if (checkLatest()) {\n const prevErrors = this.getMeta().errors\n try {\n const rawError = await doValidate()\n if (checkLatest()) {\n const error = normalizeError(rawError)\n this.setMeta((prev) => ({\n ...prev,\n isValidating: false,\n errorMap: {\n ...prev.errorMap,\n [getErrorMapKey(cause)]: error,\n },\n }))\n this.getInfo().validationResolve?.([...prevErrors, error])\n }\n } catch (error) {\n if (checkLatest()) {\n this.getInfo().validationReject?.([...prevErrors, error])\n throw error\n }\n } finally {\n if (checkLatest()) {\n this.setMeta((prev) => ({ ...prev, isValidating: false }))\n delete this.getInfo().validationPromise\n }\n }\n }\n\n // Always return the latest validation promise to the caller\n return this.getInfo().validationPromise ?? []\n }\n\n validate = (\n cause: ValidationCause,\n value?: TData,\n ): ValidationError[] | Promise<ValidationError[]> => {\n // If the field is pristine and validatePristine is false, do not validate\n if (!this.state.meta.isTouched) return []\n\n // Store the previous error for the errorMapKey (eg. onChange, onBlur, onSubmit)\n const errorMapKey = getErrorMapKey(cause)\n const prevError = this.getMeta().errorMap[errorMapKey]\n\n // Attempt to sync validate first\n this.validateSync(value, cause)\n\n // If there is a new error mapped to the errorMapKey (eg. onChange, onBlur, onSubmit), return the errors array, do not attempt async validation\n const newError = this.getMeta().errorMap[errorMapKey]\n\n if (prevError !== newError) {\n if (!this.options.asyncAlways) {\n return this.state.meta.errors\n }\n }\n // No error? Attempt async validation\n return this.validateAsync(value, cause)\n }\n\n handleChange = (updater: Updater<TData>) => {\n this.setValue(updater, { touch: true })\n }\n\n handleBlur = () => {\n const prevTouched = this.state.meta.isTouched\n if (!prevTouched) {\n this.setMeta((prev) => ({ ...prev, isTouched: true }))\n this.validate('change')\n }\n this.validate('blur')\n }\n}\n\nfunction normalizeError(rawError?: ValidationError) {\n if (rawError) {\n if (typeof rawError !== 'string') {\n return 'Invalid Form Values'\n }\n\n return rawError\n }\n\n return undefined\n}\n\nfunction getErrorMapKey(cause: ValidationCause) {\n switch (cause) {\n case 'submit':\n return 'onSubmit'\n case 'change':\n return 'onChange'\n case 'blur':\n return 'onBlur'\n case 'mount':\n return 'onMount'\n }\n}\n"],"mappings":"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","names":[]}
@@ -167,6 +167,10 @@ var FormApi = class {
167
167
  });
168
168
  });
169
169
  };
170
+ this.deleteField = (field) => {
171
+ delete this.state.values[field];
172
+ delete this.state.fieldMeta[field];
173
+ };
170
174
  this.pushFieldValue = (field, value, opts) => {
171
175
  return this.setFieldValue(
172
176
  field,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsB;AAEtB,mBAAgE;AAiFhE,SAAS,oBACP,cACkB;AAClB,SAAO;AAAA,IACL,QAAQ,aAAa,UAAW,CAAC;AAAA,IACjC,WAAW,aAAa,aAAc,CAAC;AAAA,IACvC,WAAW,aAAa,aAAa;AAAA,IACrC,eAAe,aAAa,iBAAiB;AAAA,IAC7C,oBAAoB,aAAa,sBAAsB;AAAA,IACvD,aAAa,aAAa,eAAe;AAAA,IACzC,kBAAkB,aAAa,oBAAoB;AAAA,IACnD,aAAa,aAAa,eAAe;AAAA,IACzC,cAAc,aAAa,gBAAgB;AAAA,IAC3C,WAAW,aAAa,aAAa;AAAA,IACrC,SAAS,aAAa,WAAW;AAAA,IACjC,cAAc,aAAa,gBAAgB;AAAA,IAC3C,oBAAoB,aAAa,sBAAsB;AAAA,IACvD,qBAAqB,aAAa,uBAAuB;AAAA,EAC3D;AACF;AAEO,IAAM,UAAN,MAAwC;AAAA,EAY7C,YAAY,MAA8C;AAV1D;AAAA,mBAAiD,CAAC;AAKlD,qBACE,CAAC;AAEH,0BAAiC,CAAC;AA0DlC,kBAAS,CAAC,YAAoD;AAC5D,UAAI,CAAC;AAAS;AAEd,WAAK,MAAM,MAAM,MAAM;AACrB,cAAM,qBACJ,QAAQ,iBACR,QAAQ,kBAAkB,KAAK,QAAQ,iBACvC,CAAC,KAAK,MAAM;AAEd,cAAM,oBACJ,QAAQ,iBAAiB,KAAK,QAAQ,gBACtC,CAAC,KAAK,MAAM;AAEd,aAAK,MAAM;AAAA,UAAS,MAClB;AAAA,YACE,OAAO;AAAA,cACL,CAAC;AAAA,cACD,KAAK;AAAA;AAAA,cAEL,oBAAoB,QAAQ,eAAe,CAAC;AAAA;AAAA,cAE5C,qBACI;AAAA,gBACE,QAAQ,QAAQ;AAAA,cAClB,IACA,CAAC;AAAA,YACP;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAED,WAAK,UAAU;AAAA,IACjB;AAEA,iBAAQ,MACN,KAAK,MAAM;AAAA,MAAS,MAClB,oBAAoB;AAAA,QAClB,GAAI,KAAK,QAAQ;AAAA,QACjB,QAAQ,KAAK,QAAQ,iBAAiB,KAAK,QAAQ,cAAc;AAAA,MACnE,CAAC;AAAA,IACH;AAEF,6BAAoB,OAAO,UAA2B;AACpD,YAAM,0BAAwD,CAAC;AAC/D,WAAK,MAAM,MAAM,MAAM;AACrB,aACE,OAAO,OAAO,KAAK,SAAS,EAC5B,QAAQ,CAAC,UAAU;AACnB,iBAAO,OAAO,MAAM,SAAS,EAAE,QAAQ,CAAC,aAAa;AAEnD,gBAAI,CAAC,SAAS,MAAM,KAAK,WAAW;AAElC,uBAAS,QAAQ,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW,KAAK,EAAE;AAEzD,sCAAwB;AAAA,gBACtB,QAAQ,QAAQ,EAAE,KAAK,MAAM,SAAS,SAAS,KAAK,CAAC;AAAA,cACvD;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAAA,MACH,CAAC;AAED,aAAO,QAAQ,IAAI,uBAAuB;AAAA,IAC5C;AAEA,wBAAe,YAAY;AAMzB,WAAK,MAAM,SAAS,CAAC,SAAS;AAAA,QAC5B,GAAG;AAAA;AAAA,QAEH,aAAa;AAAA;AAAA,QAEb,oBAAoB,IAAI,qBAAqB;AAAA,MAC/C,EAAE;AAGF,UAAI,CAAC,KAAK,MAAM;AAAW;AAE3B,WAAK,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,cAAc,KAAK,EAAE;AAEzD,YAAM,OAAO,MAAM;AACjB,aAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,cAAc,MAAM,EAAE;AAAA,MAClE;AAGA,YAAM,KAAK,kBAAkB,QAAQ;AAGrC,UAAI,CAAC,KAAK,MAAM,eAAe;AAC7B,aAAK;AACL,aAAK,QAAQ,kBAAkB,KAAK,MAAM,QAAQ,IAAI;AACtD;AAAA,MACF;AAKA,UAAI,CAAC,KAAK,MAAM,SAAS;AACvB,aAAK;AACL,aAAK,QAAQ,kBAAkB,KAAK,MAAM,QAAQ,IAAI;AACtD;AAAA,MACF;AAEA,UAAI;AAEF,cAAM,KAAK,QAAQ,WAAW,KAAK,MAAM,QAAQ,IAAI;AAErD,aAAK,MAAM,MAAM,MAAM;AACrB,eAAK,MAAM,SAAS,CAAC,UAAU,EAAE,GAAG,MAAM,aAAa,KAAK,EAAE;AAC9D,eAAK;AAAA,QACP,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,aAAK;AACL,cAAM;AAAA,MACR;AAAA,IACF;AAEA,yBAAgB,CACd,cACiC,oBAAM,KAAK,MAAM,QAAQ,KAAK;AAEjE,wBAAe,CACb,UAC0B;AAC1B,aAAO,KAAK,MAAM,UAAU,KAAK;AAAA,IACnC;AAEA,wBAAe,CACb,UACwC;AAExC,aAAQ,KAAK,UAAU,KAAK,MAAM;AAAA,QAChC,WAAW,CAAC;AAAA,MACd;AAAA,IACF;AAEA,wBAAe,CACb,OACA,YACG;AACH,WAAK,MAAM,SAAS,CAAC,SAAS;AAC5B,eAAO;AAAA,UACL,GAAG;AAAA,UACH,WAAW;AAAA,YACT,GAAG,KAAK;AAAA,YACR,CAAC,KAAK,OAAG,+BAAiB,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA,UAC1D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,yBAAgB,CACd,OACA,SACA,SACG;AACH,YAAM,QAAQ,MAAM;AAEpB,WAAK,MAAM,MAAM,MAAM;AACrB,YAAI,OAAO;AACT,eAAK,aAAa,OAAO,CAAC,UAAU;AAAA,YAClC,GAAG;AAAA,YACH,WAAW;AAAA,UACb,EAAE;AAAA,QACJ;AAEA,aAAK,MAAM,SAAS,CAAC,SAAS;AAC5B,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,YAAQ,oBAAM,KAAK,QAAQ,OAAO,OAAO;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,0BAAiB,CACf,OACA,OAGA,SACG;AACH,aAAO,KAAK;AAAA,QACV;AAAA,QACA,CAAC,SAAS,CAAC,GAAI,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAI,KAAK;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAEA,4BAAmB,CACjB,OACA,OACA,OAGA,SACG;AACH,WAAK;AAAA,QACH;AAAA,QACA,CAAC,SAAS;AACR,iBAAQ,KAAwC;AAAA,YAAI,CAAC,GAAG,MACtD,MAAM,QAAQ,QAAQ;AAAA,UACxB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,4BAAmB,CACjB,OACA,OACA,SACG;AACH,WAAK;AAAA,QACH;AAAA,QACA,CAAC,SAAS;AACR,iBAAQ,KAAwC;AAAA,YAC9C,CAAC,IAAI,MAAM,MAAM;AAAA,UACnB;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,2BAAkB,CAChB,OACA,QACA,WACG;AACH,WAAK,cAAc,OAAO,CAAC,SAAc;AACvC,cAAM,QAAQ,KAAK,MAAM;AACzB,cAAM,QAAQ,KAAK,MAAM;AACzB,mBAAO,wBAAM,oBAAM,MAAM,GAAG,MAAM,IAAI,KAAK,GAAG,GAAG,MAAM,IAAI,KAAK;AAAA,MAClE,CAAC;AAAA,IACH;AArSE,SAAK,QAAQ,IAAI;AAAA,MACf,oBAAoB;AAAA,QAClB,GAAI,MAAM;AAAA,QACV,QAAQ,MAAM,iBAAiB,MAAM,cAAc;AAAA,QACnD,aAAa;AAAA,MACf,CAAC;AAAA,MACD;AAAA,QACE,UAAU,MAAM;AACd,cAAI,EAAE,MAAM,IAAI,KAAK;AAErB,gBAAM,kBAAkB,OAAO,OAAO,MAAM,SAAS;AAKrD,gBAAM,qBAAqB,gBAAgB;AAAA,YACzC,CAAC,UAAU,OAAO;AAAA,UACpB;AAEA,gBAAM,gBAAgB,CAAC,gBAAgB;AAAA,YACrC,CAAC,UACC,OAAO,gBACP,8BAAgB,OAAO,OAAO,MAAM,QAAQ,EAAE,OAAO,OAAO,CAAC;AAAA,UACjE;AAEA,gBAAM,YAAY,gBAAgB,KAAK,CAAC,UAAU,OAAO,SAAS;AAElE,gBAAM,eAAe,sBAAsB,MAAM;AACjD,gBAAM,cAAc,CAAC,MAAM;AAC3B,gBAAM,UAAU,iBAAiB;AACjC,gBAAM,YACH,MAAM,uBAAuB,KAAK,CAAC,aACnC,CAAC,gBAAgB,CAAC,MAAM,gBAAgB;AAE3C,kBAAQ;AAAA,YACN,GAAG;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAEA,eAAK,MAAM,QAAQ;AACnB,eAAK,QAAQ;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAEA,SAAK,QAAQ,KAAK,MAAM;AAExB,SAAK,OAAO,QAAQ,CAAC,CAAC;AAAA,EACxB;AAiPF;","names":[]}
1
+ {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {\n delete this.state.values[field as keyof TFormData]\n delete this.state.fieldMeta[field]\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -143,6 +143,10 @@ var FormApi = class {
143
143
  });
144
144
  });
145
145
  };
146
+ this.deleteField = (field) => {
147
+ delete this.state.values[field];
148
+ delete this.state.fieldMeta[field];
149
+ };
146
150
  this.pushFieldValue = (field, value, opts) => {
147
151
  return this.setFieldValue(
148
152
  field,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/FormApi.ts"],"sourcesContent":["import { Store } from '@tanstack/store'\nimport type { DeepKeys, DeepValue, Updater } from './utils'\nimport { functionalUpdate, getBy, isNonEmptyArray, setBy } from './utils'\nimport type { FieldApi, FieldMeta, ValidationCause } from './FieldApi'\nimport type { ValidationError, Validator } from './types'\n\ntype ValidateFn<TData, ValidatorType> = (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n) => ValidationError\n\ntype ValidateOrFn<TData, ValidatorType> = ValidatorType extends Validator<TData>\n ? Parameters<ReturnType<ValidatorType>['validate']>[1]\n : ValidateFn<TData, ValidatorType>\n\ntype ValidateAsyncFn<TData, ValidatorType> = (\n value: TData,\n fieldApi: FormApi<TData, ValidatorType>,\n) => ValidationError | Promise<ValidationError>\n\nexport type FormOptions<TData, ValidatorType> = {\n defaultValues?: TData\n defaultState?: Partial<FormState<TData>>\n asyncDebounceMs?: number\n validator?: ValidatorType\n onMount?: ValidateOrFn<TData, ValidatorType>\n onMountAsync?: ValidateAsyncFn<TData, ValidatorType>\n onMountAsyncDebounceMs?: number\n onChange?: ValidateOrFn<TData, ValidatorType>\n onChangeAsync?: ValidateAsyncFn<TData, ValidatorType>\n onChangeAsyncDebounceMs?: number\n onBlur?: ValidateOrFn<TData, ValidatorType>\n onBlurAsync?: ValidateAsyncFn<TData, ValidatorType>\n onBlurAsyncDebounceMs?: number\n onSubmit?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => any | Promise<any>\n onSubmitInvalid?: (\n values: TData,\n formApi: FormApi<TData, ValidatorType>,\n ) => void\n}\n\nexport type FieldInfo<TFormData, ValidatorType> = {\n instances: Record<string, FieldApi<TFormData, any, unknown, ValidatorType>>\n} & ValidationMeta\n\nexport type ValidationMeta = {\n validationCount?: number\n validationAsyncCount?: number\n validationPromise?: Promise<ValidationError[]>\n validationResolve?: (errors: ValidationError[]) => void\n validationReject?: (errors: unknown) => void\n}\n\nexport type ValidationErrorMapKeys = `on${Capitalize<ValidationCause>}`\n\nexport type ValidationErrorMap = {\n [K in ValidationErrorMapKeys]?: ValidationError\n}\n\nexport type FormState<TData> = {\n values: TData\n // Form Validation\n isFormValidating: boolean\n formValidationCount: number\n isFormValid: boolean\n formError?: ValidationError\n // Fields\n fieldMeta: Record<DeepKeys<TData>, FieldMeta>\n isFieldsValidating: boolean\n isFieldsValid: boolean\n isSubmitting: boolean\n // General\n isTouched: boolean\n isSubmitted: boolean\n isValidating: boolean\n isValid: boolean\n canSubmit: boolean\n submissionAttempts: number\n}\n\nfunction getDefaultFormState<TData>(\n defaultState: Partial<FormState<TData>>,\n): FormState<TData> {\n return {\n values: defaultState.values ?? ({} as never),\n fieldMeta: defaultState.fieldMeta ?? ({} as never),\n canSubmit: defaultState.canSubmit ?? true,\n isFieldsValid: defaultState.isFieldsValid ?? false,\n isFieldsValidating: defaultState.isFieldsValidating ?? false,\n isFormValid: defaultState.isFormValid ?? false,\n isFormValidating: defaultState.isFormValidating ?? false,\n isSubmitted: defaultState.isSubmitted ?? false,\n isSubmitting: defaultState.isSubmitting ?? false,\n isTouched: defaultState.isTouched ?? false,\n isValid: defaultState.isValid ?? false,\n isValidating: defaultState.isValidating ?? false,\n submissionAttempts: defaultState.submissionAttempts ?? 0,\n formValidationCount: defaultState.formValidationCount ?? 0,\n }\n}\n\nexport class FormApi<TFormData, ValidatorType> {\n // // This carries the context for nested fields\n options: FormOptions<TFormData, ValidatorType> = {}\n store!: Store<FormState<TFormData>>\n // Do not use __state directly, as it is not reactive.\n // Please use form.useStore() utility to subscribe to state\n state!: FormState<TFormData>\n fieldInfo: Record<DeepKeys<TFormData>, FieldInfo<TFormData, ValidatorType>> =\n {} as any\n fieldName?: string\n validationMeta: ValidationMeta = {}\n\n constructor(opts?: FormOptions<TFormData, ValidatorType>) {\n this.store = new Store<FormState<TFormData>>(\n getDefaultFormState({\n ...(opts?.defaultState as any),\n values: opts?.defaultValues ?? opts?.defaultState?.values,\n isFormValid: true,\n }),\n {\n onUpdate: () => {\n let { state } = this.store\n // Computed state\n const fieldMetaValues = Object.values(state.fieldMeta) as (\n | FieldMeta\n | undefined\n )[]\n\n const isFieldsValidating = fieldMetaValues.some(\n (field) => field?.isValidating,\n )\n\n const isFieldsValid = !fieldMetaValues.some(\n (field) =>\n field?.errorMap &&\n isNonEmptyArray(Object.values(field.errorMap).filter(Boolean)),\n )\n\n const isTouched = fieldMetaValues.some((field) => field?.isTouched)\n\n const isValidating = isFieldsValidating || state.isFormValidating\n const isFormValid = !state.formError\n const isValid = isFieldsValid && isFormValid\n const canSubmit =\n (state.submissionAttempts === 0 && !isTouched) ||\n (!isValidating && !state.isSubmitting && isValid)\n\n state = {\n ...state,\n isFieldsValidating,\n isFieldsValid,\n isFormValid,\n isValid,\n canSubmit,\n isTouched,\n }\n\n this.store.state = state\n this.state = state\n },\n },\n )\n\n this.state = this.store.state\n\n this.update(opts || {})\n }\n\n update = (options?: FormOptions<TFormData, ValidatorType>) => {\n if (!options) return\n\n this.store.batch(() => {\n const shouldUpdateValues =\n options.defaultValues &&\n options.defaultValues !== this.options.defaultValues &&\n !this.state.isTouched\n\n const shouldUpdateState =\n options.defaultState !== this.options.defaultState &&\n !this.state.isTouched\n\n this.store.setState(() =>\n getDefaultFormState(\n Object.assign(\n {},\n this.state as any,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateState ? options.defaultState : {},\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n shouldUpdateValues\n ? {\n values: options.defaultValues,\n }\n : {},\n ),\n ),\n )\n })\n\n this.options = options\n }\n\n reset = () =>\n this.store.setState(() =>\n getDefaultFormState({\n ...(this.options.defaultState as any),\n values: this.options.defaultValues ?? this.options.defaultState?.values,\n }),\n )\n\n validateAllFields = async (cause: ValidationCause) => {\n const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any\n this.store.batch(() => {\n void (\n Object.values(this.fieldInfo) as FieldInfo<any, ValidatorType>[]\n ).forEach((field) => {\n Object.values(field.instances).forEach((instance) => {\n // If any fields are not touched\n if (!instance.state.meta.isTouched) {\n // Mark them as touched\n instance.setMeta((prev) => ({ ...prev, isTouched: true }))\n // Validate the field\n fieldValidationPromises.push(\n Promise.resolve().then(() => instance.validate(cause)),\n )\n }\n })\n })\n })\n\n return Promise.all(fieldValidationPromises)\n }\n\n handleSubmit = async () => {\n // Check to see that the form and all fields have been touched\n // If they have not, touch them all and run validation\n // Run form validation\n // Submit the form\n\n this.store.setState((old) => ({\n ...old,\n // Submission attempts mark the form as not submitted\n isSubmitted: false,\n // Count submission attempts\n submissionAttempts: old.submissionAttempts + 1,\n }))\n\n // Don't let invalid forms submit\n if (!this.state.canSubmit) return\n\n this.store.setState((d) => ({ ...d, isSubmitting: true }))\n\n const done = () => {\n this.store.setState((prev) => ({ ...prev, isSubmitting: false }))\n }\n\n // Validate all fields\n await this.validateAllFields('submit')\n\n // Fields are invalid, do not submit\n if (!this.state.isFieldsValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n // Run validation for the form\n // await this.validateForm()\n\n if (!this.state.isValid) {\n done()\n this.options.onSubmitInvalid?.(this.state.values, this)\n return\n }\n\n try {\n // Run the submit code\n await this.options.onSubmit?.(this.state.values, this)\n\n this.store.batch(() => {\n this.store.setState((prev) => ({ ...prev, isSubmitted: true }))\n done()\n })\n } catch (err) {\n done()\n throw err\n }\n }\n\n getFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): DeepValue<TFormData, TField> => getBy(this.state.values, field)\n\n getFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldMeta | undefined => {\n return this.state.fieldMeta[field]\n }\n\n getFieldInfo = <TField extends DeepKeys<TFormData>>(\n field: TField,\n ): FieldInfo<TFormData, ValidatorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return (this.fieldInfo[field] ||= {\n instances: {},\n })\n }\n\n setFieldMeta = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<FieldMeta>,\n ) => {\n this.store.setState((prev) => {\n return {\n ...prev,\n fieldMeta: {\n ...prev.fieldMeta,\n [field]: functionalUpdate(updater, prev.fieldMeta[field]),\n },\n }\n })\n }\n\n setFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n updater: Updater<DeepValue<TFormData, TField>>,\n opts?: { touch?: boolean },\n ) => {\n const touch = opts?.touch\n\n this.store.batch(() => {\n if (touch) {\n this.setFieldMeta(field, (prev) => ({\n ...prev,\n isTouched: true,\n }))\n }\n\n this.store.setState((prev) => {\n return {\n ...prev,\n values: setBy(prev.values, field, updater),\n }\n })\n })\n }\n\n deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {\n delete this.state.values[field as keyof TFormData]\n delete this.state.fieldMeta[field]\n }\n\n pushFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n return this.setFieldValue(\n field,\n (prev) => [...(Array.isArray(prev) ? prev : []), value] as any,\n opts,\n )\n }\n\n insertFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n value: DeepValue<TFormData, TField> extends any[]\n ? DeepValue<TFormData, TField>[number]\n : never,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).map((d, i) =>\n i === index ? value : d,\n ) as any\n },\n opts,\n )\n }\n\n removeFieldValue = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index: number,\n opts?: { touch?: boolean },\n ) => {\n this.setFieldValue(\n field,\n (prev) => {\n return (prev as DeepValue<TFormData, TField>[]).filter(\n (_d, i) => i !== index,\n ) as any\n },\n opts,\n )\n }\n\n swapFieldValues = <TField extends DeepKeys<TFormData>>(\n field: TField,\n index1: number,\n index2: number,\n ) => {\n this.setFieldValue(field, (prev: any) => {\n const prev1 = prev[index1]!\n const prev2 = prev[index2]!\n return setBy(setBy(prev, `${index1}`, prev2), `${index2}`, prev1)\n })\n }\n}\n"],"mappings":"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","names":[]}
@@ -73,6 +73,7 @@ declare class FormApi<TFormData, ValidatorType> {
73
73
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
74
74
  touch?: boolean;
75
75
  }) => void;
76
+ deleteField: <TField extends DeepKeys<TFormData>>(field: TField) => void;
76
77
  pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
77
78
  touch?: boolean;
78
79
  }) => void;
@@ -96,6 +97,7 @@ interface FieldOptions<TParentData, TName extends DeepKeys<TParentData>, Validat
96
97
  defaultValue?: TData;
97
98
  asyncDebounceMs?: number;
98
99
  asyncAlways?: boolean;
100
+ preserveValue?: boolean;
99
101
  validator?: ValidatorType;
100
102
  onMount?: (formApi: FieldApi<TParentData, TName, ValidatorType, TData>) => void;
101
103
  onChange?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>;
@@ -73,6 +73,7 @@ declare class FormApi<TFormData, ValidatorType> {
73
73
  setFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, updater: Updater<DeepValue<TFormData, TField>>, opts?: {
74
74
  touch?: boolean;
75
75
  }) => void;
76
+ deleteField: <TField extends DeepKeys<TFormData>>(field: TField) => void;
76
77
  pushFieldValue: <TField extends DeepKeys<TFormData>>(field: TField, value: DeepValue<TFormData, TField> extends any[] ? DeepValue<TFormData, TField>[number] : never, opts?: {
77
78
  touch?: boolean;
78
79
  }) => void;
@@ -96,6 +97,7 @@ interface FieldOptions<TParentData, TName extends DeepKeys<TParentData>, Validat
96
97
  defaultValue?: TData;
97
98
  asyncDebounceMs?: number;
98
99
  asyncAlways?: boolean;
100
+ preserveValue?: boolean;
99
101
  validator?: ValidatorType;
100
102
  onMount?: (formApi: FieldApi<TParentData, TName, ValidatorType, TData>) => void;
101
103
  onChange?: ValidateOrFn<TParentData, TName, ValidatorType, FormValidator, TData>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/form-core",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Powerful, type-safe, framework agnostic forms.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/FieldApi.ts CHANGED
@@ -69,6 +69,7 @@ export interface FieldOptions<
69
69
  defaultValue?: TData
70
70
  asyncDebounceMs?: number
71
71
  asyncAlways?: boolean
72
+ preserveValue?: boolean
72
73
  validator?: ValidatorType
73
74
  onMount?: (
74
75
  formApi: FieldApi<TParentData, TName, ValidatorType, TData>,
@@ -239,10 +240,14 @@ export class FieldApi<
239
240
  this.options.onMount?.(this as never)
240
241
 
241
242
  return () => {
243
+ const preserveValue = this.options.preserveValue
242
244
  unsubscribe()
243
- delete info.instances[this.uid]
244
- if (!Object.keys(info.instances).length) {
245
- delete this.form.fieldInfo[this.name as never]
245
+ if (!preserveValue) {
246
+ delete info.instances[this.uid]
247
+ }
248
+
249
+ if (!Object.keys(info.instances).length && !preserveValue) {
250
+ delete this.form.fieldInfo[this.name]
246
251
  }
247
252
  }
248
253
  }
package/src/FormApi.ts CHANGED
@@ -349,6 +349,11 @@ export class FormApi<TFormData, ValidatorType> {
349
349
  })
350
350
  }
351
351
 
352
+ deleteField = <TField extends DeepKeys<TFormData>>(field: TField) => {
353
+ delete this.state.values[field as keyof TFormData]
354
+ delete this.state.fieldMeta[field]
355
+ }
356
+
352
357
  pushFieldValue = <TField extends DeepKeys<TFormData>>(
353
358
  field: TField,
354
359
  value: DeepValue<TFormData, TField> extends any[]
@@ -563,4 +563,43 @@ describe('field api', () => {
563
563
 
564
564
  expect(field.state.value).toBe('test')
565
565
  })
566
+
567
+ // test the unmounting of the fieldAPI
568
+ it('should preserve value on unmount', () => {
569
+ const form = new FormApi({
570
+ defaultValues: {
571
+ name: 'test',
572
+ },
573
+ })
574
+
575
+ const field = new FieldApi({
576
+ form,
577
+ name: 'name',
578
+ preserveValue: true,
579
+ })
580
+
581
+ const unmount = field.mount()
582
+ unmount()
583
+ expect(form.getFieldInfo(field.name).instances[field.uid]).toBeDefined()
584
+ expect(form.getFieldInfo(field.name)).toBeDefined()
585
+ })
586
+
587
+ it('should not preserve field value on ummount', () => {
588
+ const form = new FormApi({
589
+ defaultValues: {
590
+ name: 'test',
591
+ },
592
+ })
593
+
594
+ const field = new FieldApi({
595
+ form,
596
+ name: 'name',
597
+ })
598
+
599
+ const unmount = field.mount()
600
+ unmount()
601
+ const info = form.getFieldInfo(field.name)
602
+ expect(info.instances[field.uid]).toBeUndefined()
603
+ expect(Object.keys(info.instances).length).toBe(0)
604
+ })
566
605
  })
@@ -269,6 +269,21 @@ describe('form api', () => {
269
269
  expect(form.getFieldValue('name')).toEqual('two')
270
270
  })
271
271
 
272
+ it('should delete field from the form', () => {
273
+ const form = new FormApi({
274
+ defaultValues: {
275
+ names: 'kittu',
276
+ age: 4,
277
+ },
278
+ })
279
+
280
+ form.deleteField('names')
281
+
282
+ expect(form.getFieldValue('age')).toStrictEqual(4)
283
+ expect(form.getFieldValue('names')).toStrictEqual(undefined)
284
+ expect(form.getFieldMeta('names')).toStrictEqual(undefined)
285
+ })
286
+
272
287
  it("form's valid state should be work fine", () => {
273
288
  const form = new FormApi({
274
289
  defaultValues: {