import { ReadonlyStore, Store } from '@tanstack/store'; import { ValidationLogicFn } from './ValidationLogic.js'; import { StandardSchemaV1, StandardSchemaV1Issue, TStandardSchemaValidatorValue } from './standardSchemaValidator.js'; import { AnyFieldApi, AnyFieldMeta, AnyFieldMetaBase, FieldApi } from './FieldApi.js'; import { ExtractGlobalFormError, FieldManipulator, FormValidationError, FormValidationErrorMap, ListenerCause, UpdateMetaOptions, ValidationCause, ValidationError, ValidationErrorMap, ValidationErrorMapKeys } from './types.js'; import { DeepKeys, DeepKeysOfType, DeepValue, RejectPromiseValidator } from './util-types.js'; import { Updater } from './utils.js'; /** * @private */ type FormErrorMapFromValidator, TOnChange extends undefined | FormValidateOrFn, TOnChangeAsync extends undefined | FormAsyncValidateOrFn, TOnBlur extends undefined | FormValidateOrFn, TOnBlurAsync extends undefined | FormAsyncValidateOrFn, TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnDynamic extends undefined | FormValidateOrFn, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn> = Partial, ValidationErrorMap>>; export type FormValidateFn = (props: { value: TFormData; formApi: FormApi; }) => unknown; /** * @private */ export type FormValidateOrFn = FormValidateFn | StandardSchemaV1; export type UnwrapFormValidateOrFn> = [TValidateOrFn] extends [FormValidateFn] ? ExtractGlobalFormError> : [TValidateOrFn] extends [StandardSchemaV1] ? Record : undefined; /** * @private */ export type FormValidateAsyncFn = (props: { value: TFormData; formApi: FormApi; signal: AbortSignal; }) => unknown | Promise; export type FormValidator = { validate(options: { value: TType; }, fn: TFn): ValidationError; validateAsync(options: { value: TType; }, fn: TFn): Promise>; }; /** * @private */ export type FormAsyncValidateOrFn = FormValidateAsyncFn | StandardSchemaV1; export type UnwrapFormAsyncValidateOrFn> = [TValidateOrFn] extends [FormValidateAsyncFn] ? ExtractGlobalFormError>> : [TValidateOrFn] extends [StandardSchemaV1] ? Record : undefined; export interface FormValidators, TOnChange extends undefined | FormValidateOrFn, TOnChangeAsync extends undefined | FormAsyncValidateOrFn, TOnBlur extends undefined | FormValidateOrFn, TOnBlurAsync extends undefined | FormAsyncValidateOrFn, TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnDynamic extends undefined | FormValidateOrFn, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn> { /** * Optional function that fires as soon as the component mounts. */ onMount?: RejectPromiseValidator; /** * Optional function that checks the validity of your data whenever a value changes */ onChange?: RejectPromiseValidator; /** * Optional onChange asynchronous counterpart to onChange. Useful for more complex validation logic that might involve server requests. */ onChangeAsync?: TOnChangeAsync; /** * The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds. */ onChangeAsyncDebounceMs?: number; /** * Optional function that validates the form data when a field loses focus, returns a `FormValidationError` */ onBlur?: RejectPromiseValidator; /** * Optional onBlur asynchronous validation method for when a field loses focus returns a ` FormValidationError` or a promise of `Promise` */ onBlurAsync?: TOnBlurAsync; /** * The default time in milliseconds that if set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds. */ onBlurAsyncDebounceMs?: number; onSubmit?: RejectPromiseValidator; onSubmitAsync?: TOnSubmitAsync; onDynamic?: RejectPromiseValidator; onDynamicAsync?: TOnDynamicAsync; onDynamicAsyncDebounceMs?: number; } export interface FormListeners, TOnChange extends undefined | FormValidateOrFn, TOnChangeAsync extends undefined | FormAsyncValidateOrFn, TOnBlur extends undefined | FormValidateOrFn, TOnBlurAsync extends undefined | FormAsyncValidateOrFn, TOnSubmit extends undefined | FormValidateOrFn, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, TOnDynamic extends undefined | FormValidateOrFn, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, TSubmitMeta = never> { onChange?: (props: { formApi: FormApi; fieldApi: AnyFieldApi; }) => void; onChangeDebounceMs?: number; onBlur?: (props: { formApi: FormApi; fieldApi: AnyFieldApi; }) => void; onBlurDebounceMs?: number; onMount?: (props: { formApi: FormApi; }) => void; onSubmit?: (props: { formApi: FormApi; meta: TSubmitMeta; }) => void; onFieldUnmount?: (props: { formApi: FormApi; fieldApi: AnyFieldApi; }) => void; } /** * An object representing the base properties of a form, unrelated to any validators */ export interface BaseFormOptions { /** * Set initial values for your form. */ defaultValues?: TFormData; /** * onSubmitMeta, the data passed from the handleSubmit handler, to the onSubmit function props */ onSubmitMeta?: TSubmitMeta; } /** * An object representing the options for a form. */ export interface FormOptions, in out TOnChange extends undefined | FormValidateOrFn, in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn, in out TOnBlur extends undefined | FormValidateOrFn, in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn, in out TOnSubmit extends undefined | FormValidateOrFn, in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, in out TOnDynamic extends undefined | FormValidateOrFn, in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, in out TOnServer extends undefined | FormAsyncValidateOrFn, in out TSubmitMeta = never> extends BaseFormOptions { /** * The form name, used for devtools and identification */ formId?: string; /** * The default state for the form. */ defaultState?: Partial>; /** * If true, always run async validation, even when sync validation has produced an error. Defaults to undefined. */ asyncAlways?: boolean; /** * Optional time in milliseconds if you want to introduce a delay before firing off an async action. */ asyncDebounceMs?: number; /** * If true, allows the form to be submitted in an invalid state i.e. canSubmit will remain true regardless of validation errors. Defaults to undefined. */ canSubmitWhenInvalid?: boolean; /** * A list of validators to pass to the form */ validators?: FormValidators; validationLogic?: ValidationLogicFn; /** * form level listeners */ listeners?: FormListeners; /** * A function to be called when the form is submitted, what should happen once the user submits a valid form returns `any` or a promise `Promise` */ onSubmit?: (props: { value: TFormData; formApi: FormApi; meta: TSubmitMeta; }) => any | Promise; /** * Specify an action for scenarios where the user tries to submit an invalid form. */ onSubmitInvalid?: (props: { value: TFormData; formApi: FormApi; meta: TSubmitMeta; }) => void; transform?: (data: unknown) => unknown; } export type AnyFormOptions = FormOptions; /** * An object representing the validation metadata for a field. Not intended for public usage. */ export type ValidationMeta = { /** * An abort controller stored in memory to cancel previous async validation attempts. */ lastAbortController: AbortController; }; /** * An object representing the field information for a specific field within the form. */ export type FieldInfo = { /** * An instance of the FieldAPI. */ instance: FieldApi | null; /** * A record of field validation internal handling. */ validationMetaMap: Record; }; /** * An object representing the current state of the form. */ export type BaseFormState, in out TOnChange extends undefined | FormValidateOrFn, in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn, in out TOnBlur extends undefined | FormValidateOrFn, in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn, in out TOnSubmit extends undefined | FormValidateOrFn, in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, in out TOnDynamic extends undefined | FormValidateOrFn, in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, in out TOnServer extends undefined | FormAsyncValidateOrFn> = { /** * The current values of the form fields. */ values: TFormData; /** * The error map for the form itself. */ errorMap: ValidationErrorMap, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormAsyncValidateOrFn>; /** * An internal mechanism used for keeping track of validation logic in a form. */ validationMetaMap: Record; /** * A record of field metadata for each field in the form, not including the derived properties, like `errors` and such */ fieldMetaBase: Partial, AnyFieldMetaBase>>; /** * A boolean indicating if the form is currently in the process of being submitted after `handleSubmit` is called. * * Goes back to `false` when submission completes for one of the following reasons: * - the validation step returned errors. * - the `onSubmit` function has completed. * * Note: if you're running async operations in your `onSubmit` function make sure to await them to ensure `isSubmitting` is set to `false` only when the async operation completes. * * This is useful for displaying loading indicators or disabling form inputs during submission. * */ isSubmitting: boolean; /** * A boolean indicating if the `onSubmit` function has completed successfully. * * Goes back to `false` at each new submission attempt. * * Note: you can use isSubmitting to check if the form is currently submitting. */ isSubmitted: boolean; /** * A boolean indicating if the form or any of its fields are currently validating. */ isValidating: boolean; /** * A counter for tracking the number of submission attempts. */ submissionAttempts: number; /** * A boolean indicating if the last submission was successful. */ isSubmitSuccessful: boolean; /** * @private, used to force a re-evaluation of the form state when options change */ _force_re_eval?: boolean; }; export type AnyBaseFormState = BaseFormState; export type DerivedFormState, in out TOnChange extends undefined | FormValidateOrFn, in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn, in out TOnBlur extends undefined | FormValidateOrFn, in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn, in out TOnSubmit extends undefined | FormValidateOrFn, in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, in out TOnDynamic extends undefined | FormValidateOrFn, in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, in out TOnServer extends undefined | FormAsyncValidateOrFn> = { /** * A boolean indicating if the form is currently validating. */ isFormValidating: boolean; /** * A boolean indicating if the form is valid. */ isFormValid: boolean; /** * The error array for the form itself. */ errors: Array | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormAsyncValidateOrFn>>; /** * A boolean indicating if any of the form fields are currently validating. */ isFieldsValidating: boolean; /** * A boolean indicating if all the form fields are valid. Evaluates `true` if there are no field errors. */ isFieldsValid: boolean; /** * A boolean indicating if any of the form fields have been touched. */ isTouched: boolean; /** * A boolean indicating if any of the form fields have been blurred. */ isBlurred: boolean; /** * A boolean indicating if any of the form's fields' values have been modified by the user. Evaluates `true` if the user have modified at least one of the fields. Opposite of `isPristine`. */ isDirty: boolean; /** * A boolean indicating if none of the form's fields' values have been modified by the user. Evaluates `true` if the user have not modified any of the fields. Opposite of `isDirty`. */ isPristine: boolean; /** * A boolean indicating if all of the form's fields are the same as default values. */ isDefaultValue: boolean; /** * A boolean indicating if the form and all its fields are valid. Evaluates `true` if there are no errors. */ isValid: boolean; /** * A boolean indicating if the form can be submitted based on its current state. */ canSubmit: boolean; /** * A record of field metadata for each field in the form. */ fieldMeta: Partial, AnyFieldMeta>>; }; export interface FormState, in out TOnChange extends undefined | FormValidateOrFn, in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn, in out TOnBlur extends undefined | FormValidateOrFn, in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn, in out TOnSubmit extends undefined | FormValidateOrFn, in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, in out TOnDynamic extends undefined | FormValidateOrFn, in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, in out TOnServer extends undefined | FormAsyncValidateOrFn> extends BaseFormState, DerivedFormState { } export type AnyFormState = FormState; /** * @public * * A type representing the Form API with all generics set to `any` for convenience. */ export type AnyFormApi = FormApi; /** * We cannot use methods and must use arrow functions. Otherwise, our React adapters * will break due to loss of the method when using spread. */ /** * A class representing the Form API. It handles the logic and interactions with the form state. * * Normally, you will not need to create a new `FormApi` instance directly. Instead, you will use a framework * hook/function like `useForm` or `createForm` to create a new instance for you that uses your framework's reactivity model. * However, if you need to create a new instance manually, you can do so by calling the `new FormApi` constructor. */ export declare class FormApi, in out TOnChange extends undefined | FormValidateOrFn, in out TOnChangeAsync extends undefined | FormAsyncValidateOrFn, in out TOnBlur extends undefined | FormValidateOrFn, in out TOnBlurAsync extends undefined | FormAsyncValidateOrFn, in out TOnSubmit extends undefined | FormValidateOrFn, in out TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, in out TOnDynamic extends undefined | FormValidateOrFn, in out TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, in out TOnServer extends undefined | FormAsyncValidateOrFn, in out TSubmitMeta = never> implements FieldManipulator { /** * The options for the form. */ options: FormOptions; baseStore: Store>; fieldMetaDerived: Store['fieldMeta']>; store: ReadonlyStore>; /** * A record of field information for each field in the form. */ fieldInfo: Partial, FieldInfo>>; get state(): FormState; /** * @private */ timeoutIds: { validations: Record | null>; listeners: Record | null>; formListeners: Record | null>; }; /** * @private */ _formId: string; /** * @private */ private _devtoolsSubmissionOverride; /** * Constructs a new `FormApi` instance with the given form options. */ constructor(opts?: FormOptions); get formId(): string; /** * @private */ runValidator & { formApi: AnyFormApi; }, TType extends 'validate' | 'validateAsync'>(props: { validate: TType extends 'validate' ? FormValidateOrFn : FormAsyncValidateOrFn; value: TValue; type: TType; }): unknown; mount: () => () => void; /** * Updates the form options and form state. */ update: (options?: FormOptions) => void; /** * Resets the form state to the default values. * If values are provided, the form will be reset to those values instead and the default values will be updated. * * @param values - Optional values to reset the form to. * @param opts - Optional options to control the reset behavior. */ reset: (values?: TFormData, opts?: { keepDefaultValues?: boolean; }) => void; /** * Validates all fields according to the FIELD level validators. * This will ignore FORM level validators, use form.validate({ValidationCause}) for a complete validation */ validateAllFields: (cause: ValidationCause) => Promise; /** * Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type. */ validateArrayFieldsStartingFrom: >(field: TField, index: number, cause: ValidationCause) => Promise; /** * Validates a specified field in the form using the correct handlers for a given validation type. */ validateField: >(field: TField, cause: ValidationCause) => any[] | Promise; /** * TODO: This code is copied from FieldApi, we should refactor to share * @private */ validateSync: (cause: ValidationCause) => { hasErrored: boolean; fieldsErrorMap: FormErrorMapFromValidator; }; /** * @private */ validateAsync: (cause: ValidationCause) => Promise>; /** * @private */ validate: (cause: ValidationCause) => FormErrorMapFromValidator | Promise>; handleSubmit(): Promise; handleSubmit(submitMeta: TSubmitMeta): Promise; /** * Handles the form submission, performs validation, and calls the appropriate onSubmit or onSubmitInvalid callbacks. */ _handleSubmit: (submitMeta?: TSubmitMeta) => Promise; /** * Gets the value of the specified field. */ getFieldValue: >(field: TField) => DeepValue; /** * Gets the metadata of the specified field. */ getFieldMeta: >(field: TField) => AnyFieldMeta | undefined; /** * Gets the field info of the specified field. */ getFieldInfo: >(field: TField) => FieldInfo; /** * Updates the metadata of the specified field. */ setFieldMeta: >(field: TField, updater: Updater) => void; /** * resets every field's meta */ resetFieldMeta: >(fieldMeta: Partial>) => Partial>; /** * Sets the value of the specified field and optionally updates the touched state. */ setFieldValue: >(field: TField, updater: Updater>, opts?: UpdateMetaOptions) => void; deleteField: >(field: TField) => void; /** * Pushes a value into an array field. */ pushFieldValue: >(field: TField, value: DeepValue extends any[] ? DeepValue[number] : never, options?: UpdateMetaOptions) => void; insertFieldValue: >(field: TField, index: number, value: DeepValue extends any[] ? DeepValue[number] : never, options?: UpdateMetaOptions) => Promise; /** * Replaces a value into an array field at the specified index. */ replaceFieldValue: >(field: TField, index: number, value: DeepValue extends any[] ? DeepValue[number] : never, options?: UpdateMetaOptions) => Promise; /** * Removes a value from an array field at the specified index. */ removeFieldValue: >(field: TField, index: number, options?: UpdateMetaOptions) => Promise; /** * Swaps the values at the specified indices within an array field. */ swapFieldValues: >(field: TField, index1: number, index2: number, options?: UpdateMetaOptions) => void; /** * Moves the value at the first specified index to the second specified index within an array field. */ moveFieldValues: >(field: TField, index1: number, index2: number, options?: UpdateMetaOptions) => void; /** * Clear all values within an array field. */ clearFieldValues: >(field: TField, options?: UpdateMetaOptions) => void; /** * Resets the field value and meta to default state */ resetField: >(field: TField) => void; /** * Updates the form's errorMap */ setErrorMap: (errorMap: FormValidationErrorMap, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormAsyncValidateOrFn>) => void; /** * Returns form and field level errors */ getAllErrors: () => { form: { errors: Array | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormValidateOrFn | UnwrapFormAsyncValidateOrFn | UnwrapFormAsyncValidateOrFn>>; errorMap: ValidationErrorMap, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormValidateOrFn, UnwrapFormAsyncValidateOrFn, UnwrapFormAsyncValidateOrFn>; }; fields: Record, { errors: ValidationError[]; errorMap: ValidationErrorMap; }>; }; /** * Parses the form's values with a given standard schema and returns * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ parseValuesWithSchema: (schema: StandardSchemaV1) => { form: Record; fields: Record; } | undefined; /** * Parses the form's values with a given standard schema and returns * issues (if any). This method does NOT set any internal errors. * @param schema The standard schema to parse the form values with. */ parseValuesWithSchemaAsync: (schema: StandardSchemaV1) => Promise<{ form: Record; fields: Record; } | undefined>; } export {};