import { type Accessor, createContext, useContext } from "solid-js"; /** * x/y pixel pair — used for scroll positions and corner measurements. */ export interface Coords { x: number; y: number; } /** * width/height pixel pair — used for thumb and corner measurements. */ export interface Size { width: number; height: number; } /** * Overflow-edge flags. Each flag indicates whether the scroll offset on the * corresponding side exceeds the configured threshold. */ export interface OverflowEdges { xStart: boolean; xEnd: boolean; yStart: boolean; yEnd: boolean; } /** * Scrollbar visibility. A scrollbar is hidden when the content doesn't * overflow along its axis; the corner is hidden when either scrollbar is * hidden. */ export interface HiddenState { x: boolean; y: boolean; corner: boolean; } /** * Normalized overflow-edge threshold (all four sides, pixels). Consumers * pass either a number or a partial object to ``; the root * component normalizes it into this shape before publishing to context. */ export interface OverflowEdgeThreshold { xStart: number; xEnd: number; yStart: number; yEnd: number; } /** * Shared root state projected onto the root, viewport, content and * scrollbar parts. Mirrors Base UI React's `ScrollAreaRootState`. */ export interface ScrollAreaRootState { /** Whether the user is currently scrolling. */ scrolling: boolean; /** Whether horizontal overflow is present. */ hasOverflowX: boolean; /** Whether vertical overflow is present. */ hasOverflowY: boolean; /** Whether there is overflow past the inline-start edge. */ overflowXStart: boolean; /** Whether there is overflow past the inline-end edge. */ overflowXEnd: boolean; /** Whether there is overflow past the block-start edge. */ overflowYStart: boolean; /** Whether there is overflow past the block-end edge. */ overflowYEnd: boolean; /** Whether the scrollbar corner is currently hidden. */ cornerHidden: boolean; } /** * Mutable ref container — mirrors React's `RefObject` so the * scroll-area port reads 1:1 against the React source. */ export interface MutableRef { current: T | null; } /** * Value published by `` to its descendants. * * Every reactive field is an `Accessor` so subscribers stay narrow. Setters * are plain functions that write into the root's signals. Element refs are * mutable containers rather than accessors — the ref targets don't need to * trigger re-renders (their imperative reads happen inside event handlers * and layout effects). */ export interface ScrollAreaRootContextValue { // Measurements published by the viewport. cornerSize: Accessor; setCornerSize: (next: Size | ((prev: Size) => Size)) => void; thumbSize: Accessor; setThumbSize: (next: Size | ((prev: Size) => Size)) => void; hasMeasuredScrollbar: Accessor; setHasMeasuredScrollbar: (next: boolean) => void; // Interaction state. hovering: Accessor; setHovering: (next: boolean) => void; scrollingX: Accessor; setScrollingX: (next: boolean) => void; scrollingY: Accessor; setScrollingY: (next: boolean) => void; // Element refs. Mutated imperatively; not reactive. viewportRef: MutableRef; rootRef: MutableRef; scrollbarYRef: MutableRef; scrollbarXRef: MutableRef; thumbYRef: MutableRef; thumbXRef: MutableRef; cornerRef: MutableRef; // Pointer-interaction handlers (shared between root / scrollbar / thumb). handlePointerDown: (event: PointerEvent) => void; handlePointerMove: (event: PointerEvent) => void; handlePointerUp: (event: PointerEvent) => void; handleScroll: (scrollPosition: Coords) => void; // Identity and per-axis visibility. rootId: string; hiddenState: Accessor; setHiddenState: ( next: HiddenState | ((prev: HiddenState) => HiddenState), ) => void; overflowEdges: Accessor; setOverflowEdges: ( next: OverflowEdges | ((prev: OverflowEdges) => OverflowEdges), ) => void; // Shared state accessor (used by Content's `data-*` projection). viewportState: Accessor; overflowEdgeThreshold: OverflowEdgeThreshold; } /** * Root context. `createContext(null)` + `== null` sentinel — * Solid 2's `useContext` throws `ContextNotFoundError` when the * default is `undefined`, which would shadow the intended Base UI error * message below. */ export const ScrollAreaRootContext = createContext(null); /** * Read the `` context. Throws when used outside a root. */ export function useScrollAreaRootContext(): ScrollAreaRootContextValue { const context = useContext(ScrollAreaRootContext); if (context == null) { throw new Error( "Base UI: ScrollAreaRootContext is missing. ScrollArea parts must be placed within .", ); } return context; }