@atlaskit/drawer
Version:
A drawer is a panel that slides in from the left side of the screen.
82 lines (80 loc) • 3.41 kB
JavaScript
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';
/**
* **Major versions will not know about each other**
*
* An array holding references to all currently open drawers. This only works
* for drawers of the same major version, because the reference differs between
* majors (for example V13 will not know about any from V14).
*
* Adapted from `useModalStack` in `@atlaskit/modal-dialog`. The top-layer drawer
* path uses this to know its position in the stack so that only the foreground
* drawer renders a visible `::backdrop` (this avoids cumulative darkening for
* nested or stacked drawers). Unlike the modal version, the register is timed
* off `isOpen` rather than `@atlaskit/motion`'s `useExitingPersistence`: the
* `Dialog` primitive owns its own exit lifecycle, so the top-layer drawer needs
* no `ExitingPersistence` wrapper.
*/
var drawerStackRegister = [];
/**
* Returns the position of the calling drawer in the drawer stack. A stack index
* of `0` is the foreground (top of the stack); higher numbers are further back.
*/
export default function useDrawerStack(_ref) {
var isOpen = _ref.isOpen,
onStackChange = _ref.onStackChange;
var _useStateRef = useStateRef(0),
_useStateRef2 = _slicedToArray(_useStateRef, 2),
stackIndexRef = _useStateRef2[0],
setStackIndex = _useStateRef2[1];
var currentStackIndex = stackIndexRef.current;
var previousStackIndex = usePreviousValue(stackIndexRef.current);
// Kept stable for the lifetime of the component so it can be the identity in
// the shared register. This is why it is a lazy callback and not a
// useMemo/useCallback value.
var updateStack = useLazyCallback(function () {
var newStackIndex = drawerStackRegister.indexOf(updateStack);
// Read from the ref rather than state: this closure only ever sees the
// initial state value.
if (stackIndexRef.current !== newStackIndex) {
setStackIndex(newStackIndex);
stackIndexRef.current = newStackIndex;
}
});
useEffect(function () {
if (!isOpen) {
return;
}
// Opening: join the front of the register (the newest open drawer is the
// foreground at index 0), then notify every open drawer to recompute.
if (drawerStackRegister.indexOf(updateStack) === -1) {
drawerStackRegister.unshift(updateStack);
}
drawerStackRegister.forEach(function (callback) {
return callback();
});
return function () {
// Closing or unmounting: leave the register and notify the rest.
var index = drawerStackRegister.indexOf(updateStack);
if (index !== -1) {
drawerStackRegister.splice(index, 1);
}
drawerStackRegister.forEach(function (callback) {
return callback();
});
};
}, [isOpen, updateStack]);
useEffect(function () {
if (previousStackIndex === undefined) {
// Initial render: nothing to notify about.
return;
}
if (previousStackIndex !== currentStackIndex) {
onStackChange === null || onStackChange === void 0 || onStackChange(currentStackIndex);
}
}, [onStackChange, previousStackIndex, currentStackIndex]);
return currentStackIndex;
}