/**
 * `<Menu.Root>` — Solid 2 port of Base UI React's `MenuRoot`
 * (`packages/react/src/menu/root/MenuRoot.tsx`).
 *
 * Groups every menu part. Owns the open/closed state (controlled `open`
 * prop or internal signal seeded from `defaultOpen`, per Base UI) and the
 * interaction-hook bundle (click + dismiss + listnav + typeahead). Wraps
 * itself in a `<FloatingTree>` when it's the top-level root so submenus
 * can participate in the tree.
 *
 * ### Port deviations from React
 *
 *  - The React port threads a shared store + selectors across every part.
 *    The Solid port instead exposes plain signal-accessors on the root
 *    context (same shape as `<Select.Root>`). `MenuStore` + `MenuHandle`
 *    exist for the imperative + multi-trigger surface;
 *    their internal state mirrors the root's signals and is the only
 *    surface the handle reads.
 *  - `useOpenInteractionType` is threaded; `openMethod`
 *    + trigger-props are exposed on `MenuRootContext`. Downstream
 *    behavior gating (touch-bypass, focus-on-open decisions) needs
 *    browser coverage to observe actual pointerType values.
 *  - Floating-tree cross-menu events are emitted:
 *    `menuopenchange` fires from `setOpen`, `itemhover` from
 *    `<Menu.Item>`'s `onPointerEnter`. `"close"` (cancel-on-mouseup)
 *    still pending the mouseup-on-item window.
 *  - Trigger focus-guard sentinels wired; hover-to-open
 *    + safe-polygon sibling auto-close still pending.
 *  - `closeParentOnEsc` is wired via `useDismiss`'s `bubbles.escapeKey`
 *    (latched at init).
 */

