/** * Escape-key containment between the legacy prop-based `Modal` and Base UI's * `Dialog`. * * The problem: both overlays listen for Escape at the `document` level and both * portal their content to ``. When one is nested inside the other, a * single Escape reaches both listeners, so both layers close. Only the top-most * layer should close. * * The legacy `Modal` can't join Base UI's floating-element tree, so this module * coordinates the two with a small, explicit protocol: * * 1. Top-most check — because each overlay portals to ``, the focused * content's *nearest* `[role="dialog"]` ancestor is always its own * container. So the Modal only acts when that nearest dialog is itself; if * it's a different (nested) dialog, the Modal defers. See * {@link isTopMostOverlayForEscape}. * * 2. Deferral signal — when the Modal is top-most it stamps the native event * during the CAPTURE phase (which runs before Base UI's bubble-phase * dismissal). An outer Base UI `Dialog` reads that flag in its * `onOpenChange` handler and cancels, so the Dialog stays open while the * Modal closes. See {@link markEscapeHandledByModal} / * {@link isEscapeHandledByModal}. * * Note: this is a DOM-proximity heuristic rather than a true overlay stack. It * holds only because both overlays portal to `` and expose a dialog role. */ /** * Whether `container` is the top-most overlay for this Escape — i.e. the * Escape originated inside `container` and not inside a more deeply nested * dialog/overlay. Returns `true` when the Escape happened outside any dialog * (e.g. focus not yet inside the Modal) so the Modal keeps its historical * focus-independent close behaviour. */ export declare function isTopMostOverlayForEscape(event: KeyboardEvent, container: Element | null): boolean; /** Marks the Escape as owned by a Modal so an outer Dialog can defer. */ export declare function markEscapeHandledByModal(event: KeyboardEvent): void; /** Whether this Escape was already claimed by a nested Modal. */ export declare function isEscapeHandledByModal(event: Event | undefined): boolean; /** * Wires up Escape containment for a legacy `Modal` and returns a cleanup * function. While active it: * * - marks the event in the CAPTURE phase when the Modal is top-most, so an * outer Base UI `Dialog` defers instead of closing; and * - closes the Modal in the BUBBLE phase when it is top-most. Bubble phase * means a nested overlay (Select, Popover, DatePicker, …) that stops the * Escape below the document prevents this from firing, so the Modal * correctly stays open in that case. * * `getContainer` is read lazily so the current Modal element is always used. */ export declare function addModalEscapeContainment({ getModal, onRequestClose, }: { getModal: () => Element | null; onRequestClose?: () => void; }): () => void;