@atlaskit/modal-dialog
Version:
A modal dialog displays content that requires user interaction, in a layer above the page.
96 lines (92 loc) • 4.16 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useModalStack;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = require("react");
var _useLazyCallback = _interopRequireDefault(require("@atlaskit/ds-lib/use-lazy-callback"));
var _usePreviousValue = _interopRequireDefault(require("@atlaskit/ds-lib/use-previous-value"));
var _useStateRef3 = _interopRequireDefault(require("@atlaskit/ds-lib/use-state-ref"));
var _exitingPersistence = require("@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.
*/
function useModalStack(_ref) {
var onStackChange = _ref.onStackChange;
var _useExitingPersistenc = (0, _exitingPersistence.useExitingPersistence)(),
isExiting = _useExitingPersistenc.isExiting;
var _useStateRef = (0, _useStateRef3.default)(0),
_useStateRef2 = (0, _slicedToArray2.default)(_useStateRef, 2),
stackIndexRef = _useStateRef2[0],
setStackIndex = _useStateRef2[1];
var currentStackIndex = stackIndexRef.current;
var previousStackIndex = (0, _usePreviousValue.default)(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 = (0, _useLazyCallback.default)(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;
}
});
(0, _react.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]);
(0, _react.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]);
(0, _react.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;
}