import { createEffect, createSignal, onCleanup, untrack, useContext } from "solid-js";
import { ContextMenuRootContext } from "../../context-menu/root/context-menu-root-context.jsx";
import { useBaseUiId } from "../../internals/base-ui-id.jsx";
import { createChangeEventDetails, REASONS } from "../../internals/change-event-details.jsx";
import { createControlledState } from "../../internals/create-controlled-state.jsx";
import { FloatingNode, FloatingTree, useFloatingNodeId, useFloatingParentNodeId, useFloatingTree } from "../../internals/floating-ui/components/floating-tree.jsx";
import { useDismiss } from "../../internals/floating-ui/hooks/use-dismiss.jsx";
import { useFloatingRootContext } from "../../internals/floating-ui/hooks/use-floating-root-context.jsx";
import { useInteractions } from "../../internals/floating-ui/hooks/use-interactions.jsx";
import { useListNavigation } from "../../internals/floating-ui/hooks/use-list-navigation.jsx";
import { useRole } from "../../internals/floating-ui/hooks/use-role.jsx";
import { useTypeahead } from "../../internals/floating-ui/hooks/use-typeahead.jsx";
import { readOption } from "../../internals/maybe-accessor.jsx";
import { useOpenChangeComplete } from "../../internals/popups/use-open-change-complete.jsx";
import { useOpenInteractionType } from "../../internals/popups/use-open-interaction-type.jsx";
import { useTimeout } from "../../internals/use-timeout.jsx";
import { useTransitionStatus } from "../../internals/use-transition-status/use-transition-status.jsx";
import { MenubarContext } from "../../menubar/menubar-context.jsx";
import { createMenu } from "../create-menu.jsx";
import { MenuStore } from "../store/menu-store.jsx";
import { useMenuSubmenuRootContext } from "../submenu-root/menu-submenu-root-context.jsx";
import { MenuRootContext, useMenuRootContext } from "./menu-root-context.jsx";
function MenuRootComponent(props) {
  const submenuContext = useMenuSubmenuRootContext();
  const parentMenu = useMenuRootContext(true);
  const nested = submenuContext != null && parentMenu != null;

  // Menubar interop — when this Menu.Root is nested inside a `<Menubar>` as
  // a TOP-LEVEL menu (not a submenu), we coordinate sibling
  // auto-close and the `data-has-submenu-open` flag on the menubar. Keep
  // the branching minimal: only read the context + register an opener/closer
  // slot. All cross-menu logic lives in the menubar; menu-root only emits
  // the "I opened/closed" signal.
  const parentMenubarContext = !nested ? useContext(MenubarContext) : null;

  // Context-menu interop — when this Menu.Root is
  // wrapped by `<ContextMenu.Root>`, we read the virtual-anchor signal
  // it owns and thread it into the floating-root context (as the
  // `positionReference` slot, which `useFloating` prefers over the raw
  // `referenceElement`). We also populate the context's `actionsRef`
  // with our `setOpen` so `<ContextMenu.Trigger>` can open imperatively,
  // and mirror the positioner element back to the context's
  // `positionerRef` so the trigger's mouseup handler knows whether a
  // release landed inside the popup. React's `<ContextMenu.Root>`
  // wraps the inner menu in `<MenuRootContext value={undefined}>` to
  // prevent outer menus from shadowing the context-menu detection; the
  // Solid port passes `null` for the same purpose, so `parentMenu`
  // reads null here. Nested context-menu detection (React's
  // `'nested-context-menu'` parent type) is scoped out of this pass.
  const parentContextMenuContext = !nested && parentMenu == null ? useContext(ContextMenuRootContext) : null;
  const modalProp = () => props.modal ?? true;
  const disabledProp = () => props.disabled ?? false;
  const loopFocusProp = () => props.loopFocus ?? true;
  const orientationProp = () => props.orientation ?? "vertical";
  const highlightItemOnHoverProp = () => props.highlightItemOnHover ?? true;
  const closeParentOnEscProp = () => props.closeParentOnEsc ?? false;

  // Controlled / uncontrolled open state (Base UI `open` / `defaultOpen`
  // contract): the `open` prop wins while its resolved value is defined;
  // otherwise an internal signal (seeded once from `defaultOpen`) backs
  // the state and the root's `setOpen` writes it.
  const [resolvedOpen, setUncontrolledOpen] = createControlledState({
    controlled: () => {
      const controlled = readOption(props.open);
      return controlled === undefined ? undefined : Boolean(controlled);
    },
    default: () => readOption(props.defaultOpen),
    fallback: false,
    name: "<Menu.Root>",
    state: "open"
  });

  // Compose the headless Menu primitive — owns open, disabled, modal,
  // activeIndex, and payload. The wrapper layers floating-UI, focus trap,
  // portal, and animation on top.
  const menuPrimitive = createMenu({
    open: resolvedOpen,
    disabled: () => props.disabled ?? false,
    modal: () => (props.modal ?? true) && !nested
    // onOpenChange is NOT wired here — the wrapper's own setOpen (below)
    // is the sole writer so it can thread reason/event details through the
    // full floating-UI pipeline. The primitive's setOpen is unused in the
    // wrapper path.
  });
  const open = menuPrimitive.open;

  // `activeTriggerId` is purely-internal coordination state — it tracks
  // which trigger spawned this menu instance for positioning + focus-return.
  // It is NOT a consumer-facing controlled prop (consumers use `triggerId`
  // to seed the initial value, but they don't own the signal). A plain
  // `createSignal` with `ownedWrite: true` is correct here; this is an
  // internal implementation detail, not the same kind of consumer-owned
  // state as `open`.
  const [activeTriggerId, setActiveTriggerId] = createSignal(props.triggerId ?? null, {
    ownedWrite: true,
    name: "menu.activeTriggerId"
  });

  // Delegate activeIndex + payload to the primitive for unified ownership.
  const activeIndex = menuPrimitive.activeIndex;
  const setActiveIndex = menuPrimitive.setActiveIndex;
  const payload = menuPrimitive.payload;
  const setPayload = menuPrimitive.setPayload;
  const [activeTriggerElement, setActiveTriggerElement] = createSignal(null, {
    ownedWrite: true
  });
  const [lastOpenChangeReason, setLastOpenChangeReason] = createSignal(null, {
    ownedWrite: true
  });
  const [instantType, setInstantType] = createSignal(undefined, {
    ownedWrite: true
  });

  // Element refs.
  const popupRef = {
    current: null
  };
  const positionerRef = {
    current: null
  };
  const listRef = {
    current: []
  };
  const labelsRef = {
    current: []
  };
  const typingRef = {
    current: false
  };
  const closeDelayRef = {
    current: 0
  };

  // Focus-guard refs — populated by `<Menu.Trigger>`'s
  // `useTriggerFocusGuards` integration. The root owns these slots so
  // other parts can reach them if needed.
  const beforeContentFocusGuardRef = {
    current: null
  };
  const triggerFocusTargetRef = {
    current: null
  };

  // Adopt the handle's store if supplied; otherwise own one. Mirrors the
  // popover pattern so `MenuHandle.open(triggerId)` writes
  // through to the same registry triggers read from.
  const store = props.handle?.store ?? new MenuStore();

  // Menu root id — useful for context-menu `data-rootownerid` attribution.
  const rootId = useBaseUiId();

  // Parent-type discriminant (mirrors React's `parent.type`). When the
  // outer `<ContextMenu.Root>` itself is nested inside another context-
  // menu, mark the inner menu's parent as `'nested-context-menu'` so
  // dismissal / open-behavior routes like a regular submenu.
  const parentType = (() => {
    if (nested) return "menu";
    if (parentMenubarContext != null) return "menubar";
    if (parentContextMenuContext != null) {
      return parentContextMenuContext.nestedInsideContextMenu ? "nested-context-menu" : "context-menu";
    }
    return undefined;
  })();

  // Mount / transition machinery.
  const {
    mounted,
    transitionStatus,
    setMounted
  } = useTransitionStatus(open, () => true, () => false);

  // Deferred unmount on exit animation.
  useOpenChangeComplete({
    open,
    ref: popupRef,
    onComplete: () => {
      if (!open()) {
        setMounted(false);
        setActiveIndex(null);
        props.onOpenChangeComplete?.(false);
      } else {
        props.onOpenChangeComplete?.(true);
      }
    }
  });

  // Floating node id + parent id for submenus / cross-tree events.
  const floatingNodeIdFromContext = useFloatingNodeId();
  const floatingParentNodeIdFromContext = useFloatingParentNodeId();
  const floatingTreeFromContext = useFloatingTree();

  // `useOpenInteractionType` — captures mouse/keyboard/touch at open time
  // Trigger spreads `openInteractionTypeProps`; `openMethod`
  // reads reactive and gates focus-on-open downstream.
  const {
    openMethod,
    triggerProps: openInteractionTypeProps
  } = useOpenInteractionType(open);
  const floatingRootContext = useFloatingRootContext({
    open: untrack(open),
    onOpenChange: (nextOpen, details) => {
      setOpen(nextOpen, details);
    }
  });
  createEffect(() => ({
    o: open(),
    trigger: activeTriggerElement(),
    s: transitionStatus(),
    // When wrapped by a context-menu, the anchor becomes the
    // virtual-element source of truth. Re-read on each tick so
    // positioning tracks the most recent right-click point.
    anchor: parentContextMenuContext?.anchor()
  }), ({
    o,
    trigger,
    s,
    anchor
  }) => {
    floatingRootContext.update({
      open: o,
      referenceElement: trigger,
      domReferenceElement: trigger,
      transitionStatus: s
    });
    // Prefer the virtual anchor for positioning when supplied —
    // `useFloating` uses `positionReference` over `referenceElement`.
    if (parentContextMenuContext != null && anchor) {
      floatingRootContext.update({
        positionReference: anchor
      });
    }
  });

  // setOpen — mirrors Base UI's change flow: fire `props.onOpenChange`,
  // honor `details.cancel()`, then write the uncontrolled state (the write
  // is ignored for reads while the `open` prop is controlled — the
  // consumer's signal is the source of truth), then trigger mirroring.
  const setOpen = (nextOpen, details) => {
    const reason = details.reason;
    const prevOpen = untrack(open);
    if (prevOpen === nextOpen && details.trigger === untrack(activeTriggerElement) && untrack(lastOpenChangeReason) === reason) {
      return;
    }
    setLastOpenChangeReason(reason);
    // Stash the native event type so the context-menu outside-press
    // grace window can distinguish a `contextmenu`-initiated open
    // (dismiss-through allowed) from a `mousedown`/long-press open
    // (dismiss-through gated by the 500ms timer).
    const rawEvent = details.event;
    if (nextOpen && rawEvent != null) {
      openEventType = rawEvent.type;
    }
    props.onOpenChange?.(nextOpen, details);
    if (details.isCanceled) return;

    // Uncontrolled write — inert while the `open` prop is controlled.
    setUncontrolledOpen(nextOpen);

    // Track the trigger across close for focus-return / exit animations.
    if (details.trigger) {
      setActiveTriggerElement(details.trigger);
    }

    // On close, retain the trigger element briefly (React pattern).
    if (!nextOpen && details.trigger == null) {
      // leave element in place
    }

    // Skip entry animation for dismiss + keyboard clicks.
    const nativeEvent = details.event;
    const isKeyboardClick = (reason === REASONS.triggerPress || reason === REASONS.itemPress) && nativeEvent != null && "detail" in nativeEvent && nativeEvent.detail === 0 && nativeEvent.isTrusted;
    const isDismissClose = !nextOpen && (reason === REASONS.escapeKey || reason == null);
    if (isKeyboardClick) setInstantType("click");else if (isDismissClose) setInstantType("dismiss");else setInstantType(undefined);

    // Emit `menuopenchange` on the top-level floating tree so menubar /
    // typeahead-routing / sibling-menu coordinators can react.
    // Matches React's `floatingTreeRoot.events.emit("menuopenchange",
    // ...)` emission shape.
    const tree = floatingTreeFromContext;
    if (tree != null) {
      tree.events.emit("menuopenchange", {
        open: nextOpen,
        nodeId: floatingNodeIdFromContext,
        parentNodeId: floatingParentNodeIdFromContext,
        reason,
        trigger: details.trigger ?? null
      });
    }
  };

  // Wire the store's imperative setOpen so `MenuHandle.open/close` flows
  // through the same reason-threading setOpen every other entry point uses.
  store.attachSetOpen((next, details) => {
    setOpen(next, details);
  });
  onCleanup(() => {
    store.attachSetOpen(null);
  });

  // Mirror reactive open into the store's synchronous `isOpen` for
  // `handle.isOpen` reads.
  createEffect(() => open(), o => {
    store.setOpenMirror(o);
  });

  // Implicit active-trigger selection — when a handle-driven consumer opens
  // without setting a `triggerId`, pick the sole registered trigger so
  // positioning + focus return still have a DOM anchor.
  createEffect(() => open(), o => {
    if (!o) return;
    if (untrack(activeTriggerId) !== null) return;
    if (store.context.triggerElements.size !== 1) return;
    const entry = store.context.triggerElements.entries().next();
    if (!entry.done) {
      const [id, el] = entry.value;
      setActiveTriggerId(id);
      setActiveTriggerElement(el);
    }
  });

  // Imperative actions.
  const handleUnmount = () => {
    setMounted(false);
    setActiveIndex(null);
  };
  const handleClose = () => {
    setOpen(false, createChangeEventDetails(REASONS.imperativeAction));
  };
  const actions = {
    unmount: handleUnmount,
    close: handleClose
  };
  const ar = props.actionsRef;
  if (typeof ar === "function") {
    ar(actions);
    onCleanup(() => ar(null));
  } else if (ar && typeof ar === "object") {
    ar.current = actions;
    onCleanup(() => {
      ar.current = null;
    });
  }

  // Context-menu interop: populate the outer context's `actionsRef` so
  // `<ContextMenu.Trigger>` can call `setOpen(true, ...)` imperatively
  // after writing the anchor. The `setOpen` captured here is the same
  // reason-threading path used by every other menu entry point.
  if (parentContextMenuContext != null) {
    const cm = parentContextMenuContext;
    cm.actionsRef.current = {
      setOpen: (nextOpen, details) => {
        setOpen(nextOpen, details);
      }
    };
    onCleanup(() => {
      if (cm.actionsRef.current != null) {
        cm.actionsRef.current = null;
      }
    });
  }

  // Mirror our positioner element back to the context-menu context so
  // the trigger's mouseup listener can tell whether a release landed
  // inside the popup (and therefore shouldn't cancel the open).
  const ownPositionerRef = positionerRef;
  if (parentContextMenuContext != null) {
    const cm = parentContextMenuContext;
    createEffect(() => mounted(), () => {
      cm.positionerRef.current = ownPositionerRef.current;
    });
    onCleanup(() => {
      cm.positionerRef.current = null;
    });
  }

  // Menubar interop — when mounted under a `<Menubar>`, emit
  // our open state to the parent so sibling menus can auto-close and the
  // menubar's `data-has-submenu-open` flag stays live.
  if (parentMenubarContext != null) {
    const mb = parentMenubarContext;
    let unregister = null;
    createEffect(() => open(), isOpen => {
      if (isOpen) {
        unregister?.();
        unregister = mb.registerOpenMenu(() => {
          setOpen(false, createChangeEventDetails(REASONS.siblingOpen));
        });
      } else {
        unregister?.();
        unregister = null;
      }
      return undefined;
    });
    onCleanup(() => {
      unregister?.();
      unregister = null;
    });
  }

  // Context-menu outside-press grace window. Mirrors React's
  // `allowOutsidePressDismissalRef` + 500ms post-open timer. After
  // `<ContextMenu.Trigger>` opens the menu, a racing `mousedown` on the
  // page can otherwise fire `outsidePress` → close before the user even
  // sees the menu. We gate the dismiss callback on this flag.
  //
  // Writes happen from an event handler (mount open-flip → timer callback),
  // both of which run in unowned scopes — no `ownedWrite` escape needed.
  let allowOutsidePressDismissal = parentType !== "context-menu";
  const allowOutsidePressTimeout = useTimeout();

  // React tracks the native event that opened the menu so a `contextmenu`
  // open is allowed to dismiss through immediately (the same native event
  // is what triggered the open — otherwise the very first right-click
  // outside the menu wouldn't close it). Mirror by stashing the reason.
  let openEventType = null;
  createEffect(() => open(), isOpen => {
    if (!isOpen) {
      openEventType = null;
    }
    if (parentType !== "context-menu") return;
    if (!isOpen) {
      allowOutsidePressTimeout.clear();
      allowOutsidePressDismissal = false;
      return;
    }
    allowOutsidePressTimeout.start(500, () => {
      allowOutsidePressDismissal = true;
    });
  });

  // Interaction hooks. `enabled` / `bubbles` accept accessors (P3);
  // reactive flips re-install on the fly.
  //
  // `outsidePress` is a function, so `createStableCallback` re-reads our
  // closure on each dispatch — this is safe to wire reactively.
  // Floating-UI interaction hooks build their prop bags eagerly at body time
  // and read store-backed signals while doing so. Those reads are intentional
  // snapshots (the hooks themselves expose getters that re-fire on tracked
  // reads), so wrap each call in `untrack(...)` to clear the
  // `[STRICT_READ_UNTRACKED]` label inherited from the `MenuRootComponent`
  // devComponent wrapper. The hooks aren't Solid components, so the outer
  // `untrack` is effective (not "futile" the way it is for child component
  // bodies that re-set their own `strictRead` label).
  const dismiss = untrack(() => useDismiss(floatingRootContext, {
    enabled: () => !disabledProp(),
    bubbles: {
      escapeKey: () => closeParentOnEscProp() && nested
    },
    outsidePress: () => {
      if (parentType !== "context-menu" || openEventType === "contextmenu") {
        return true;
      }
      return allowOutsidePressDismissal;
    }
  }));
  const role = untrack(() => useRole(floatingRootContext, {
    role: "menu"
  }));
  const listNavigation = untrack(() => useListNavigation(floatingRootContext, {
    enabled: () => !disabledProp(),
    listRef,
    activeIndex,
    nested: () => nested,
    loopFocus: loopFocusProp,
    orientation: orientationProp,
    onNavigate: nextIndex => {
      setActiveIndex(nextIndex);
    },
    focusItemOnHover: highlightItemOnHoverProp,
    // Suppress the arrow-key open path when the parent is
    // a context menu (React parity). Context-menu triggers don't
    // receive keyboard focus by default, but this closes the
    // corner-case where a trigger is inside a focusable wrapper.
    openOnArrowKeyDown: parentType !== "context-menu" && parentType !== "nested-context-menu"
  }));
  const typeahead = untrack(() => useTypeahead(floatingRootContext, {
    enabled: () => !disabledProp() && open(),
    listRef: labelsRef,
    activeIndex,
    resetMs: 500,
    onMatch: index => {
      if (untrack(open) && index !== untrack(activeIndex)) {
        setActiveIndex(index);
      }
    },
    onTypingChange: next => {
      typingRef.current = next;
    }
  }));
  const interactions = useInteractions([dismiss, role, listNavigation, typeahead]);
  const parentAccessor = () => parentMenu;
  const contextValue = {
    nested: () => nested,
    parent: parentAccessor,
    open,
    mounted,
    transitionStatus,
    modal: () => nested ? false : modalProp(),
    disabled: disabledProp,
    orientation: orientationProp,
    loopFocus: loopFocusProp,
    highlightItemOnHover: highlightItemOnHoverProp,
    activeIndex,
    setActiveIndex,
    payload,
    setPayload,
    activeTriggerElement,
    setActiveTriggerElement,
    activeTriggerId,
    setActiveTriggerId: id => {
      setActiveTriggerId(id);
    },
    lastOpenChangeReason,
    instantType,
    setOpen,
    floatingRootContext,
    interactions,
    listRef,
    labelsRef,
    typingRef,
    closeDelayRef,
    popupRef,
    positionerRef,
    onOpenChangeComplete: props.onOpenChangeComplete,
    floatingNodeId: () => floatingNodeIdFromContext,
    closeParentOnEsc: closeParentOnEscProp,
    rootId: () => rootId,
    parentType: () => parentType,
    store: store,
    openMethod,
    openInteractionTypeProps,
    beforeContentFocusGuardRef,
    triggerFocusTargetRef,
    floatingTreeRoot: () => floatingTreeFromContext,
    floatingParentNodeId: () => floatingParentNodeIdFromContext
  };

  // Render children inside a `get children()` context so the provider is
  // visible before JSX evaluates (papercut #1).
  const renderChildren = () => {
    const c = props.children;
    if (typeof c === "function") {
      return c({
        payload: payload()
      });
    }
    return c;
  };
  const treeNodeId = floatingNodeIdFromContext;
  return <MenuRootContext value={contextValue}>
			<FloatingNode id={treeNodeId}>{renderChildren()}</FloatingNode>
		</MenuRootContext>;
}

/**
 * Groups all parts of a menu. Renders no DOM element — the root is a pure
 * context provider. Wraps itself in a `<FloatingTree>` when not already
 * inside one.
 */
export function MenuRoot(props) {
  // Reuse the outer `FloatingTree` if one is already mounted (e.g. from
  // `<Menubar>` / `<ContextMenu.Root>`). A `FloatingNode` parent id isn't
  // required — the shared tree is what matters for cross-menu events.
  // Check the tree context directly rather than the parent
  // node id so standalone consumers that mount their own `<FloatingTree>`
  // can subscribe to `menuopenchange` without nesting under a
  // `<FloatingNode>`.
  if (useFloatingTree() != null) {
    return <MenuRootComponent {...props} />;
  }
  return <FloatingTree>
			<MenuRootComponent {...props} />
		</FloatingTree>;
}
//# sourceMappingURL=menu-root.jsx.map
