import { type Accessor, createContext, useContext } from "solid-js"; import type { BaseUIChangeEventDetails, REASONS, } from "../internals/change-event-details"; import type { FloatingRootStore } from "../internals/floating-ui/components/floating-root-store"; import type { UseInteractionsReturn } from "../internals/floating-ui/hooks/use-interactions"; import type { MutableRef } from "../internals/floating-ui/types"; import type { TransitionStatus } from "../internals/use-transition-status/use-transition-status"; import type { PreviewCardStore } from "./store/preview-card-store"; /** * Reasons a preview card's open state can change. Mirrors Base UI React's * `PreviewCardRoot.ChangeEventReason` union. */ export type PreviewCardRootChangeEventReason = | typeof REASONS.triggerHover | typeof REASONS.triggerFocus | typeof REASONS.triggerPress | typeof REASONS.outsidePress | typeof REASONS.escapeKey | typeof REASONS.imperativeAction | typeof REASONS.none; export type PreviewCardRootChangeEventDetails = BaseUIChangeEventDetails; /** Public projected state — surfaces via `data-*` attrs on the root/parts. */ export interface PreviewCardRootState { open: boolean; } /** * `instant` is the string flag that Base UI emits as `data-instant` on the * popup / arrow / positioner so CSS can disable animations for specific * programmatic transitions. * * Only `"dismiss"` and `"focus"` are emitted on preview cards (no delay-group * instant-phase, since preview-card has no delay-group integration). */ export type PreviewCardInstantType = "dismiss" | "focus" | undefined; /** * Context shape shared between `` and every preview-card * sub-part. Every reactive field is an `Accessor` so consumers don't need * to guard against top-level reactive reads. */ export interface PreviewCardRootContextValue { open: Accessor; mounted: Accessor; transitionStatus: Accessor; instantType: Accessor; setInstantType: (value: PreviewCardInstantType) => void; /** Public projected state (used by `renderElement`). */ state: Accessor; /** Imperative open setter. */ setOpen: (next: boolean, details: PreviewCardRootChangeEventDetails) => void; /** Floating-UI shared store. */ floatingRootContext: FloatingRootStore; /** Interaction prop bags produced by the root's `useInteractions`. */ interactions: UseInteractionsReturn; /** Setter / accessor for the active trigger DOM element. */ setTriggerElement: (node: HTMLElement | null) => void; triggerElement: Accessor; /** Setter / accessor for the popup DOM element. */ setPopupElement: (node: HTMLElement | null) => void; popupElement: Accessor; /** Plain-ref mirror of popup element — consumed by `useOpenChangeComplete`. */ popupRef: MutableRef; /** The shared `PreviewCardStore` owned by this root (or by its `handle`). */ store: PreviewCardStore; /** Active trigger id. */ activeTriggerId: Accessor; setActiveTriggerId: (id: string | null) => void; /** Payload written by the active trigger. */ payload: Accessor; setPayload: (payload: unknown) => void; /** * Mutable ref holding the currently-active trigger's `closeDelay`. Read by * ``'s hover-floating handler (so moving off the popup * starts the right close timer). */ closeDelayRef: MutableRef; } /** * Solid 2's `useContext` throws `ContextNotFoundError` when the resolved * value is `undefined`, so the "no provider" sentinel is `null` here — the * helper below turns that into the Base UI error. */ export const PreviewCardRootContext = createContext(null); /** * Read the current `` context. Throws unless `optional` is * `true`. */ export function usePreviewCardRootContext(): PreviewCardRootContextValue; export function usePreviewCardRootContext( optional: true, ): PreviewCardRootContextValue | null; export function usePreviewCardRootContext( optional?: boolean, ): PreviewCardRootContextValue | null { const context = useContext(PreviewCardRootContext); if (context == null && !optional) { throw new Error( "Base UI: PreviewCardRootContext is missing. PreviewCard parts must be placed within .", ); } return context; }