import { BaseIssue, BaseSchema, BaseSchemaAsync, InferInput, InferIssue, InferOutput, objectAsync } from 'valibot'; import { ComponentInternalInstance, InjectionKey, Ref, TemplateRef } from 'vue'; import { getValidateFunction } from './validation'; export type Validation = BaseSchema> | BaseSchemaAsync>; export type ValidationIssues = InferIssue[]; export type ExtractModelKey = Extract; export type FormSchema = { [K in Extract as Model[K] extends Required[K] ? K : never]: Validation; } & { [K in Extract as Model[K] extends Required[K] ? never : K]?: Validation; }; export type CustomInstance>> = ComponentInternalInstance & { formContexts?: Map>, FormContext>; }; export interface FormValidatorOptions> = ExtractModelKey>> { /** * Validation mode * - lazy: validate on input value change * - aggressive: validate all fields immediately on form creation and on input value change * - blur: validate on blur * - eager: validate on blur at first (only if the field is not empty) and then on input value change * - progressive: field becomes valid after the first validation and then validate on input value change. If invalid validate on blur. * @default 'lazy' */ mode?: 'eager' | 'lazy' | 'aggressive' | 'blur' | 'progressive'; /** * Fields to validate with throttling * Useful for fields that require a network request to avoid spamming the server * @example { name: 1000 } or { name: true } for the default throttle time (1000ms) */ throttledFields?: Partial> | null; /** * Fields to validate with debouncing * Useful to wait for the user to finish typing before validating * Useful for fields that require a network request to avoid spamming the server * @example { name: 300 } or { name: true } for the default debounce time (300ms) */ debouncedFields?: Partial> | null; /** * Scroll to the first error found * @default '.has-field-error' */ scrollToError?: string | false; /** * Identifier to use for the form * Useful to have multiple forms on the same page * @default `main-form-validator` */ identifier?: string | symbol; /** * Reset the form on submit success - you must use handleSubmit to handle the form submission * @default true */ resetOnSuccess?: boolean; } export type StrictOptions>> = Required>; export interface FormContext>> { fieldsStates: Ref>; options: StrictOptions; internalSchema: Ref>; payload: Ref; errorMessages: Ref>; isSubmitted: Ref; } export interface FieldState>, FieldType = Model[ModelKey]> { blurred: boolean; dirty: boolean; error: boolean; errors: ValidationIssues; valid: boolean; initialValue?: Readonly; validating: boolean; validated: boolean; validateFunction: ReturnType>; mode?: StrictOptions['mode']; } export type FieldsStates>> = Record>; export type BaseFormPayload = Record; export interface FormFieldOptions>, FieldType> { /** * Default value of the field * @default undefined */ defaultValue?: FieldType; /** * Validation mode * To override the form validation mode */ mode?: StrictOptions['mode']; /** * Reference to the component or HTML element to associate and trigger validation events * Necessary for 'eager', 'progressive' and 'blur' validation modes */ ref?: Ref | TemplateRef; /** * Identifier for the form * Useful when you have multiple forms on the same component * Should be the same as the one used in `useFormValidator` */ formIdentifier?: string | symbol; } export type InferSchemaFormValidator = T extends Ref ? U extends FormSchema ? Partial>>> : never : T extends (...args: any[]) => FormSchema ? Partial>>>> : T extends FormSchema ? Partial>>> : never; export type InferOutputSchemaFormValidator = T extends Ref ? U extends FormSchema ? InferOutput>> : never : T extends (...args: any[]) => FormSchema ? InferOutput>>> : T extends FormSchema ? InferOutput>> : never;