UNPKG

@atlaskit/page-layout

Version:

A collection of components which let you compose an application's page layout.

262 lines (250 loc) 11.6 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.SidebarResizeController = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _react = _interopRequireWildcard(require("react")); var _bindEventListener = require("bind-event-listener"); var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop")); var _motion = require("@atlaskit/motion"); var _responsive = require("@atlaskit/primitives/responsive"); var _constants = require("../common/constants"); var _utils = require("../common/utils"); var _sidebarResizeContext = require("./sidebar-resize-context"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } var handleDataAttributesAndCb = function handleDataAttributesAndCb() { var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _noop.default; var leftSidebarState = arguments.length > 1 ? arguments[1] : undefined; document.documentElement.removeAttribute(_constants.IS_SIDEBAR_COLLAPSING); callback(leftSidebarState); }; var leftSidebarSelector = (0, _utils.getPageLayoutSlotCSSSelector)('left-sidebar'); // eslint-disable-next-line @repo/internal/react/require-jsdoc var SidebarResizeController = exports.SidebarResizeController = function SidebarResizeController(_ref) { var children = _ref.children, onExpand = _ref.onLeftSidebarExpand, onCollapse = _ref.onLeftSidebarCollapse; var _useState = (0, _react.useState)({ isFlyoutOpen: false, isResizing: false, isLeftSidebarCollapsed: false, leftSidebarWidth: 0, lastLeftSidebarWidth: 0, flyoutLockCount: 0, isFixed: true, hasInit: false }), _useState2 = (0, _slicedToArray2.default)(_useState, 2), leftSidebarState = _useState2[0], setLeftSidebarState = _useState2[1]; var leftSidebarWidth = leftSidebarState.leftSidebarWidth, lastLeftSidebarWidth = leftSidebarState.lastLeftSidebarWidth, isResizing = leftSidebarState.isResizing, flyoutLockCount = leftSidebarState.flyoutLockCount, isFixed = leftSidebarState.isFixed, isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed, isFlyoutOpen = leftSidebarState.isFlyoutOpen, hasInit = leftSidebarState.hasInit; // We put the latest callbacks into a ref so we can always have the latest // functions in our transitionend listeners var stableRef = (0, _react.useRef)({ onExpand: onExpand, onCollapse: onCollapse }); (0, _react.useEffect)(function () { stableRef.current = { onExpand: onExpand, onCollapse: onCollapse }; }, [onExpand, onCollapse]); var transition = (0, _react.useRef)(null); var mobileMediaQuery = (0, _responsive.UNSAFE_useMediaQuery)('below.sm'); var isOpen = mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches ? isFlyoutOpen : !isLeftSidebarCollapsed; var expandLeftSidebar = (0, _react.useCallback)(function () { var _transition$current2, _transition$current3; if (isOpen) { return; } // If the user is at a mobile viewport when this runs, we handle it differently // We don't expand at mobile widths; instead we use a flyout which is treated the same otherwise if (mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches) { var _transition$current; var flyoutOpenSidebarState = { isResizing: false, isLeftSidebarCollapsed: true, leftSidebarWidth: _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH, lastLeftSidebarWidth: leftSidebarWidth, isFlyoutOpen: true, flyoutLockCount: 0, isFixed: isFixed, hasInit: hasInit }; setLeftSidebarState(flyoutOpenSidebarState); // Flush the desktop transitions, cleanup, and call the `onExpand` still (_transition$current = transition.current) === null || _transition$current === void 0 || _transition$current.complete(); handleDataAttributesAndCb(stableRef.current.onExpand, flyoutOpenSidebarState); return; } if (isResizing || !isLeftSidebarCollapsed || // already expanding ((_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.action) === 'expand') { return; } // flush existing transition (_transition$current3 = transition.current) === null || _transition$current3 === void 0 || _transition$current3.complete(); var width = Math.max(lastLeftSidebarWidth, _constants.DEFAULT_LEFT_SIDEBAR_WIDTH); var updatedLeftSidebarState = { isLeftSidebarCollapsed: false, isFlyoutOpen: false, leftSidebarWidth: width, lastLeftSidebarWidth: lastLeftSidebarWidth, isResizing: isResizing, flyoutLockCount: flyoutLockCount, isFixed: isFixed, hasInit: hasInit }; setLeftSidebarState(updatedLeftSidebarState); function finish() { handleDataAttributesAndCb(stableRef.current.onExpand, updatedLeftSidebarState); } var sidebar = document.querySelector(leftSidebarSelector); // onTransitionEnd isn't triggered when a user prefers reduced motion if ((0, _motion.isReducedMotion)() || !sidebar) { finish(); return; } var unbindEvent = (0, _bindEventListener.bind)(sidebar, { type: 'transitionend', listener: function listener(event) { if (event.target === sidebar && event.propertyName === 'width') { var _transition$current4; (_transition$current4 = transition.current) === null || _transition$current4 === void 0 || _transition$current4.complete(); } } }); var value = { action: 'expand', complete: function complete() { value.abort(); finish(); }, abort: function abort() { unbindEvent(); transition.current = null; } }; transition.current = value; }, [isOpen, mobileMediaQuery, isResizing, isLeftSidebarCollapsed, lastLeftSidebarWidth, flyoutLockCount, isFixed, leftSidebarWidth, hasInit]); var collapseLeftSidebar = (0, _react.useCallback)(function (event, collapseWithoutTransition) { var _transition$current6, _transition$current7; if (!isOpen) { return; } // If the user is at a mobile viewport when this runs, we handle it differently // We don't collapse at mobile widths; instead we close the flyout. if (mobileMediaQuery !== null && mobileMediaQuery !== void 0 && mobileMediaQuery.matches) { var _transition$current5; var flyoutCloseSidebarState = { isResizing: false, isLeftSidebarCollapsed: true, leftSidebarWidth: _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH, lastLeftSidebarWidth: lastLeftSidebarWidth, isFlyoutOpen: false, flyoutLockCount: 0, isFixed: isFixed, hasInit: hasInit }; setLeftSidebarState(flyoutCloseSidebarState); // Flush the desktop transitions, cleanup, and call the `onCollapse` still (_transition$current5 = transition.current) === null || _transition$current5 === void 0 || _transition$current5.complete(); handleDataAttributesAndCb(stableRef.current.onCollapse, flyoutCloseSidebarState); return; } if (isResizing || isLeftSidebarCollapsed || // already collapsing ((_transition$current6 = transition.current) === null || _transition$current6 === void 0 ? void 0 : _transition$current6.action) === 'collapse') { return; } // flush existing transition (_transition$current7 = transition.current) === null || _transition$current7 === void 0 || _transition$current7.complete(); // data-attribute is used as a CSS selector to sync the hiding/showing // of the nav contents with expand/collapse animation document.documentElement.setAttribute(_constants.IS_SIDEBAR_COLLAPSING, 'true'); var updatedLeftSidebarState = { isLeftSidebarCollapsed: true, isFlyoutOpen: false, leftSidebarWidth: _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH, lastLeftSidebarWidth: leftSidebarWidth, isResizing: isResizing, flyoutLockCount: flyoutLockCount, isFixed: isFixed, hasInit: hasInit }; setLeftSidebarState(updatedLeftSidebarState); function finish() { handleDataAttributesAndCb(stableRef.current.onCollapse, updatedLeftSidebarState); } var sidebar = document.querySelector(leftSidebarSelector); // onTransitionEnd isn't triggered when a user prefers reduced motion if (collapseWithoutTransition || (0, _motion.isReducedMotion)() || !sidebar) { finish(); return; } var unbindEvent = (0, _bindEventListener.bind)(sidebar, { type: 'transitionend', listener: function listener(event) { if (sidebar === event.target && event.propertyName === 'width') { var _transition$current8; (_transition$current8 = transition.current) === null || _transition$current8 === void 0 || _transition$current8.complete(); } } }); var value = { action: 'collapse', complete: function complete() { value.abort(); finish(); }, abort: function abort() { unbindEvent(); transition.current = null; } }; transition.current = value; }, [isOpen, mobileMediaQuery, isResizing, isLeftSidebarCollapsed, leftSidebarWidth, flyoutLockCount, isFixed, lastLeftSidebarWidth, hasInit]); /** * Conditionally toggle the expanding or collapsing the sidebars. * This supports our mobile flyout mode as well. */ var toggleLeftSidebar = (0, _react.useCallback)(function (event, collapseWithoutTransition) { if (isOpen) { collapseLeftSidebar(event, collapseWithoutTransition); } else { expandLeftSidebar(); } }, [isOpen, expandLeftSidebar, collapseLeftSidebar]); // Make sure we finish any lingering transitions when unmounting (0, _react.useEffect)(function mount() { return function unmount() { var _transition$current9; (_transition$current9 = transition.current) === null || _transition$current9 === void 0 || _transition$current9.abort(); }; }, []); var context = (0, _react.useMemo)(function () { return { isLeftSidebarCollapsed: !isOpen, // Technically this isn't quite true, but with mobile it's a bit safer if products are using this to roll their own collapse/expand expandLeftSidebar: expandLeftSidebar, collapseLeftSidebar: collapseLeftSidebar, leftSidebarState: leftSidebarState, setLeftSidebarState: setLeftSidebarState, toggleLeftSidebar: toggleLeftSidebar }; }, [isOpen, expandLeftSidebar, collapseLeftSidebar, leftSidebarState, toggleLeftSidebar]); return /*#__PURE__*/_react.default.createElement(_sidebarResizeContext.SidebarResizeContext.Provider, { value: context }, children); };