UNPKG

@matthew.ngo/reform

Version:

A flexible and powerful React form management library with advanced validation, state observation, and multi-group support

215 lines (196 loc) 7.25 kB
export {} // import { FieldError, UseFormReturn } from 'react-hook-form'; // import { FormDataPath } from '../../../types'; // import { // FormValidation, // BaseValidationContext as ValidationContext, // } from './types'; // import { debounce } from '../../../utils/debounce'; // import { FieldPath, FormGroup } from '../../form/form-groups'; // import { useRef } from 'react'; // import { ValidationConfig, ValidationResult } from '../types'; // export const useFormValidation = <T extends Record<string, any>>( // config: ValidationConfig<T>, // methods: UseFormReturn<{ groups: FormGroup<T>[] }> // ): FormValidation<T> => { // // Ref to store custom field validators // const customValidatorsRef = useRef< // Record<string, (value: any, groupIndex: number) => boolean | string> // >({}); // const transformFieldError = (error: FieldError): string => { // return error.message || 'Invalid field'; // }; // const transformErrors = ( // errors: Record<string, FieldError> | undefined // ): Record<string, string> | undefined => { // if (!errors) return undefined; // return Object.entries(errors).reduce( // (acc, [key, error]) => ({ // ...acc, // [key]: transformFieldError(error), // }), // {} // ); // }; // /** // * Register a custom field validator // * // * @param field - Field path to validate // * @param validator - Validation function // */ // const registerFieldValidator = <K extends FieldPath<T>>( // field: K, // validator: (value: any, groupIndex: number) => boolean | string // ): void => { // customValidatorsRef.current[field] = validator; // }; // // Modify validateField to use custom validators if registered // const validateField = async <K extends keyof T>( // fieldName: K, // value: T[K], // groupIndex: number // ): Promise<ValidationResult> => { // const fieldPath = `groups.${groupIndex}.data.${String( // fieldName // )}` as FormDataPath<T>; // try { // // Check if there's a custom validator for this field // const customValidator = customValidatorsRef.current[fieldName as string]; // if (customValidator) { // const result = customValidator(value, groupIndex); // if (result === false || typeof result === 'string') { // const message = // typeof result === 'string' ? result : 'Validation failed'; // methods.setError(fieldPath, { // type: 'custom', // message, // }); // return { // isValid: false, // message, // errors: { [String(fieldName)]: message }, // }; // } // } // // Continue with standard validation // const isValid = await methods.trigger(fieldPath); // const fieldState = methods.getFieldState(fieldPath); // // Handle async validation if configured // if (isValid && config.fields?.[fieldName]?.asyncValidate) { // const groupPath = `groups.${groupIndex}.data` as FormDataPath<T>; // const context: ValidationContext<T, K> = { // formData: methods.getValues(groupPath) as T, // groupIndex, // fieldName, // allGroups: methods.getValues().groups, // }; // const asyncError = await config.fields[fieldName].asyncValidate!( // value, // context // ); // if (asyncError) { // methods.setError(fieldPath, { // type: 'async', // message: asyncError, // }); // return { // isValid: false, // message: asyncError, // }; // } // } // return { // isValid: !fieldState.error, // message: fieldState.error?.message, // errors: fieldState.error // ? { [String(fieldName)]: fieldState.error.message || 'Invalid field' } // : undefined, // }; // } catch (error) { // const message = // error instanceof Error ? error.message : 'Validation failed'; // methods.setError(fieldPath, { type: 'validation', message }); // return { isValid: false, message }; // } // }; // const validateGroup = async ( // groupIndex: number // ): Promise<ValidationResult> => { // const groupPath = `groups.${groupIndex}.data` as FormDataPath<T>; // try { // const isValid = await methods.trigger(groupPath); // const groupErrors = methods.formState.errors?.groups?.[groupIndex]?.data; // return { // isValid, // errors: groupErrors // ? transformErrors(groupErrors as Record<string, FieldError>) // : undefined, // message: isValid ? undefined : 'Group validation failed', // }; // } catch (error) { // const message = // error instanceof Error ? error.message : 'Group validation failed'; // methods.setError(`groups.${groupIndex}`, { type: 'validation', message }); // return { isValid: false, message }; // } // }; // const validateAllGroups = async (): Promise<ValidationResult> => { // try { // const isValid = await methods.trigger(); // const formErrors: Record<string, FieldError> = {}; // // Transform group errors into flat structure // const groups = methods.formState.errors?.groups; // if (Array.isArray(groups)) { // groups.forEach((group, index) => { // // Add field errors // if (group?.data) { // Object.entries(group.data).forEach(([field, error]) => { // // Add type assertion to error // if ((error as any)?.message) { // formErrors[`groups.${index}.${field}`] = { // type: 'manual', // message: (error as any).message, // }; // } // }); // } // // Add group level error // if (group?.message) { // formErrors[`group${index}`] = { // type: 'manual', // message: group.message, // }; // } // }); // } // return { // isValid, // errors: // Object.keys(formErrors).length > 0 // ? transformErrors(formErrors) // : undefined, // message: isValid ? undefined : 'Form validation failed', // }; // } catch (error) { // const message = // error instanceof Error ? error.message : 'Form validation failed'; // methods.setError('root', { type: 'validation', message }); // return { isValid: false, message }; // } // }; // const createDebouncedValidation = (delay: number = 500) => { // return { // validateField: debounce(validateField, delay), // validateGroup: debounce(validateGroup, delay), // validateAllGroups: debounce(validateAllGroups, delay), // }; // }; // // Add registerFieldValidator to the return object // return { // validateField, // validateGroup, // validateAllGroups, // createDebouncedValidation, // registerFieldValidator, // }; // };