UNPKG

koval-ui

Version:

React components collection with minimalistic design. Supports theming, layout, and input validation.

1 lines 8.87 kB
{"version":3,"file":"useValidation.cjs","sources":["../../../../src/internal/inputs/useValidation.ts"],"sourcesContent":["import type {Dispatch, FormEvent, SetStateAction} from 'react';\nimport {useCallback, useState} from 'react';\nimport AwesomeDebouncePromise from 'awesome-debounce-promise';\n\nimport type {ValidationProps, ValidatorFn} from './ValidationProps.ts';\nimport {ValidationState} from './ValidationProps.ts';\nimport {defaultValidator} from './defaultValidator.ts';\nimport {useHandleFormReset} from './useHandleFormReset.ts';\nimport {getFormState} from './getFormState.ts';\n\ntype InputMode = 'interactive' | 'textual';\n\nconst getValue = <TEvent extends FormEvent, TElement extends HTMLInputElement>(\n event: TEvent,\n mode: InputMode\n) => {\n return mode === 'interactive'\n ? (event.target as TElement).checked\n : (event.target as TElement).value;\n};\n\nconst useValidatorFn = <TEvent extends FormEvent, TElement extends HTMLInputElement>({\n validatorFn,\n reportValidity,\n setValidity,\n}: {\n validatorFn: ValidatorFn;\n reportValidity: (event: TEvent) => void;\n setValidity: Dispatch<SetStateAction<keyof typeof ValidationState>>;\n}) => {\n const createValidatorSync = useCallback(\n (mode: InputMode, event: TEvent) => {\n const value = getValue(event, mode);\n const formState = getFormState((event.target as TElement)!.form);\n const validationError = validatorFn?.(\n value,\n (event.target as TElement).validity,\n formState\n );\n (event.target as TElement).setCustomValidity(validationError as string);\n reportValidity(event);\n },\n [validatorFn, reportValidity]\n );\n\n const createValidatorExternal = useCallback(() => {}, []);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const debouncedValidator = useCallback(AwesomeDebouncePromise(validatorFn!, 1000), [\n validatorFn,\n ]);\n\n const createValidatorAsync = useCallback(\n async (mode: InputMode, event: TEvent) => {\n (event.target as TElement).setCustomValidity('');\n const value = getValue(event, mode);\n const formState = getFormState((event.target as TElement)!.form);\n setValidity(ValidationState.inProgress);\n let validationError = '';\n try {\n validationError = await debouncedValidator(\n value,\n (event.target as TElement).validity,\n formState\n );\n } catch (error) {\n (event.target as TElement).setCustomValidity(error as string);\n }\n (event.target as TElement).setCustomValidity(validationError);\n reportValidity(event);\n },\n [setValidity, debouncedValidator, reportValidity]\n );\n\n return {createValidatorSync, createValidatorAsync, createValidatorExternal};\n};\n\nconst createValidatorFn = (validation: ValidationProps['validation']): ValidatorFn => {\n if (typeof validation === 'function') {\n return validation;\n }\n return defaultValidator;\n};\n\nenum Modes {\n async = 'async',\n external = 'external',\n sync = 'sync',\n}\n\nconst getMode = (validation: ValidationProps['validation']) => {\n if (validation?.constructor.name === 'AsyncFunction') {\n return Modes.async;\n } else if (typeof validation === 'string') {\n return Modes.external;\n }\n\n return Modes.sync;\n};\n\nexport const useValidation = <TEvent extends FormEvent, TElement extends HTMLInputElement>({\n validation,\n hasValidators,\n}: ValidationProps & {hasValidators: boolean}) => {\n const validatorFn = createValidatorFn(validation);\n const mode = getMode(validation);\n\n const [validity, setValidity] = useState<keyof typeof ValidationState>(\n ValidationState.pristine\n );\n\n useHandleFormReset(setValidity);\n\n const reportValidity = useCallback(\n (event: TEvent) => {\n const isValid = (event.target as TElement).reportValidity();\n const ValidState = hasValidators ? ValidationState.valid : ValidationState.pristine;\n const nextValidationState = isValid ? ValidState : ValidationState.error;\n /**\n * Change state only when input has validators or is in the error state.\n * This is required to avoid always showing a green checkmark for input without validation.\n */\n (hasValidators || validity === ValidationState.error) &&\n setValidity(nextValidationState);\n },\n [hasValidators, validity]\n );\n\n const {createValidatorAsync, createValidatorSync, createValidatorExternal} = useValidatorFn({\n validatorFn,\n reportValidity,\n setValidity,\n });\n\n const validateInteractive = useCallback(\n (event: TEvent) => {\n switch (mode) {\n case 'sync': {\n return createValidatorSync('interactive', event);\n }\n case 'async': {\n return createValidatorAsync('interactive', event);\n }\n case 'external': {\n return createValidatorExternal();\n }\n }\n },\n [createValidatorAsync, createValidatorExternal, createValidatorSync, mode]\n );\n\n const validateTextual = useCallback(\n (event: TEvent) => {\n switch (mode) {\n case 'sync': {\n return createValidatorSync('textual', event);\n }\n case 'async': {\n return createValidatorAsync('textual', event);\n }\n case 'external': {\n return createValidatorExternal();\n }\n }\n },\n [mode, createValidatorSync, createValidatorAsync, createValidatorExternal]\n );\n\n return {validateInteractive, validateTextual, validity, setValidity};\n};\n"],"names":["getValue","event","mode","useValidatorFn","validatorFn","reportValidity","setValidity","createValidatorSync","useCallback","value","formState","getFormState","validationError","createValidatorExternal","debouncedValidator","AwesomeDebouncePromise","createValidatorAsync","ValidationState","error","createValidatorFn","validation","defaultValidator","getMode","useValidation","hasValidators","validity","useState","useHandleFormReset","isValid","ValidState","nextValidationState","validateInteractive","validateTextual"],"mappings":"ySAYMA,EAAW,CACbC,EACAC,IAEOA,IAAS,cACTD,EAAM,OAAoB,QAC1BA,EAAM,OAAoB,MAG/BE,EAAiB,CAA8D,CACjF,YAAAC,EACA,eAAAC,EACA,YAAAC,CACJ,IAIM,CACF,MAAMC,EAAsBC,EAAA,YACxB,CAACN,EAAiBD,IAAkB,CAC1B,MAAAQ,EAAQT,EAASC,EAAOC,CAAI,EAC5BQ,EAAYC,EAAA,aAAcV,EAAM,OAAqB,IAAI,EACzDW,EAAkBR,GAAA,YAAAA,EACpBK,EACCR,EAAM,OAAoB,SAC3BS,GAEHT,EAAM,OAAoB,kBAAkBW,CAAyB,EACtEP,EAAeJ,CAAK,CACxB,EACA,CAACG,EAAaC,CAAc,CAChC,EAEMQ,EAA0BL,EAAAA,YAAY,IAAM,CAAC,EAAG,EAAE,EAGlDM,EAAqBN,EAAA,YAAYO,EAAuBX,EAAc,GAAI,EAAG,CAC/EA,CAAA,CACH,EAEKY,EAAuBR,EAAA,YACzB,MAAON,EAAiBD,IAAkB,CACrCA,EAAM,OAAoB,kBAAkB,EAAE,EACzC,MAAAQ,EAAQT,EAASC,EAAOC,CAAI,EAC5BQ,EAAYC,EAAA,aAAcV,EAAM,OAAqB,IAAI,EAC/DK,EAAYW,kBAAgB,UAAU,EACtC,IAAIL,EAAkB,GAClB,GAAA,CACAA,EAAkB,MAAME,EACpBL,EACCR,EAAM,OAAoB,SAC3BS,CACJ,QACKQ,EAAO,CACXjB,EAAM,OAAoB,kBAAkBiB,CAAe,CAAA,CAE/DjB,EAAM,OAAoB,kBAAkBW,CAAe,EAC5DP,EAAeJ,CAAK,CACxB,EACA,CAACK,EAAaQ,EAAoBT,CAAc,CACpD,EAEO,MAAA,CAAC,oBAAAE,EAAqB,qBAAAS,EAAsB,wBAAAH,CAAuB,CAC9E,EAEMM,EAAqBC,GACnB,OAAOA,GAAe,WACfA,EAEJC,EAAA,iBASLC,EAAWF,IACTA,GAAA,YAAAA,EAAY,YAAY,QAAS,gBAC1B,QACA,OAAOA,GAAe,SACtB,WAGJ,OAGEG,EAAgB,CAA8D,CACvF,WAAAH,EACA,cAAAI,CACJ,IAAkD,CACxC,MAAApB,EAAce,EAAkBC,CAAU,EAC1ClB,EAAOoB,EAAQF,CAAU,EAEzB,CAACK,EAAUnB,CAAW,EAAIoB,EAAA,SAC5BT,kBAAgB,QACpB,EAEAU,EAAAA,mBAAmBrB,CAAW,EAE9B,MAAMD,EAAiBG,EAAA,YAClBP,GAAkB,CACT,MAAA2B,EAAW3B,EAAM,OAAoB,eAAe,EACpD4B,EAAaL,EAAgBP,EAAgB,gBAAA,MAAQA,EAAgB,gBAAA,SACrEa,EAAsBF,EAAUC,EAAaZ,EAAgB,gBAAA,OAKlEO,GAAiBC,IAAaR,EAAAA,gBAAgB,QAC3CX,EAAYwB,CAAmB,CACvC,EACA,CAACN,EAAeC,CAAQ,CAC5B,EAEM,CAAC,qBAAAT,EAAsB,oBAAAT,EAAqB,wBAAAM,CAAA,EAA2BV,EAAe,CACxF,YAAAC,EACA,eAAAC,EACA,YAAAC,CAAA,CACH,EAEKyB,EAAsBvB,EAAA,YACvBP,GAAkB,CACf,OAAQC,EAAM,CACV,IAAK,OACM,OAAAK,EAAoB,cAAeN,CAAK,EAEnD,IAAK,QACM,OAAAe,EAAqB,cAAef,CAAK,EAEpD,IAAK,WACD,OAAOY,EAAwB,CACnC,CAER,EACA,CAACG,EAAsBH,EAAyBN,EAAqBL,CAAI,CAC7E,EAEM8B,EAAkBxB,EAAA,YACnBP,GAAkB,CACf,OAAQC,EAAM,CACV,IAAK,OACM,OAAAK,EAAoB,UAAWN,CAAK,EAE/C,IAAK,QACM,OAAAe,EAAqB,UAAWf,CAAK,EAEhD,IAAK,WACD,OAAOY,EAAwB,CACnC,CAER,EACA,CAACX,EAAMK,EAAqBS,EAAsBH,CAAuB,CAC7E,EAEA,MAAO,CAAC,oBAAAkB,EAAqB,gBAAAC,EAAiB,SAAAP,EAAU,YAAAnB,CAAW,CACvE"}