maz-ui
Version:
A standalone components library for Vue.Js 3 & Nuxt.Js 3
99 lines (98 loc) • 5.57 kB
TypeScript
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<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>;
export type ValidationIssues = InferIssue<Validation>[];
export type ExtractModelKey<T> = Extract<keyof T, string>;
export type FormSchema<Model extends BaseFormPayload> = {
[K in Extract<keyof Model, string> as Model[K] extends Required<Model>[K] ? K : never]: Validation;
} & {
[K in Extract<keyof Model, string> as Model[K] extends Required<Model>[K] ? never : K]?: Validation;
};
export type CustomInstance<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>> = ComponentInternalInstance & {
formContexts?: Map<string | symbol | InjectionKey<FormContext<Model, ModelKey>>, FormContext<Model, ModelKey>>;
};
export interface FormValidatorOptions<Model extends BaseFormPayload = BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>> = ExtractModelKey<FormSchema<Model>>> {
/**
* 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<Record<ModelKey, number | true>> | 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<Record<ModelKey, number | true>> | 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;
}
export type StrictOptions<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>> = Required<FormValidatorOptions<Model, ModelKey>>;
export interface FormContext<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>> {
fieldsStates: Ref<FieldsStates<Model, ModelKey>>;
options: StrictOptions<Model, ModelKey>;
internalSchema: Ref<FormSchema<Model>>;
payload: Ref<Model>;
errorMessages: Ref<Record<ModelKey, string | undefined>>;
isSubmitted: Ref<boolean>;
}
export interface FieldState<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>, FieldType = Model[ModelKey]> {
blurred: boolean;
dirty: boolean;
error: boolean;
errors: ValidationIssues;
valid: boolean;
initialValue?: Readonly<FieldType>;
validating: boolean;
validated: boolean;
validateFunction: ReturnType<typeof getValidateFunction<Model, ModelKey>>;
mode?: StrictOptions<Model, ModelKey>['mode'];
}
export type FieldsStates<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>> = Record<ModelKey, FieldState<Model, ModelKey, Model[ModelKey]>>;
export type BaseFormPayload = Record<string, any>;
export interface FormFieldOptions<Model extends BaseFormPayload, ModelKey extends ExtractModelKey<FormSchema<Model>>, FieldType> {
/**
* Default value of the field
* @default undefined
*/
defaultValue?: FieldType;
/**
* Validation mode
* To override the form validation mode
*/
mode?: StrictOptions<Model, ModelKey>['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> = T extends Ref<infer U> ? U extends FormSchema<BaseFormPayload> ? Partial<InferInput<ReturnType<typeof objectAsync<U>>>> : never : T extends (...args: any[]) => FormSchema<BaseFormPayload> ? Partial<InferInput<ReturnType<typeof objectAsync<ReturnType<T>>>>> : T extends FormSchema<BaseFormPayload> ? Partial<InferInput<ReturnType<typeof objectAsync<T>>>> : never;
export type InferOutputSchemaFormValidator<T> = T extends Ref<infer U> ? U extends FormSchema<BaseFormPayload> ? InferOutput<ReturnType<typeof objectAsync<U>>> : never : T extends (...args: any[]) => FormSchema<BaseFormPayload> ? InferOutput<ReturnType<typeof objectAsync<ReturnType<T>>>> : T extends FormSchema<BaseFormPayload> ? InferOutput<ReturnType<typeof objectAsync<T>>> : never;