/* flyout-menu-item-content.tsx generated by @compiled/babel-plugin v0.38.1 */
import "./flyout-menu-item-content.compiled.css";
import { ax, ix } from "@compiled/react/runtime";
import React, { forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
import { useAnalyticsEvents } from '@atlaskit/analytics-next';
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
import { fg } from '@atlaskit/platform-feature-flags';
import { PopupContent } from '@atlaskit/popup/experimental';
import { OnCloseContext, SetIsOpenContext, TitleIdContextProvider } from './flyout-menu-item-context';
/**
 * The vertical offset in px to ensure the flyout container does not exceed the bounds of
 * the window. This matches the padding of the content container, and it's position within
 * the viewport.
 * 
 * - FlyoutMenuItemContent: paddingBlock: token('space.100'); – 8px top, 8px bottom
 * - Position: 5px top, 5px bottom
 * 
 * Total vertical padding:
 * 		(8px [content top] + 8px [content bottom]) +
 * 		(5px [position top] + 5px [position bottom]) = 26px
 */
const FLYOUT_MENU_VERTICAL_OFFSET_PX = 26;
const flyoutMenuItemContentStyles = {
  root: "_1q51u2gc _85i5u2gc _bozgu2gc _y4tiu2gc _14b51kdj"
};
const flyoutMenuItemContentContainerStyles = {
  container: "_1e0c1txw _4t3i1osq _c71l2gk1 _2lx21bp4"
};
/**
 * __FlyoutMenuItemContent__
 *
 * The content that appears when the flyout menu is open.
 */
export const FlyoutMenuItemContent = /*#__PURE__*/forwardRef(({
  children,
  containerTestId,
  onClose,
  autoFocus
}, forwardedRef) => {
  const setIsOpen = useContext(SetIsOpenContext);
  const onCloseRef = useContext(OnCloseContext);
  const {
    createAnalyticsEvent
  } = useAnalyticsEvents();

  // The source of the close is not accessible to the consumer, it is determined within the
  // handleClose function, or passed in as a parameter in FlyoutMenuItemTrigger (outside-click),
  // or FlyoutHeader (close-button).
  const handleClose = useCallback((event, source) => {
    if (fg("platform_dst_nav4_flyout_menu_slots_close_button")) {
      // Use the passed source if provided, otherwise determine from event
      let determinedSource = source || 'other';
      if (!source) {
        if (event instanceof KeyboardEvent) {
          const keyboardEvent = event;
          if (keyboardEvent.key === 'Escape' || keyboardEvent.key === 'Esc') {
            determinedSource = 'escape-key';
          }
        } else if (event instanceof MouseEvent) {
          if (event && 'type' in event && event.type === 'click') {
            determinedSource = 'outside-click';
          }
        }
      }

      // When flyout menu is closed, fire analytics event
      const navigationAnalyticsEvent = createAnalyticsEvent({
        source: 'sideNav',
        actionSubject: 'flyoutMenu',
        action: 'closed',
        attributes: {
          closeSource: determinedSource
        }
      });
      navigationAnalyticsEvent.fire('navigation');
    }
    onClose === null || onClose === void 0 ? void 0 : onClose();
    setIsOpen(false);
  }, [setIsOpen, onClose, createAnalyticsEvent]);

  // Register handleClose in the ref to allow the FlyoutMenuItemTrigger to access it
  useEffect(() => {
    onCloseRef.current = handleClose;
  }, [handleClose, onCloseRef]);
  const titleId = useId();
  return /*#__PURE__*/React.createElement(PopupContent, {
    appearance: "UNSAFE_modal-below-sm",
    onClose: handleClose,
    placement: "right-start"
    // Using a capture event listener so that we are more resilient against
    // code that stops events. We _really_ want to close the flyout whenever
    // user user clicks outside the flyout content
    ,
    shouldUseCaptureOnOutsideClick: true,
    shouldFitViewport: true,
    testId: containerTestId,
    xcss: flyoutMenuItemContentStyles.root,
    autoFocus: autoFocus,
    role: fg("platform_dst_nav4_flyout_menu_slots_close_button") ? "dialog" : undefined,
    titleId: fg("platform_dst_nav4_flyout_menu_slots_close_button") ? titleId : undefined
    /**
     * Disabling GPU acceleration removes the use of `transform` by popper.js for this popup.
     *
     * This allows makers to use popups with `shouldRenderToParent` inside the flyout.
     *
     * Without this, the `transform` makes the flyout the containing element for fixed positioning.
     * Because the flyout is also a scroll container then any nested, layered element is unable to
     * escape the flyout.
     *
     * Disabling the `transform` is the simplest way to resolve this layering issue,
     * and should have negligible performance impacts, because the flyout menus should rarely
     * need to be repositioned.
     */,
    shouldDisableGpuAcceleration: true,
    shouldRenderToParent: fg('platform_dst_nav4_flyoutmenuitem_render_to_parent')
  }, ({
    update
  }) => /*#__PURE__*/React.createElement(UpdatePopperOnContentResize, {
    ref: forwardedRef,
    update: update
  }, fg("platform_dst_nav4_flyout_menu_slots_close_button") ? /*#__PURE__*/React.createElement(TitleIdContextProvider, {
    value: titleId
  }, /*#__PURE__*/React.createElement("div", {
    className: ax([flyoutMenuItemContentContainerStyles.container])
  }, children)) : children));
});
function createResizeObserver(update) {
  return new ResizeObserver(update);
}

/**
 * Will call the Popper.js `update()` method to recalculate positioning, when the flyout menu changes size.
 * This is the size of the scroll container, NOT the scroll content.
 *
 * We could potentially bake this into `@atlaskit/popup` or `@atlaskit/popper` but there are a few
 * reasons to keep it scoped to flyout menus for now:
 *
 * 1. It's easier to unwind
 * 2. We've only had bug reports for flyout menus
 * 3. Popup exposes the `update` function so consumers can already do this themselves if necessary
 * 4. Flyout menus are a lot more restricted to other popups, it might not make sense more generally
 */
const UpdatePopperOnContentResize = /*#__PURE__*/forwardRef(({
  update,
  children
}, forwardedRef) => {
  /**
   * Storing our `update` function in a ref so that we have a stable reference to it.
   * We need this because our `ResizeObserver` callback cannot be changed after creating it.
   */
  const updateRef = useRef(update);
  useEffect(() => {
    updateRef.current = update;
  }, [update]);

  /**
   * Stable function that calls the latest `update` function by calling it through the stable ref.
   */
  const triggerUpdate = useCallback(() => {
    var _updateRef$current;
    (_updateRef$current = updateRef.current) === null || _updateRef$current === void 0 ? void 0 : _updateRef$current.call(updateRef);
  }, []);
  const [resizeObserver] = useState(() => createResizeObserver(triggerUpdate));

  /**
   * This is a callback ref that will update which `HTMLElement` we are observing,
   * if or when the underlying `HTMLElement` changes or unmounts.
   */
  const observeCallbackRef = useCallback(element => {
    /**
     * Unobserves all observed elements.
     * Allows us to cleanup without needing to store a reference to the previous element.
     */
    resizeObserver.disconnect();
    if (!element) {
      return;
    }
    resizeObserver.observe(element);
  }, [resizeObserver]);

  /**
   * We need to memoize the ref otherwise `triggerUpdate` is repeatedly called.
   *
   * This stems from ResizeObserver firing once after calling `.observe()` even if there
   * was no resize.
   *
   * Without memoizing the ref, the update causes a rerender, which causes the ref to
   * get recreated, which triggers an update and so on in a loop.
   */
  const ref = useMemo(() => {
    return mergeRefs([forwardedRef, observeCallbackRef]);
  }, [forwardedRef, observeCallbackRef]);
  return /*#__PURE__*/React.createElement("div", {
    ref: ref
  }, children);
});