@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
393 lines (392 loc) • 15.5 kB
JavaScript
"use client";
import _extends from "@babel/runtime/helpers/esm/extends";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
const _excluded = ["variant", "sidebarId"],
_excluded2 = ["className", "id", "mode", "initialActiveIndex", "omitScrollManagement", "omitFocusManagement", "onStepChange", "children", "noAnimation", "expandedInitially", "prerenderFieldProps", "keepInDOM", "validationMode", "outset"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import "core-js/modules/web.dom-collections.iterator.js";
import React, { useContext, useCallback, useRef, useReducer, useMemo, useEffect } from 'react';
import classnames from 'classnames';
import { Space } from '../../../../components';
import { warn } from '../../../../shared/component-helper';
import { isAsync } from '../../../../shared/helpers/isAsync';
import useId from '../../../../shared/helpers/useId';
import WizardContext from '../Context/WizardContext';
import DataContext from '../../DataContext/Context';
import Handler from '../../Form/Handler/Handler';
import { createReferenceKey, useSharedState } from '../../../../shared/helpers/useSharedState';
import useHandleLayoutEffect from './useHandleLayoutEffect';
import useStepAnimation from './useStepAnimation';
import useVisibility from '../../Form/Visibility/useVisibility';
import { DisplaySteps } from './DisplaySteps';
import { IterateOverSteps } from './IterateOverSteps';
import { PrerenderFieldPropsOfOtherSteps } from './PrerenderFieldPropsOfOtherSteps';
const useLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
function handleDeprecatedProps(props) {
const {
variant,
sidebarId
} = props,
rest = _objectWithoutProperties(props, _excluded);
return rest;
}
function WizardContainer(props) {
var _dataContext$setField;
const _handleDeprecatedProp = handleDeprecatedProps(props),
{
className,
id: idProp,
mode = 'strict',
initialActiveIndex = 0,
omitScrollManagement,
omitFocusManagement,
onStepChange,
children,
noAnimation = false,
expandedInitially = false,
prerenderFieldProps = true,
keepInDOM,
validationMode,
outset = true
} = _handleDeprecatedProp,
rest = _objectWithoutProperties(_handleDeprecatedProp, _excluded2);
const dataContext = useContext(DataContext);
const {
hasContext,
setFormState,
handleSubmitCall,
setShowAllErrors,
setSubmitState
} = dataContext;
const id = useId(idProp);
const [, forceUpdate] = useReducer(() => ({}), {});
const activeIndexRef = useRef(initialActiveIndex);
const totalStepsRef = useRef(NaN);
const submitCountRef = useRef(0);
const visitedStepsRef = useRef(new Map());
const fieldErrorRef = useRef(new Map());
const storeStepStateRef = useRef(new Map());
const hasErrorInOtherStepRef = useRef(false);
const elementRef = useRef();
const stepElementRef = useRef();
const preventNextStepRef = useRef(false);
const stepsRef = useRef(new Map());
const tmpStepsRef = useRef();
const stepIndexRef = useRef(-1);
const updateTitlesRef = useRef();
const prerenderFieldPropsRef = useRef({});
const bypassOnNavigation = validationMode === 'bypassOnNavigation';
const sharedStateRef = useRef();
sharedStateRef.current = useSharedState(hasContext && id ? createReferenceKey(id, 'wizard') : undefined);
const hasFieldErrorInStep = useCallback(index => {
return Array.from(fieldErrorRef.current.values()).some(_ref => {
let {
index: i,
hasError
} = _ref;
return i === index && hasError;
});
}, []);
const setStepAsVisited = useCallback(index => {
visitedStepsRef.current.set(index, true);
}, []);
useEffect(() => {
if (!initialActiveIndex) {
setStepAsVisited(activeIndexRef.current);
}
}, [initialActiveIndex, setStepAsVisited]);
const writeStepsState = useCallback(function () {
let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
let forStates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['unknown', 'error'];
for (let i = 0; i < totalStepsRef.current; i++) {
if (index !== undefined && index !== i) {
continue;
}
let result = undefined;
const existingState = storeStepStateRef.current.get(i);
if (forStates.includes('unknown')) {
const state = i < activeIndexRef.current && visitedStepsRef.current.get(i) === undefined;
if (state) {
result = 'unknown';
}
}
if (forStates.includes('error')) {
const state = hasFieldErrorInStep(i);
if (state) {
result = 'error';
} else if (existingState === 'error') {
if (i === activeIndexRef.current) {
result = undefined;
} else {
result = existingState;
}
}
}
storeStepStateRef.current.set(i, result);
}
}, [hasFieldErrorInStep]);
const hasInvalidStepsState = useCallback(function () {
let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
let forStates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['unknown', 'error'];
for (let i = 0; i < totalStepsRef.current; i++) {
if (index !== undefined && index !== i) {
continue;
}
const state = storeStepStateRef.current.get(i);
if (forStates.includes('unknown')) {
if (state === 'unknown') {
return true;
}
}
if (forStates.includes('error')) {
if (state === 'error') {
return true;
}
}
}
return false;
}, []);
const setFieldError = useCallback((index, path, hasError) => {
fieldErrorRef.current.set(path, {
index,
hasError
});
}, []);
const preventNavigation = useCallback(function () {
let shouldPrevent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
preventNextStepRef.current = shouldPrevent;
}, []);
const getStepChangeOptions = useCallback(index => {
var _stepsRef$current$get, _stepsRef$current$get2;
const previousIndex = activeIndexRef.current;
const options = {
preventNavigation,
previousStep: {
index: previousIndex
}
};
const id = (_stepsRef$current$get = stepsRef.current.get(index)) === null || _stepsRef$current$get === void 0 ? void 0 : _stepsRef$current$get.id;
if (id) {
Object.assign(options, {
id
});
}
const previousId = (_stepsRef$current$get2 = stepsRef.current.get(previousIndex)) === null || _stepsRef$current$get2 === void 0 ? void 0 : _stepsRef$current$get2.id;
if (previousId) {
Object.assign(options.previousStep, {
id: previousId
});
}
return options;
}, [preventNavigation]);
const callOnStepChange = useCallback(async (index, mode) => {
if (isAsync(onStepChange)) {
return await onStepChange(index, mode, getStepChangeOptions(index));
}
return onStepChange === null || onStepChange === void 0 ? void 0 : onStepChange(index, mode, getStepChangeOptions(index));
}, [getStepChangeOptions, onStepChange]);
const {
setFocus,
scrollToTop,
isInteractionRef
} = useHandleLayoutEffect({
elementRef,
stepElementRef
});
const executeLayoutAnimationRef = useRef();
useStepAnimation({
activeIndexRef,
stepElementRef,
executeLayoutAnimationRef
});
const handleLayoutEffect = useCallback(() => {
if (!omitFocusManagement) {
setFocus();
}
if (!omitScrollManagement) {
scrollToTop();
}
}, [omitScrollManagement, omitFocusManagement, setFocus, scrollToTop]);
const handleStepChange = useCallback(async _ref2 => {
let {
index,
skipErrorCheck,
skipStepChangeCall,
skipStepChangeCallBeforeMounted,
skipStepChangeCallFromHook,
mode
} = _ref2;
let didSubmit = false;
const onSubmit = async () => {
if (!skipStepChangeCallFromHook) {
var _sharedStateRef$curre, _sharedStateRef$curre2, _sharedStateRef$curre3;
(_sharedStateRef$curre = sharedStateRef.current) === null || _sharedStateRef$curre === void 0 ? void 0 : (_sharedStateRef$curre2 = _sharedStateRef$curre.data) === null || _sharedStateRef$curre2 === void 0 ? void 0 : (_sharedStateRef$curre3 = _sharedStateRef$curre2.onStepChange) === null || _sharedStateRef$curre3 === void 0 ? void 0 : _sharedStateRef$curre3.call(_sharedStateRef$curre2, index, mode, getStepChangeOptions(index));
}
let result = undefined;
if (!skipStepChangeCall && !(skipStepChangeCallBeforeMounted && !isInteractionRef.current)) {
result = await callOnStepChange(index, mode);
}
setFormState('abort');
setShowAllErrors(bypassOnNavigation ? false : hasInvalidStepsState(index, ['error']));
if (!preventNextStepRef.current && !(result instanceof Error)) {
handleLayoutEffect();
activeIndexRef.current = index;
setStepAsVisited(activeIndexRef.current);
forceUpdate();
}
preventNextStepRef.current = false;
didSubmit = true;
return result;
};
await handleSubmitCall({
skipErrorCheck,
skipFieldValidation: skipErrorCheck,
enableAsyncBehavior: isAsync(onStepChange),
onSubmit: bypassOnNavigation ? () => null : onSubmit
});
if (!didSubmit) {
if (bypassOnNavigation) {
await onSubmit();
} else {
if (mode === 'next') {
writeStepsState(activeIndexRef.current, ['error']);
if (!hasInvalidStepsState(activeIndexRef.current)) {
await onSubmit();
}
}
}
}
}, [bypassOnNavigation, callOnStepChange, getStepChangeOptions, handleLayoutEffect, handleSubmitCall, hasInvalidStepsState, isInteractionRef, onStepChange, setFormState, setShowAllErrors, setStepAsVisited, writeStepsState]);
const setActiveIndex = useCallback((index, options) => {
if (index === activeIndexRef.current) {
return;
}
const mode = index > activeIndexRef.current ? 'next' : 'previous';
handleStepChange(_objectSpread({
index,
skipErrorCheck: mode === 'previous',
mode
}, options));
}, [handleStepChange]);
const handlePrevious = useCallback(() => {
setActiveIndex(activeIndexRef.current - 1);
}, [setActiveIndex]);
const handleNext = useCallback(() => {
setActiveIndex(activeIndexRef.current + 1);
}, [setActiveIndex]);
const handleChange = useCallback(_ref3 => {
let {
current_step
} = _ref3;
setActiveIndex(current_step, mode === 'loose' ? {
skipErrorCheck: true
} : undefined);
}, [mode, setActiveIndex]);
const setFormError = useCallback(error => {
setSubmitState === null || setSubmitState === void 0 ? void 0 : setSubmitState({
error
});
}, [setSubmitState]);
const handleSubmit = useCallback(_ref4 => {
let {
preventSubmit
} = _ref4;
submitCountRef.current += 1;
if (hasInvalidStepsState()) {
return preventSubmit();
}
if (activeIndexRef.current + 1 < totalStepsRef.current) {
handleNext();
preventSubmit();
}
}, [hasInvalidStepsState, handleNext]);
(_dataContext$setField = dataContext.setFieldEventListener) === null || _dataContext$setField === void 0 ? void 0 : _dataContext$setField.call(dataContext, undefined, 'onSubmit', handleSubmit);
const {
check
} = useVisibility();
const mapOverChildrenRef = useRef(false);
const enableMapOverChildren = useCallback(() => {
mapOverChildrenRef.current = true;
}, []);
const activeIndex = activeIndexRef.current;
const providerValue = useMemo(() => {
return {
id,
activeIndex,
initialActiveIndex,
stepElementRef,
stepsRef,
updateTitlesRef,
activeIndexRef,
stepIndexRef,
totalStepsRef,
submitCountRef,
prerenderFieldProps,
prerenderFieldPropsRef,
hasErrorInOtherStepRef,
keepInDOM,
enableMapOverChildren,
mapOverChildrenRef,
check,
setActiveIndex,
handlePrevious,
hasInvalidStepsState,
writeStepsState,
setFieldError,
handleNext,
setFormError
};
}, [id, activeIndex, initialActiveIndex, prerenderFieldProps, keepInDOM, enableMapOverChildren, check, setActiveIndex, handlePrevious, hasInvalidStepsState, writeStepsState, setFieldError, handleNext, setFormError]);
useLayoutEffect(() => {
if (id && hasContext) {
sharedStateRef.current.extend(providerValue);
}
}, [hasContext, id, providerValue]);
useLayoutEffect(() => {
var _updateTitlesRef$curr;
(_updateTitlesRef$curr = updateTitlesRef.current) === null || _updateTitlesRef$curr === void 0 ? void 0 : _updateTitlesRef$curr.call(updateTitlesRef);
}, [stepsRef.current]);
const stepsLengthDidChange = useCallback(() => {
const tmpCount = tmpStepsRef.current;
if (tmpCount === undefined) {
return false;
}
const count = totalStepsRef.current;
return count !== 0 && tmpCount !== 0 && count !== tmpCount;
}, []);
useLayoutEffect(() => {
if (stepsLengthDidChange()) {
var _executeLayoutAnimati;
callOnStepChange(activeIndexRef.current, 'stepListModified');
(_executeLayoutAnimati = executeLayoutAnimationRef.current) === null || _executeLayoutAnimati === void 0 ? void 0 : _executeLayoutAnimati.call(executeLayoutAnimationRef);
}
tmpStepsRef.current = totalStepsRef.current;
}, [totalStepsRef.current, callOnStepChange, stepsLengthDidChange]);
if (!hasContext) {
warn('You may wrap Wizard.Container in Form.Handler');
return React.createElement(Handler, null, React.createElement(WizardContainer, _extends({}, props, {
id: id
})));
}
return React.createElement(WizardContext.Provider, {
value: providerValue
}, React.createElement(Space, _extends({
className: classnames('dnb-forms-wizard-layout', className),
innerRef: elementRef
}, rest), React.createElement(DisplaySteps, {
mode: mode,
noAnimation: noAnimation,
expandedInitially: expandedInitially,
handleChange: handleChange,
outset: outset
}), React.createElement("div", {
className: "dnb-forms-wizard-layout__contents"
}, React.createElement(IterateOverSteps, null, children))), prerenderFieldProps && !keepInDOM && React.createElement(PrerenderFieldPropsOfOtherSteps, {
prerenderFieldPropsRef: prerenderFieldPropsRef
}));
}
WizardContainer._supportsSpacingProps = true;
export default WizardContainer;
//# sourceMappingURL=WizardContainer.js.map