"use client" /** * Exxat DS — one rail at a time. * * Product sheets are undimmed floating rails pinned to the same edge at the * same `z-[80]`. Each one owns its own `open` boolean in a different parent * (export lives on the page client, properties inside `useTableState`, invite * in `CollaborationAccessFlow`), so nothing stopped two of them from stacking * on top of each other. * * This registry keeps the ownership where it is and adds a single rule: * opening a rail closes whichever rail was already open. It is a module * singleton rather than a context because overlays are global to the document * and this way no app shell has to mount a provider to get the behaviour. */ import * as React from "react" type RailEntry = { id: string; close: () => void } let activeRail: RailEntry | null = null /** Take the rail. Any other open rail is asked to close first. */ export function claimSheetRail(id: string, close: () => void): void { if (activeRail && activeRail.id !== id) activeRail.close() activeRail = { id, close } } /** Give up the rail. No-ops if another sheet already took it. */ export function releaseSheetRail(id: string): void { if (activeRail?.id === id) activeRail = null } /** * Join the single-rail group for as long as `open` is true. * * `onOpenChange` is read through a ref so a caller passing an inline arrow * does not re-register the rail on every render, which would close the sheet * that is currently open. */ export function useSheetRail( open: boolean, onOpenChange: ((open: boolean) => void) | undefined, enabled = true, ): void { const id = React.useId() const onOpenChangeRef = React.useRef(onOpenChange) React.useEffect(() => { onOpenChangeRef.current = onOpenChange }, [onOpenChange]) React.useEffect(() => { if (!enabled || !open) return claimSheetRail(id, () => onOpenChangeRef.current?.(false)) return () => releaseSheetRail(id) }, [enabled, open, id]) }