@atlaskit/navigation-system
Version:
The latest navigation system for Atlassian apps.
1,091 lines • 53.1 kB
JavaScript
/* side-nav.tsx generated by @compiled/babel-plugin v3.0.2 */
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SideNav = SideNav;
exports.onPeekStartDelayMs = void 0;
require("./side-nav.compiled.css");
var _runtime = require("@compiled/react/runtime");
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _reactDom = require("react-dom");
var _bindEventListener = require("bind-event-listener");
var _useAnalyticsEvents2 = require("@atlaskit/analytics-next/useAnalyticsEvents");
var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
var _useStableRef = _interopRequireDefault(require("@atlaskit/ds-lib/use-stable-ref"));
var _openLayerObserverNamespaceProvider = require("@atlaskit/layering/open-layer-observer-namespace-provider");
var _useOpenLayerObserver = require("@atlaskit/layering/use-open-layer-observer");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _elementAdapter = require("@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter");
var _hasOpenDialogOrPopover = require("@atlaskit/top-layer/page-surface/has-open-dialog-or-popover");
var _useSkipLinkInternal = require("../../../context/skip-links/use-skip-link-internal");
var _topNavStartElement = require("../../../context/top-nav-start/top-nav-start-element");
var _constants = require("../constants");
var _dangerouslyHoistCssVarToDocumentRoot = require("../dangerously-hoist-css-var-to-document-root");
var _hoistSlotSizesContext = require("../hoist-slot-sizes-context");
var _convertResizeBoundToPixels = require("../panel-splitter/convert-resize-bound-to-pixels");
var _provider = require("../panel-splitter/provider");
var _resolveLayoutWidth = require("../resolve-layout-width");
var _root = require("../root");
var _useLayoutAreaSizing2 = require("../use-layout-area-sizing");
var _useLayoutId = require("../use-layout-id");
var _useResizingWidthCssVarOnRootElement = require("../use-resizing-width-css-var-on-root-element");
var _useSafeDefaultWidth = require("../use-safe-default-width");
var _bindOpenLayerChange = require("./bind-open-layer-change");
var _flyoutCloseDelayMs = require("./flyout-close-delay-ms");
var _setSideNavVisibilityState = require("./set-side-nav-visibility-state");
var _sideNavToggleButtonElement = require("./side-nav-toggle-button-element");
var _sideNavToggleTooltipKeyboardShortcut = require("./side-nav-toggle-tooltip-keyboard-shortcut");
var _sideNavVisibilityState = require("./side-nav-visibility-state");
var _useExpandSideNav = require("./use-expand-side-nav");
var _useIsSideNavShortcutEnabled = require("./use-is-side-nav-shortcut-enabled");
var _useSideNavRef = require("./use-side-nav-ref");
var _useSideNavToggleKeyboardShortcut = require("./use-side-nav-toggle-keyboard-shortcut");
var _useSideNavVisibility2 = require("./use-side-nav-visibility");
var _useSideNavVisibilityCallbacks = require("./use-side-nav-visibility-callbacks");
var _useToggleSideNav = require("./use-toggle-side-nav");
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); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- TODO: migrate to @atlaskit/primitives/compiled
var panelSplitterResizingVar = '--n_snvRsz';
var fallbackMinWidth = '240px';
function getPixelCustomProperty(element, propertyName, fallback) {
var raw = getComputedStyle(element).getPropertyValue(propertyName).trim();
var value = Number.parseFloat(raw);
return Number.isFinite(value) ? (0, _resolveLayoutWidth.resolveLayoutWidth)(raw.endsWith('vw') ? "".concat(value, "vw") : value, window.innerWidth) : fallback;
}
/**
* We are using JS to detect Firefox and disable animations, instead of using CSS, as Compiled currently does not merge duplicate
* CSS at-rules when at-rules are nested: https://github.com/atlassian-labs/compiled/blob/e04a325915e1d13010205089e4915de0e53bc2d4/packages/css/src/plugins/merge-duplicate-at-rules.ts#L5
* Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
*/
var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var textEntryInputTypes = new Set(['text', 'search', 'email', 'url', 'tel', 'password', 'number']);
function isEditableContent(_ref) {
var element = _ref.element;
if (element instanceof HTMLElement && typeof element.isContentEditable === 'boolean') {
return element.isContentEditable;
}
// Fallback for DOM implementations without `isContentEditable`.
var editingHost = element.closest('[contenteditable]');
return editingHost !== null && editingHost.getAttribute('contenteditable') !== 'false';
}
/**
* Returns `true` for an element that the user can type text into.
*/
function isTextEntryElement(_ref2) {
var element = _ref2.element;
if (element instanceof HTMLInputElement) {
return textEntryInputTypes.has(element.type) && !element.readOnly && !element.disabled;
}
if (element instanceof HTMLTextAreaElement) {
return !element.readOnly && !element.disabled;
}
return isEditableContent({
element: element
});
}
/**
* Used when the `platform-dst-top-layer` gate is on.
*
* The flyout stays open while a layer opened from the side nav is open, or while the user
* is typing in a text field inside the side nav (for example while renaming an item inline).
*/
function isFlyoutHeldOpen(_ref3) {
var nav = _ref3.nav;
if ((0, _hasOpenDialogOrPopover.hasOpenDialogOrPopover)({
within: nav
})) {
return true;
}
var activeElement = nav.ownerDocument.activeElement;
return activeElement !== null && nav.contains(activeElement) && isTextEntryElement({
element: activeElement
});
}
var styles = {
root: "_191wglyw _t51zglyw _bfhk1bhr _16qs130s _vchhusvi _4t3ieqxy _152timx3 _kqsw1if8 _1bsb1ego _1pbycs5v _14b5hc79 _qilnk0mc _p5clia51 _4ap3vuon _scbpglyw _89fi1b6c",
chatPanelSizing: "_1bsbhc79",
managedOverlay: "_cpcf1gzg _6kttglyw _w8v11bhr _1jug130s",
flyoutOpen: "_qiln1gzg _p5clglyw _4ap31bhr _scbp130s _139f8iot _1tpvfnf5 _sylc1a5r _oyeijq3t",
flyoutAnimateClosed: "_1e0cglyw _qiln1gzg _139f8iot _1tpvfnf5 _sylc1a5r _1uwsjq3t",
flexContainer: "_4t3i1osq _1e0c1txw _2lx21bp4 _1bah1yb4",
hiddenMobileAndDesktop: "_1e0cglyw",
hiddenMobileOnly: "_1e0cglyw _dm2518uv",
hiddenDesktopOnly: "_dm25glyw"
};
/**
* Styles for the side nav enter/exit animations when `platform-dst-motion-uplift-sidenav` is enabled.
*
* These use the `motion.sidenav.*` tokens (keyframe animations) instead of the `transform` transitions
* with `@starting-style` used by `styles`.
*
* ⚠️ The `transition-duration` values below need to stay in sync with the duration baked into the tokens
* (`motion.sidenav.enter.*` is 250ms, `motion.sidenav.exit.*` is 200ms). The `display` property is
* transitioned (with `allow-discrete`) so the element stays rendered for the length of the exit animation —
* if the transition is shorter than the animation, `display: none` applies early and the exit animation
* never runs.
*/
var motionUpliftStyles = {
animationRTLSupport: "_cjprp6j8 _1p2s1vfh _1jq41dy6 _2qe0jwwr",
animationBaseStyles: "_10t81t0s _1vrh1a5r _xrrpi9qs",
expandAnimationMobile: "_k1af10ed",
collapseAnimationMobile: "_1lh81gzg _k1affm5y",
flyoutOpen: "_qiln1gzg _p5clglyw _4ap31bhr _scbp130s _4xci10ed _1tpv1mzy _sylc1a5r",
flyoutAnimateClosed: "_1e0cglyw _qiln1gzg _4xcifm5y _1tpvi9qs _sylc1a5r",
expandAnimationDesktop: "_1sg8k0mc _n2in10ed _1xpz1gzg _17ly1q8v _hjoi147i _1wxo4so3",
collapseAnimationDesktop: "_1sg81gzg _n2infm5y _17ly1wzp _hjoi1wsm _1wxo1cio"
};
var fallbackDefaultWidth = 320;
var onPeekStartDelayMs = exports.onPeekStartDelayMs = 500;
/**
* We need an additional component layer so we can wrap the side nav in a `OpenLayerObserver` and have access to the
* context value.
*/
function SideNavInternal(_ref4) {
var _managedSizing$mode, _managedSizing$width, _sideNavState$lastTri;
var children = _ref4.children,
defaultCollapsed = _ref4.defaultCollapsed,
_ref4$defaultWidth = _ref4.defaultWidth,
defaultWidthProp = _ref4$defaultWidth === void 0 ? fallbackDefaultWidth : _ref4$defaultWidth,
_ref4$minWidth = _ref4.minWidth,
minWidth = _ref4$minWidth === void 0 ? fallbackMinWidth : _ref4$minWidth,
maxWidth = _ref4.maxWidth,
testId = _ref4.testId,
_ref4$label = _ref4.label,
label = _ref4$label === void 0 ? 'Sidebar' : _ref4$label,
_ref4$skipLinkLabel = _ref4.skipLinkLabel,
skipLinkLabel = _ref4$skipLinkLabel === void 0 ? label : _ref4$skipLinkLabel,
onExpand = _ref4.onExpand,
onCollapse = _ref4.onCollapse,
onPeekStart = _ref4.onPeekStart,
onPeekEnd = _ref4.onPeekEnd,
providedId = _ref4.id,
canToggleWithShortcut = _ref4.canToggleWithShortcut;
var isChatPanelLayoutEnabled = (0, _fg.fg)('platform-dst-chat-panel-layout');
var id = (0, _useLayoutId.useLayoutId)({
providedId: providedId
});
var expandAndFocusSideNav = (0, _useExpandSideNav.useExpandSideNav)({
trigger: 'skip-link'
});
/**
* Called after clicking on the side nav skip link, and ensures the side nav is expanded so that it is focusable.
*
* We need to update the DOM synchronously because `.focus()` is called synchronously after this state update.
*
* Only used when `platform_dst_nav4_skip_link_a11y_1` is OFF; can be removed on gate cleanup.
*/
var synchronouslyExpandSideNav = (0, _react.useCallback)(function () {
(0, _reactDom.flushSync)(function () {
/**
* Calling this unconditionally and relying on it to avoid no-op renders.
*
* We _could_ call it conditionally, but we'd be duplicating the screen size checks `expandAndFocusSideNav` makes.
*/
expandAndFocusSideNav();
});
}, [expandAndFocusSideNav]);
(0, _useSkipLinkInternal.useSkipLinkInternal)({
id: id,
label: skipLinkLabel,
/**
* `navigate` is the gate-on contract: it owns expanding the side nav AND moving
* focus to the first nav item, atomically (via `useExpandSideNav`'s `flushSync`).
*
* `onBeforeNavigate` is the legacy contract used only when the gate is OFF.
* On gate cleanup, drop `onBeforeNavigate` and `synchronouslyExpandSideNav` here.
*/
navigate: (0, _fg.fg)('platform_dst_nav4_skip_link_a11y_1') ? expandAndFocusSideNav : undefined,
onBeforeNavigate: (0, _fg.fg)('platform_dst_nav4_skip_link_a11y_1') ? undefined : synchronouslyExpandSideNav
});
var sideNavState = (0, _react.useContext)(_sideNavVisibilityState.SideNavVisibilityState);
var setSideNavState = (0, _react.useContext)(_setSideNavVisibilityState.SetSideNavVisibilityState);
var _useSideNavVisibility = (0, _useSideNavVisibility2.useSideNavVisibility)({
defaultCollapsed: defaultCollapsed
}),
isExpandedOnDesktop = _useSideNavVisibility.isExpandedOnDesktop,
isExpandedOnMobile = _useSideNavVisibility.isExpandedOnMobile;
// We are placing `defaultCollapsed` into a state container so we can have a stable reference to the initial value.
// This is so we can use it in an effect _that only runs once_, after the initial render on the client,
// to sync the side nav context (provided in `<Root>`) with the `defaultCollapsed` prop provided to `<SideNav>`.
var _useState = (0, _react.useState)(defaultCollapsed),
_useState2 = (0, _slicedToArray2.default)(_useState, 1),
initialDefaultCollapsed = _useState2[0];
var _useAnalyticsEvents = (0, _useAnalyticsEvents2.useAnalyticsEvents)(),
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
var _useState3 = (0, _react.useState)(isExpandedOnDesktop),
_useState4 = (0, _slicedToArray2.default)(_useState3, 1),
initialIsExpandedOnDesktop = _useState4[0];
/**
* Captures the initial collapsed/expanded state of the side nav.
*
* Only firing on desktop because the nav is never open by default on mobile.
*/
(0, _react.useEffect)(function () {
if (initialIsExpandedOnDesktop) {
var isDesktop = window.matchMedia('(min-width: 64rem)').matches;
if (isDesktop) {
var navigationAnalyticsEvent = createAnalyticsEvent({
source: 'topNav',
actionSubject: 'sideNav',
action: 'viewedOnLoad',
actionSubjectId: 'sideNavMenu',
attributes: {
screen: 'desktop'
}
});
navigationAnalyticsEvent.fire('navigation');
}
}
}, [createAnalyticsEvent, initialIsExpandedOnDesktop]);
var defaultWidth = (0, _useSafeDefaultWidth.useSafeDefaultWidth)({
defaultWidthProp: defaultWidthProp,
fallbackDefaultWidth: fallbackDefaultWidth,
slotName: 'SideNav'
});
var _useState5 = (0, _react.useState)(defaultWidth),
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
inlineWidth = _useState6[0],
setInlineWidth = _useState6[1];
var _useState7 = (0, _react.useState)(defaultWidth),
_useState8 = (0, _slicedToArray2.default)(_useState7, 2),
overlayWidth = _useState8[0],
setOverlayWidth = _useState8[1];
var _useState9 = (0, _react.useState)(false),
_useState0 = (0, _slicedToArray2.default)(_useState9, 2),
isInline = _useState0[0],
setIsInline = _useState0[1];
(0, _react.useEffect)(function () {
if (!isChatPanelLayoutEnabled) {
return;
}
var mediaQuery = window.matchMedia('(min-width: 64rem)');
setIsInline(mediaQuery.matches);
var onChange = function onChange(event) {
return setIsInline(event.matches);
};
return (0, _bindEventListener.bind)(mediaQuery, {
type: 'change',
listener: onChange
});
}, [isChatPanelLayoutEnabled]);
var toggleVisibilityForChatPanel = (0, _useToggleSideNav.useToggleSideNav)({
trigger: 'programmatic'
});
var closeOverlayForChatPanel = (0, _react.useCallback)(function () {
// Allocation can overlay an expanded desktop nav when other slots consume
// its space. Dismiss the visibility state selected by the viewport, not the mode.
var isDesktop = window.matchMedia('(min-width: 64rem)').matches;
if (isDesktop ? isExpandedOnDesktop : isExpandedOnMobile) {
toggleVisibilityForChatPanel();
}
}, [isExpandedOnDesktop, isExpandedOnMobile, toggleVisibilityForChatPanel]);
var _useLayoutAreaSizing = (0, _useLayoutAreaSizing2.useLayoutAreaSizing)({
area: 'side-nav',
config: {
defaultWidth: defaultWidth,
minWidth: minWidth,
maxWidth: maxWidth,
isOpen: isInline ? isExpandedOnDesktop : isExpandedOnMobile,
onRequestClose: closeOverlayForChatPanel
}
}),
managedSizing = _useLayoutAreaSizing.state,
isManaged = _useLayoutAreaSizing.isManaged,
prepareResize = _useLayoutAreaSizing.prepareResize,
getManagedResizeBounds = _useLayoutAreaSizing.getResizeBounds,
startResize = _useLayoutAreaSizing.startResize,
resize = _useLayoutAreaSizing.resize,
completeResize = _useLayoutAreaSizing.completeResize;
var mode = (_managedSizing$mode = managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) !== null && _managedSizing$mode !== void 0 ? _managedSizing$mode : isInline ? 'inline' : 'overlay';
var renderedWidth = isChatPanelLayoutEnabled ? (_managedSizing$width = managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.width) !== null && _managedSizing$width !== void 0 ? _managedSizing$width : mode === 'inline' ? inlineWidth : overlayWidth : inlineWidth;
var configuredMaxWidth = maxWidth !== null && maxWidth !== void 0 ? maxWidth : '100vw';
var clampedWidth = !isChatPanelLayoutEnabled ? "clamp(240px, ".concat(inlineWidth, "px, 50vw)") : managedSizing ? "".concat(renderedWidth, "px") : "clamp(".concat(minWidth, ", ").concat(renderedWidth, "px, ").concat(configuredMaxWidth, ")");
var dangerouslyHoistSlotSizes = (0, _react.useContext)(_hoistSlotSizesContext.DangerouslyHoistSlotSizes);
var navRef = (0, _react.useRef)(null);
var getResizeBounds = (0, _react.useCallback)(function () {
if (!isChatPanelLayoutEnabled) {
return {
min: '240px',
max: '50vw'
};
}
var managedBounds = getManagedResizeBounds();
if (managedBounds) {
return managedBounds;
}
var sideNav = navRef.current;
var layoutRoot = sideNav === null || sideNav === void 0 ? void 0 : sideNav.parentElement;
var main = layoutRoot === null || layoutRoot === void 0 ? void 0 : layoutRoot.querySelector(':scope > [data-layout-slot][role="main"]');
var fallbackMaxWidth = maxWidth !== null && maxWidth !== void 0 ? maxWidth : mode === 'inline' ? '100vw' : '90vw';
if (!sideNav || mode === 'overlay') {
var chatPanel = layoutRoot === null || layoutRoot === void 0 ? void 0 : layoutRoot.querySelector(':scope > [data-layout-chat-panel]');
var inlineChatPanelWidth = chatPanel && getComputedStyle(chatPanel).gridArea === 'chat-panel' ? chatPanel.getBoundingClientRect().width : 0;
var overlayMaxWidth = Math.floor((window.innerWidth - inlineChatPanelWidth) * 0.9);
var _constrainedMaxWidth = maxWidth ? Math.min((0, _convertResizeBoundToPixels.convertResizeBoundToPixels)(maxWidth), overlayMaxWidth) : overlayMaxWidth;
return {
min: minWidth,
max: "".concat(Math.max((0, _convertResizeBoundToPixels.convertResizeBoundToPixels)(minWidth), _constrainedMaxWidth), "px")
};
}
if (!main) {
return {
min: minWidth,
max: fallbackMaxWidth
};
}
var sideNavWidth = sideNav.getBoundingClientRect().width;
var mainWidth = main.getBoundingClientRect().width;
// JSDOM does not perform layout, so retain the static fallback in unit tests and other
// environments where used widths cannot be measured.
if (sideNavWidth === 0 || mainWidth === 0) {
return {
min: minWidth,
max: fallbackMaxWidth
};
}
var localPanel = main.querySelector('[data-layout-with-panel-slot]');
var isLocalPanelInline = localPanel && getComputedStyle(localPanel).gridArea === 'panel';
var localPanelWidth = isLocalPanelInline ? localPanel.getBoundingClientRect().width : 0;
var mainContentWidth = mainWidth - localPanelWidth;
var mainMinimumWidth = getPixelCustomProperty(main, _constants.mainMinimumWidthVar, _constants.defaultMainMinWidth);
var availableWidth = Math.max(0, mainContentWidth - mainMinimumWidth);
var availableInlineWidth = Math.floor(sideNavWidth + availableWidth);
var constrainedMaxWidth = maxWidth ? Math.min((0, _convertResizeBoundToPixels.convertResizeBoundToPixels)(maxWidth), availableInlineWidth) : availableInlineWidth;
return {
min: minWidth,
max: "".concat(Math.max((0, _convertResizeBoundToPixels.convertResizeBoundToPixels)(minWidth), constrainedMaxWidth), "px")
};
}, [getManagedResizeBounds, isChatPanelLayoutEnabled, maxWidth, minWidth, mode]);
/**
* Used to share the side nav element with the `Panel`,
* which observes the side nav to determine its maximum width.
*/
var sharedRef = (0, _useSideNavRef.useSideNavRef)();
var mergedRef = (0, _mergeRefs.default)([navRef, sharedRef]);
var onCompleteResize = (0, _react.useCallback)(function (finalWidth) {
if (!isChatPanelLayoutEnabled) {
setInlineWidth(finalWidth);
return;
}
if (mode === 'inline') {
setInlineWidth(finalWidth);
} else {
setOverlayWidth(finalWidth);
}
completeResize(mode, finalWidth);
}, [completeResize, isChatPanelLayoutEnabled, mode]);
var onResizeStartInternal = (0, _react.useCallback)(function () {
startResize(mode);
}, [mode, startResize]);
var onResizeInternal = (0, _react.useCallback)(function (width) {
return resize(mode, width);
}, [mode, resize]);
var toggleButtonElement = (0, _react.useContext)(_sideNavToggleButtonElement.SideNavToggleButtonElement);
var topNavStartElement = (0, _react.useContext)(_topNavStartElement.TopNavStartElement);
var devTimeOnlyAttributes = {};
var openLayerObserver = (0, _useOpenLayerObserver.useOpenLayerObserver)({
isEnabled: !(0, _fg.fg)('platform-dst-top-layer')
});
var flyoutStateRef = (0, _react.useRef)({
type: 'not-active'
});
var isFlyoutVisible = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open';
// Flyouts, including their close animation, already have overlay placement.
// The CSS-first inline allocation must not override it with an ID selector.
var shouldApplyUnmanagedSizing = !managedSizing && (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) !== 'open' && (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) !== 'triggered-animate-close';
var isExpandedOnDesktopRef = (0, _useStableRef.default)(isExpandedOnDesktop);
var hasPeekStartedRef = (0, _react.useRef)(false);
var onPeekStartRef = (0, _useStableRef.default)(onPeekStart);
var onPeekEndRef = (0, _useStableRef.default)(onPeekEnd);
var onPeekStartTimeoutIdRef = (0, _react.useRef)(undefined);
(0, _react.useEffect)(function () {
return function () {
clearTimeout(onPeekStartTimeoutIdRef.current);
};
}, []);
var updateFlyoutState = (0, _react.useMemo)(function () {
function tryAbortPendingClose() {
if (flyoutStateRef.current.type === 'waiting-for-close') {
flyoutStateRef.current.abort();
}
}
function open() {
var prevFlyoutState = flyoutStateRef.current;
tryAbortPendingClose();
flyoutStateRef.current = {
type: 'open'
};
setSideNavState(function (currentState) {
if ((currentState === null || currentState === void 0 ? void 0 : currentState.desktop) === 'collapsed' && (currentState === null || currentState === void 0 ? void 0 : currentState.flyout) !== 'open') {
return _objectSpread(_objectSpread({}, currentState), {}, {
flyout: 'open'
});
}
return currentState;
});
// Avoid redundant calls to `onPeekStart()`
if (prevFlyoutState.type === 'not-active') {
clearTimeout(onPeekStartTimeoutIdRef.current);
onPeekStartTimeoutIdRef.current = setTimeout(function () {
var _onPeekStartRef$curre;
// If the flyout isn't still open after ~500ms then we won't count the peek
// As we want to track user intention rather than all hovers
if (isExpandedOnDesktopRef.current || flyoutStateRef.current.type !== 'open') {
return;
}
hasPeekStartedRef.current = true;
(_onPeekStartRef$curre = onPeekStartRef.current) === null || _onPeekStartRef$curre === void 0 || _onPeekStartRef$curre.call(onPeekStartRef);
}, onPeekStartDelayMs);
}
}
function close() {
var prevFlyoutState = flyoutStateRef.current;
tryAbortPendingClose();
flyoutStateRef.current = {
type: 'not-active'
};
setSideNavState(function (currentState) {
if ((currentState === null || currentState === void 0 ? void 0 : currentState.desktop) === 'collapsed' && (currentState === null || currentState === void 0 ? void 0 : currentState.flyout) === 'open') {
return _objectSpread(_objectSpread({}, currentState), {}, {
flyout: 'triggered-animate-close'
});
}
return currentState;
});
// Avoid redundant calls to `onPeekEnd()`
if (prevFlyoutState.type !== 'not-active' && hasPeekStartedRef.current) {
var _onPeekEndRef$current;
hasPeekStartedRef.current = false;
(_onPeekEndRef$current = onPeekEndRef.current) === null || _onPeekEndRef$current === void 0 || _onPeekEndRef$current.call(onPeekEndRef, {
trigger: isExpandedOnDesktopRef.current ? 'side-nav-expand' : 'mouse-leave'
});
}
}
function canClose() {
if (!(0, _fg.fg)('platform-dst-top-layer')) {
return (openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.getCount({
namespace: _constants.openLayerObserverSideNavNamespace
})) === 0;
}
var nav = navRef.current;
return !(nav && isFlyoutHeldOpen({
nav: nav
}));
}
return function onAction(action) {
if (action === 'drag-from-flyout-started') {
open();
flyoutStateRef.current = {
type: 'is-dragging-from-flyout'
};
return;
}
if (action === 'drag-from-flyout-finished') {
open();
return;
}
// ignoring all actions until the drag is finished
if (flyoutStateRef.current.type === 'is-dragging-from-flyout') {
return;
}
if (action === 'open') {
open();
return;
}
if (action === 'waiting-for-close') {
if (flyoutStateRef.current.type === 'waiting-for-close') {
return;
}
// A timeout is used to close the flyout after a delay when the user mouses out of the flyout area, and to allow
// us to cancel the close if the user mouses back in.
var timeout = setTimeout(function () {
updateFlyoutState('ready-to-close');
}, _flyoutCloseDelayMs.sideNavFlyoutCloseDelayMs);
flyoutStateRef.current = {
type: 'waiting-for-close',
abort: function abort() {
clearTimeout(timeout);
}
};
return;
}
if (action === 'ready-to-close') {
// If there are no open layers, we can close the flyout.
if (canClose()) {
close();
return;
}
flyoutStateRef.current = {
type: 'ready-to-close'
};
return;
}
if (action === 'force-close') {
close();
return;
}
};
}, [isExpandedOnDesktopRef, onPeekEndRef, onPeekStartRef, openLayerObserver, setSideNavState]);
var toggleVisibilityByScreenResize = (0, _useToggleSideNav.useToggleSideNav)({
trigger: 'screen-resize'
});
var toggleVisibilityByClickOutsideOnMobile = (0, _useToggleSideNav.useToggleSideNav)({
trigger: 'click-outside-on-mobile'
});
// Using `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
// For SSR the new API is still necessary
(0, _react.useLayoutEffect)(function () {
if (sideNavState !== null) {
// Only need to do an initial sync if it hasn't been initialized from Root
return;
}
// Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
// after SSR hydration. This should only run once, after the initial render on the client.
(0, _react.startTransition)(function () {
setSideNavState({
desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
mobile: 'collapsed',
flyout: 'closed',
lastTrigger: null
});
});
}, [initialDefaultCollapsed, setSideNavState, sideNavState]);
var handleExpand = (0, _react.useCallback)(function (_ref5) {
var screen = _ref5.screen,
trigger = _ref5.trigger;
onExpand === null || onExpand === void 0 || onExpand({
screen: screen,
trigger: trigger
});
var navigationAnalyticsEvent = createAnalyticsEvent({
source: 'topNav',
actionSubject: 'sideNav',
action: 'expanded',
actionSubjectId: 'sideNavMenu',
attributes: {
trigger: trigger
}
});
navigationAnalyticsEvent.fire('navigation');
// When the side nav gets expanded, we close the flyout to reset it.
// This prevents the flyout from staying open and ensures we are respecting the user's intent to expand.
updateFlyoutState('force-close');
}, [onExpand, updateFlyoutState, createAnalyticsEvent]);
var handleCollapse = (0, _react.useCallback)(function (_ref6) {
var screen = _ref6.screen,
trigger = _ref6.trigger;
onCollapse === null || onCollapse === void 0 || onCollapse({
screen: screen,
trigger: trigger
});
var navigationAnalyticsEvent = createAnalyticsEvent({
source: 'topNav',
actionSubject: 'sideNav',
action: 'collapsed',
actionSubjectId: 'sideNavMenu',
attributes: {
trigger: trigger
}
});
navigationAnalyticsEvent.fire('navigation');
// When the side nav gets collapsed, we close the flyout to reset it.
// This prevents the flyout from staying open and ensures we are respecting the user's intent to collapse.
updateFlyoutState('force-close');
}, [onCollapse, updateFlyoutState, createAnalyticsEvent]);
(0, _useSideNavVisibilityCallbacks.useSideNavVisibilityCallbacks)({
onExpand: handleExpand,
onCollapse: handleCollapse,
isExpandedOnDesktop: isExpandedOnDesktop,
isExpandedOnMobile: isExpandedOnMobile,
lastTrigger: (_sideNavState$lastTri = sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.lastTrigger) !== null && _sideNavState$lastTri !== void 0 ? _sideNavState$lastTri : null
});
(0, _react.useEffect)(function () {
var mediaQueryList = window.matchMedia('(min-width: 64rem)');
return (0, _bindEventListener.bind)(mediaQueryList, {
type: 'change',
listener: function listener() {
if (mediaQueryList.matches) {
// We're transitioning from tablet to desktop viewport size.
// We forcibly show the side nav if it was shown on mobile.
if (isExpandedOnMobile && !isExpandedOnDesktop) {
toggleVisibilityByScreenResize();
}
}
}
});
}, [toggleVisibilityByScreenResize, isExpandedOnDesktop, isExpandedOnMobile]);
/**
* Close the mobile side nav if there is a click outside.
*
* Binding/unbinding on changes to `isExpandedOnMobile` instead of window size changes for simplicity.
* If we listened to window size changes we'd need nested `bind()` calls.
*/
(0, _react.useEffect)(function () {
if (!isExpandedOnMobile) {
return;
}
/**
* The expected URL fragment used by the side nav skip link.
*/
var skipLinkUrlFragment = "#".concat(id);
return (0, _bindEventListener.bind)(window, {
type: 'click',
listener: function listener(event) {
if (window.matchMedia('(min-width: 64rem)').matches) {
// Clicks on desktop should do nothing
return;
}
var sideNav = navRef.current;
if (!sideNav || !(event.target instanceof Node)) {
// Ignore if our element references are invalid
return;
}
if (!event.target.isConnected) {
/**
* If the element that was clicked is no longer in the document, we should not collapse the side nav.
* This can happen when the user clicks on a dropdown menu item (such as one used in a `...` More menu),
* as the dropdown menu will close after being clicked. By the time this event listener runs, the clicked
* element will no longer be in the document, so the check below for `sideNav.contains(event.target)` will fail.
*/
return;
}
if (sideNav.contains(event.target)) {
// Clicks inside the side nav should not close it
return;
}
if (toggleButtonElement !== null && toggleButtonElement !== void 0 && toggleButtonElement.contains(event.target)) {
// Clicks on the toggle button will already toggle the side nav,
// so we need to ignore it or we end up doing a double toggle
return;
}
if (event.target instanceof HTMLAnchorElement &&
/**
* Intentionally using `.getAttribute()` for simplicity to match exactly what we expect
* for skip links.
*
* Alternatives considered:
*
* - Using `.href` returns a full URL, which would then require extra parsing.
* - Using `.hash` could incorrectly capture `<a href="/another-page#skip-link-id>` so we'd need extra checks.
*
* This approach has an edge case of `<a href="/same-page#skip-link-id>` not being captured,
* but we are okay with that trade-off.
*/
event.target.getAttribute('href') === skipLinkUrlFragment) {
// This was a click on a link to the side nav, so we shouldn't try to collapse the SideNav.
// It was most likely the skip link, although anyone can render a link with the ID.
// In either case keeping the SideNav expanded makes sense.
return;
}
toggleVisibilityByClickOutsideOnMobile();
}
});
}, [id, isExpandedOnMobile, toggleButtonElement, toggleVisibilityByClickOutsideOnMobile]);
(0, _react.useEffect)(function () {
if (!toggleButtonElement) {
return;
}
return (0, _bindEventListener.bind)(toggleButtonElement, {
type: 'mouseenter',
listener: function listener() {
// Only flyout the side nav if the user hovered while the side nav was collapsed
if (isExpandedOnDesktop) {
return;
}
// Only flyout the side nav on desktop viewports
var _window$matchMedia = window.matchMedia('(min-width: 64rem)'),
matches = _window$matchMedia.matches;
if (matches) {
updateFlyoutState('open');
}
}
});
}, [updateFlyoutState, toggleButtonElement, isExpandedOnDesktop]);
(0, _react.useEffect)(function () {
if (!toggleButtonElement) {
return;
}
return (0, _bindEventListener.bind)(toggleButtonElement, {
type: 'mouseleave',
listener: function listener() {
// If the side nav is not in flyout mode, we don't need to do anything
if (!isFlyoutVisible) {
return;
}
updateFlyoutState('waiting-for-close');
}
});
}, [isFlyoutVisible, toggleButtonElement, updateFlyoutState]);
(0, _react.useEffect)(function () {
if (!navRef.current) {
return;
}
return (0, _bindEventListener.bind)(navRef.current, {
type: 'mouseenter',
listener: function listener() {
// If the side nav is not in flyout mode, we don't need to do anything
if (isExpandedOnDesktop || !isFlyoutVisible) {
return;
}
// The user mouses has moused back over the side nav
updateFlyoutState('open');
}
});
}, [isFlyoutVisible, updateFlyoutState, isExpandedOnDesktop]);
(0, _react.useEffect)(function () {
if (!navRef.current) {
return;
}
return (0, _bindEventListener.bind)(navRef.current, {
type: 'mouseleave',
listener: function listener() {
// If the side nav is not in flyout mode, we don't need to do anything
if (!isFlyoutVisible) {
return;
}
updateFlyoutState('waiting-for-close');
}
});
}, [isFlyoutVisible, updateFlyoutState]);
(0, _react.useEffect)(function () {
var nav = navRef.current;
if (!nav) {
return;
}
if (!isFlyoutVisible) {
return;
}
// Using a monitor rather than a drop target. Rationale:
// - We are not creating a drop target for the users to drop on,
// we are just interested in listening to events within an element
// - We do not want to impact `location.{*}.dropTargets` in events
return (0, _elementAdapter.monitorForElements)({
canMonitor: function canMonitor(_ref7) {
var source = _ref7.source;
return nav.contains(source.element);
},
onGenerateDragPreview: function onGenerateDragPreview() {
updateFlyoutState('drag-from-flyout-started');
},
onDrop: function onDrop(_ref8) {
var location = _ref8.location;
// Always marking the drag and done
updateFlyoutState('drag-from-flyout-finished');
// If the user dropped outside of the flyout, we will close the flyout
var underUsersPointer = document.elementFromPoint(location.current.input.clientX, location.current.input.clientY);
if (!nav.contains(underUsersPointer)) {
updateFlyoutState('waiting-for-close');
}
}
});
}, [isFlyoutVisible, updateFlyoutState]);
(0, _react.useEffect)(function () {
if (!topNavStartElement || !toggleButtonElement) {
return;
}
return (0, _bindEventListener.bind)(topNavStartElement, {
type: 'mouseover',
listener: function listener(event) {
// If the side nav is not in flyout mode, we don't need to do anything
if (isExpandedOnDesktop || !isFlyoutVisible) {
return;
}
if (event.target === topNavStartElement) {
// The mouse is directly over the TopNavStart element, so cancel any pending flyout closes.
updateFlyoutState('open');
return;
}
if (event.target instanceof Element && toggleButtonElement.contains(event.target)) {
// The mouse is over the toggle button or any of its children, so we don't want to close the flyout.
// We also don't need to cancel any pending closes, as we have separate event listeners for the toggle button mouse events.
return;
}
// The user has moused over a child element of the TopNavStart element that isn't the toggle button, e.g. the app switcher or nav logo,
// so we should close the flyout (with a delay).
updateFlyoutState('waiting-for-close');
}
});
}, [topNavStartElement, isFlyoutVisible, toggleButtonElement, isExpandedOnDesktop, updateFlyoutState]);
(0, _react.useEffect)(function () {
if (!topNavStartElement) {
return;
}
return (0, _bindEventListener.bind)(topNavStartElement, {
type: 'mouseleave',
listener: function listener() {
// If the side nav is not in flyout mode, we don't need to do anything
if (!isFlyoutVisible) {
return;
}
// The mouse has left the TopNavStart element, so we should close the flyout with a delay.
updateFlyoutState('waiting-for-close');
}
});
}, [topNavStartElement, isFlyoutVisible, updateFlyoutState]);
(0, _react.useEffect)(function () {
// Close the flyout if there are no more layers open and the user is not mousing over the
// flyout areas (side nav, TopNavStart element)
return openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.onChange(function (_ref9) {
var count = _ref9.count;
if (flyoutStateRef.current.type === 'ready-to-close' && count === 0) {
updateFlyoutState('force-close');
}
}, {
namespace: _constants.openLayerObserverSideNavNamespace
});
}, [openLayerObserver, updateFlyoutState]);
var focusOutTimeoutIdRef = (0, _react.useRef)(undefined);
(0, _react.useEffect)(function () {
// The same as the open layer observer `onChange` above, for when the `platform-dst-top-layer`
// gate is on. Top-layer surfaces opened from the side nav are DOM descendants of it.
var nav = navRef.current;
if (!nav || !(0, _fg.fg)('platform-dst-top-layer')) {
return;
}
function closeIfReady() {
if (flyoutStateRef.current.type !== 'ready-to-close' || !nav) {
return;
}
if (isFlyoutHeldOpen({
nav: nav
})) {
return;
}
updateFlyoutState('force-close');
}
var unbindLayerChange = (0, _bindOpenLayerChange.bindOpenLayerChange)({
within: nav,
onChange: closeIfReady
});
var unbindFocusOut = (0, _bindEventListener.bind)(nav, {
type: 'focusout',
listener: function listener() {
// Wait for focus to move before checking where it went.
clearTimeout(focusOutTimeoutIdRef.current);
focusOutTimeoutIdRef.current = setTimeout(closeIfReady, 0);
}
});
// Removing the focused text field (for example when an inline rename ends) fires no
// `focusout` in some browsers, so also check again when content inside the side nav changes.
var removalObserver = new MutationObserver(closeIfReady);
removalObserver.observe(nav, {
childList: true,
subtree: true
});
return function cleanup() {
unbindLayerChange();
unbindFocusOut();
removalObserver.disconnect();
clearTimeout(focusOutTimeoutIdRef.current);
};
}, [updateFlyoutState]);
(0, _react.useEffect)(function () {
// Clear flyout close timer if being unmounted
return function cleanupPendingFlyoutClose() {
if (flyoutStateRef.current.type === 'waiting-for-close') {
flyoutStateRef.current.abort();
}
};
}, []);
if (process.env.NODE_ENV !== 'production') {
var visible = [];
if (isExpandedOnMobile) {
visible.push('small');
}
if (isExpandedOnDesktop) {
visible.push('large');
}
if (isFlyoutVisible) {
visible.push('flyout');
}
devTimeOnlyAttributes['data-visible'] = visible.length ? visible.join(',') : 'false';
}
(0, _useSideNavToggleKeyboardShortcut.useSideNavToggleKeyboardShortcut)({
canToggleWithShortcut: canToggleWithShortcut
});
// Used to conditionally display the keyboard shortcut in the SideNavPanelSplitter tooltip.
var isShortcutEnabled = (0, _useIsSideNavShortcutEnabled.useIsSideNavShortcutEnabled)();
(0, _useResizingWidthCssVarOnRootElement.useResizingWidthCssVarOnRootElement)({
isEnabled: !isChatPanelLayoutEnabled || !managedSizing,
cssVar: panelSplitterResizingVar,
panelId: _constants.sideNavPanelSplitterId
});
var isExpandedStateDifferentFromInitial = isExpandedOnMobile || isExpandedOnDesktop !== initialIsExpandedOnDesktop;
/**
* Used to track (between renders) if the expanded state of the side nav has ever changed since mount.
*
* Cannot rely solely on `isExpandedStateDifferentFromInitial` because the expanded state can go back to the initial state.
*/
var hasExpandedStateChangedRef = (0, _react.useRef)(false);
(0, _react.useEffect)(function () {
if (hasExpandedStateChangedRef.current) {
return;
}
if (isExpandedStateDifferentFromInitial) {
hasExpandedStateChangedRef.current = true;
}
}, [isExpandedStateDifferentFromInitial]);
/**
* If the expanded state of the side nav has ever changed since mount.
*
* Used to prevent sidebar expand animations from running on the initial load.
* Otherwise, the `@starting-style` rule will cause the sidebar to slide in initially.
*/
var hasExpandedStateChanged = isExpandedStateDifferentFromInitial || hasExpandedStateChangedRef.current;
var isMotionUpliftEnabled = (0, _fg.fg)('platform-dst-motion-uplift-sidenav');
// This is only used for the regular expand and collapse animations, not the flyout animations.
var shouldShowSidebarToggleAnimation = isMotionUpliftEnabled &&
// We do not apply the animation styles on the initial render, as the `@starting-style` rule will cause the sidebar to
// slide in initially.
hasExpandedStateChanged &&
// We also do not apply the animation styles if the side nav is in flyout mode to make sure we don't override flyout styles.
// If we instead try to unset the `transform` property in the flyout styles using `transform: initial`, it results in the entire
// flyout animation being disabled.
!isFlyoutVisible &&
/**
* Disabling animations for Firefox, as it doesn't support animating the `display` property:
* https://caniuse.com/mdn-css_properties_display_is_transitionable
*
* As of October 2025, Firefox supports the expand animation, but not the collapse animation (it instantly changes to `display: none`).
* Because of this, we are disabling all sidebar animations for Firefox so it doesn't look like a bug.
*
* Unfortunately we cannot use `@supports` to target the support of `transition-behavior: allow-discrete` specifically
* for the `display` property. And `@supports at-rule(@starting-style)` is also not ready for browser use yet.
*
* We are using JS to detect Firefox and disable animations, instead of using CSS, as Compiled currently does not merge duplicate
* CSS at-rules when at-rules are nested: https://github.com/atlassian-labs/compiled/blob/e04a325915e1d13010205089e4915de0e53bc2d4/packages/css/src/plugins/merge-duplicate-at-rules.ts#L5
* Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
*/
!isFirefox;
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("nav", (0, _extends2.default)({
id: id
}, devTimeOnlyAttributes, {
"data-layout-slot": true,
"aria-label": label,
style: (0, _defineProperty2.default)({}, _constants.sideNavVar, clampedWidth),
ref: mergedRef,
"data-testid": testId,
className: (0, _runtime.ax)([styles.root, isChatPanelLayoutEnabled && styles.chatPanelSizing, isChatPanelLayoutEnabled && (managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) === 'overlay' && styles.managedOverlay, isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileOnly, !isExpandedOnDesktop && isExpandedOnMobile && !isFlyoutVisible && styles.hiddenDesktopOnly, !isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileAndDesktop, isMotionUpliftEnabled && motionUpliftStyles.animationRTLSupport, shouldShowSidebarToggleAnimation && isMotionUpliftEnabled && motionUpliftStyles.animationBaseStyles, isExpandedOnMobile && shouldShowSidebarToggleAnimation && isMotionUpliftEnabled && motionUpliftStyles.expandAnimationMobile, !isExpandedOnMobile && shouldShowSidebarToggleAnimation && isMotionUpliftEnabled && motionUpliftStyles.collapseAnimationMobile, isExpandedOnDesktop && shouldShowSidebarToggleAnimation && isMotionUpliftEnabled && motionUpliftStyles.expandAnimationDesktop, !isExpandedOnDesktop && shouldShowSidebarToggleAnimation && isMotionUpliftEnabled && motionUpliftStyles.collapseAnimationDesktop, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open' && isMotionUpliftEnabled && motionUpliftStyles.flyoutOpen, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open' && !isMotionUpliftEnabled && styles.flyoutOpen, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close' && isMotionUpliftEnabled && motionUpliftStyles.flyoutAnimateClosed, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close' && !isMotionUpliftEnabled && styles.flyoutAnimateClosed])
}), isChatPanelLayoutEnabled &&
/*#__PURE__*/
// CSS reserves only the nav minimum for chat's fit decision, before effects or lazy loading.
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
_react.default.createElement("style", null, "\n#".concat(_root.gridRootChatFitId, " { --n_fitNav: 0px; }\n").concat(shouldApplyUnmanagedSizing ? "#".concat(_root.gridRootId, " nav[data-layout-slot] { grid-area: main / aside / aside / aside; width: min(90%, ").concat(clampedWidth, "); }") : '', "\n@media (min-width: 64rem) {\n @container navigation-main-space (min-width: ").concat(minWidth, ") {\n #").concat(_root.gridRootChatFitId, " { --n_fitNav: ").concat(isExpandedOnDesktop ? minWidth : '0px', "; }\n ").concat(shouldApplyUnmanagedSizing ? "#".concat(_root.gridRootId, " nav[data-layout-slot] { grid-area: side-nav; width: min(").concat(clampedWidth, ", calc(100cqw + var(--n_fitNav) - var(--n_chatTrack, 0px))); }") : '', "\n }\n}\n")), /*#__PURE__*/_react.default.createElement(_dangerouslyHoistCssVarToDocumentRoot.DangerouslyHoistCssVarToDocumentRoot, {
variableName: _constants.sideNavLiveWidthVar,
value: "0px",
mediaQuery: '@media (min-width: 64rem)',
responsiveValue: isExpandedOnDesktop ? "var(".concat(panelSplitterResizingVar, ", ").concat(clampedWidth, ")") : 0
}), dangerouslyHoistSlotSizes &&
/*#__PURE__*/
// ------ START UNSAFE STYLES ------
// These styles are only needed for the UNSAFE legacy use case for Jira + Confluence.
// When they aren't needed anymore we can delete them wholesale.
_react.default.createElement(_dangerouslyHoistCssVarToDocumentRoot.DangerouslyHoistCssVarToDocumentRoot, {
variableName: _constants.UNSAFE_sideNavLayoutVar,
value: "var(".concat(_constants.sideNavLiveWidthVar, ")")
})
// ------ END UNSAFE STYLES ------
, /*#__PURE__*/_react.default.createElement(_provider.PanelSplitterProvider, {
panelId: _constants.sideNavPanelSplitterId,
panelRef: navRef,
panelWidth: renderedWidth,
onCompleteResize: onCompleteResize,
onResizeStartInternal: isChatPanelLayoutEnabled && isManaged ? onResizeStartInternal : undefined,
onResizeInternal: isChatPanelLayoutEnabled && isManaged ? onResizeInternal : undefined,
prepareResize: isChatPanelLayoutEnabled ? prepareResize : undefined,
getResizeBounds: getResizeBounds,
resizingCssVar: panelSplitterResizingVar
// Not resizable when in peek (flyout) mode.
,
isEnabled: !isFlyoutVisible,
shortcut: isShortcutEnabled ? _sideNavToggleTooltipKeyboardShortcut.sideNavToggleTooltipKeyboardShortcut : undefined
}, /*#__PURE__*/_react.default.createElement("div", {
className: (0, _runtime.ax)([styles.flexContainer])
}, children))));
}
/**
* The side navigation layout area. It will show on the left (inline start) of the screen.
*
* Use the side nav area components (`SideNavHeader`, `SideNavBody`, `SideNavFooter`) to position
* content within areas of the side nav.
*
* You can optionally render a `PanelSplitter` as a child to make the side navigation slot resizable.
*/
function SideNav(_ref1) {
var children = _ref1.children,
defaultCollapsed = _ref1.defaultCollapsed,
_ref1$defaultWidth = _ref1.defaultWidth,
defaultWidth = _ref1$defaultWidth === void 0 ? 320 : _ref1$defaultWidth,
minWidth = _ref1.minWidth,
maxWidth = _ref1.maxWidth,
testId = _ref1.testId,
label = _ref1.label,
_ref1$skipLinkLabel = _ref1.skipLinkLabel,
skipLinkLabel = _ref1$skipLinkLabel === void 0 ? label : _ref1$skipLinkLabel,
onExpand = _ref1.onExpand,
onCollapse = _ref1.onCollapse,
onPeekStart = _ref1.onPeekStart,
onPeekEnd = _ref1.onPeekEnd,
canToggleWithShortcut = _ref1.canToggleWithShortcut,
id = _ref1.id;
var sideNav = /*#__PURE__*/_react.default.createElement(SideNavInternal, {
defaultCollapsed: defaultCollapsed,
defaultWidth: defaultWidth,
minWidth: minWidth,
maxWidth: maxWidth,
testId: testId,
label: label,
skipLinkLabel: skipLinkLabel,
onExpand: onExpand,
onCollapse: onCollapse,
onPeekStart: onPeekStart,
onPeekEnd: onPeekEnd,
id: id,
canToggleWithShortcut: canToggleWithShortcut
}, children);
// Top-layer surfaces are found through the DOM, so the namespace is only needed when the gate is off.
if ((0, _fg.fg)('platform-dst-top-layer')) {
return sideNav;
}
return /*#__PURE__*/_react.default.createElement(_openLayerObserverNamespaceProvider.OpenLayerObserverNamespaceProvider, {
namespace: _constants.openLayerObserverSideNavNamespace
}, sideNav);
}