@atlaskit/page-layout
Version:
A collection of components which let you compose an application's page layout.
262 lines (250 loc) • 11.7 kB
JavaScript
"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 _getPageLayoutSlotCssSelector = require("../common/get-page-layout-slot-css-selector");
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, _getPageLayoutSlotCssSelector.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 apps 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);
};