UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

479 lines (478 loc) 18.4 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = useFieldValidation; var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js")); var _react = require("react"); var _index = require("../utils/index.js"); var _index2 = _interopRequireDefault(require("../utils/json-pointer/index.js")); var _isAsync = require("../../../shared/helpers/isAsync.js"); var _useProcessManager = _interopRequireDefault(require("./useProcessManager.js")); var _useUpdateEffect = _interopRequireDefault(require("../../../shared/helpers/useUpdateEffect.js")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function useFieldValidation({ finalSchema, hasZodSchema, onChangeValidatorProp, onBlurValidator, validateInitially, validateUnchanged, validateContinuously, identifier, disabled, emptyValue, required, hasDataContext, getAjvInstanceDataContext, setFieldEventListener, getValueByPath, getSourceValue, exportValidators, props, dataContext, combinedErrorMessages, makeIteratePath, errorPrioritization, sectionPath, hasSectionSchema, dataContextSchema, valueRef, changedRef, transformers, schemaValidatorRef, asyncProcessRef, validatedValueRef, changeEventResultRef, localErrorInitiatorRef, error, persistErrorState, clearErrorState, revealError, hideError, setFieldState, ensureErrorMessageObject, asyncBehaviorIsEnabled, defineAsyncProcess, forceUpdate, revealErrorRef }) { const { startProcess } = (0, _useProcessManager.default)(); const onChangeValidator = (0, _react.useMemo)(() => { if (onChangeValidatorProp) { return onChangeValidatorProp; } if (validateContinuously && onBlurValidator) { return onBlurValidator; } return undefined; }, [onChangeValidatorProp, validateContinuously, onBlurValidator]); const onChangeValidatorRef = (0, _react.useRef)(onChangeValidator); (0, _useUpdateEffect.default)(() => { onChangeValidatorRef.current = onChangeValidator; }, [onChangeValidator]); const onBlurValidatorRef = (0, _react.useRef)(onBlurValidator); (0, _useUpdateEffect.default)(() => { onBlurValidatorRef.current = onBlurValidator; }, [onBlurValidator]); const getAjvInstance = (0, _react.useCallback)(() => { if (hasDataContext) { return getAjvInstanceDataContext === null || getAjvInstanceDataContext === void 0 ? void 0 : getAjvInstanceDataContext(); } return undefined; }, [hasDataContext, getAjvInstanceDataContext]); if (!schemaValidatorRef.current && finalSchema) { if (hasZodSchema) { schemaValidatorRef.current = (0, _index.createZodValidator)(finalSchema); } else { var _getAjvInstance, _getAjvInstance$compi; schemaValidatorRef.current = (_getAjvInstance = getAjvInstance()) === null || _getAjvInstance === void 0 || (_getAjvInstance$compi = _getAjvInstance.compile) === null || _getAjvInstance$compi === void 0 ? void 0 : _getAjvInstance$compi.call(_getAjvInstance, finalSchema); } } (0, _useUpdateEffect.default)(() => { if (finalSchema) { if (hasZodSchema) { schemaValidatorRef.current = (0, _index.createZodValidator)(finalSchema); } else { var _getAjvInstance2, _getAjvInstance2$comp; schemaValidatorRef.current = (_getAjvInstance2 = getAjvInstance()) === null || _getAjvInstance2 === void 0 || (_getAjvInstance2$comp = _getAjvInstance2.compile) === null || _getAjvInstance2$comp === void 0 ? void 0 : _getAjvInstance2$comp.call(_getAjvInstance2, finalSchema); } } else { schemaValidatorRef.current = undefined; } validateValue(); }, [finalSchema, hasZodSchema]); const connectWithPathListenerRef = (0, _react.useRef)(() => { runOnChangeValidator(); runOnBlurValidator(); }); const handleConnectWithPath = (0, _react.useCallback)(path => { setFieldEventListener === null || setFieldEventListener === void 0 || setFieldEventListener(path, 'onPathChange', connectWithPathListenerRef.current); return { getValue: () => getValueByPath(path) }; }, [getValueByPath, setFieldEventListener]); const additionalArgsRef = (0, _react.useRef)({ validators: exportValidators, props, dataContext, getValueByPath, getSourceValue, setFieldEventListener }); additionalArgsRef.current.validators = exportValidators; additionalArgsRef.current.props = props; const additionalArgs = (0, _react.useMemo)(() => { const args = { errorMessages: combinedErrorMessages, ...additionalArgsRef.current, connectWithPath: path => { return handleConnectWithPath(path); }, connectWithItemPath: itemPath => { return handleConnectWithPath(makeIteratePath(itemPath)); } }; return args; }, [combinedErrorMessages, handleConnectWithPath, makeIteratePath]); const callStackRef = (0, _react.useRef)([]); const hasBeenCalledRef = (0, _react.useCallback)(validator => { var _context; const result = callStackRef.current.includes(validator); (0, _push.default)(_context = callStackRef.current).call(_context, validator); return result; }, []); const callValidatorFnAsync = (0, _react.useCallback)(async (validator, value = valueRef.current) => { if (typeof validator !== 'function') { return undefined; } const result = await validator(value, additionalArgs); if (Array.isArray(result)) { const errors = []; for (const validatorOrError of result) { if (validatorOrError instanceof Error) { (0, _push.default)(errors).call(errors, validatorOrError); } else if (!hasBeenCalledRef(validatorOrError)) { const result = await callValidatorFnAsync(validatorOrError, value); if (result instanceof Error) { callStackRef.current = []; return result; } } } if (errors.length > 0) { return new _index.FormError('Error', { errors }); } callStackRef.current = []; } else { return ensureErrorMessageObject(result); } }, [additionalArgs, hasBeenCalledRef, ensureErrorMessageObject, valueRef]); const callValidatorFnSync = (0, _react.useCallback)((validator, value = valueRef.current) => { if (typeof validator !== 'function') { return undefined; } const result = validator(value, additionalArgs); if (Array.isArray(result)) { const hasAsyncValidator = result.some(validator => (0, _isAsync.isAsync)(validator)); if (hasAsyncValidator) { return new Promise(resolve => { callValidatorFnAsync(validator, value).then(result => { resolve(result); }); }); } const errors = []; for (const validatorOrError of result) { if (validatorOrError instanceof Error) { (0, _push.default)(errors).call(errors, validatorOrError); } else if (!hasBeenCalledRef(validatorOrError)) { const result = callValidatorFnSync(validatorOrError, value); if (result instanceof Error) { callStackRef.current = []; return result; } } } if (errors.length > 0) { return new _index.FormError('Error', { errors }); } callStackRef.current = []; } else { return ensureErrorMessageObject(result); } }, [additionalArgs, callValidatorFnAsync, hasBeenCalledRef, ensureErrorMessageObject, valueRef]); const validatorCacheRef = (0, _react.useRef)({ onChangeValidator: null, onBlurValidator: null }); const revealOnChangeValidatorResult = (0, _react.useCallback)(({ result, unchangedValue }) => { const runAsync = (0, _isAsync.isAsync)(onChangeValidatorRef.current); if (unchangedValue) { persistErrorState(runAsync ? 'gracefully' : 'weak', 'onChangeValidator', result); if (validateInitially && !changedRef.current || validateUnchanged || validateContinuously || runAsync) { window.requestAnimationFrame(() => { if (localErrorInitiatorRef.current === 'onChangeValidator') { revealError(); forceUpdate(); } }); } } if (runAsync) { defineAsyncProcess(undefined); if (unchangedValue) { setFieldState(result instanceof Error ? 'error' : 'complete'); } else { setFieldState('pending'); } } }, [validateContinuously, defineAsyncProcess, persistErrorState, revealError, setFieldState, validateInitially, validateUnchanged, changedRef, localErrorInitiatorRef]); const callOnChangeValidator = (0, _react.useCallback)(async () => { if (typeof onChangeValidatorRef.current !== 'function') { return {}; } const tmpValue = valueRef.current; let result = (0, _isAsync.isAsync)(onChangeValidatorRef.current) ? await callValidatorFnAsync(onChangeValidatorRef.current) : callValidatorFnSync(onChangeValidatorRef.current); if (result instanceof Promise) { result = await result; } const unchangedValue = tmpValue === valueRef.current; return { result, unchangedValue }; }, [callValidatorFnAsync, callValidatorFnSync, valueRef]); const startOnChangeValidatorValidation = (0, _react.useCallback)(async () => { if (typeof onChangeValidatorRef.current !== 'function') { return undefined; } if ((0, _isAsync.isAsync)(onChangeValidatorRef.current)) { defineAsyncProcess('onChangeValidator'); setFieldState('validating'); hideError(); } const tmpValue = valueRef.current; let result = (0, _isAsync.isAsync)(onChangeValidatorRef.current) ? await callValidatorFnAsync(onChangeValidatorRef.current) : callValidatorFnSync(onChangeValidatorRef.current); if (result instanceof Promise) { result = await result; } const unchangedValue = tmpValue === valueRef.current; revealOnChangeValidatorResult({ result, unchangedValue }); return { result }; }, [callValidatorFnAsync, callValidatorFnSync, defineAsyncProcess, hideError, revealOnChangeValidatorResult, setFieldState, valueRef]); const runOnChangeValidator = (0, _react.useCallback)(async () => { if (!onChangeValidatorRef.current) { return undefined; } const { result, unchangedValue } = await callOnChangeValidator(); if (String(result) !== String(validatorCacheRef.current.onChangeValidator)) { if (result) { revealOnChangeValidatorResult({ result, unchangedValue }); } else { hideError(); clearErrorState(); } } validatorCacheRef.current.onChangeValidator = result || null; }, [callOnChangeValidator, clearErrorState, hideError, revealOnChangeValidatorResult]); const callOnBlurValidator = (0, _react.useCallback)(async ({ overrideValue = null } = {}) => { if (typeof onBlurValidatorRef.current !== 'function') { return {}; } const value = transformers.current.toEvent(overrideValue !== null && overrideValue !== void 0 ? overrideValue : valueRef.current, 'onBlurValidator'); let result = (0, _isAsync.isAsync)(onBlurValidatorRef.current) ? await callValidatorFnAsync(onBlurValidatorRef.current, value) : callValidatorFnSync(onBlurValidatorRef.current, value); if (result instanceof Promise) { result = await result; } return { result }; }, [callValidatorFnAsync, callValidatorFnSync, transformers, valueRef]); const revealOnBlurValidatorResult = (0, _react.useCallback)(({ result }) => { persistErrorState('gracefully', 'onBlurValidator', result); if ((0, _isAsync.isAsync)(onBlurValidatorRef.current)) { defineAsyncProcess(undefined); setFieldState(result instanceof Error ? 'error' : 'complete'); } revealError(); }, [defineAsyncProcess, persistErrorState, revealError, setFieldState]); const startOnBlurValidatorProcess = (0, _react.useCallback)(async ({ overrideValue = null } = {}) => { if (typeof onBlurValidatorRef.current !== 'function') { return undefined; } if ((localErrorInitiatorRef.current === 'required' || localErrorInitiatorRef.current === 'schema') && !asyncBehaviorIsEnabled && !(0, _isAsync.isAsync)(onChangeValidatorRef.current)) { return undefined; } if ((0, _isAsync.isAsync)(onBlurValidatorRef.current)) { defineAsyncProcess('onBlurValidator'); setFieldState('validating'); } const value = transformers.current.toEvent(overrideValue !== null && overrideValue !== void 0 ? overrideValue : valueRef.current, 'onBlurValidator'); let result = (0, _isAsync.isAsync)(onBlurValidatorRef.current) ? await callValidatorFnAsync(onBlurValidatorRef.current, value) : callValidatorFnSync(onBlurValidatorRef.current, value); if (result instanceof Promise) { result = await result; } revealOnBlurValidatorResult({ result }); return { result }; }, [asyncBehaviorIsEnabled, callValidatorFnAsync, callValidatorFnSync, defineAsyncProcess, revealOnBlurValidatorResult, setFieldState, localErrorInitiatorRef, transformers, valueRef]); const runOnBlurValidator = (0, _react.useCallback)(async () => { if (!onBlurValidatorRef.current) { return undefined; } const { result } = await callOnBlurValidator(); if (String(result) !== String(validatorCacheRef.current.onBlurValidator) && revealErrorRef.current) { if (result) { revealOnBlurValidatorResult({ result }); } else { hideError(); clearErrorState(); } } validatorCacheRef.current.onBlurValidator = result || null; }, [callOnBlurValidator, clearErrorState, hideError, revealOnBlurValidatorResult]); const prioritizeContextSchema = (0, _react.useMemo)(() => { if (errorPrioritization) { const contextSchema = dataContextSchema; if ((0, _index.isZodSchema)(contextSchema)) { return (errorPrioritization === null || errorPrioritization === void 0 ? void 0 : errorPrioritization.indexOf('contextSchema')) === 0; } const schemaPath = identifier.split('/').join('/properties/'); const hasContextSchema = _index2.default.has(contextSchema || {}, schemaPath); return hasContextSchema && (errorPrioritization === null || errorPrioritization === void 0 ? void 0 : errorPrioritization.indexOf('contextSchema')) === 0; } return undefined; }, [dataContextSchema, errorPrioritization, identifier]); const prioritizeSectionSchema = (0, _react.useMemo)(() => { return (errorPrioritization === null || errorPrioritization === void 0 ? void 0 : errorPrioritization.indexOf('sectionSchema')) === 0 && hasSectionSchema; }, [errorPrioritization, hasSectionSchema]); const validateValue = (0, _react.useCallback)(async () => { const isProcessActive = startProcess(); if (disabled) { if (isProcessActive()) { clearErrorState(); } hideError(); setFieldState(undefined); return undefined; } const value = valueRef.current; changeEventResultRef.current = null; validatedValueRef.current = null; let initiator = null; try { const requiredError = transformers.current.validateRequired(value, { emptyValue, required, isChanged: changedRef.current, error: new _index.FormError('Field.errorRequired') }); if (requiredError instanceof Error) { initiator = 'required'; throw requiredError; } if (error instanceof Error) { initiator = 'errorProp'; throw error; } const skipLocalSchema = prioritizeContextSchema || prioritizeSectionSchema; if (value !== undefined && !skipLocalSchema && typeof schemaValidatorRef.current === 'function') { const validationResult = schemaValidatorRef.current(value); if (validationResult !== true) { let error; if (hasZodSchema) { const zodError = validationResult; error = (0, _index.zodErrorsToOneFormError)(zodError.issues); } else { var _getAjvInstance3; error = (_getAjvInstance3 = getAjvInstance()) === null || _getAjvInstance3 === void 0 ? void 0 : _getAjvInstance3.ajvErrorsToOneFormError(schemaValidatorRef.current.errors, value); } initiator = 'schema'; throw error; } } if (onChangeValidatorRef.current && (changedRef.current || validateInitially || validateUnchanged)) { const { result } = await startOnChangeValidatorValidation(); if (result instanceof Error) { initiator = 'onChangeValidator'; throw result; } } if (onBlurValidatorRef.current && validateInitially && !changedRef.current) { const { result } = await startOnBlurValidatorProcess(); if (result instanceof Error) { initiator = 'onBlurValidator'; throw result; } } if (isProcessActive()) { clearErrorState(); } validatedValueRef.current = value; } catch (error) { if (isProcessActive()) { persistErrorState('weak', initiator, error); if (validateContinuously && changedRef.current) { revealError(); } } } }, [clearErrorState, disabled, emptyValue, error, hasZodSchema, hideError, persistErrorState, prioritizeContextSchema, prioritizeSectionSchema, required, revealError, setFieldState, startOnBlurValidatorProcess, startOnChangeValidatorValidation, startProcess, validateInitially, validateContinuously, validateUnchanged, valueRef, changedRef, changeEventResultRef, validatedValueRef, transformers, schemaValidatorRef]); connectWithPathListenerRef.current = () => { runOnChangeValidator(); runOnBlurValidator(); }; return { validateValue, startOnChangeValidatorValidation, startOnBlurValidatorProcess, runOnChangeValidator, runOnBlurValidator, callOnBlurValidator, handleConnectWithPath, onChangeValidator, onChangeValidatorRef, onBlurValidatorRef, additionalArgs }; } //# sourceMappingURL=useFieldValidation.js.map