/** * overlayStack.ts — which modal surface currently owns the keyboard. * * Every modal organism here (Sheet, Dialog, ConfirmDialog) listens for keydown on * `document`, so when one is raised BY another — `OrderCreateSheet`'s discard guard * opening a ConfirmDialog over its own Sheet — both handlers see the same key. Left * alone, Escape dismisses two layers at once and the lower surface's `trapFocus` * drags focus out of the upper one (`focusTrap.ts`'s FIX-466 clause pulls focus back * in whenever the active element sits outside its container). * * WHY A STACK AND NOT `event.target.closest('[role=dialog]')`: * that was the first attempt, and it has a real race. A modal moves initial focus on * a `setTimeout(0)` (deferred so the portal paints first), so between "ConfirmDialog * opened" and "focus arrived in it" the event target is still the Sheet underneath — * and in that window BOTH handlers ran. Reproduced: one Escape produced * `onCancel: 1` AND `onClose("escape")`. Ownership therefore cannot be derived from * where focus happens to be; it has to be registered explicitly. * * Registration order IS stacking order: each surface registers from its `[open]` * effect, so the most recently opened one is last and owns the keyboard until it * unregisters. Non-modal anchored panels (Select/Combobox/DatePicker — `listbox` * and `combobox` roles) deliberately do NOT register: they are not modal layers, so * the Sheet keeps owning Tab while one is open and FIX-466's portaled-focusable * wrap still applies. * * BEHAVIOUR NOTE: with two modals open at once, Escape now dismisses only the * topmost. Previously both closed on a single press. (NB-SHEET-DISMISS-01) */ /** * Registers `el` as the topmost modal surface. Returns the matching unregister — * call it from the same effect's cleanup. */ export declare function registerOverlay(el: HTMLElement | null): () => void; /** * Whether `el` is the modal surface that should handle a keyboard event right now. * * An empty stack means nothing has registered — the caller handles the key, which * keeps every pre-existing single-overlay case behaving exactly as before. */ export declare function isTopmostOverlay(el: HTMLElement | null): boolean; //# sourceMappingURL=overlayStack.d.ts.map