@vorms/core
Version:
Vue Form Validation with Composition API
274 lines (265 loc) • 11.1 kB
TypeScript
import { Ref, UnwrapNestedRefs, WritableComputedRef, ComputedRef } from 'vue';
type MaybeRef<T> = T | Ref<T>;
type FormValues = Record<string, any>;
type FieldValidator<Value> = (value: Value) => string | void | Promise<string | void>;
type FieldArrayValidator<Value extends Array<any>> = (value: Value) => FormErrors<Value> | void | Promise<FormErrors<Value> | void>;
type FormTouched<Values> = {
[K in keyof Values]?: Values[K] extends any[] ? Values[K][number] extends object ? FormTouched<Values[K][number]>[] | boolean : boolean | boolean[] : Values[K] extends object ? FormTouched<Values[K]> : boolean;
};
type FormErrors<Values> = {
[K in keyof Values]?: Values[K] extends any[] ? Values[K][number] extends object ? FormErrors<Values[K][number]>[] | string | string[] : string | string[] : Values[K] extends object ? FormErrors<Values[K]> : string;
};
interface FormState<Values extends FormValues> {
values: UnwrapNestedRefs<Values>;
touched: Ref<FormTouched<Values>>;
errors: Ref<FormErrors<Values>>;
submitCount: Ref<number>;
isSubmitting: Ref<boolean>;
isValidating: Ref<boolean>;
}
interface FormEventHandler {
handleBlur: {
(event: Event, name?: string): void;
<T = string | Event>(name: T): T extends string ? () => void : void;
};
handleChange: () => void;
handleInput: () => void;
}
interface FieldRegisterOptions<Values> {
validate?: FieldValidator<Values>;
}
type UseFormRegisterReturn<Value> = FieldMeta & {
value: WritableComputedRef<Value>;
attrs: ComputedRef<FieldAttrs>;
};
type SetFieldArrayValue = <T extends (...args: any) => any>(name: string, value: any[], method?: T, args?: Partial<{
argA: Parameters<T>[1];
argB: Parameters<T>[2];
}>, shouldSetValue?: boolean) => void;
type UseFormRegister<Values extends FormValues> = <Name extends Path<Values>, Value = PathValue<Values, Name>>(name: MaybeRef<Name>, options?: FieldRegisterOptions<Value>) => UseFormRegisterReturn<Value>;
type UseFormSetFieldValue<Values extends FormValues> = <Name extends Path<Values>, FieldValue extends PathValue<Values, Name>>(name: Name, value: FieldValue, shouldValidate?: boolean) => void;
type ValidateForm<Values extends FormValues> = (values?: Values) => Promise<FormErrors<Values>>;
type ValidateField<Values extends FormValues> = <Name extends Path<Values>>(name: Name) => Promise<void>;
interface FormResetState<Values extends FormValues = FormValues> {
values: Values;
touched: FormTouched<Values>;
errors: FormErrors<Values>;
submitCount: number;
}
type ResetForm<Values extends FormValues> = (nextState?: Partial<FormResetState<Values>>) => void;
interface UseFormReturn<Values extends FormValues> {
values: UnwrapNestedRefs<Values>;
touched: ComputedRef<FormTouched<Values>>;
errors: ComputedRef<FormErrors<Values>>;
submitCount: ComputedRef<number>;
isSubmitting: Ref<boolean>;
isValidating: ComputedRef<boolean>;
dirty: ComputedRef<boolean>;
register: UseFormRegister<Values>;
setValues: (values: Values, shouldValidate?: boolean) => void;
setFieldValue: UseFormSetFieldValue<Values>;
handleSubmit: (event?: Event) => void;
handleReset: (event?: Event) => void;
resetForm: ResetForm<Values>;
validateForm: ValidateForm<Values>;
validateField: ValidateField<Values>;
}
type FieldAttrs = {
name: string;
onBlur: (event: Event) => void;
onChange: () => void;
onInput: () => void;
};
type FieldMeta = {
dirty: ComputedRef<boolean>;
error: ComputedRef<string | undefined>;
touched: ComputedRef<boolean | undefined>;
};
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false : true;
type PathImpl<K extends string | number, V> = V extends Primitive ? `${K}` : `${K}` | `${K}.${Path<V>}`;
type TupleKeys<T extends ReadonlyArray<any>> = Exclude<keyof T, keyof any[]>;
type Path<T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
[K in TupleKeys<T>]-?: PathImpl<K & string, T[K]>;
}[TupleKeys<T>] : PathImpl<number, V> : {
[K in keyof T]-?: PathImpl<K & string, T[K]>;
}[keyof T];
type ArrayPathImpl<K extends string | number, V> = V extends Primitive ? never : V extends ReadonlyArray<infer U> ? U extends Primitive ? never : `${K}` | `${K}.${ArrayPath<V>}` : `${K}.${ArrayPath<V>}`;
type ArrayPath<T> = T extends ReadonlyArray<infer V> ? IsTuple<T> extends true ? {
[K in TupleKeys<T>]-?: ArrayPathImpl<K & string, T[K]>;
}[TupleKeys<T>] : ArrayPathImpl<number, V> : {
[K in keyof T]-?: ArrayPathImpl<K & string, T[K]>;
}[keyof T];
type PathValue<T, P extends Path<T> | ArrayPath<T>> = T extends any ? P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? Rest extends Path<T[Key]> ? PathValue<T[Key], Rest> : never : Key extends `${number}` ? T extends ReadonlyArray<infer V> ? PathValue<V, Rest & Path<V>> : never : never : P extends keyof T ? T[P] : P extends `${number}` ? T extends ReadonlyArray<infer V> ? V : never : never : never;
interface FormSubmitHelper<Values extends FormValues> {
setSubmitting: (isSubmitting: boolean) => void;
readonly initialValues: Values;
}
type ValidateMode = 'blur' | 'input' | 'change' | 'submit';
interface UseFormOptions<Values extends FormValues> {
initialValues: Values;
initialErrors?: FormErrors<Values>;
initialTouched?: FormTouched<Values>;
validateMode?: ValidateMode;
reValidateMode?: ValidateMode;
validateOnMounted?: boolean;
onSubmit: (values: Values, helper: FormSubmitHelper<Values>) => void | Promise<any>;
onInvalid?: (errors: FormErrors<Values>) => void;
validate?: (values: Values) => void | object | Promise<FormErrors<Values>>;
}
/**
* Custom composition API to mange the entire form.
*
* @param options - form configuration and validation parameters. {@link UseFormOptions}
*
* @returns methods and state of this form. {@link UseFormReturn}
*
* @example
* ```vue
* <script setup lang="ts">
* const { register, handleSubmit } = useForm({
* initialValues: {
* name: 'Alex',
* age: 18,
* },
* onSubmit (values) {
* console.log({ values })
* },
* });
*
* const { value: name, attrs: nameAttrs } = register('name')
* const { value: age, attrs: ageAttrs } = register('name')
* </script>
*
* <template>
* <form v-on:submit="handleSubmit">
* <input v-model="name" type="text" v-bind="nameAttrs" />
* <input v-model.number="age" type="text" v-bind="ageAttrs" />
* <input type="submit" />
* </form>
* </template>
* ```
*/
declare function useForm<Values extends FormValues = FormValues>(options: UseFormOptions<Values>): UseFormReturn<Values>;
type UseFieldOptions<Value> = {
validate?: FieldValidator<Value>;
};
/**
* Custom composition API that will return specific field value, meta (state) and attributes.
*
* @param name - field name
*
* @param options - field configuration and validation parameters. {@link UseFieldOptions}
*
* @returns field value, meta (state) and attributes. {@link UseFormRegisterReturn}
*
* @example
* ```vue
* <script setup lang="ts">
* const { handleSubmit } = useForm({
* initialValues: {
* name: '',
* },
* onSubmit(values) {
* console.log({ values });
* },
* });
*
* const { value: name, attrs: nameAttrs } = useField('name')
* </script>
*
* <template>
* <form v-on:submit="handleSubmit">
* <input v-model="name" type="text" v-bind="nameAttrs" />
* <input type="submit" />
* </form>
* </template>
* ```
*/
declare function useField<Value>(name: MaybeRef<string>, options?: UseFieldOptions<Value>): UseFormRegisterReturn<Value>;
interface FieldEntry<Value> {
key: number;
value: Value;
name: string;
error: FormErrors<Value>;
touched: Value extends Primitive ? boolean : FormTouched<Value> | undefined;
dirty: boolean;
attrs: Omit<FieldAttrs, 'name'>;
}
interface UseFieldArrayOptions<Value> {
validate?: FieldArrayValidator<Value[]>;
}
type UseFieldArrayReturn<Value> = {
fields: Ref<FieldEntry<Value>[]>;
append: (value: Value) => void;
prepend: (value: Value) => void;
swap: (indexA: number, indexB: number) => void;
remove: (index?: number) => void;
move: (from: number, to: number) => void;
insert: (index: number, value: Value) => void;
update: (index: number, value: Value) => void;
replace: (values: Value[]) => void;
};
/**
* Custom composition API that exposes convenient to perform operations with a list of fields.
*
* @param name - field name
*
* @param options - field array configuration and validation parameters {@link UseFieldArrayOptions}
*
* @returns fields state and methods. {@link UseFieldArrayReturn}
*
* @example
* ```vue
* <script setup lang="ts">
* import { useForm, useFieldArray } from '@vorms/core';
*
* const { values, handleSubmit } = useForm({
* initialValues: {
* list: ['vue', 'vue-router'],
* },
* onSubmit(values) {
* console.log({ values });
* },
* });
*
* const { fields, append } = useFieldArray('list');
* </script>
*
* <template>
* <pre>{{ values }}</pre>
* <form v-on:submit="handleSubmit">
* <template v-for="field in fields" :key="field.key">
* <input v-model="field.value" :name="field.name" v-bind="field.attrs" />
* </template>
* <button type="button" v-on:click="append('pinia')">Append</button>
* <input type="submit" />
* </form>
* </template>
* ```
*/
declare function useFieldArray<Value>(name: MaybeRef<string>, options?: UseFieldArrayOptions<Value>): UseFieldArrayReturn<Value>;
type FormContextValues<Values extends FormValues = FormValues> = UseFormReturn<Values>;
/**
* Custom composition API that return form context
*
* @returns methods and state of form (As same as useForm). {@link FormContextValues}
*
* @example
* ```vue
* <script setup lang="ts">
* import { useFormContext } from '@vorms/core'
*
* const { validateField } = useFormContext()
* const { value, attrs } = useField('drink') // You can also use `register` return from `useFormContext()`
* </script>
*
* <template>
* <input v-model="value" type="text" v-bind="attrs" />
* </template>
* ```
*/
declare function useFormContext<Values extends FormValues = FormValues>(): FormContextValues<Values>;
declare const _default$1: (obj: Record<string, any>, path: string, value?: any) => Record<string, any>;
declare const _default: <T>(obj: T, path: string, defaultValue?: unknown) => any;
export { ArrayPath, FieldArrayValidator, FieldAttrs, FieldMeta, FieldRegisterOptions, FieldValidator, FormErrors, FormEventHandler, FormResetState, FormState, FormSubmitHelper, FormTouched, FormValues, IsTuple, MaybeRef, Path, PathValue, Primitive, ResetForm, SetFieldArrayValue, TupleKeys, UseFieldArrayOptions, UseFormOptions, UseFormRegister, UseFormRegisterReturn, UseFormReturn, UseFormSetFieldValue, ValidateField, ValidateForm, ValidateMode, _default as get, _default$1 as set, useField, useFieldArray, useForm, useFormContext };