mirror of
https://github.com/cupcakearmy/formhero.git
synced 2024-12-22 16:16:24 +00:00
84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
|
import React, { useState } from 'react'
|
||
|
|
||
|
|
||
|
|
||
|
export type useFormExtractor = (from: any) => any
|
||
|
|
||
|
export type useFormAutoOptions = {
|
||
|
getter?: string,
|
||
|
setter?: string,
|
||
|
extractor?: useFormExtractor
|
||
|
}
|
||
|
|
||
|
export type useFormOptions = {
|
||
|
extractor?: useFormExtractor,
|
||
|
}
|
||
|
|
||
|
export type useFormValidatorFunction = ((s: any) => boolean | Promise<boolean>)
|
||
|
export type useFormValidatorMethod = useFormValidatorFunction | RegExp
|
||
|
|
||
|
export type useFormValidatorObject = {
|
||
|
validator: useFormValidatorMethod,
|
||
|
message?: string,
|
||
|
}
|
||
|
|
||
|
export type useFormValidator = useFormValidatorMethod | useFormValidatorObject
|
||
|
|
||
|
export const HTMLInputExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.value
|
||
|
export const HTMLCheckboxExtractor: useFormExtractor = (e: React.FormEvent<HTMLInputElement>) => e.currentTarget.checked
|
||
|
|
||
|
export const useForm = <T, U extends { [key in keyof T]: useFormValidator }, E extends { [key in keyof U]?: string }>(init: T, validators: Partial<U> = {}, options: useFormOptions = {}) => {
|
||
|
const [form, setForm] = useState<T>(init)
|
||
|
|
||
|
const [errors, setErrors] = useState<Partial<E>>({})
|
||
|
|
||
|
const _set = (key: keyof T, value: any) => {
|
||
|
setForm({
|
||
|
...form,
|
||
|
[key]: value,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
const _validateAll = async (value: any, validator: useFormValidatorMethod): Promise<boolean> => {
|
||
|
if (validator.constructor.name === 'Function' || validator.constructor.name === 'AsyncFunction')
|
||
|
return (validator as useFormValidatorFunction)(value)
|
||
|
else if (validator.constructor.name === 'RegExp')
|
||
|
return (validator as RegExp).test(value)
|
||
|
else return false
|
||
|
}
|
||
|
|
||
|
const _getValidatorMessage = (key: keyof T): string => {
|
||
|
// @ts-ignore
|
||
|
if (validators[key] && validators[key].message) return validators[key].message
|
||
|
else return `Error in: ${key}`
|
||
|
}
|
||
|
|
||
|
const _validate = (key: keyof T, value: any) => {
|
||
|
const validator: useFormValidator | undefined = validators[key]
|
||
|
if (!validator) return
|
||
|
|
||
|
// @ts-ignore
|
||
|
_validateAll(value, validator.constructor.name === 'Object' ? (validator as useFormValidatorObject).validator : validator)
|
||
|
.then((valid: boolean) => {
|
||
|
setErrors({
|
||
|
...errors,
|
||
|
[key]: valid
|
||
|
? undefined
|
||
|
: _getValidatorMessage(key),
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
const update = (key: keyof T, extractor = options.extractor) => (value: any) => {
|
||
|
const extracted = extractor ? extractor(value) : HTMLInputExtractor(value)
|
||
|
_set(key, extracted)
|
||
|
_validate(key, extracted)
|
||
|
}
|
||
|
|
||
|
const auto = (key: keyof T, opts: useFormAutoOptions = {}) => ({
|
||
|
[opts.getter || 'onChange']: update(key, opts.extractor),
|
||
|
[opts.setter || 'value']: form[key] as any,
|
||
|
})
|
||
|
|
||
|
return { form, update, auto, errors }
|
||
|
}
|