UNPKG

@atlaskit/modal-dialog

Version:

A modal dialog displays content that requires user interaction, in a layer above the page.

90 lines (86 loc) 3.75 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import { useEffect } from 'react'; import useLazyCallback from '@atlaskit/ds-lib/use-lazy-callback'; import usePreviousValue from '@atlaskit/ds-lib/use-previous-value'; import useStateRef from '@atlaskit/ds-lib/use-state-ref'; import { useExitingPersistence } from '@atlaskit/motion/exiting-persistence'; /** * ________________________________________________ * | MAJOR VERSIONS WILL NOT KNOW ABOUT EACH OTHER! | * ------------------------------------------------ * * An array which holds references to all currently open modal dialogs. * This will only work for modal dialogs of the same major version, * as the reference will be different between them. * * E.g. V11 won't know about any from V12. */ var modalStackRegister = []; /** * Returns the position of the calling modal dialog in the modal dialog stack. * Stack index of `0` is the highest position in the stack, * with every higher number being behind in the stack. */ export default function useModalStack(_ref) { var onStackChange = _ref.onStackChange; var _useExitingPersistenc = useExitingPersistence(), isExiting = _useExitingPersistenc.isExiting; var _useStateRef = useStateRef(0), _useStateRef2 = _slicedToArray(_useStateRef, 2), stackIndexRef = _useStateRef2[0], setStackIndex = _useStateRef2[1]; var currentStackIndex = stackIndexRef.current; var previousStackIndex = usePreviousValue(stackIndexRef.current); // We want to ensure this function **never changes** during the lifecycle of this component. // This is why it's assigned to a ref and not in a useMemo/useCallback. var updateStack = useLazyCallback(function () { var newStackIndex = modalStackRegister.indexOf(updateStack); // We access the stack index ref instead of state because this closure will always only // have the initial state and not any of the later values. if (stackIndexRef.current !== newStackIndex) { setStackIndex(newStackIndex); stackIndexRef.current = newStackIndex; } }); useEffect(function () { var currentStackIndex = modalStackRegister.indexOf(updateStack); if (!isExiting && currentStackIndex === -1) { // We are opening the modal dialog. // Add ourselves to the modal stack register! modalStackRegister.unshift(updateStack); } if (isExiting && currentStackIndex !== -1) { // We are closing the modal dialog using a wrapping modal transition component. // Remove ourselves from the modal stack register! // NOTE: Modal dialogs that don't have a wrapping modal transition component won't flow through here! // For that scenario we cleanup when the component unmounts. modalStackRegister.splice(currentStackIndex, 1); } // Fire all registered modal dialogs to update their position in the stack. modalStackRegister.forEach(function (cb) { return cb(); }); }, [updateStack, isExiting]); useEffect(function () { return function () { // Final cleanup just in case this modal dialog did not have a wrapping modal transition. var currentStackIndex = modalStackRegister.indexOf(updateStack); if (currentStackIndex !== -1) { modalStackRegister.splice(currentStackIndex, 1); modalStackRegister.forEach(function (cb) { return cb(); }); } }; }, [updateStack]); useEffect(function () { if (previousStackIndex === undefined) { // Initial case that we don't need to notify about. return; } if (previousStackIndex !== currentStackIndex) { onStackChange(currentStackIndex); } }, [onStackChange, previousStackIndex, currentStackIndex]); return currentStackIndex; }