UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

1,062 lines (1,061 loc) 38 kB
"use client"; import { useRef, useEffect, useContext, useCallback, useMemo, useReducer } from 'react'; import pointer from "../utils/json-pointer/index.js"; import { isZodSchema } from "../utils/index.js"; import { Context as DataContext } from "../DataContext/index.js"; import { clearedData } from "../DataContext/Provider/Provider.js"; import FieldProviderContext from "../Field/Provider/FieldProviderContext.js"; import { combineDescribedBy, warn } from "../../../shared/component-helper.js"; import useId from "../../../shared/helpers/useId.js"; import useUpdateEffect from "../../../shared/helpers/useUpdateEffect.js"; import FieldBlockContext from "../FieldBlock/FieldBlockContext.js"; import IterateItemContext from "../Iterate/IterateItemContext.js"; import SectionContext from "../Form/Section/SectionContext.js"; import FieldBoundaryContext from "../DataContext/FieldBoundary/FieldBoundaryContext.js"; import VisibilityContext from "../Form/Visibility/VisibilityContext.js"; import WizardContext from "../Wizard/Context/index.js"; import WizardStepContext from "../Wizard/Step/StepContext.js"; import SnapshotContext from "../Form/Snapshot/SnapshotContext.js"; import usePath from "./usePath.js"; import SharedContext from "../../../shared/Context.js"; import { createReferenceKey, createSharedState, useSharedState } from "../../../shared/helpers/useSharedState.js"; import { isAsync } from "../../../shared/helpers/isAsync.js"; import useTranslation from "./useTranslation.js"; import useExternalValue from "./useExternalValue.js"; import useDataValue from "./useDataValue.js"; import useFieldTransform from "./useFieldTransform.js"; import useFieldError, { resolveValidatingState } from "./useFieldError.js"; import useFieldAsync from "./useFieldAsync.js"; import useFieldValidation from "./useFieldValidation.js"; import { useIsomorphicLayoutEffect as useLayoutEffect } from "../../../shared/helpers/useIsomorphicLayoutEffect.js"; export default function useFieldProps(localProps, { executeOnChangeRegardlessOfError = false, executeOnChangeRegardlessOfUnchangedValue = false, updateContextDataInSync = false, omitMultiplePathWarning = false, forceUpdateWhenContextDataIsSet = false, omitSectionPath = false } = {}) { var _dataContext$props$lo, _props$autoComplete; const { extend } = useContext(FieldProviderContext); const props = extend(localProps); const { path: pathProp, value: valueProp, defaultValue, itemPath, emptyValue, required: requiredProp, disabled: disabledProp, readOnly, info: infoProp, warning: warningProp, error: initialErrorProp = 'initial', errorMessages, onStatusChange, onFocus, onBlur, onChange, onBlurValidator, onChangeValidator: onChangeValidatorProp, exportValidators, schema, validateInitially, validateUnchanged, validateContinuously, transformIn = external => external, transformOut = internal => internal, toInput = value => value, fromInput = value => value, toEvent = value => value, transformValue = value => value, provideAdditionalArgs = (value, additionalArgs) => additionalArgs, fromExternal = value => value, validateRequired = (value, { emptyValue, required, error }) => { if (required && (value === emptyValue || typeof emptyValue === 'undefined' && value === '')) { return error; } return undefined; } } = props; const [salt, forceUpdate] = useReducer(() => ({}), {}); const isInternalRerenderRef = useRef(undefined); useMemo(() => { isInternalRerenderRef.current = salt; }, [salt]); const id = useId(props.id); const dataContext = useContext(DataContext); const { locale: sharedLocale } = useContext(SharedContext) || {}; const fieldBlockContext = useContext(FieldBlockContext); const iterateItemContext = useContext(IterateItemContext); const sectionContext = useContext(SectionContext); const fieldBoundaryContext = useContext(FieldBoundaryContext); const wizardContext = useContext(WizardContext); const wizardStepContext = useContext(WizardStepContext); const { setMountedField: setMountedFieldSnapshot } = useContext(SnapshotContext) || {}; const { isVisible, keepInDOM } = useContext(VisibilityContext) || {}; const handleFieldAsVisible = isVisible || keepInDOM; const { getValueByPath, getSourceValue } = useDataValue(); const translation = useTranslation(); const { formatMessage } = translation; const translationRef = useRef(translation); translationRef.current = translation; const { handlePathChangeUnvalidated: handlePathChangeUnvalidatedDataContext, handlePathChange: handlePathChangeDataContext, updateDataValue: updateDataValueDataContext, validateData: validateDataDataContext, setFieldState: setFieldStateDataContext, setFieldError: setFieldErrorDataContext, setFieldInternals: setFieldInternalsDataContext, setFieldConnection: setFieldConnectionDataContext, revealError: revealErrorDataContext, setMountedFieldState: setMountedFieldStateDataContext, getAjvInstance: getAjvInstanceDataContext, setFieldEventListener, errors: dataContextErrors, showAllErrors, contextErrorMessages, fieldDisplayValueRef, existingFieldsRef, fieldInternalsRef, mountedFieldsRef, sectionSchemaPathsRef, prerenderFieldProps, hasContext: hasDataContext } = dataContext || {}; const onChangeContext = dataContext?.props?.onChange; const locale = (_dataContext$props$lo = dataContext?.props?.locale) !== null && _dataContext$props$lo !== void 0 ? _dataContext$props$lo : sharedLocale; const disabled = disabledProp !== null && disabledProp !== void 0 ? disabledProp : readOnly; const inFieldBlock = Boolean(fieldBlockContext && fieldBlockContext.disableStatusSummary !== true); const resolvedSchema = useMemo(() => { const s = schema; if (typeof s === 'function') { try { return s(props); } catch (_) { return undefined; } } return s; }, [schema]); const finalSchema = useMemo(() => { const s = resolvedSchema; if (typeof s === 'function') { try { return s(props); } catch (_) { return undefined; } } return s; }, [resolvedSchema]); const hasZodSchema = isZodSchema(finalSchema); const { setBlockRecord, setFieldState: setFieldStateFieldBlock, showFieldError: showFieldErrorFieldBlock, mountedFieldsRef: mountedFieldsRefFieldBlock } = inFieldBlock ? fieldBlockContext : {}; const { activeIndex, activeIndexRef, setFieldError: setFieldErrorWizard } = wizardContext || {}; const { index: wizardIndex } = wizardStepContext || {}; const { handleChange: handleChangeIterateContext, index: iterateIndex, arrayValue: iterateArrayValue, nestedIteratePath } = iterateItemContext || {}; const { path: sectionPath, errorPrioritization } = sectionContext || {}; const { setFieldError: setFieldErrorBoundary, revealError: revealErrorBoundary, showBoundaryErrors } = fieldBoundaryContext || {}; const hasPath = Boolean(pathProp); const isParentRelativePath = typeof pathProp === 'string' && pathProp.startsWith('../'); const hasItemPath = Boolean(itemPath); const { path, identifier, makeIteratePath, joinPath, cleanPath } = usePath({ id, path: pathProp, itemPath, omitSectionPath }); const sectionSchemaPaths = sectionSchemaPathsRef?.current; const hasSectionSchema = Boolean(sectionSchemaPaths?.size && identifier && Array.from(sectionSchemaPaths).some(sectionSchemaPath => { if (sectionSchemaPath === '/') { return true; } return identifier === sectionSchemaPath || identifier.startsWith(`${sectionSchemaPath}/`); })); const defaultValueRef = useRef(defaultValue); useLayoutEffect(() => { defaultValueRef.current = defaultValue; }, []); const valueRef = useRef(undefined); const changedRef = useRef(undefined); const hasFocusRef = useRef(undefined); const { transformers, getEventArgs } = useFieldTransform({ transformIn: transformIn, transformOut, toInput: toInput, fromInput, toEvent, transformValue, provideAdditionalArgs, fromExternal, validateRequired, valueRef }); const tmpValue = useExternalValue({ path: identifier, itemPath, value: valueProp, transformers, emptyValue: defaultValue ? undefined : emptyValue }); const externalValueDeps = tmpValue; const externalValue = transformers.current.transformIn(tmpValue !== null && tmpValue !== void 0 ? tmpValue : defaultValueRef.current); const valueInitializedRef = useRef(false); if (!valueInitializedRef.current) { valueInitializedRef.current = true; valueRef.current = externalValue; } useEffect(() => { if (finalSchema && !hasZodSchema && !omitMultiplePathWarning && hasDataContext && !dataContext?.props?.ajvInstance) { warn(`Field${identifier ? ` (${identifier})` : ''} received a JSON Schema but no ajvInstance was provided to Form.Handler. Provide "ajvInstance" on Form.Handler.`); } }, [finalSchema, hasZodSchema, identifier, hasDataContext, dataContext?.props?.ajvInstance, omitMultiplePathWarning]); const required = useMemo(() => { if (typeof requiredProp !== 'undefined') { return requiredProp; } if (schema || dataContext?.schema) { const paths = identifier.split('/'); if (paths.length > 0) { const requiredInSchema = [schema?.['required']]; if (paths.length > 1) { const schema = dataContext.schema; const pathWithoutLast = paths.slice(0, -1).join('/properties/'); const schemaPart = pointer.has(schema, pathWithoutLast) ? pointer.get(schema, pathWithoutLast) : schema; const requiredSchemaList = schemaPart?.['required']; if (Array.isArray(requiredSchemaList)) { const rootPath = pathWithoutLast.replace(/properties\//g, ''); const requiredList = requiredSchemaList.map(path => { path = cleanPath('/' + path); return sectionPath && path.startsWith(sectionPath) ? path : joinPath([sectionPath || rootPath, path]); }); requiredInSchema.push(requiredList); } } const collected = requiredInSchema.flatMap(value => value).filter(Boolean); if (collected.filter(Boolean).some(path => { path = cleanPath('/' + path); return identifier === path || sectionPath === path; })) { return true; } } } return undefined; }, [cleanPath, dataContext.schema, identifier, joinPath, requiredProp, schema, sectionPath]); const getFieldByPath = useCallback(path => { return fieldInternalsRef.current?.[path] || { props: undefined, id: undefined }; }, [fieldInternalsRef]); const schemaValidatorRef = useRef(undefined); const { error, warning, info, combinedErrorMessages, bufferedError, bufferedErrorRef, errorIsVisible: errorIsVisibleBase, ensureErrorMessageObject, prepareError, persistErrorState, clearErrorState, revealError, hideError, setFieldState, hasError, handleError, revealErrorRef, localErrorRef, localErrorInitiatorRef, contextErrorRef, fieldStateRef, warningRef, infoRef } = useFieldError({ initialErrorProp, warningProp, infoProp, errorMessages, validateInitially, validateContinuously, disabled, identifier, locale, handleFieldAsVisible, inFieldBlock, prerenderFieldProps, updateContextDataInSync, hasZodSchema, setFieldStateDataContext, setFieldStateFieldBlock, setFieldErrorDataContext, setFieldErrorBoundary, setFieldErrorWizard, setBlockRecord, showFieldErrorFieldBlock, revealErrorDataContext, revealErrorBoundary, wizardIndex, dataContextErrors, contextErrorMessages: contextErrorMessages, valueRef, hasFocusRef, isInternalRerenderRef, schemaValidatorRef, translationRef, formatMessage, getFieldByPath, getValueByPath, forceUpdate }); const errorIsVisible = errorIsVisibleBase || inFieldBlock && fieldBlockContext.hasErrorProp; const removeErrorRef = useRef(() => {}); const { asyncBehaviorIsEnabled, defineAsyncProcess, addToPool, runPool, yieldAsyncProcess, setEventResult, callOnChangeContext, asyncProcessRef, validatedValueRef, changeEventResultRef } = useFieldAsync({ onChange, onChangeContext, valueRef, forceUpdate, persistErrorState, revealError, setFieldState, hasError, warningRef, infoRef, fieldStateRef, removeErrorRef, hasPath, identifier, executeOnChangeRegardlessOfError, handlePathChangeDataContext: handlePathChangeDataContext }); const { validateValue, startOnChangeValidatorValidation, startOnBlurValidatorProcess, onChangeValidatorRef, onBlurValidatorRef, additionalArgs } = 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: dataContext?.schema, valueRef, changedRef, transformers, schemaValidatorRef, asyncProcessRef, validatedValueRef, changeEventResultRef, localErrorInitiatorRef, error, persistErrorState, clearErrorState, revealError, hideError, setFieldState, ensureErrorMessageObject, asyncBehaviorIsEnabled, defineAsyncProcess, forceUpdate, revealErrorRef }); const valueEqualsEmptyValue = useCallback(value => { if (value === emptyValue) { return true; } if (value instanceof Date && emptyValue instanceof Date) { return value.getTime() === emptyValue.getTime(); } return false; }, [emptyValue]); const setChanged = useCallback(state => { changedRef.current = state; }, []); const removeError = useCallback(() => { setChanged(false); hideError(); clearErrorState(); validateValue(); }, [clearErrorState, hideError, setChanged, validateValue]); removeErrorRef.current = removeError; const externalValueDidChangeRef = useRef(false); useLayoutEffect(() => { if (valueRef.current !== externalValue) { valueRef.current = externalValue; externalValueDidChangeRef.current = true; } }, [externalValueDeps, hasItemPath]); useUpdateEffect(() => { if (externalValueDidChangeRef.current) { externalValueDidChangeRef.current = false; if (!validateContinuously && valueEqualsEmptyValue(valueRef.current)) { hideError(); } validateValue(); forceUpdate(); } }, [externalValueDeps, validateContinuously, valueEqualsEmptyValue]); const setHasFocus = useCallback(async (hasFocus, overrideValue, localAdditionalArgs) => { const args = getEventArgs({ eventName: hasFocus ? 'onFocus' : 'onBlur', overrideValue, additionalArgs: localAdditionalArgs ? { ...additionalArgs, ...localAdditionalArgs } : additionalArgs }); if (hasFocus) { hasFocusRef.current = true; onFocus?.(...args); setMountedFieldStateDataContext(identifier, { isFocused: true }); } else { hasFocusRef.current = false; onBlur?.(...args); setMountedFieldStateDataContext(identifier, { isFocused: false }); if (!changedRef.current && !validateUnchanged) { return undefined; } addToPool('onBlurValidator', async () => await startOnBlurValidatorProcess({ overrideValue }), isAsync(onBlurValidatorRef.current)); await runPool(() => { revealError(); forceUpdate(); }); } }, [getEventArgs, additionalArgs, onFocus, setMountedFieldStateDataContext, identifier, onBlur, validateUnchanged, addToPool, onBlurValidatorRef, runPool, startOnBlurValidatorProcess, revealError]); const updateValue = useCallback(async newValue => { var _transformers$current; const currentValue = valueRef.current; const valueIsUnchanged = newValue === currentValue; if (!executeOnChangeRegardlessOfUnchangedValue && valueIsUnchanged) { return undefined; } const transformedValue = (_transformers$current = transformers.current.transformValue(newValue, currentValue)) !== null && _transformers$current !== void 0 ? _transformers$current : emptyValue; const contextValue = transformers.current.transformOut(transformedValue, transformers.current.provideAdditionalArgs(transformedValue, additionalArgs)); valueRef.current = transformedValue; if (hasPath || itemPath) { handlePathChangeUnvalidatedDataContext(nestedIteratePath || identifier, contextValue); } if (itemPath) { handleChangeIterateContext?.(makeIteratePath(itemPath, '', { omitSectionPath: true }), contextValue); } addToPool('onChangeValidator', validateValue, isAsync(onChangeValidatorRef.current)); addToPool('onChangeContext', callOnChangeContext, isAsync(onChangeContext)); await runPool(() => { handleError(); }); }, [executeOnChangeRegardlessOfUnchangedValue, transformers, emptyValue, additionalArgs, hasPath, itemPath, addToPool, validateValue, onChangeValidatorRef, callOnChangeContext, onChangeContext, runPool, handlePathChangeUnvalidatedDataContext, nestedIteratePath, identifier, handleChangeIterateContext, makeIteratePath, handleError]); const setDisplayValue = useCallback((value, options) => { const { path: fieldPath = itemPath ? identifier : path, type = 'field' } = options || {}; if (!fieldPath || !fieldDisplayValueRef?.current) { return undefined; } fieldDisplayValueRef.current[fieldPath] = valueEqualsEmptyValue(valueRef.current) ? { type } : { value, type }; }, [identifier, fieldDisplayValueRef, itemPath, path, valueEqualsEmptyValue]); const handleChange = useCallback(async (argFromInput, localAdditionalArgs = undefined) => { const currentValue = valueRef.current; const fromInput = transformers.current.fromInput(argFromInput); const valueIsUnchanged = fromInput === currentValue; if (!executeOnChangeRegardlessOfUnchangedValue && valueIsUnchanged) { return undefined; } setChanged(true); if (asyncBehaviorIsEnabled) { hideError(); await updateValue(fromInput); } else { updateValue(fromInput); } if (isAsync(onChange)) { addToPool('onChangeLocal', async () => { const args = getEventArgs({ eventName: 'onChange', additionalArgs: localAdditionalArgs ? { ...additionalArgs, ...localAdditionalArgs } : additionalArgs }); await yieldAsyncProcess({ name: 'onChangeLocal', waitFor: [{ processName: 'onChangeValidator', withStates: ['validating', 'error'], hasValue: args[0] }, { processName: 'onBlurValidator', withStates: ['validating', 'error'], hasValue: args[0] }, { processName: 'onChangeContext', withStates: ['pending', 'error'], hasValue: args[0] }] }); defineAsyncProcess('onChangeLocal'); if (!hasError()) { var _await$onChange; await setEventResult((_await$onChange = await onChange?.(...args)) !== null && _await$onChange !== void 0 ? _await$onChange : null); } else { await setEventResult(null); } }, true); } else { var _onChange; const args = getEventArgs({ eventName: 'onChange', additionalArgs: localAdditionalArgs ? { ...additionalArgs, ...localAdditionalArgs } : additionalArgs }); setEventResult((_onChange = onChange?.(...args)) !== null && _onChange !== void 0 ? _onChange : null); } await runPool(); }, [addToPool, additionalArgs, asyncBehaviorIsEnabled, defineAsyncProcess, executeOnChangeRegardlessOfUnchangedValue, getEventArgs, hasError, hideError, onChange, runPool, setChanged, setEventResult, transformers, updateValue, yieldAsyncProcess]); const handleFocus = useCallback(() => setHasFocus(true), [setHasFocus]); const handleBlur = useCallback(() => setHasFocus(false), [setHasFocus]); setFieldInternalsDataContext?.(identifier, { id, props }); const activeIndexTmpRef = useRef(activeIndex); useEffect(() => { activeIndexTmpRef.current = activeIndex; }, [activeIndex]); useMemo(() => { setMountedFieldStateDataContext(identifier, { isPreMounted: true }); if (typeof isVisible === 'boolean') { setMountedFieldStateDataContext(identifier, { isVisible }); } }, [setMountedFieldStateDataContext, identifier, isVisible]); useEffect(() => { if (prerenderFieldProps) { return undefined; } if (typeof activeIndexRef?.current === 'number') { setMountedFieldStateDataContext(identifier, { wasStepChange: false }); return () => { const wasStepChange = typeof activeIndex === 'number' && activeIndexRef.current !== activeIndexTmpRef.current; setMountedFieldStateDataContext(identifier, { wasStepChange }); }; } return undefined; }, [activeIndex, activeIndexRef, identifier, prerenderFieldProps, setMountedFieldStateDataContext]); useEffect(() => { if (prerenderFieldProps) { return undefined; } setMountedFieldStateDataContext(identifier, { isMounted: true, isPreMounted: true }); setMountedFieldSnapshot?.(identifier, { isMounted: true }); return () => { setMountedFieldStateDataContext(identifier, { isMounted: false, isPreMounted: false }); setMountedFieldSnapshot?.(identifier, { isMounted: false }); }; }, [identifier, prerenderFieldProps, setMountedFieldSnapshot, setMountedFieldStateDataContext]); useEffect(() => { if (!omitMultiplePathWarning && !isParentRelativePath && process.env.NODE_ENV !== 'production' && (hasPath || hasItemPath) && (hasPath ? !iterateItemContext : true) && existingFieldsRef?.current) { const existingFields = existingFieldsRef.current; if (existingFields.has(identifier)) { warn('Path declared multiple times:', identifier); } else { existingFields.set(identifier, true); return () => { existingFields.delete(identifier); }; } } return undefined; }, [existingFieldsRef, hasItemPath, hasPath, identifier, iterateItemContext, isParentRelativePath, omitMultiplePathWarning]); useEffect(() => { if (prerenderFieldProps) { return undefined; } const mountedFields = mountedFieldsRef?.current; return () => { Promise.resolve().then(() => { const isMounted = mountedFields?.get?.(identifier)?.isMounted === true; const sharedAttachments = dataContext?.id ? createSharedState(createReferenceKey(dataContext.id, 'attachments')).get?.() : undefined; const hasFieldConnection = Boolean(sharedAttachments?.fieldConnectionsRef?.current?.[identifier]); if (!isMounted && !hasFieldConnection) { setFieldErrorDataContext?.(identifier, undefined); setFieldErrorBoundary?.(identifier, undefined); } }); localErrorRef.current = undefined; }; }, [identifier, dataContext.id, mountedFieldsRef, prerenderFieldProps, setFieldErrorBoundary, setFieldErrorDataContext, localErrorRef]); useEffect(() => { if (prerenderFieldProps) { return undefined; } return () => { setFieldErrorWizard?.(wizardIndex, identifier, undefined); }; }, [identifier, prerenderFieldProps, setFieldErrorWizard, wizardIndex]); useEffect(() => { validateValue(); }, [validateValue]); const previousLocaleRef = useRef(locale); useUpdateEffect(() => { if (previousLocaleRef.current !== locale) { previousLocaleRef.current = locale; const hasValidationError = hasError() || fieldStateRef.current === 'error'; const hasVisibleError = revealErrorRef.current === true; const shouldRevalidateOnLocaleChange = changedRef.current || hasValidationError || hasVisibleError || validateInitially || validateUnchanged; if (prerenderFieldProps || valueEqualsEmptyValue(valueRef.current) && !shouldRevalidateOnLocaleChange) { return undefined; } if (onBlurValidatorRef.current && shouldRevalidateOnLocaleChange) { addToPool('onBlurValidator', async () => await startOnBlurValidatorProcess(), isAsync(onBlurValidatorRef.current)); runPool(() => { revealError(); forceUpdate(); }); return undefined; } } }, [addToPool, forceUpdate, hasError, locale, prerenderFieldProps, revealError, runPool, startOnBlurValidatorProcess, validateInitially, validateUnchanged, valueEqualsEmptyValue]); useEffect(() => { if (prerenderFieldProps || !dataContext?.id) { return undefined; } const sharedAttachments = createSharedState(createReferenceKey(dataContext.id, 'attachments')).get?.(); const status = sharedAttachments?.fieldStatusRef?.current?.[identifier]; if (status) { void setEventResult(status); } }, [dataContext?.id, identifier, locale, prerenderFieldProps, setEventResult]); useEffect(() => { if (!localErrorInitiatorRef.current) { const error = contextErrorRef.current; if (error) { persistErrorState('weak', 'dataContextError', error); if (validateInitially) { handleError(); } } else { clearErrorState(); } } }, [dataContextErrors, clearErrorState, handleError, persistErrorState, prepareError, validateInitially, localErrorInitiatorRef, contextErrorRef]); const internalData = dataContext.internalDataRef?.current; const tmpTransValueRef = useRef({ itemPath: id }); const setContextData = useCallback(({ preventUpdate = undefined } = {}) => { if (!hasPath && !hasItemPath) { return undefined; } let valueToStore = valueProp; const storePath = nestedIteratePath ? makeIteratePath(itemPath, nestedIteratePath) : identifier; const hasValue = pointer.has(internalData, storePath) || storePath === '/'; const existingValue = storePath === '/' ? internalData : hasValue ? pointer.get(internalData, storePath) : undefined; if (dataContext.id && !hasValue && typeof existingValue === 'undefined' && typeof valueToStore === 'undefined') { const sharedState = createSharedState(dataContext.id); const hasValue = pointer.has(sharedState.data, storePath); if (hasValue) { const sharedValue = pointer.get(sharedState.data, storePath); if (sharedValue) { valueToStore = sharedValue; } } } const hasDefaultValue = typeof defaultValueRef.current !== 'undefined' && typeof valueToStore === 'undefined'; if (hasDefaultValue) { valueToStore = defaultValueRef.current; defaultValueRef.current = undefined; } else if (!hasValue && typeof valueToStore === 'undefined') { valueToStore = emptyValue; } let skipEqualCheck = false; if (hasItemPath) { if (existingValue === valueToStore) { if (hasValue) { return; } else { if (tmpTransValueRef.current['itemPath'] === valueToStore) { return; } } } tmpTransValueRef.current['itemPath'] = valueToStore; if (iterateArrayValue === clearedArray) { return; } if (typeof valueToStore === 'undefined' && typeof existingValue !== 'undefined') { valueToStore = existingValue; } if (itemPath === '/') { if (existingValue === clearedData) { valueRef.current = undefined; } if (hasDefaultValue && Array.isArray(existingValue)) { skipEqualCheck = true; } } } if (updateContextDataInSync) { if (hasDefaultValue && hasValue) { return; } if (Array.isArray(existingValue) && Array.isArray(valueToStore)) { if (valueToStore.length !== existingValue.length) { skipEqualCheck = true; } valueRef.current = existingValue; } } if (typeof valueToStore === 'undefined' && typeof existingValue !== 'undefined') { valueToStore = existingValue; } if (!skipEqualCheck && hasValue && (valueToStore === existingValue || valueRef.current === existingValue)) { return; } if (storePath in tmpTransValueRef.current && tmpTransValueRef.current[storePath] === valueToStore) { return; } const valueIn = transformers.current.transformIn(valueToStore); const transformedValue = transformers.current.transformOut(valueIn, transformers.current.provideAdditionalArgs(valueIn)); if (transformedValue !== valueToStore) { tmpTransValueRef.current[storePath] = valueToStore; valueToStore = transformedValue; } if (hasItemPath && !nestedIteratePath && iterateIndex < iterateArrayValue?.length - 1) { preventUpdate = true; } updateDataValueDataContext?.(storePath, valueToStore, { preventUpdate }); if (!preventUpdate) { validateDataDataContext?.(); } }, [dataContext.id, emptyValue, hasItemPath, hasPath, identifier, internalData, itemPath, iterateArrayValue, iterateIndex, makeIteratePath, nestedIteratePath, transformers, updateContextDataInSync, updateDataValueDataContext, validateDataDataContext, valueProp]); const isEmptyData = useCallback(() => { var _dataContext$props$em; return dataContext.isEmptyDataRef?.current || dataContext.internalDataRef?.current === ((_dataContext$props$em = dataContext.props?.emptyData) !== null && _dataContext$props$em !== void 0 ? _dataContext$props$em : clearedData); }, [dataContext.internalDataRef, dataContext.props?.emptyData, externalValueDeps]); useLayoutEffect(() => { if (isEmptyData()) { defaultValueRef.current = defaultValue; setChanged(false); hideError(); clearErrorState(); } }, [clearErrorState, defaultValue, hideError, isEmptyData, setChanged]); useMemo(() => { if (updateContextDataInSync && !isEmptyData()) { setContextData({ preventUpdate: true }); } }, [isEmptyData, updateContextDataInSync, setContextData]); useLayoutEffect(() => { if (!updateContextDataInSync && !isEmptyData()) { setContextData(); } if (forceUpdateWhenContextDataIsSet) { forceUpdate(); } }, [forceUpdateWhenContextDataIsSet, isEmptyData, setContextData, updateContextDataInSync]); useEffect(() => { if (isEmptyData()) { setContextData(); validateValue(); } }, [isEmptyData, setContextData, validateValue]); useEffect(() => { if (showAllErrors || showBoundaryErrors) { if (fieldStateRef.current !== 'validating') { revealError(); } } else if (showBoundaryErrors === false) { hideError(); } }, [fieldStateRef, hideError, revealError, showAllErrors, showBoundaryErrors]); useEffect(() => { if (dataContext.formState === 'pending' && (onChangeValidatorRef.current || onBlurValidatorRef.current)) { hideError(); forceUpdate(); } }, [dataContext.formState, hideError, onBlurValidatorRef, onChangeValidatorRef]); const onSubmitHandler = useCallback(async () => { if (hasError()) { return undefined; } addToPool('onChangeValidator', startOnChangeValidatorValidation, isAsync(onChangeValidatorRef.current)); addToPool('onBlurValidator', startOnBlurValidatorProcess, isAsync(onBlurValidatorRef.current)); await runPool(); }, [hasError, addToPool, startOnChangeValidatorValidation, onChangeValidatorRef, startOnBlurValidatorProcess, onBlurValidatorRef, runPool]); useEffect(() => { setFieldEventListener?.(identifier, 'onSubmitCall', onSubmitHandler); }, [identifier, onSubmitHandler, setFieldEventListener]); useEffect(() => { if (inFieldBlock) { setBlockRecord?.({ identifier, type: 'error', content: error, showInitially: true, show: true }); setBlockRecord?.({ identifier, type: 'warning', content: warning, showInitially: true, show: true }); setBlockRecord?.({ identifier, type: 'information', content: info, showInitially: true, show: true }); return () => { if (mountedFieldsRefFieldBlock) { mountedFieldsRefFieldBlock.current.set(identifier, true); } }; } return undefined; }, [error, identifier, inFieldBlock, info, mountedFieldsRefFieldBlock, setBlockRecord, warning]); const statusRef = useRef(null); useEffect(() => { if (!onStatusChange) { return undefined; } const status = { info: infoRef.current, warning: warningRef.current, error: bufferedErrorRef.current }; const statusVisible = Boolean(status.info) || Boolean(status.warning) || errorIsVisible && Boolean(status.error); const previous = statusRef.current; const hasChanged = !previous || previous.error !== status.error || previous.warning !== status.warning || previous.info !== status.info || previous.validateInitially !== validateInitially; if (hasChanged && (statusVisible || previous)) { statusRef.current = { warning: status.warning, info: status.info, error: status.error, validateInitially }; onStatusChange(status); } }, [onStatusChange, bufferedError, warning, info, validateInitially, errorIsVisible, infoRef, warningRef, bufferedErrorRef]); const connections = useMemo(() => { return { setEventResult, emptyValue }; }, [emptyValue, setEventResult]); setFieldConnectionDataContext?.(identifier, connections); const htmlAttributes = useMemo(() => { return Object.keys(props).reduce((acc, cur) => { if (cur.startsWith('aria-') || cur.startsWith('data-')) { acc[cur] = props[cur]; } return acc; }, { ...props.htmlAttributes }); }, [props]); if (bufferedError) { htmlAttributes['aria-invalid'] = bufferedError ? 'true' : 'false'; } if (required) { htmlAttributes['aria-required'] = 'true'; } if (inFieldBlock) { if (mountedFieldsRefFieldBlock) { mountedFieldsRefFieldBlock.current.set(identifier, true); } const stateIds = fieldBlockContext.fieldStateIdsRef?.current; if (stateIds) { htmlAttributes['aria-describedby'] = combineDescribedBy(htmlAttributes, [bufferedError && stateIds.error, warning && stateIds.warning, info && stateIds.information].filter(Boolean)); } } else { const ids = [(bufferedError || error) && `${id}-form-status--error`, warning && `${id}-form-status--warning`, info && `${id}-form-status--information`].filter(Boolean); if (ids.length) { htmlAttributes['aria-describedby'] = combineDescribedBy(htmlAttributes, ids); } } const help = props.help; if (help?.title || help?.content) { htmlAttributes['aria-describedby'] = combineDescribedBy(htmlAttributes, `${id}-help`); } const fieldBlockProps = { info: !inFieldBlock ? infoRef.current : undefined, warning: !inFieldBlock ? warningRef.current : undefined, error: !inFieldBlock ? bufferedError : undefined, required, label: props.label, labelDescription: props.labelDescription, labelDescriptionInline: props.labelDescriptionInline, labelSuffix: props.labelSuffix, labelSize: props.labelSize, labelSrOnly: props.labelSrOnly, statusPosition: props.statusPosition, layout: props.layout, layoutOptions: props.layoutOptions, help: props.help, disabled: onBlurValidator && asyncProcessRef.current === 'onBlurValidator' && fieldStateRef.current === 'validating' ? true : disabled, fieldState: resolveValidatingState(fieldStateRef.current), labelHeight: typeof props['size'] === 'string' ? props['size'] : undefined }; const sharedData = useSharedState('field-block-props-' + id); sharedData.set(fieldBlockProps); useEffect(() => { isInternalRerenderRef.current = undefined; }); return { ...props, ...fieldBlockProps, name: props.name || props.path?.replace('/', '') || id, autoComplete: (_props$autoComplete = props.autoComplete) !== null && _props$autoComplete !== void 0 ? _props$autoComplete : typeof dataContext.autoComplete === 'boolean' ? dataContext.autoComplete ? 'on' : 'off' : undefined, id, value: transformers.current.toInput(valueRef.current), hasError: errorIsVisible, isChanged: Boolean(changedRef.current), props, htmlAttributes, setHasFocus, handleFocus, handleBlur, handleChange, updateValue, setChanged, setDisplayValue, validateValue, revealError, handleError, forceUpdate, additionalArgs, dataContext }; } export { checkForError } from "./useFieldError.js"; export const clearedArray = []; //# sourceMappingURL=useFieldProps.js.map