import { type RefObject } from 'react'; import { type TAnimationPreset } from '../animations/types'; type TUseAnimatedVisibilityArgs = { /** * Whether the element is logically open. */ isOpen: boolean; /** * Animation preset for entry/exit transitions. * Pass `false` or `undefined` to disable animation. */ animate: TAnimationPreset | false | undefined; /** * Ref to the DOM element that plays the entry/exit transitions. * Used to listen for `transitionend`. */ elementRef: RefObject; /** * Called after the entry animation completes (or immediately on open * when there is no animation or reduced motion is active). * This includes initial mount with `isOpen=true`. */ onEnterFinish?: () => void; /** * Called after the exit animation completes (or immediately on close * when there is no animation or reduced motion is active). */ onExitFinish?: () => void; }; type TUseAnimatedVisibilityResult = { /** * Current visibility phase. See `TPhase` for the full lifecycle. * * `phase !== 'closed'` means the host element is mounted and on * screen, including during animated entry, animated exit, and the * non-animated close handshake. Use this when you need "the user * can still see / interact with the element" semantics (e.g. focus * trapping, listeners that must outlive the exit). */ phase: TPhase; /** * Resolved animation preset (with CSS injected), or `null` if * animation is disabled. */ preset: TAnimationPreset | null; }; /** * Visibility lifecycle phase shared between `useAnimatedVisibility`, * `useFocusWrap`, `useInitialFocus`, and the `Popover` / `Dialog` * components. * * - `closed`: host element is unmounted. No DOM presence. * - `entering`: host element is mounted and the CSS entry transition * is playing. **Only emitted when animation is enabled.** With * animation disabled the lifecycle goes directly `closed → open`. * - `open`: host element is mounted and settled. No transition is * playing. * - `exiting`: `isOpen` has flipped to `false` but the host element * is still mounted. Emitted while the CSS exit transition is playing * (animation enabled) OR while we wait for the browser's * `toggle`/`close` event to fire on the still-attached element so * focus restoration can run (animation disabled). * * State machine: * ``` * animation ON: closed → entering → open → exiting → closed * animation OFF: closed → open → exiting → closed * interrupts: entering → exiting (close mid-entry) * exiting → entering (reopen mid-exit, animated) * exiting → open (reopen mid-handshake, non-animated) * ``` * * `phase !== 'closed'` is the canonical "host is currently in the DOM * and on screen" predicate. */ export type TPhase = 'closed' | 'entering' | 'open' | 'exiting'; /** * Manages the children mount/unmount lifecycle around CSS exit transitions. * * Used by both `Popover` and `Dialog` to share the same animation lifecycle * logic. Those components own their own show/hide mechanisms (showPopover / * hidePopover vs showModal / close) and event handling - this hook only * manages the relationship between `isOpen` and when children are rendered. * * ## Problem * * We want to delay the unmount of children until a CSS exit transition has * finished playing. If we unmount children the moment `isOpen` becomes * `false`, the exit animation is never visible - the content just disappears. * * ## How it works * * The lifecycle is modeled as a `phase` discriminated union (`closed`, * `entering`, `open`, `exiting`). The host element is rendered while * `phase !== 'closed'`, so the `exiting` phase keeps the host mounted * while the CSS exit transition plays (or, with animation disabled, * while we wait for the browser `toggle` / `close` event to fire on the * still-attached element): * * ``` * isOpen: true ──────────────── false * phase: open ──────────────── exiting ─── (exit settle) ─── closed * ``` * * Every effect dispatches on the single `phase` value instead of a * cross-product of booleans. */ export declare function useAnimatedVisibility({ isOpen, animate, elementRef, onEnterFinish, onExitFinish, }: TUseAnimatedVisibilityArgs): TUseAnimatedVisibilityResult; export {};