
import { RefObject } from "react";

//#region src/utils/react/usePopoverSync.d.ts
interface UsePopoverSyncOptions {
  /** Stable identifier for this popover instance (typically a generateRandomId() memo). */
  menuId: string;
  /** Current open state of this popover. */
  isOpen: boolean;
  /** Called when another popover opens while this one is open. */
  onClose: () => void;
  /**
   * When `false`, this popover does not participate in the sync (no listening,
   * no emitting). Useful for "dummy" triggers that proxy a real one (see
   * `MenuTrigger`'s `isDummy`). Defaults to `true`.
   */
  enabled?: boolean;
  /**
   * Ref to the popover's trigger element. When provided, the element is
   * included in the `popover:open` emit payload so peers can detect whether
   * the new opener is nested inside their own overlay (and skip closing in
   * that case). Optional — omitting it preserves the legacy "always close on
   * peer open" behaviour.
   */
  triggerRef?: RefObject<HTMLElement | null>;
  /**
   * Ref to the overlay/container element that hosts this popover's content.
   * When provided, the listener performs a DOM `contains()` check on incoming
   * peer triggers: peers whose trigger lives inside this container are
   * considered nested children and do NOT close us.
   */
  containerRef?: RefObject<HTMLElement | null>;
  /**
   * Whether this overlay closes when a Button/ItemButton inside its container
   * is pressed. Defaults to `true` (popover semantics — popovers are transient
   * surfaces and any action inside them should dismiss them). Set to `false`
   * for modals/trays/fullscreen dialogs — buttons inside a Dialog should not
   * auto-close it. Requires `containerRef` to be set; without it the listener
   * has no way to determine whether the dispatching button is "inside" this
   * overlay and is effectively a no-op.
   */
  dismissOnInnerButtonPress?: boolean;
  /**
   * Whether this overlay closes when a peer popover opens. Defaults to `true`
   * (popover semantics — only one popover open at a time). Set to `false` for
   * modals/trays/fullscreen dialogs so a peer popover opening cannot bypass
   * the dialog's own `isDismissable` / `onClose` handling and yank it shut.
   *
   * The host still EMITS `popover:open` regardless of this flag, so opening a
   * modal/tray correctly dismisses any peer popover that was open before.
   */
  closeOnPeerOpen?: boolean;
}
/**
 * Coordinates the "only one popover open at a time" invariant via the EventBus.
 *
 * - When `isOpen` flips `false -> true`, emits `popover:open` once.
 * - While open, listens for peers' `popover:open` events and calls `onClose`.
 *
 * Implementation notes (ALL of these matter — losing any one re-introduces a
 * race that surfaces only under rapid trigger switching, which is hard to
 * reproduce in tests):
 *
 * 1. `isOpen` and `onClose` are read through refs inside the listener, so the
 *    subscription effect's dep array does NOT include `isOpen`/`onClose`. This
 *    keeps the listener identity stable across open/close transitions and
 *    avoids the unsubscribe-emit-resubscribe window where an emit can be
 *    delivered to a stale listener (or no listener).
 * 2. The emit fires only on the `false -> true` transition, gated by
 *    `wasOpenRef`. A re-render where `isOpen` is still `true` must NOT
 *    re-emit, otherwise it could re-trigger listeners on peers that just
 *    opened in the same render flush.
 * 3. The `enabled` flag short-circuits both effects symmetrically. When it
 *    flips off, `wasOpenRef` is reset so re-enabling later still emits if
 *    `isOpen` is true at that moment.
 */
declare function usePopoverSync({
  menuId,
  isOpen,
  onClose,
  enabled,
  triggerRef,
  containerRef,
  dismissOnInnerButtonPress,
  closeOnPeerOpen
}: UsePopoverSyncOptions): void;
/**
 * Hook that returns a dispatcher to close the popover that contains a given
 * DOM element. Used by `Button` / `ItemButton` to implement the default
 * "press inside a popover closes the popover" behaviour. Custom (non-Cube)
 * interactive controls can call this directly:
 *
 * ```tsx
 * const dismiss = useDismissParentPopover();
 * <MyCustomPressable onPress={(e) => { doThing(); dismiss(e.currentTarget); }} />
 * ```
 *
 * The actual dismiss is dispatched through the EventBus, which defers via
 * `setTimeout(0)` — so the user's synchronous handler (and any React state
 * updates it triggers) flushes BEFORE the popover closes. This is critical
 * for the "open a hoisted modal from a popover footer" case: the modal
 * mounts first, then the popover closes.
 *
 * Only popover-type containers (those that pass `dismissOnInnerButtonPress`
 * as `true`, the default) react to the event. Modal/tray/fullscreen Dialog
 * containers explicitly opt out so a Button inside their content does not
 * auto-close them.
 *
 * When called outside an `EventBusProvider` (e.g. in unit tests that render
 * a Button without wrapping in `<Root>`), the returned function is a no-op —
 * the dismiss flow gracefully degrades rather than throwing.
 */
declare function useDismissParentPopover(): (from: Element | null) => void;
//#endregion
export { UsePopoverSyncOptions, useDismissParentPopover, usePopoverSync };
//# sourceMappingURL=usePopoverSync.d.ts.map