/* side-nav.tsx generated by @compiled/babel-plugin v0.38.1 */
import _extends from "@babel/runtime/helpers/extends";
import "./side-nav.compiled.css";
import { ax, ix } from "@compiled/react/runtime";
import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { bind } from 'bind-event-listener';
import { flushSync } from 'react-dom';
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
import { OpenLayerObserverNamespaceProvider, useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
import { fg } from '@atlaskit/platform-feature-flags';
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { media } from '@atlaskit/primitives/responsive';
import { useSkipLinkInternal } from '../../../context/skip-links/skip-links-context';
import { TopNavStartElement } from '../../../context/top-nav-start/top-nav-start-context';
import { useIsFhsEnabled } from '../../fhs-rollout/use-is-fhs-enabled';
import { bannerMountedVar, contentHeightWhenFixed, contentInsetBlockStart, localSlotLayers, openLayerObserverSideNavNamespace, sideNavLiveWidthVar, sideNavPanelSplitterId, sideNavVar, topNavMountedVar, UNSAFE_sideNavLayoutVar } from '../constants';
import { DangerouslyHoistSlotSizes } from '../hoist-slot-sizes-context';
import { DangerouslyHoistCssVarToDocumentRoot } from '../hoist-utils';
import { useLayoutId } from '../id-utils';
import { PanelSplitterProvider } from '../panel-splitter/provider';
import { useResizingWidthCssVarOnRootElement } from '../use-resizing-width-css-var-on-root-element';
import { useSafeDefaultWidth } from '../use-safe-default-width';
import { useSideNavRef } from './element-context';
import { sideNavFlyoutCloseDelayMs } from './flyout-close-delay-ms';
import { useIsSideNavShortcutEnabled } from './is-side-nav-shortcut-enabled-context';
import { sideNavToggleTooltipKeyboardShortcut } from './side-nav-toggle-tooltip-keyboard-shortcut';
import { SideNavToggleButtonElement } from './toggle-button-context';
import { useExpandSideNav } from './use-expand-side-nav';
import { useSideNavToggleKeyboardShortcut } from './use-side-nav-toggle-keyboard-shortcut';
import { useSideNavVisibility } from './use-side-nav-visibility';
import { useSideNavVisibilityCallbacks } from './use-side-nav-visibility-callbacks';
import { useToggleSideNav } from './use-toggle-side-nav';
import { SetSideNavVisibilityState, SideNavVisibilityState } from './visibility-context';
const panelSplitterResizingVar = '--n_snvRsz';
const widthResizeBounds = {
  min: '240px',
  max: '50vw'
};
function getResizeBounds() {
  return widthResizeBounds;
}

/**
 * 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.
 */
const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
const panelSplitterPortalTargetStyles = {
  root: "_kqswstnw _rjxpidpf _u7coidpf _4t3i1osq _165t56xv"
};
const styles = {
  root: "_nd5l1b6c _191wglyw _t51zglyw _bfhk1bhr _16qs130s _vchhusvi _4t3ieqxy _152timx3 _kqsw1if8 _1bsb1ego _1pbycs5v _14b5hc79 _qilnk0mc _p5clia51 _4ap3vuon _scbpglyw",
  flyoutOpen: "_qiln1gzg _p5clglyw _4ap31bhr _scbp130s _139f8iot _1tpvfnf5 _sylc1a5r _oyeijq3t",
  flyoutAnimateClosed: "_1e0cglyw _qiln1gzg _139f8iot _1tpvfnf5 _sylc1a5r _1uwsjq3t",
  animationRTLSupport: "_15yekb7n _65m41mrw",
  flyoutBaseStylesFullHeightSidebar: "_qiln1gzg _p5clglyw _4ap31bhr _scbp130s _17ly8iot _177m1a5r",
  flyoutOpenFullHeightSidebar: "_hjoifnf5 _vgub1ku9 _s2eg1p9u",
  flyoutAnimateClosedFullHeightSidebar: "_dm25glyw _hjoifnf5 _vgub1mm8 _hh1u1p9u",
  flexContainer: "_4t3i1osq _1e0c1txw _2lx21bp4 _1bah1yb4",
  hiddenMobileAndDesktop: "_1e0cglyw",
  hiddenMobileOnly: "_1e0cglyw _dm2518uv",
  hiddenDesktopOnly: "_dm25glyw",
  animationBaseStyles: "_10t88iot _1vrh1a5r _xrrpfnf5",
  expandAnimationMobile: "_1xq51ku9 _1nu51p9u",
  collapseAnimationMobile: "_1lh81gzg _1xq51mm8 _aadi1p9u",
  expandAnimationDesktop: "_1sg8k0mc _vgub1ku9 _s2eg1p9u",
  collapseAnimationDesktop: "_1sg81gzg _vgub1mm8 _hh1u1p9u",
  fullHeightSidebar: "_165t56xv _180k1wjm _26vxoned _1mt19dtb"
};
const fallbackDefaultWidth = 320;
export const 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({
  children,
  defaultCollapsed,
  defaultWidth: defaultWidthProp = fallbackDefaultWidth,
  testId,
  label = 'Sidebar',
  skipLinkLabel = label,
  onExpand,
  onCollapse,
  onPeekStart,
  onPeekEnd,
  id: providedId,
  canToggleWithShortcut
}) {
  var _sideNavState$lastTri;
  const isFhsEnabled = useIsFhsEnabled();
  const id = useLayoutId({
    providedId
  });
  const expandSideNav = 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.
   */
  const synchronouslyExpandSideNav = useCallback(() => {
    flushSync(() => {
      /**
       * 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 `expandSideNav` makes.
       */
      expandSideNav();
    });
  }, [expandSideNav]);
  useSkipLinkInternal({
    id,
    label: skipLinkLabel,
    onBeforeNavigate: synchronouslyExpandSideNav
  });
  const sideNavState = useContext(SideNavVisibilityState);
  const setSideNavState = useContext(SetSideNavVisibilityState);
  const {
    isExpandedOnDesktop,
    isExpandedOnMobile
  } = useSideNavVisibility({
    defaultCollapsed
  });
  // 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>`.
  const [initialDefaultCollapsed] = useState(defaultCollapsed);
  const {
    createAnalyticsEvent
  } = useAnalyticsEvents();
  const [initialIsExpandedOnDesktop] = useState(isExpandedOnDesktop);

  /**
   * Captures the initial collapsed/expanded state of the side nav.
   *
   * Only firing on desktop because the nav is never open by default on mobile.
   */
  useEffect(() => {
    if (initialIsExpandedOnDesktop && fg('platform_dst_nav4_fhs_instrumentation_1')) {
      const isDesktop = window.matchMedia('(min-width: 64rem)').matches;
      if (isDesktop) {
        const navigationAnalyticsEvent = createAnalyticsEvent({
          source: 'topNav',
          actionSubject: 'sideNav',
          action: 'viewedOnLoad',
          actionSubjectId: 'sideNavMenu',
          attributes: {
            screen: 'desktop'
          }
        });
        navigationAnalyticsEvent.fire('navigation');
      }
    }
  }, [createAnalyticsEvent, initialIsExpandedOnDesktop]);
  const defaultWidth = useSafeDefaultWidth({
    defaultWidthProp,
    fallbackDefaultWidth,
    slotName: 'SideNav'
  });
  const [width, setWidth] = useState(defaultWidth);
  const clampedWidth = `clamp(${widthResizeBounds.min}, ${width}px, ${widthResizeBounds.max})`;
  const dangerouslyHoistSlotSizes = useContext(DangerouslyHoistSlotSizes);
  const navRef = useRef(null);
  const panelSplitterPortalTargetRef = useRef(null);
  /**
   * Used to share the side nav element with the `Panel`,
   * which observes the side nav to determine its maximum width.
   */
  const sharedRef = useSideNavRef();
  const mergedRef = mergeRefs([navRef, sharedRef]);
  const toggleButtonElement = useContext(SideNavToggleButtonElement);
  const topNavStartElement = useContext(TopNavStartElement);
  const devTimeOnlyAttributes = {};
  const openLayerObserver = useOpenLayerObserver();
  const flyoutStateRef = useRef({
    type: 'not-active'
  });
  const isFlyoutVisible = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open';
  const isExpandedOnDesktopRef = useStableRef(isExpandedOnDesktop);
  const hasPeekStartedRef = useRef(false);
  const onPeekStartRef = useStableRef(onPeekStart);
  const onPeekEndRef = useStableRef(onPeekEnd);
  const onPeekStartTimeoutIdRef = useRef(undefined);
  useEffect(() => {
    return () => {
      clearTimeout(onPeekStartTimeoutIdRef.current);
    };
  }, []);
  const updateFlyoutState = useMemo(() => {
    function tryAbortPendingClose() {
      if (flyoutStateRef.current.type === 'waiting-for-close') {
        flyoutStateRef.current.abort();
      }
    }
    function open() {
      // Prevent the flyout from being opened if there are any open layers in the top nav start
      if (openLayerObserver && openLayerObserver.getCount({
        namespace: 'top-nav-start',
        type: 'popup'
      }) > 0 && fg('platform_dst_nav4_side_nav_resize_tooltip_feedback')) {
        return;
      }
      const prevFlyoutState = flyoutStateRef.current;
      tryAbortPendingClose();
      flyoutStateRef.current = {
        type: 'open'
      };
      setSideNavState(currentState => {
        if ((currentState === null || currentState === void 0 ? void 0 : currentState.desktop) === 'collapsed' && (currentState === null || currentState === void 0 ? void 0 : currentState.flyout) !== 'open') {
          return {
            ...currentState,
            flyout: 'open'
          };
        }
        return currentState;
      });

      // Avoid redundant calls to `onPeekStart()`
      if (prevFlyoutState.type === 'not-active') {
        clearTimeout(onPeekStartTimeoutIdRef.current);
        onPeekStartTimeoutIdRef.current = setTimeout(() => {
          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 ? void 0 : _onPeekStartRef$curre.call(onPeekStartRef);
        }, onPeekStartDelayMs);
      }
    }
    function close() {
      const prevFlyoutState = flyoutStateRef.current;
      tryAbortPendingClose();
      flyoutStateRef.current = {
        type: 'not-active'
      };
      setSideNavState(currentState => {
        if ((currentState === null || currentState === void 0 ? void 0 : currentState.desktop) === 'collapsed' && (currentState === null || currentState === void 0 ? void 0 : currentState.flyout) === 'open') {
          return {
            ...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 ? void 0 : _onPeekEndRef$current.call(onPeekEndRef, {
          trigger: isExpandedOnDesktopRef.current ? 'side-nav-expand' : 'mouse-leave'
        });
      }
    }
    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.
        const timeout = setTimeout(() => {
          updateFlyoutState('ready-to-close');
        }, sideNavFlyoutCloseDelayMs);
        flyoutStateRef.current = {
          type: 'waiting-for-close',
          abort() {
            clearTimeout(timeout);
          }
        };
        return;
      }
      if (action === 'ready-to-close') {
        // If there are no open layers, we can close the flyout.
        if ((openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.getCount({
          namespace: openLayerObserverSideNavNamespace
        })) === 0) {
          close();
          return;
        }
        flyoutStateRef.current = {
          type: 'ready-to-close'
        };
        return;
      }
      if (action === 'force-close') {
        close();
        return;
      }
    };
  }, [isExpandedOnDesktopRef, onPeekEndRef, onPeekStartRef, openLayerObserver, setSideNavState]);
  const toggleVisibilityByScreenResize = useToggleSideNav({
    trigger: 'screen-resize'
  });
  const toggleVisibilityByClickOutsideOnMobile = 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
  useLayoutEffect(() => {
    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.
    setSideNavState({
      desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
      mobile: 'collapsed',
      flyout: 'closed',
      lastTrigger: null
    });
  }, [initialDefaultCollapsed, setSideNavState, sideNavState]);
  const handleExpand = useCallback(({
    screen,
    trigger
  }) => {
    if (fg('platform_dst_nav4_fhs_instrumentation_1')) {
      onExpand === null || onExpand === void 0 ? void 0 : onExpand({
        screen,
        trigger
      });
      const navigationAnalyticsEvent = createAnalyticsEvent({
        source: 'topNav',
        actionSubject: 'sideNav',
        action: 'expanded',
        actionSubjectId: 'sideNavMenu',
        attributes: {
          trigger
        }
      });
      navigationAnalyticsEvent.fire('navigation');
    } else {
      onExpand === null || onExpand === void 0 ? void 0 : onExpand({
        screen
      });
    }

    // 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]);
  const handleCollapse = useCallback(({
    screen,
    trigger
  }) => {
    if (fg('platform_dst_nav4_fhs_instrumentation_1')) {
      onCollapse === null || onCollapse === void 0 ? void 0 : onCollapse({
        screen,
        trigger
      });
      const navigationAnalyticsEvent = createAnalyticsEvent({
        source: 'topNav',
        actionSubject: 'sideNav',
        action: 'collapsed',
        actionSubjectId: 'sideNavMenu',
        attributes: {
          trigger
        }
      });
      navigationAnalyticsEvent.fire('navigation');
    } else {
      onCollapse === null || onCollapse === void 0 ? void 0 : onCollapse({
        screen
      });
    }

    // 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]);
  useSideNavVisibilityCallbacks({
    onExpand: handleExpand,
    onCollapse: handleCollapse,
    isExpandedOnDesktop,
    isExpandedOnMobile,
    lastTrigger: (_sideNavState$lastTri = sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.lastTrigger) !== null && _sideNavState$lastTri !== void 0 ? _sideNavState$lastTri : null
  });
  useEffect(() => {
    const mediaQueryList = window.matchMedia('(min-width: 64rem)');
    return bind(mediaQueryList, {
      type: 'change',
      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.
   */
  useEffect(() => {
    if (!isExpandedOnMobile) {
      return;
    }

    /**
     * The expected URL fragment used by the side nav skip link.
     */
    const skipLinkUrlFragment = `#${id}`;
    return bind(window, {
      type: 'click',
      listener(event) {
        if (window.matchMedia('(min-width: 64rem)').matches) {
          // Clicks on desktop should do nothing
          return;
        }
        const 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]);
  useEffect(() => {
    if (!toggleButtonElement) {
      return;
    }
    return bind(toggleButtonElement, {
      type: 'mouseenter',
      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
        const {
          matches
        } = window.matchMedia('(min-width: 64rem)');
        if (matches) {
          updateFlyoutState('open');
        }
      }
    });
  }, [updateFlyoutState, toggleButtonElement, isExpandedOnDesktop]);
  useEffect(() => {
    if (!toggleButtonElement) {
      return;
    }
    return bind(toggleButtonElement, {
      type: 'mouseleave',
      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]);
  useEffect(() => {
    if (!navRef.current) {
      return;
    }
    return bind(navRef.current, {
      type: 'mouseenter',
      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]);
  useEffect(() => {
    if (!navRef.current) {
      return;
    }
    return bind(navRef.current, {
      type: 'mouseleave',
      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]);
  useEffect(() => {
    const 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 monitorForElements({
      canMonitor({
        source
      }) {
        return nav.contains(source.element);
      },
      onGenerateDragPreview() {
        updateFlyoutState('drag-from-flyout-started');
      },
      onDrop({
        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
        const underUsersPointer = document.elementFromPoint(location.current.input.clientX, location.current.input.clientY);
        if (!nav.contains(underUsersPointer)) {
          updateFlyoutState('waiting-for-close');
        }
      }
    });
  }, [isFlyoutVisible, updateFlyoutState]);
  useEffect(() => {
    if (!topNavStartElement || !toggleButtonElement) {
      return;
    }
    return bind(topNavStartElement, {
      type: 'mouseover',
      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]);
  useEffect(() => {
    if (!topNavStartElement) {
      return;
    }
    return bind(topNavStartElement, {
      type: 'mouseleave',
      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]);
  useEffect(() => {
    // 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(({
      count
    }) => {
      if (flyoutStateRef.current.type === 'ready-to-close' && count === 0) {
        updateFlyoutState('force-close');
      }
    }, {
      namespace: openLayerObserverSideNavNamespace
    });
  }, [openLayerObserver, updateFlyoutState]);
  useEffect(() => {
    // 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') {
    const visible = [];
    if (isExpandedOnMobile) {
      visible.push('small');
    }
    if (isExpandedOnDesktop) {
      visible.push('large');
    }
    if (isFlyoutVisible) {
      visible.push('flyout');
    }
    devTimeOnlyAttributes['data-visible'] = visible.length ? visible.join(',') : 'false';
  }
  useSideNavToggleKeyboardShortcut({
    canToggleWithShortcut
  });
  // Used to conditionally display the keyboard shortcut in the SideNavPanelSplitter tooltip.
  const isShortcutEnabled = useIsSideNavShortcutEnabled();
  useResizingWidthCssVarOnRootElement({
    isEnabled: true,
    cssVar: panelSplitterResizingVar,
    panelId: sideNavPanelSplitterId
  });
  const isFlyoutClosed = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'closed' || (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === undefined;
  const 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.
   */
  const hasExpandedStateChangedRef = useRef(false);
  useEffect(() => {
    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.
   */
  const hasExpandedStateChanged = isExpandedStateDifferentFromInitial || hasExpandedStateChangedRef.current;

  // This is only used for the regular expand and collapse animations, not the flyout animations.
  const shouldShowSidebarToggleAnimation = isFhsEnabled &&
  // 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.createElement("nav", _extends({
    id: id
  }, devTimeOnlyAttributes, {
    "data-layout-slot": true,
    "aria-label": label,
    style: {
      // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/enforce-style-prop
      [sideNavVar]: clampedWidth
    },
    ref: mergedRef,
    "data-testid": testId,
    className: ax([styles.root, isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileOnly, !isExpandedOnDesktop && isExpandedOnMobile && !isFlyoutVisible && styles.hiddenDesktopOnly, !isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileAndDesktop, isFhsEnabled && styles.animationRTLSupport, shouldShowSidebarToggleAnimation && styles.animationBaseStyles, isExpandedOnMobile && shouldShowSidebarToggleAnimation && styles.expandAnimationMobile, !isExpandedOnMobile && shouldShowSidebarToggleAnimation && styles.collapseAnimationMobile, isExpandedOnDesktop && shouldShowSidebarToggleAnimation && styles.expandAnimationDesktop, !isExpandedOnDesktop && shouldShowSidebarToggleAnimation && styles.collapseAnimationDesktop, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open' && !isFhsEnabled && styles.flyoutOpen, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close' && !isFhsEnabled && styles.flyoutAnimateClosed, ((sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open' || (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close') && !isFirefox && isFhsEnabled && styles.flyoutBaseStylesFullHeightSidebar, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close' && !isFirefox && isFhsEnabled && styles.flyoutAnimateClosedFullHeightSidebar, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open' && !isFirefox && isFhsEnabled && styles.flyoutOpenFullHeightSidebar, (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'triggered-animate-close' && !isFirefox && isFhsEnabled && styles.flyoutAnimateClosedFullHeightSidebar, isFlyoutClosed && isFhsEnabled && !fg('platform-dst-side-nav-layering-fixes') && styles.fullHeightSidebar])
  }), /*#__PURE__*/React.createElement(DangerouslyHoistCssVarToDocumentRoot, {
    variableName: sideNavLiveWidthVar,
    value: "0px",
    mediaQuery: media.above.md,
    responsiveValue: isExpandedOnDesktop ? `var(${panelSplitterResizingVar}, ${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.createElement(DangerouslyHoistCssVarToDocumentRoot, {
    variableName: UNSAFE_sideNavLayoutVar,
    value: `var(${sideNavLiveWidthVar})`
  })
  // ------ END UNSAFE STYLES ------
  , /*#__PURE__*/React.createElement(PanelSplitterProvider, {
    panelId: sideNavPanelSplitterId,
    panelRef: navRef,
    portalRef: isFhsEnabled && fg('platform-dst-side-nav-layering-fixes') ? panelSplitterPortalTargetRef : undefined,
    panelWidth: width,
    onCompleteResize: setWidth,
    getResizeBounds: getResizeBounds,
    resizingCssVar: panelSplitterResizingVar
    // Not resizable when in peek (flyout) mode.
    ,
    isEnabled: fg('platform-dst-side-nav-layering-fixes') ? !isFlyoutVisible :
    // Old behaviour has a bug: the panel splitter would only be visible on sm screens (between 48rem and 64rem)
    // if the side nav was expanded on desktop.
    isExpandedOnDesktop && !isFlyoutVisible,
    shortcut: isShortcutEnabled ? sideNavToggleTooltipKeyboardShortcut : undefined
  }, /*#__PURE__*/React.createElement("div", {
    className: ax([styles.flexContainer])
  }, children)), isFhsEnabled && fg('platform-dst-side-nav-layering-fixes') && /*#__PURE__*/React.createElement("div", {
    ref: panelSplitterPortalTargetRef,
    className: ax([panelSplitterPortalTargetStyles.root])
  }));
}

/**
 * The side navigation layout area. It will show on the left (inline start) of the screen.
 *
 * Use the side nav area components (`SideNavHeader`, `SideNavContent`, `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.
 */
export function SideNav({
  children,
  defaultCollapsed,
  defaultWidth = 320,
  testId,
  label,
  // Default value is defined in `SideNavInternal`
  skipLinkLabel = label,
  // Default value is defined in `SideNavInternal`
  onExpand,
  onCollapse,
  onPeekStart,
  onPeekEnd,
  canToggleWithShortcut,
  id
}) {
  return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
    namespace: openLayerObserverSideNavNamespace
  }, /*#__PURE__*/React.createElement(SideNavInternal, {
    defaultCollapsed: defaultCollapsed,
    defaultWidth: defaultWidth,
    testId: testId,
    label: label,
    skipLinkLabel: skipLinkLabel,
    onExpand: onExpand,
    onCollapse: onCollapse,
    onPeekStart: onPeekStart,
    onPeekEnd: onPeekEnd,
    id: id,
    canToggleWithShortcut: canToggleWithShortcut
  }, children));
}