@atlaskit/navigation-system
Version:
The latest navigation system for Atlassian apps.
154 lines (152 loc) • 8.03 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
import invariant from 'tiny-invariant';
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
import ExitingPersistence from '@atlaskit/motion/exiting-persistence';
import { fg } from '@atlaskit/platform-feature-flags/fg';
import { getPanelLayoutDefaults } from './get-panel-layout-defaults';
import { LayoutAreaSizingContext } from './layout-area-sizing-context';
import { LayoutAreaSizingProvider } from './layout-area-sizing-provider';
import { LocalPanelSurface } from './local-panel-surface';
import { PanelLayoutShell } from './panel-layout-shell';
import { useLayoutAreaSizing } from './use-layout-area-sizing';
// Development-only ownership checks are scoped to each coordinator, not the whole page.
var mountedPanelLayouts = new WeakSet();
function CoordinatedPanelLayout(_ref) {
var _ref2, _ref3, _ref4, _state$width;
var children = _ref.children,
panel = _ref.panel,
isPanelOpen = _ref.isPanelOpen,
panelKey = _ref.panelKey,
panelWidth = _ref.panelWidth,
defaultPanelWidth = _ref.defaultPanelWidth,
compactDefaultPanelWidth = _ref.compactDefaultPanelWidth,
suppliedMinWidth = _ref.panelMinWidth,
panelMaxWidth = _ref.panelMaxWidth,
onPanelWidthChange = _ref.onPanelWidthChange,
onResizeEnd = _ref.onResizeEnd,
_ref$panelLabel = _ref.panelLabel,
panelLabel = _ref$panelLabel === void 0 ? 'Panel' : _ref$panelLabel,
_ref$resizeLabel = _ref.resizeLabel,
resizeLabel = _ref$resizeLabel === void 0 ? 'Resize panel' : _ref$resizeLabel,
testId = _ref.testId,
layoutRef = _ref.layoutRef;
var _useState = useState(getPanelLayoutDefaults),
_useState2 = _slicedToArray(_useState, 1),
defaults = _useState2[0];
var panelMinWidth = suppliedMinWidth !== null && suppliedMinWidth !== void 0 ? suppliedMinWidth : defaults.panelMinWidth;
var coordinator = useContext(LayoutAreaSizingContext);
useEffect(function () {
if (process.env.NODE_ENV !== 'production' && coordinator) {
invariant(!mountedPanelLayouts.has(coordinator), 'Only one PanelLayout may be mounted per navigation Root/sizing coordinator, including closed hosts and LayoutWithPanel. Reuse the host to switch panel content.');
mountedPanelLayouts.add(coordinator);
return function () {
mountedPanelLayouts.delete(coordinator);
};
}
}, [coordinator]);
var _useState3 = useState((_ref2 = defaultPanelWidth !== null && defaultPanelWidth !== void 0 ? defaultPanelWidth : panelWidth) !== null && _ref2 !== void 0 ? _ref2 : defaults.panelWidth),
_useState4 = _slicedToArray(_useState3, 1),
defaultWidth = _useState4[0];
var _useState5 = useState((_ref3 = (_ref4 = compactDefaultPanelWidth !== null && compactDefaultPanelWidth !== void 0 ? compactDefaultPanelWidth : defaultPanelWidth) !== null && _ref4 !== void 0 ? _ref4 : panelWidth) !== null && _ref3 !== void 0 ? _ref3 : defaults.compactPanelWidth),
_useState6 = _slicedToArray(_useState5, 1),
compactWidth = _useState6[0];
// Use the controlled request before measurement too, without changing the mount-time default.
var fallbackWidth = Math.max(panelMinWidth, Math.min(panelWidth !== null && panelWidth !== void 0 ? panelWidth : defaultWidth, panelMaxWidth !== null && panelMaxWidth !== void 0 ? panelMaxWidth : Infinity));
var _useLayoutAreaSizing = useLayoutAreaSizing({
area: 'panel',
config: {
isOpen: isPanelOpen,
requestedWidth: panelWidth,
defaultWidth: defaultWidth,
compactDefaultWidth: compactWidth,
minWidth: panelMinWidth,
maxWidth: panelMaxWidth
}
}),
state = _useLayoutAreaSizing.state,
prepareResize = _useLayoutAreaSizing.prepareResize,
getResizeBounds = _useLayoutAreaSizing.getResizeBounds,
startResize = _useLayoutAreaSizing.startResize,
resize = _useLayoutAreaSizing.resize,
completeResize = _useLayoutAreaSizing.completeResize;
// Host callback identity is unrelated to sizing. Keep drag bindings stable while
// still delivering completions to the latest product-owned state and analytics.
var onWidthChangeRef = useStableRef(onPanelWidthChange);
var onResizeEndRef = useStableRef(onResizeEnd);
var handleWidthChange = useCallback(function (width) {
var _onWidthChangeRef$cur;
return (_onWidthChangeRef$cur = onWidthChangeRef.current) === null || _onWidthChangeRef$cur === void 0 ? void 0 : _onWidthChangeRef$cur.call(onWidthChangeRef, width);
}, [onWidthChangeRef]);
var handleResizeEnd = useCallback(function (event) {
var _onResizeEndRef$curre;
return (_onResizeEndRef$curre = onResizeEndRef.current) === null || _onResizeEndRef$curre === void 0 ? void 0 : _onResizeEndRef$curre.call(onResizeEndRef, event);
}, [onResizeEndRef]);
var isMotionEnabled = fg('platform-dst-motion-uplift-panel');
var surface = isPanelOpen && /*#__PURE__*/React.createElement(LocalPanelSurface, {
key: "local-panel",
contentKey: panelKey,
defaultWidth: fallbackWidth,
minWidth: panelMinWidth,
maxWidth: panelMaxWidth,
layoutRef: layoutRef,
managedSizing: state,
getManagedResizeBounds: getResizeBounds,
prepareResize: prepareResize,
onManagedResizeStart: startResize,
onManagedResize: resize,
onManagedResizeEnd: completeResize,
onWidthChange: handleWidthChange,
onResizeEnd: handleResizeEnd,
label: panelLabel,
resizeLabel: resizeLabel,
testId: testId && "".concat(testId, "--panel-slot")
}, panel);
return /*#__PURE__*/React.createElement(PanelLayoutShell, {
isPanelOpen: isPanelOpen,
panelWidth: (_state$width = state === null || state === void 0 ? void 0 : state.width) !== null && _state$width !== void 0 ? _state$width : fallbackWidth,
compactPanelWidth: Math.max(panelMinWidth, Math.min(panelWidth !== null && panelWidth !== void 0 ? panelWidth : compactWidth, panelMaxWidth !== null && panelMaxWidth !== void 0 ? panelMaxWidth : Infinity)),
panelMinWidth: panelMinWidth,
mode: state === null || state === void 0 ? void 0 : state.mode,
layoutRef: layoutRef,
shouldClipOverflow: isMotionEnabled,
testId: testId,
panel: isMotionEnabled ? /*#__PURE__*/React.createElement(ExitingPersistence, null, surface) : surface
}, children);
}
/**
* A coordinated local panel with caller-owned content and state. Keep this host mounted
* while closed to retain resized widths. Unmount it to reset its width preferences.
*
* Mount only one local-panel host per navigation Root/sizing coordinator, including
* closed hosts and panel-system's LayoutWithPanel. Switch content through the same host;
* separate Roots may each own a local panel.
*
* With chat layout enabled it shares Root's sizing coordinator. Otherwise it measures
* its own Main region, leaving existing navigation/chat behavior unchanged.
* Local-panel placement and resize constraints use this layout in both cases; disabling
* the chat-layout gate does not restore the previous panel-system layout engine.
*/
export function PanelLayout(props) {
var parentSizing = useContext(LayoutAreaSizingContext);
var layoutRef = useRef(null);
var content = /*#__PURE__*/React.createElement(CoordinatedPanelLayout, {
layoutRef: layoutRef,
panel: props.panel,
isPanelOpen: props.isPanelOpen,
panelKey: props.panelKey,
panelWidth: props.panelWidth,
defaultPanelWidth: props.defaultPanelWidth,
compactDefaultPanelWidth: props.compactDefaultPanelWidth,
panelMinWidth: props.panelMinWidth,
panelMaxWidth: props.panelMaxWidth,
onPanelWidthChange: props.onPanelWidthChange,
onResizeEnd: props.onResizeEnd,
panelLabel: props.panelLabel,
resizeLabel: props.resizeLabel,
testId: props.testId
}, props.children);
return parentSizing ? content : /*#__PURE__*/React.createElement(LayoutAreaSizingProvider, {
layoutRef: layoutRef
}, content);
}