/**
 * `MenuStore` + `MenuHandle` — thin Solid 2 port of Base UI React's
 * `MenuStore` / `MenuHandle` (`packages/react/src/menu/store/MenuStore.ts` +
 * `MenuHandle.ts`), mirroring the popover pattern already shipped
 * (`src/popover/store/popover-store.ts`).
 *
 * The React source threads a full store + selector pair through every
 * sub-part. The Solid `<Menu.Root>` uses its local signal-accessor context
 * (same shape as `<Select.Root>`); this store only carries the minimal
 * surface the imperative handle + multi-trigger registry need:
 *
 *  - A `PopupTriggerMap` so `<Menu.Trigger id="...">` can register and
 *    `MenuHandle.open(id)` can resolve the correct trigger.
 *  - A `setOpen` slot that `<Menu.Root>` binds during setup via
 *    {@link attachSetOpen}.
 *  - An `open` mirror so `handle.isOpen` returns synchronously.
 */

import { createChangeEventDetails, REASONS } from "../../internals/change-event-details.jsx";
import { PopupTriggerMap } from "../../internals/floating-ui/popup-trigger-map.jsx";

/**
 * Writer signature for `setOpen`. Matches the Solid root's internal
 * `setOpen(next, details)` callback.
 */

/**
 * Minimal store shape consumed by the handle + imperative callers.
 */
export class MenuStore {
  /**
   * Registry of triggers keyed by id. `<Menu.Trigger id={...}>` adds
   * itself here on mount, and `MenuHandle.open(id)` looks it up.
   */

  /**
   * Internal setOpen slot. `<Menu.Root>` binds this during setup via
   * {@link attachSetOpen}; clears on teardown.
   */
  _setOpen = null;

  /**
   * Last-seen reactive open flag. Used by `MenuHandle.isOpen` so the
   * caller can read synchronously without needing a subscription hook.
   */
  _open = false;
  constructor() {
    this.context = {
      triggerElements: new PopupTriggerMap()
    };
  }

  /**
   * Bind the root's `setOpen` writer.
   *
   * @internal
   */
  attachSetOpen(fn) {
    this._setOpen = fn;
  }

  /**
   * Mirror the root's current open flag. Called by `<Menu.Root>`'s effect
   * so `handle.isOpen` returns the latest value synchronously.
   *
   * @internal
   */
  setOpenMirror(next) {
    this._open = next;
  }

  /**
   * Imperative open-state writer. No-op when no root is bound.
   */
  setOpen(next, details) {
    this._setOpen?.(next, details);
  }

  /**
   * Synchronous snapshot of the current open state.
   */
  get isOpen() {
    return this._open;
  }
}

/**
 * `MenuHandle` — imperative surface + detached-trigger association.
 * Matches React's `MenuHandle` 1:1 on the public methods.
 */
export class MenuHandle {
  /** @internal */

  constructor() {
    this.store = new MenuStore();
  }

  /**
   * Opens the menu and associates it with the trigger with the given id.
   * Throws if no trigger with that id is registered.
   */
  open(triggerId) {
    const triggerElement = this.store.context.triggerElements.getById(triggerId);
    if (!triggerElement) {
      throw new Error(`Base UI: MenuHandle.open: No trigger found with id "${triggerId}".`);
    }
    this.store.setOpen(true, createChangeEventDetails(REASONS.imperativeAction, undefined, triggerElement));
  }

  /** Closes the menu. */
  close() {
    this.store.setOpen(false, createChangeEventDetails(REASONS.imperativeAction, undefined, undefined));
  }

  /** Whether the menu is currently open. Synchronous. */
  get isOpen() {
    return this.store.isOpen;
  }
}

/**
 * Factory for `MenuHandle` — matches React's `createMenuHandle()`.
 */
export function createMenuHandle() {
  return new MenuHandle();
}
//# sourceMappingURL=menu-store.jsx.map
