Skip to content

Commit

Permalink
feat: added form wide sync validators
Browse files Browse the repository at this point in the history
  • Loading branch information
YannicEl committed Aug 23, 2023
1 parent 87ac2c5 commit e0cbb48
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 17 deletions.
42 changes: 36 additions & 6 deletions packages/lib/src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export interface Form<T = any> {
pending: boolean;
submitted: boolean;

// Validators and errors
validators: Set<Validator<UnwrappedValues<T>>>;
asyncValidators: Set<AsyncValidator<UnwrappedValues<T>>>;
errors: string[];

// functions
setValues: (values: Partial<{ [Key in keyof T]: T[Key] }>) => void;
reset: () => void;
Expand All @@ -36,6 +41,8 @@ export type Values<T> = {
[Key in keyof T]: ComputedRef<T[Key]>;
};

export type UnwrappedValues<T> = UnwrapRef<Values<T>>;

export type Fields<T> = {
[Key in keyof T]: Field<T[Key]>;
};
Expand All @@ -51,16 +58,17 @@ export type FieldOptions<T> = {
// #endregion FieldOptions

// #region UseFormOptions
export type UseFormOptions = {
export type UseFormOptions<T> = {
plugins?: Plugin[];
validators?: Validator[];
validators?: Validator<UnwrappedValues<T>>[];
asyncValidators?: AsyncValidator<UnwrappedValues<T>>[];
};
// #endregion UseFormOptions

// #region useForm
export function useForm<T>(
fieldOptions: FieldOptions<T>,
options: UseFormOptions = { plugins: [], validators: [] }
options: UseFormOptions<T> = {}
): Form<T> {
const fields: Fields<T> = {} as any;
const values: Values<T> = reactive({}) as any;
Expand All @@ -74,6 +82,24 @@ export function useForm<T>(
fields[key] = field;
}

const validators = ref(new Set<Validator<UnwrappedValues<T>>>(options.validators));
const asyncValidators = ref(new Set<AsyncValidator<UnwrappedValues<T>>>(options.asyncValidators));

const syncErrors = computed(() => {
const ret: string[] = [];
validators.value.forEach((validator) => {
const isValid = validator.validate(values as any);
if (!isValid) ret.push(validator.name);
});

return ret;
});

const asyncErrors = ref<string[]>([]);

// combine async and sync errors
const errors = computed(() => [...syncErrors.value, ...asyncErrors.value]);

// Checks if all fields in the form are valid
const invalid = computed(() => {
let invalid = false;
Expand Down Expand Up @@ -165,10 +191,12 @@ export function useForm<T>(
});
}

function addPlugin(plugin: Plugin): void {
function addPlugin(plugin: Plugin<Form<T>>): void {
plugin(form);
}

options.plugins?.forEach((plugin) => plugin(form));

const form: Form<T> = reactive({
values,
fields,
Expand All @@ -185,6 +213,10 @@ export function useForm<T>(
touched,
submitted,

validators,
asyncValidators,
errors,

// functions
reset,
disable,
Expand All @@ -194,8 +226,6 @@ export function useForm<T>(
addPlugin,
});

options.plugins?.forEach((plugin) => plugin(form));

return form;
}
// #endregion useForm
26 changes: 15 additions & 11 deletions packages/lib/src/validators/utils.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
// #region Validator
export type Validator = {
export type Validator<TValue = unknown> = {
name: string;
validate: (value: unknown) => boolean;
validate: (value: TValue) => boolean;
};
// #endregion Validator

// #region AsyncValidator
export type AsyncValidator = {
export type AsyncValidator<TValue = unknown> = {
name: string;
validate: (value: unknown) => Promise<boolean>;
validate: (value: TValue) => Promise<boolean>;
};
// #endregion AsyncValidator

export function defineValidator<TReturn extends boolean | Promise<boolean>>(
export function defineValidator<TReturn extends boolean | Promise<boolean>, TValue = unknown>(
name: string,
validate: (value: unknown) => TReturn
validate: (value: TValue) => TReturn
): TReturn extends boolean ? Validator : AsyncValidator {
return {
name,
validate,
} as any;
}

export function defineValidatorWithArgs<Targs, TReturn extends boolean | Promise<boolean>>(
export function defineValidatorWithArgs<
TArgs,
TReturn extends boolean | Promise<boolean>,
TValue = unknown,
>(
name: string,
validate: (value: unknown, args: Targs) => TReturn
): TReturn extends boolean ? (args: Targs) => Validator : (args: Targs) => AsyncValidator {
return (args: Targs) => {
validate: (value: TValue, args: TArgs) => TReturn
): TReturn extends boolean ? (args: TArgs) => Validator : (args: TArgs) => AsyncValidator {
return (args: TArgs) => {
return {
name,
validate: (value: unknown) => validate(value, args),
validate: (value: TValue) => validate(value, args),
} as any;
};
}
Expand Down

0 comments on commit e0cbb48

Please sign in to comment.