UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

1,216 lines (1,215 loc) 44 kB
"use client"; import _JSON$parse from "core-js-pure/stable/json/parse.js"; import _pushInstanceProperty from "core-js-pure/stable/instance/push.js"; import React, { useRef, useMemo, useCallback, useReducer, useEffect, useContext } from 'react'; import pointer from "../../utils/json-pointer/index.js"; import { makeAjvInstance, ajvErrorsToFormErrors, isZodSchema, createZodValidator, zodErrorsToFormErrors } from "../../utils/index.js"; import { debounce, warn } from "../../../../shared/helpers.js"; import FieldPropsProvider from "../../Field/Provider/index.js"; import useUpdateEffect from "../../../../shared/helpers/useUpdateEffect.js"; import GlobalStatusProvider from "../../../../components/global-status/GlobalStatusProvider.js"; import { isAsync } from "../../../../shared/helpers/isAsync.js"; import { createReferenceKey, useSharedState } from "../../../../shared/helpers/useSharedState.js"; import SharedContext from "../../../../shared/Context.js"; import useTranslation from "../../hooks/useTranslation.js"; import { appendPath } from "../../hooks/usePath.js"; import DataContext from "../Context.js"; import { structuredClone } from "../../../../shared/helpers/structuredClone.js"; import { useIsomorphicLayoutEffect as useLayoutEffect } from "../../../../shared/helpers/useIsomorphicLayoutEffect.js"; const isArrayJsonPointer = /^\/\d+(\/|$)/; export default function Provider(props) { var _sharedAttachments$da, _translations$resolve; const [, forceUpdate] = useReducer(() => ({}), {}); const { id, globalStatusId = 'main', defaultData, emptyData, data, schema, onChange, onPathChange, onSubmit, onSubmitRequest, onSubmitComplete, onCommit, onClear, onUpdateDataValue, scrollTopOnSubmit, minimumAsyncBehaviorTime, asyncSubmitTimeout, sessionStorageId, ajvInstance, transformIn, transformOut, filterSubmitData, countryCode, locale, translations, required, errorMessages, isolate, children, ...rest } = props; if (data !== undefined && sessionStorageId !== undefined) { throw new Error('Use "defaultData" instead of "data" when using sessionStorageId'); } const { hasContext } = useContext(DataContext) || {}; if (hasContext && !isolate) { throw new Error('DataContext (Form.Handler) cannot be nested'); } const formElementRef = useRef(null); const { locale: sharedLocale } = useContext(SharedContext) || {}; const translation = useTranslation().Field; const ajvRef = useRef(); const getAjvInstance = useCallback((instance = ajvInstance) => { if (!ajvRef.current) { ajvRef.current = makeAjvInstance(instance); } return ajvRef.current; }, [ajvInstance]); useEffect(() => { if (schema && !isZodSchema(schema) && !ajvInstance) { warn('Form.Handler received a JSON Schema but no ajvInstance. Provide ajvInstance={makeAjvInstance()} to enable schema validation.'); } }, [schema, ajvInstance]); const mountedFieldsRef = useRef(new Map()); const snapshotsRef = useRef(new Map()); const existingFieldsRef = useRef(new Map()); const hasVisibleErrorRef = useRef(new Map()); const errorsRef = useRef(); const addSetShowAllErrorsRef = useRef([]); const showAllErrorsRef = useRef(false); const setShowAllErrors = useCallback(showAllErrors => { showAllErrorsRef.current = showAllErrors ? Date.now() : showAllErrors; forceUpdate(); addSetShowAllErrorsRef.current.forEach(fn => fn === null || fn === void 0 ? void 0 : fn(showAllErrors)); }, []); const executeSectionValidators = useCallback(contextErrors => { if (!sectionSchemasRef.current.size) { const hasContextErrors = contextErrors && Object.keys(contextErrors).length > 0; errorsRef.current = hasContextErrors ? contextErrors : undefined; return errorsRef.current; } const sectionErrors = {}; sectionSchemasRef.current.forEach(({ path, schema, validator }) => { if (!validator) { return; } const normalizedPath = path || '/'; const sectionData = normalizedPath === '/' ? internalDataRef.current : pointer.has(internalDataRef.current, normalizedPath) ? pointer.get(internalDataRef.current, normalizedPath) : undefined; const validationResult = validator(sectionData); if (validationResult === true) { return; } let errors = {}; if (isZodSchema(schema)) { const issues = validator.errors; if (issues !== null && issues !== void 0 && issues.length) { errors = zodErrorsToFormErrors(issues); } } else { const ajvValidator = validator; const ajvErrors = ajvValidator.errors; if (ajvErrors && ajvErrors.length) { errors = ajvErrorsToFormErrors(ajvErrors, sectionData); } } Object.entries(errors).forEach(([errorPath, error]) => { const combinedPath = appendPath(normalizedPath, errorPath); sectionErrors[combinedPath] = error; }); }); const hasSectionErrors = Object.keys(sectionErrors).length > 0; const hasContextErrors = contextErrors && Object.keys(contextErrors).length > 0; if (!hasSectionErrors && !hasContextErrors) { errorsRef.current = undefined; return undefined; } errorsRef.current = { ...(contextErrors !== null && contextErrors !== void 0 ? contextErrors : {}), ...(hasSectionErrors ? sectionErrors : {}) }; return errorsRef.current; }, []); const revealError = useCallback((path, hasError) => { if (hasError) { hasVisibleErrorRef.current.set(path, hasError); } else { hasVisibleErrorRef.current.delete(path); } forceUpdate(); }, []); const submitStateRef = useRef({}); const setSubmitState = useCallback(state => { submitStateRef.current = { ...submitStateRef.current, ...state }; forceUpdate(); }, []); const formStateRef = useRef(); const activeSubmitButtonIdRef = useRef(); const keepPending = useRef(false); const setFormState = useCallback((formState, options = {}) => { if (typeof (options === null || options === void 0 ? void 0 : options.keepPending) === 'boolean') { keepPending.current = options === null || options === void 0 ? void 0 : options.keepPending; } formStateRef.current = formState; forceUpdate(); }, []); const setActiveSubmitButtonId = useCallback(id => { activeSubmitButtonIdRef.current = id; forceUpdate(); }, []); const fieldErrorRef = useRef({}); const fieldStateRef = useRef({}); const validationVersionRef = useRef(0); const bumpValidationVersionRef = useRef(() => null); const initialData = useMemo(() => { if (sessionStorageId && typeof window !== 'undefined') { var _window$sessionStorag; const sessionDataJSON = (_window$sessionStorag = window.sessionStorage) === null || _window$sessionStorag === void 0 ? void 0 : _window$sessionStorag.getItem(sessionStorageId); if (sessionDataJSON) { try { return _JSON$parse(sessionDataJSON); } catch (e) { var _window$sessionStorag2; (_window$sessionStorag2 = window.sessionStorage) === null || _window$sessionStorag2 === void 0 || _window$sessionStorag2.removeItem(sessionStorageId); } } } return data !== null && data !== void 0 ? data : defaultData; }, []); const internalDataRef = useRef(initialData); const isEmptyDataRef = useRef(false); const ajvValidatorRef = useRef(); const sectionSchemasRef = useRef(new Map()); const sectionSchemaPathsRef = useRef(new Set()); const createUnifiedValidator = useCallback(schema => { if (isZodSchema(schema)) { const zodValidator = createZodValidator(schema); const unifiedValidator = value => { const result = zodValidator(value); if (result === true) { return true; } else { unifiedValidator.errors = result.issues; return false; } }; return unifiedValidator; } else { var _getAjvInstance; return (_getAjvInstance = getAjvInstance()) === null || _getAjvInstance === void 0 ? void 0 : _getAjvInstance.compile(schema); } }, []); useMemo(() => { if (schema) { ajvValidatorRef.current = createUnifiedValidator(schema); } }, [schema, createUnifiedValidator]); const executeAjvValidator = useCallback(() => { if (!ajvValidatorRef.current) { errorsRef.current = undefined; return undefined; } const validationResult = ajvValidatorRef.current(internalDataRef.current); if (!validationResult) { const errors = ajvValidatorRef.current.errors; if (errors && Array.isArray(errors)) { if (errors.length > 0 && errors[0] && typeof errors[0] === 'object' && 'code' in errors[0]) { errorsRef.current = zodErrorsToFormErrors(errors); } else { errorsRef.current = ajvErrorsToFormErrors(errors, internalDataRef.current); } return errorsRef.current; } errorsRef.current = undefined; return undefined; } errorsRef.current = undefined; return undefined; }, []); const validateData = useCallback(() => { const contextErrors = executeAjvValidator(); executeSectionValidators(contextErrors); forceUpdate(); }, [executeAjvValidator, executeSectionValidators, forceUpdate]); const checkFieldStateFor = useCallback((path, state) => { var _errorsRef$current; return Boolean(state === 'error' ? ((_errorsRef$current = errorsRef.current) === null || _errorsRef$current === void 0 ? void 0 : _errorsRef$current[path]) instanceof Error || fieldErrorRef.current[path] instanceof Error : fieldStateRef.current[path] === state); }, []); const hasFieldState = useCallback(state => { return Array.from(mountedFieldsRef.current.entries()).some(([path, item]) => { return item.isMounted && checkFieldStateFor(path, state); }); }, [checkFieldStateFor]); const hasFieldError = useCallback(path => { return Array.from(mountedFieldsRef.current.entries()).some(([p, item]) => { return item.isMounted && p === path && checkFieldStateFor(path, 'error'); }); }, [checkFieldStateFor]); const hasErrors = useCallback(() => { return hasFieldState('error'); }, [hasFieldState]); const setFieldError = useCallback((path, error) => { if (error) { fieldErrorRef.current[path] = error; } else { delete fieldErrorRef.current[path]; } bumpValidationVersionRef.current(); for (const item of fieldEventListenersRef.current) { const { type, callback } = item; if (type === 'onSetFieldError') { callback(); } } }, []); const setFieldState = useCallback((path, fieldState) => { if (fieldState !== fieldStateRef.current[path]) { fieldStateRef.current[path] = fieldState; forceUpdate(); bumpValidationVersionRef.current(); } }, []); const getDataPathHandlerParameters = useCallback((path, data = internalDataRef.current) => { var _fieldInternalsRef$cu; const value = pointer.has(data, path) ? pointer.get(data, path) : undefined; const { value: displayValue } = fieldDisplayValueRef.current[path] || {}; const props = ((_fieldInternalsRef$cu = fieldInternalsRef.current[path]) === null || _fieldInternalsRef$cu === void 0 ? void 0 : _fieldInternalsRef$cu.props) || {}; const label = props === null || props === void 0 ? void 0 : props['label']; const error = fieldErrorRef.current[path]; return { path, value, displayValue, label, props, data: internalDataRef.current, error, internal: { error } }; }, []); const mutateDataHandler = useCallback((data, handler, { remove = false, mutate = true, fireHandlerWhen = null } = {}) => { const freshData = {}; const mutateEntry = (path, result) => { if (remove) { if (result === false) { data = structuredClone(data); pointer.remove(data, path); } } else { if (typeof result !== 'undefined') { if (mutate) { data = structuredClone(data); pointer.set(data, path, result); } else { pointer.set(freshData, path, result); } } } }; if (typeof handler === 'function') { const run = path => { const { type } = fieldDisplayValueRef.current[path] || {}; if ((fireHandlerWhen === null || fireHandlerWhen === void 0 ? void 0 : fireHandlerWhen({ type })) !== false) { const result = handler(getDataPathHandlerParameters(path, data)); mutateEntry(path, result); } }; for (const path in fieldInternalsRef.current) { const exists = pointer.has(data, path); if (exists) { run(path); } } pointer.walk(internalDataRef.current, (value, path) => { if (fieldInternalsRef.current[path] === undefined) { run(path); } }); if (!mutate) { return freshData; } return data; } else if (handler) { const runFilter = ({ path, condition }) => { const exists = pointer.has(data, path); if (exists) { const result = typeof condition === 'function' ? condition(getDataPathHandlerParameters(path, data)) : condition; mutateEntry(path, result); } }; const wildcardPaths = []; Object.entries(handler).forEach(([path, condition]) => { if (path.includes('*')) { const parts = path.split(/\/\*/g); const exists = pointer.has(data, parts[0]); if (exists) { const traverse = (subData, subPath, idx) => { if (idx === parts.length - 1) { _pushInstanceProperty(wildcardPaths).call(wildcardPaths, { path: subPath, condition }); return; } const list = pointer.get(subData, subPath); if (Array.isArray(list)) { list.forEach((_, i) => { traverse(list[i], `${subPath}/${i}${parts[idx + 1]}`, idx + 1); }); } }; traverse(data, parts[0], 0); } } else { runFilter({ path, condition }); } }); wildcardPaths.forEach(runFilter); return data; } return data; }, [getDataPathHandlerParameters]); const visibleDataHandler = useCallback((data = internalDataRef.current, { keepPaths, removePaths } = {}) => { const visibleData = {}; mountedFieldsRef.current.forEach((item, path) => { if (item && item.isVisible !== false && (item.isPreMounted !== false || item.wasStepChange === true) && (removePaths ? !removePaths.includes(path) : true) && pointer.has(data, path)) { pointer.set(visibleData, path, pointer.get(data, path)); } }); if (keepPaths) { keepPaths.forEach(path => { if (pointer.has(data, path)) { pointer.set(visibleData, path, pointer.get(data, path)); } }); } return visibleData; }, []); const filterDataHandler = useCallback((data, filter) => { if (filter) { return mutateDataHandler(data, filter, { remove: true }); } return data; }, [mutateDataHandler]); const filterData = useCallback((filter, data = internalDataRef.current) => { return filterDataHandler(data, filter); }, [filterDataHandler]); const fieldDisplayValueRef = useRef({}); const fieldConnectionsRef = useRef({}); const fieldStatusRef = useRef({}); const setFieldConnection = useCallback((path, connections) => { fieldConnectionsRef.current[path] = connections; }, []); const fieldInternalsRef = useRef({}); const setFieldInternals = useCallback((path, internals) => { fieldInternalsRef.current[path] = Object.assign(fieldInternalsRef.current[path] || {}, internals); }, []); const valueInternalsRef = useRef({}); const setValueInternals = useCallback((path, props) => { valueInternalsRef.current[path] = Object.assign(valueInternalsRef.current[path] || {}, { props }); }, []); const hasFieldWithAsyncValidator = useCallback(() => { for (const path in fieldInternalsRef.current) { const fieldInternals = fieldInternalsRef.current[path] || {}; const { enableAsyncMode, props } = fieldInternals; if (enableAsyncMode || isAsync(props === null || props === void 0 ? void 0 : props.onChangeValidator) || isAsync(props === null || props === void 0 ? void 0 : props.onBlurValidator)) { return true; } } return false; }, []); const sharedData = useSharedState(id); const sharedAttachments = useSharedState(id ? createReferenceKey(id, 'attachments') : undefined); const sharedDataContext = useSharedState(id ? createReferenceKey(id, 'data-context') : undefined); const setSharedData = sharedData.set; const extendSharedData = sharedData.extend; const extendAttachment = sharedAttachments.extend; const rerenderUseDataHook = (_sharedAttachments$da = sharedAttachments.data) === null || _sharedAttachments$da === void 0 ? void 0 : _sharedAttachments$da.rerenderUseDataHook; bumpValidationVersionRef.current = () => { if (id) { validationVersionRef.current += 1; extendAttachment({ validationVersion: validationVersionRef.current }, { preventSyncOfSameInstance: true }); } }; const hasHydratedFieldErrorRef = useRef(false); if (!hasHydratedFieldErrorRef.current) { var _sharedAttachments$da2; const sharedFieldErrorRef = (_sharedAttachments$da2 = sharedAttachments.data) === null || _sharedAttachments$da2 === void 0 ? void 0 : _sharedAttachments$da2.fieldErrorRef; if (sharedFieldErrorRef !== null && sharedFieldErrorRef !== void 0 && sharedFieldErrorRef.current) { fieldErrorRef.current = sharedFieldErrorRef.current; hasHydratedFieldErrorRef.current = true; } } const cacheRef = useRef({ data, schema, shared: sharedData.data, hasUsedInitialData: false }); const internalData = useMemo(() => { if (id && initialData && !sharedData.data) { sharedData.update(initialData); } if (id && initialData && sharedData.data && cacheRef.current.shared === sharedData.data && internalDataRef.current === initialData && typeof internalDataRef.current === 'object') { return { ...internalDataRef.current, ...(sharedData.data || {}) }; } if (id && !initialData && !internalDataRef.current && sharedData.data && cacheRef.current.shared === sharedData.data) { return sharedData.data; } if (id && sharedData.data && cacheRef.current.shared !== sharedData.data && sharedData.data !== internalDataRef.current && typeof internalDataRef.current === 'object') { cacheRef.current.shared = sharedData.data; if (isEmptyDataRef.current) { return Array.isArray(internalDataRef.current) ? [] : clearedData; } return { ...internalDataRef.current, ...(sharedData.data || {}) }; } if (data !== cacheRef.current.data) { cacheRef.current.data = data; return data; } return internalDataRef.current; }, [id, initialData, sharedData, data]); internalDataRef.current = props.path && pointer.has(internalData, props.path) ? pointer.get(internalData, props.path) : internalData; const clearData = useCallback(() => { var _ref, _requestAnimationFram; isEmptyDataRef.current = true; internalDataRef.current = (_ref = typeof emptyData === 'function' ? emptyData(internalDataRef.current) : emptyData) !== null && _ref !== void 0 ? _ref : Array.isArray(internalDataRef.current) ? [] : clearedData; if (id) { setSharedData(internalDataRef.current); } forceUpdate(); onClear === null || onClear === void 0 || onClear(); (_requestAnimationFram = requestAnimationFrame) === null || _requestAnimationFram === void 0 || _requestAnimationFram(() => { isEmptyDataRef.current = false; }); }, [emptyData, id, onClear, setSharedData]); useLayoutEffect(() => { const hasNoErrors = errorsRef.current === undefined; const contextErrors = executeAjvValidator(); executeSectionValidators(contextErrors); if (hasNoErrors && errorsRef.current !== undefined) { forceUpdate(); } }, [internalDataRef.current, executeAjvValidator, executeSectionValidators]); const registerSectionSchema = useCallback(registration => { const normalizedPath = registration.path && registration.path !== '/' ? registration.path : '/'; const validator = createUnifiedValidator(registration.schema); sectionSchemasRef.current.set(registration.id, { path: normalizedPath, schema: registration.schema, validator }); sectionSchemaPathsRef.current.add(normalizedPath); validateData(); return () => { const entry = sectionSchemasRef.current.get(registration.id); if (!entry) { return; } sectionSchemasRef.current.delete(registration.id); const stillUsesPath = Array.from(sectionSchemasRef.current.values()).some(item => item.path === entry.path); if (!stillUsesPath) { sectionSchemaPathsRef.current.delete(entry.path); } validateData(); }; }, [createUnifiedValidator, validateData]); const storeInSession = useMemo(() => { return debounce(() => { var _window$sessionStorag3; (_window$sessionStorag3 = window.sessionStorage) === null || _window$sessionStorag3 === void 0 || _window$sessionStorag3.setItem(sessionStorageId, JSON.stringify(internalDataRef.current)); }, process.env.NODE_ENV === 'test' ? 1 : 800); }, [sessionStorageId]); const setData = useCallback((newData, { preventUpdate = false } = {}) => { if (transformIn) { newData = mutateDataHandler(newData, transformIn); } internalDataRef.current = newData; if (id) { extendSharedData(newData, { preventSyncOfSameInstance: true }); if (filterSubmitData) { rerenderUseDataHook === null || rerenderUseDataHook === void 0 || rerenderUseDataHook(); } } if (sessionStorageId && typeof window !== 'undefined') { storeInSession(); } if (!preventUpdate) { forceUpdate(); } }, [extendSharedData, filterSubmitData, id, mutateDataHandler, rerenderUseDataHook, sessionStorageId, storeInSession, transformIn]); const updateDataValue = useCallback((path, value, { preventUpdate } = {}) => { var _internalDataRef$curr; if (!path) { return; } const givenData = path === '/' ? value : (_internalDataRef$curr = internalDataRef.current) !== null && _internalDataRef$curr !== void 0 ? _internalDataRef$curr : path.match(isArrayJsonPointer) ? [] : {}; let newData = null; try { newData = structuredClone(givenData); } catch (e) { newData = givenData; } if (path !== '/') { pointer.set(newData, path, value); } setData(newData, { preventUpdate }); onUpdateDataValue === null || onUpdateDataValue === void 0 || onUpdateDataValue(path, value, { preventUpdate }); }, [onUpdateDataValue, setData]); const handlePathChangeUnvalidated = useCallback(async (path, value) => { if (!path) { return null; } updateDataValue(path, value); if (isAsync(onPathChange)) { await (onPathChange === null || onPathChange === void 0 ? void 0 : onPathChange(path, value)); } else { onPathChange === null || onPathChange === void 0 || onPathChange(path, value); } for (const itm of fieldEventListenersRef.current) { if (itm.type === 'onPathChange' && itm.path === path) { const { callback } = itm; if (isAsync(callback)) { await callback({ value }); } else { callback({ value }); } } } }, [onPathChange, updateDataValue]); const handlePathChange = useCallback(async (path, value = '_undefined_') => { if (!path) { return null; } if (value !== '_undefined_') { handlePathChangeUnvalidated(path, value); } showAllErrorsRef.current = false; validateData(); const data = internalDataRef.current; const options = { filterData }; const transformedData = transformOut ? mutateDataHandler(data, transformOut) : data; for (const cb of changeHandlerStackRef.current) { if (isAsync(onChange)) { await cb(transformedData, options); } else { cb(transformedData, options); } } if (isAsync(onChange)) { return await onChange(transformedData, options); } return onChange === null || onChange === void 0 ? void 0 : onChange(transformedData, options); }, [filterData, handlePathChangeUnvalidated, mutateDataHandler, onChange, transformOut, validateData]); const changeHandlerStackRef = useRef([]); const addOnChangeHandler = useCallback(callback => { const exists = changeHandlerStackRef.current.some(cb => { return callback === cb; }); if (!exists) { var _context; _pushInstanceProperty(_context = changeHandlerStackRef.current).call(_context, callback); } }, []); const setMountedFieldState = useCallback((path, state) => { const currentState = mountedFieldsRef.current.get(path) || {}; mountedFieldsRef.current.set(path, { ...currentState, ...state }); const hasChanges = Object.keys(state).some(key => { return currentState[key] !== state[key]; }); if (hasChanges) { Promise.resolve().then(() => { bumpValidationVersionRef.current(); }); } for (const itm of fieldEventListenersRef.current) { if (itm.type === 'onMount' && itm.path === path) { const { callback } = itm; callback(); } } }, []); const scrollToTop = useCallback(() => { if (typeof window !== 'undefined') { var _window, _window$scrollTo; (_window = window) === null || _window === void 0 || (_window$scrollTo = _window.scrollTo) === null || _window$scrollTo === void 0 || _window$scrollTo.call(_window, { top: 0, behavior: 'smooth' }); } }, []); const resolveStateResult = useCallback(async fn => { try { const result = await fn(); if (result instanceof Error) { throw result; } return result; } catch (error) { return { error: error }; } }, []); const applySubmitState = useCallback(result => { if (result !== null && result !== void 0 && result.error || result !== null && result !== void 0 && result.warning || result !== null && result !== void 0 && result.info || result !== null && result !== void 0 && result.customStatus) { setSubmitState(result); } }, [setSubmitState]); const handleSubmitCall = useCallback(async args => { const { onSubmit, enableAsyncBehavior, skipFieldValidation, skipErrorCheck } = args; setSubmitState({ error: undefined, warning: undefined, info: undefined, customStatus: undefined }); const asyncBehaviorIsEnabled = (skipErrorCheck ? enableAsyncBehavior : !hasErrors() || hasFieldState('pending')) && (enableAsyncBehavior || hasFieldWithAsyncValidator()); if (asyncBehaviorIsEnabled) { setFormState('pending'); } if (!skipFieldValidation) { for (const item of fieldEventListenersRef.current) { var _mountedFieldsRef$cur; const { path, type, callback } = item; if (type === 'onSubmitCall' && (_mountedFieldsRef$cur = mountedFieldsRef.current.get(path)) !== null && _mountedFieldsRef$cur !== void 0 && _mountedFieldsRef$cur.isMounted) { if (asyncBehaviorIsEnabled) { await callback(); } else { callback(); } } } } let result; if (!(skipErrorCheck ? false : hasErrors()) && !hasFieldState('pending') && (skipFieldValidation ? true : !hasFieldState('error'))) { var _result2; result = await resolveStateResult(async () => { if (isolate) { for (const item of fieldEventListenersRef.current) { const { type, callback } = item; if (type === 'onBeforeCommit') { callback(); } } const commitResult = await (onCommit === null || onCommit === void 0 ? void 0 : onCommit(internalDataRef.current, { clearData })); for (const item of fieldEventListenersRef.current) { const { type, callback } = item; if (type === 'onAfterCommit') { callback(); } } return commitResult; } else { return await onSubmit(); } }); if (asyncBehaviorIsEnabled) { var _result; if ((_result = result) !== null && _result !== void 0 && _result.error) { setFormState('abort'); } else if (keepPending.current !== true) { setFormState('complete'); } } if ((_result2 = result) !== null && _result2 !== void 0 && _result2['status']) { var _result3; setFormState((_result3 = result) === null || _result3 === void 0 ? void 0 : _result3['status']); } applySubmitState(result); } else { if (asyncBehaviorIsEnabled) { await new Promise(resolve => window.requestAnimationFrame(resolve)); setFormState(undefined); if (!skipFieldValidation) { onSubmitContinueRef.current = () => { window.requestAnimationFrame(() => { handleSubmitCall({ ...args, skipFieldValidation: true }); }); }; } } setShowAllErrors(true); const submitRequestResult = await resolveStateResult(() => onSubmitRequest === null || onSubmitRequest === void 0 ? void 0 : onSubmitRequest({ getErrors: () => Object.keys(fieldErrorRef.current).map(path => { return getDataPathHandlerParameters(path); }).filter(({ error }) => error) })); applySubmitState(submitRequestResult); if (submitRequestResult) { result = submitRequestResult; } for (const itm of fieldEventListenersRef.current) { if (itm.type === 'onSubmitRequest') { itm.callback(); } } } return result; }, [applySubmitState, clearData, getDataPathHandlerParameters, hasErrors, hasFieldState, hasFieldWithAsyncValidator, isolate, onCommit, onSubmitRequest, resolveStateResult, setFormState, setShowAllErrors]); const getSubmitData = useCallback(() => { const data = internalDataRef.current; const mutatedData = transformOut ? mutateDataHandler(data, transformOut) : data; const filteredData = filterSubmitData ? filterDataHandler(mutatedData, filterSubmitData) : mutatedData; return filteredData; }, [filterDataHandler, filterSubmitData, mutateDataHandler, transformOut]); const getSubmitParams = useCallback(() => { const reduceToVisibleFields = (data, options) => { return visibleDataHandler(transformOut ? mutateDataHandler(data, transformOut) : data, options); }; const transformData = (data, handler) => { return mutateDataHandler(data, handler, { mutate: false, fireHandlerWhen: ({ type }) => type === 'field' }); }; const formElement = formElementRef.current; const params = { filterData, reduceToVisibleFields, transformData, resetForm: () => { var _formElement$reset; formElement === null || formElement === void 0 || (_formElement$reset = formElement.reset) === null || _formElement$reset === void 0 || _formElement$reset.call(formElement); if (typeof window !== 'undefined') { if (sessionStorageId) { window.sessionStorage.removeItem(sessionStorageId); } } forceUpdate(); }, clearData }; return params; }, [clearData, filterData, mutateDataHandler, sessionStorageId, transformOut, visibleDataHandler]); const handleSubmit = useCallback(async () => { for (const item of fieldEventListenersRef.current) { const { type, callback } = item; if (type === 'onBeforeSubmit') { callback(); } } return await handleSubmitCall({ enableAsyncBehavior: isAsync(onSubmit), onSubmit: async () => { let stop = false; const preventSubmit = () => stop = true; for (const item of fieldEventListenersRef.current) { const { type, callback } = item; if (type === 'onSubmit') { if (isAsync(callback)) { await callback({ preventSubmit }); } else { callback({ preventSubmit }); } } } if (stop) { return; } const data = getSubmitData(); const options = getSubmitParams(); let result = undefined; if (isAsync(onSubmit)) { result = await onSubmit(data, options); } else { result = onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(data, options); } const completeResult = await (onSubmitComplete === null || onSubmitComplete === void 0 ? void 0 : onSubmitComplete(data, result)); if (completeResult) { result = Object.keys(result).length > 0 ? { ...result, ...completeResult } : completeResult; } if (scrollTopOnSubmit) { scrollToTop(); } return result; } }); }, [getSubmitData, getSubmitParams, handleSubmitCall, onSubmit, onSubmitComplete, scrollToTop, scrollTopOnSubmit]); const fieldEventListenersRef = useRef([]); const setFieldEventListener = useCallback((path, type, callback, { remove = false } = {}) => { fieldEventListenersRef.current = fieldEventListenersRef.current.filter(({ path: p, type: t, callback: c }) => { return !(p === path && t === type && c === callback); }); if (!remove) { var _context2; _pushInstanceProperty(_context2 = fieldEventListenersRef.current).call(_context2, { path, type, callback }); } }, []); const onSubmitContinueRef = useRef(null); if (!hasFieldState('pending')) { var _onSubmitContinueRef$; (_onSubmitContinueRef$ = onSubmitContinueRef.current) === null || _onSubmitContinueRef$ === void 0 || _onSubmitContinueRef$.call(onSubmitContinueRef); onSubmitContinueRef.current = null; } useUpdateEffect(() => { if (schema && schema !== cacheRef.current.schema) { cacheRef.current.schema = schema; ajvValidatorRef.current = createUnifiedValidator(schema); validateData(); forceUpdate(); } }, [schema, validateData, forceUpdate, createUnifiedValidator]); const onTimeout = useCallback(() => { setFormState(undefined); setSubmitState({ info: undefined, warning: undefined, error: undefined, customStatus: undefined }); }, [setFormState, setSubmitState]); useLayoutEffect(() => { if (id) { if (initialData && !sharedData.data) { extendSharedData(initialData, { preventSyncOfSameInstance: true }); } } }, [id, initialData, extendSharedData, sharedData.data]); useLayoutEffect(() => { if (id) { extendAttachment({ visibleDataHandler, filterDataHandler, validationVersion: validationVersionRef.current, hasErrors, hasFieldError, setShowAllErrors, setSubmitState, clearData, setData, updateDataValue, fieldConnectionsRef, fieldStatusRef, fieldErrorRef, internalDataRef }, { preventSyncOfSameInstance: true }); if (filterSubmitData) { rerenderUseDataHook === null || rerenderUseDataHook === void 0 || rerenderUseDataHook(); } } }, [clearData, extendAttachment, filterDataHandler, filterSubmitData, hasErrors, hasFieldError, id, rerenderUseDataHook, setData, setShowAllErrors, setSubmitState, updateDataValue, visibleDataHandler]); const { bufferedFormState: formState } = useFormStatusBuffer({ formState: formStateRef.current, waitFor: hasFieldState('pending'), minimumAsyncBehaviorTime, asyncSubmitTimeout, onTimeout }); const submitState = submitStateRef.current; const disabled = typeof (rest === null || rest === void 0 ? void 0 : rest['disabled']) === 'boolean' ? rest === null || rest === void 0 ? void 0 : rest['disabled'] : formState === 'pending' ? true : undefined; const contextErrorMessages = (errorMessages === null || errorMessages === void 0 ? void 0 : errorMessages[locale !== null && locale !== void 0 ? locale : sharedLocale]) || errorMessages; const getSourceValue = useCallback(value => { const data = internalDataRef.current; if (String(value).startsWith('/') && pointer.has(data, String(value))) { return pointer.get(data, String(value)); } return value; }, []); const contextValue = { handlePathChange, handlePathChangeUnvalidated, handleSubmit, setMountedFieldState, handleSubmitCall, setFormState, setSubmitState, setShowAllErrors, revealError, setFieldEventListener, setFieldState, setFieldError, setFieldConnection, setFieldInternals, setValueInternals, hasErrors, hasFieldError, hasFieldState, hasFieldWithAsyncValidator, validateData, updateDataValue, setData, clearData, visibleDataHandler, filterDataHandler, getSubmitData, getSubmitParams, addOnChangeHandler, scrollToTop, registerSectionSchema, schema, disabled, required, formState, activeSubmitButtonId: activeSubmitButtonIdRef.current, submitState, setActiveSubmitButtonId, contextErrorMessages, hasContext: true, errors: errorsRef.current, showAllErrors: showAllErrorsRef.current, hasVisibleError: hasVisibleErrorRef.current.size > 0, addSetShowAllErrorsRef, fieldConnectionsRef, fieldDisplayValueRef, fieldInternalsRef, valueInternalsRef, mountedFieldsRef, snapshotsRef, existingFieldsRef, formElementRef, isEmptyDataRef, fieldErrorRef, errorsRef, sectionSchemaPathsRef, getAjvInstance, countryCode: countryCode ? getSourceValue(countryCode) : undefined, id, data: internalDataRef.current, internalDataRef, props, ...rest }; if (id) { sharedDataContext.set(contextValue); } const show = Boolean(showAllErrorsRef.current); const resolvedLocale = locale || sharedLocale; const customErrorSummaryTitle = translations === null || translations === void 0 || (_translations$resolve = translations[resolvedLocale]) === null || _translations$resolve === void 0 || (_translations$resolve = _translations$resolve.Field) === null || _translations$resolve === void 0 ? void 0 : _translations$resolve.errorSummaryTitle; const formStatusConfig = useMemo(() => { var _ref2, _status$stack$0$title, _status$stack$; const status = show ? GlobalStatusProvider.get(globalStatusId) : null; return { globalStatus: { show, id: globalStatusId, title: (_ref2 = (_status$stack$0$title = status === null || status === void 0 || (_status$stack$ = status.stack[0]) === null || _status$stack$ === void 0 ? void 0 : _status$stack$.title) !== null && _status$stack$0$title !== void 0 ? _status$stack$0$title : customErrorSummaryTitle) !== null && _ref2 !== void 0 ? _ref2 : translation.errorSummaryTitle } }; }, [globalStatusId, show, customErrorSummaryTitle, translation.errorSummaryTitle]); return React.createElement(DataContext.Provider, { value: contextValue }, React.createElement(FieldPropsProvider, { FormStatus: formStatusConfig, formElement: disabled ? { disabled: true } : undefined, locale: locale ? locale : undefined, translations: translations ? translations : undefined }, children)); } function useFormStatusBuffer(props) { const { formState, waitFor, minimumAsyncBehaviorTime, asyncSubmitTimeout, onTimeout } = props || {}; const [, forceUpdate] = useReducer(() => ({}), {}); const stateRef = useRef(); const nowRef = useRef(null); const timeoutRef = useRef({}); const setState = useCallback(state => { stateRef.current = state; forceUpdate(); }, []); const clear = useCallback(() => { for (const key in timeoutRef.current) { clearTimeout(timeoutRef.current[key]); } }, []); const hadCompleteRef = useRef(false); const activeElementRef = useRef(null); useEffect(() => { const isTest = process.env.NODE_ENV === 'test'; const minimum = minimumAsyncBehaviorTime !== null && minimumAsyncBehaviorTime !== void 0 ? minimumAsyncBehaviorTime : isTest ? 1 : 1000; if (stateRef.current && formState === 'error') { clear(); setState(undefined); return; } if (formState === 'abort') { clear(); setState('abort'); timeoutRef.current.reset = setTimeout(() => { nowRef.current = 0; setState(undefined); }, minimum); return; } if (formState === 'complete') { hadCompleteRef.current = true; } if (formState === 'pending' && stateRef.current !== 'pending') { activeElementRef.current = document.activeElement; clear(); nowRef.current = Date.now(); hadCompleteRef.current = false; setState('pending'); } else if (stateRef.current === 'pending') { const offset = Math.max(Date.now() - nowRef.current); const delay = isTest ? minimum : Math.max(minimum - offset, 0); if (!waitFor) { timeoutRef.current.complete = setTimeout(() => { if (hadCompleteRef.current) { setState('complete'); } window.requestAnimationFrame(() => { var _activeElementRef$cur, _activeElementRef$cur2; (_activeElementRef$cur = activeElementRef.current) === null || _activeElementRef$cur === void 0 || (_activeElementRef$cur2 = _activeElementRef$cur.focus) === null || _activeElementRef$cur2 === void 0 || _activeElementRef$cur2.call(_activeElementRef$cur); }); }, delay); timeoutRef.current.reset = setTimeout(() => { nowRef.current = 0; setState(undefined); clear(); }, delay + minimum); } } if (stateRef.current === 'pending') { timeoutRef.current.timeout = setTimeout(() => { clear(); setState(undefined); onTimeout === null || onTimeout === void 0 || onTimeout(); }, asyncSubmitTimeout !== null && asyncSubmitTimeout !== void 0 ? asyncSubmitTimeout : 30000); } return clear; }, [clear, minimumAsyncBehaviorTime, formState, setState, waitFor, asyncSubmitTimeout, onTimeout]); return { bufferedFormState: stateRef.current }; } export const clearedData = Object.freeze({}); //# sourceMappingURL=Provider.js.map