/** * Modal Stack Registry - Cross-Bundle Modal Awareness * * ## Architecture Overview * * This module provides a registry for tracking open modals across bundle boundaries * (host application + Iris app iframes). It enables proper modal stacking behavior * where modals can visually indicate their depth (scale, backdrop visibility, animations). * * ## Why postMessage Instead of Penpal? * * While most host-iframe communication uses Penpal (RPC-style), modal stack changes * use raw postMessage for these reasons: * * 1. **Package Independence**: This module is part of `@trackunit/react-modal`, a standalone * UI component library. It should not depend on `iris-app-runtime` to avoid circular * dependencies and keep the modal component reusable. * * 2. **Broadcast Semantics**: When a host modal opens, ALL iframe modals need to know * simultaneously (not just one specific connection). postMessage with broadcast * to all iframes fits this pattern naturally. * * 3. **Low Latency**: Stack count changes need real-time sync for smooth animations * (scale transforms, backdrop fading). Raw postMessage has less overhead than Penpal. * * 4. **Module Isolation**: Each bundle (host + each iframe) gets its own instance of * this registry. They need cross-window synchronization, not shared state. * * ## Communication Flow * * ``` * ┌─────────────────────────────────────────────────────────────────────┐ * │ HOST APPLICATION │ * │ │ * │ modalStackRegistry ←───────────────────────────────────────────┐ │ * │ │ │ │ * │ │ subscribe() │ │ * │ ▼ │ │ * │ ModalDialogProviderHost │ │ * │ │ │ │ * │ ├─► broadcasts MODAL_STACK_MESSAGE_TYPE to all iframes │ │ * │ │ │ │ * │ └─► listens for IFRAME_MODAL_STACK_MESSAGE_TYPE ──────────┘ │ * │ (aggregates per-iframe, cleans up on iframe removal) │ * └─────────────────────────────────────────────────────────────────────┘ * │ * postMessage (bidirectional) * │ * ┌─────────────────────────────────────────────────────────────────────┐ * │ IFRAME (Iris App) │ * │ │ * │ modalStackRegistry (separate instance) │ * │ │ │ * │ ├─► listens for MODAL_STACK_MESSAGE_TYPE from host │ * │ │ (updates hostModalCount) │ * │ │ │ * │ └─► on register/unregister, broadcasts │ * │ IFRAME_MODAL_STACK_MESSAGE_TYPE to parent │ * └─────────────────────────────────────────────────────────────────────┘ * ``` * * ## Usage with useModalStack Hook * * The `useModalStack` hook consumes this registry to calculate: * - `depthFromFront`: How many modals are in front of this one (0 = frontmost) * - `stackSize`: Total modals open (local + host + iframe) * - `stackSizeAtOpen`: Stack size when this modal opened (for animation decisions) * * @module modalStackRegistry */ /** * Message type for host → iframe modal stack communication. * The host broadcasts this to all iframes when its modal count changes. * Iframes listen for this to update their `hostModalCount`. */ export declare const MODAL_STACK_MESSAGE_TYPE = "IRIS_APP_HOST_MODAL_STACK_CHANGE"; /** * Message type for iframe → host modal stack communication. * Each iframe sends this to the parent when its modal count changes. * The host aggregates these per-iframe for accurate total tracking. */ export declare const IFRAME_MODAL_STACK_MESSAGE_TYPE = "IRIS_APP_IFRAME_MODAL_STACK_CHANGE"; /** * Module-level registry to track the stack of open modals. * * This registry enables: * - Tracking modal order within a single bundle (local stack) * - Awareness of modals in other bundles (host ↔ iframe communication) * - Subscriber notifications for React integration via useSyncExternalStore * * Cross-bundle counts: * - `hostModalCount`: Number of modals open in the host (relevant for iframes) * - `iframeModalCount`: Number of modals open in iframes (relevant for host) * * Note: This is intentionally NOT a React hook because: * 1. It needs to be a singleton that persists across React component lifecycles * 2. It must work with postMessage for cross-bundle communication (host ↔ iframe) * 3. The useModalStack hook consumes this registry via useSyncExternalStore for React integration */ export declare const modalStackRegistry: { register: (id: string) => void; unregister: (id: string) => void; getStackPosition: (id: string) => number; getStackSize: () => number; /** Get the number of modals open in the host (only relevant for Iris apps in iframes) */ getHostModalCount: () => number; /** Get the number of modals open in iframes (only relevant for host) */ getIframeModalCount: () => number; /** Set the host modal count (called when receiving messages from host) */ setHostModalCount: (count: number) => void; /** Set the iframe modal count (called when receiving messages from iframes) */ setIframeModalCount: (count: number) => void; subscribe: (listener: () => void) => (() => void); };