import { type ZestChangeEventDetails } from '../createChangeEventDetails'; import { REASONS } from '../reasons'; import type { PopupTriggerMap } from './PopupTriggerMap'; /** * The minimal store contract a handle exposes to detached triggers. * * Detached triggers read `store` during render and subscribe so they are notified * when the handle switches between its fallback store and a mounted root's live * store. */ export interface PopupHandleStoreProvider { readonly store: HandleStore; subscribeStore(listener: () => void): () => void; } /** * The store shape a handle needs to resolve a trigger by id. */ export interface PopupHandleStoreWithTriggers { readonly context: { readonly triggerNodes: PopupTriggerMap; }; } /** * The store shape a handle needs to drive open state. Only the root-owned store * needs this — the view exposed to detached triggers never has `setOpen` called * on it. */ export interface PopupHandleStoreWithOpen extends PopupHandleStoreWithTriggers { setOpen(open: boolean, eventDetails: ZestChangeEventDetails): void; } /** * The shared implementation behind every popup handle: it coordinates detached * triggers with a mounted root. * * Ported from upstream near-verbatim — it is pure state, and the only DOM in it * was the trigger element's type (see `PopupTriggerMap`). Subclasses add the * component-specific imperative methods; this owns the fallback store, the root * attachment stack, and subscriber notification. */ export declare class BasePopupHandle { protected readonly fallbackStore: HandleStore; private readonly componentName; private readonly throwOnMissingTrigger; /** * The stores of every root currently using this handle, in attach order. A * handle is meant for a single mounted root, but roots can transiently overlap * (an animated screen transition, say), so this stack lets `attachStore`'s * cleanup restore the previous root rather than leaving a still-mounted root * uncontrollable when a newer one detaches first. */ private readonly attachedStores; private attachedStoreValue; private readonly storeListeners; /** * @param fallbackStore An inert, closed store handed to detached triggers while * no root is attached, so they can render and register without one. Triggers * register into whichever store `store` resolves to and migrate themselves as * roots attach and detach. * @param componentName Prefixes dev warnings, e.g. `'Menu'` produces * `MenuHandle.open()`. * @param throwOnMissingTrigger Whether `open(triggerId)` throws when no trigger * with that id is registered. Anchored popups (Menu, Popover) need a trigger to * anchor to, so they throw; a Dialog is not anchored and opens unassociated * with a warning instead. */ constructor(fallbackStore: HandleStore, componentName: string, throwOnMissingTrigger?: boolean); protected get attachedStore(): Store | null; /** * The store detached triggers read from: the attached root's, or the inert * fallback while none is attached. * @internal */ get store(): HandleStore; /** @internal */ subscribeStore(listener: () => void): () => void; /** * Points the handle at a root's store and notifies subscribers, so detached * triggers re-render and re-register into it. Returns a cleanup that detaches. * @internal */ attachStore(newStore: Store): () => void; private setActiveStore; /** * Opens the attached root, associating it with the trigger of the given id. * A no-op while no root is attached. * * Call this from an event handler or an effect, never during rendering. */ protected openByTrigger(triggerId: string | null | undefined): void; /** * Points the popup at the trigger it was opened by. Anchored popups override * this to set the store's `triggerNode`, without which their positioner would * have nothing to anchor to; an unanchored one (Dialog) has nothing to do. */ protected associateTrigger(_store: Store, _triggerNode: unknown): void; /** * Closes the popup. A no-op while no root is attached. * * Call this from an event handler or an effect, never during rendering. */ protected closePopup(): void; } //# sourceMappingURL=BasePopupHandle.d.ts.map