import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from "react"; import type { RegisteredPanel } from "../panel/types"; import type { RegisteredSeparator } from "../separator/types"; /** * Panel group orientation loosely relates to the `aria-orientation` attribute. * It determines how panels are are laid out within the group group and the direction they can be resized in. */ export type Orientation = "horizontal" | "vertical"; /** * Map of Panel id to flexGrow value; */ export type Layout = { [id: string]: number; }; export type LayoutStorage = Pick; /** * Metadata describing a completed layout change, passed as the second argument * to the `onLayoutChanged` callback. See #716. */ export type LayoutChangedMeta = { /** * `true` when the change was caused by the user directly manipulating a * separator — releasing a pointer drag or pressing a resize key (arrow keys, * Home/End, Enter). `false` for every other source (programmatic `setLayout` * and other imperative API calls, constraint recompute, default-size change, * initial mount), because the library cannot attribute the caller's intent * there. */ isUserInteraction: boolean; }; export type DragState = { state: "default" | "hover" | "dragging"; separatorId: string | undefined; }; export type ResizeTargetMinimumSize = { coarse: number; fine: number; }; export type RegisteredGroup = Readonly<{ disabled: boolean; element: HTMLElement; id: string; mutableState: { defaultLayout: Readonly | undefined; disableCursor: boolean; expandedPanelSizes: { [panelId: string]: number; }; layouts: { [panelIds: string]: Layout; }; }; orientation: Orientation; panels: RegisteredPanel[]; resizeTargetMinimumSize: ResizeTargetMinimumSize; separators: RegisteredSeparator[]; }>; export type GroupContextType = { disableCursor: boolean; getPanelStyles: ( groupId: string, panelId: string ) => CSSProperties | undefined; id: string; orientation: Orientation; registerPanel: (panel: RegisteredPanel) => () => void; registerSeparator: (separator: RegisteredSeparator) => () => void; updatePanelProps: ( id: string, props: { disabled: boolean | undefined } ) => void; updateSeparatorProps: ( id: string, props: { disabled: boolean | undefined; disableDoubleClick: boolean | undefined; } ) => void; }; /** * Imperative Group API. * * ℹ️ The `useGroupRef` and `useGroupCallbackRef` hooks are exported for convenience use in TypeScript projects. */ export interface GroupImperativeHandle { /** * Get the Group's current layout as a map of Panel id to percentage (0..100) * * @return Map of Panel id to percentages (specified as numbers ranging between 0..100) */ getLayout: () => { [panelId: string]: number }; /** * Set a new layout for the Group * * @param layout Map of Panel id to percentage (a number between 0..100) * @return Applied layout (after validation) */ setLayout: (layout: { [panelId: string]: number }) => Layout; } export type GroupProps = HTMLAttributes & { /** * Panel and Separator components that comprise this group. */ children?: ReactNode | undefined; /** * CSS class name. */ className?: string | undefined; /** * Default layout for the Group. * * ℹ️ This value allows layouts to be remembered between page reloads. * * ⚠️ Slight layout shift may occur when server-rendering panels with percentage-based default sizes. * Refer to the documentation for suggestions on how to minimize the impact of this. */ defaultLayout?: Layout | undefined; /** * This library sets custom mouse cursor styles to indicate drag state. * Use this prop to disable that behavior for Panels and Separators in this group. */ disableCursor?: boolean | undefined; /** * Disable resize functionality. */ disabled?: boolean | undefined; /** * Ref attached to the root `HTMLDivElement`. */ elementRef?: Ref | undefined; /** * Exposes the following imperative API: * - `getLayout(): Layout` * - `setLayout(layout: Layout): void` * * ℹ️ The `useGroupRef` and `useGroupCallbackRef` hooks are exported for convenience use in TypeScript projects. */ groupRef?: Ref | undefined; /** * Uniquely identifies this group within an application. * Falls back to `useId` when not provided. * * ℹ️ This value will also be assigned to the `data-group` attribute. */ id?: string | number | undefined; /** * Called when the Group's layout is changing. * * ⚠️ For layout changes caused by pointer events, this method is called each time the pointer is moved. * For most cases, it is recommended to use the `onLayoutChanged` callback instead. */ onLayoutChange?: (layout: Layout) => void | undefined; /** * Called after the Group's layout has been changed. * * ℹ️ For layout changes caused by pointer events, this method is not called until the pointer has been released. * This method is recommended when saving layouts to some storage api. * * ℹ️ The second argument contains meta information about the layout change. * The `isUserInteraction` attribute signals whether the resize was caused by direct user input. * It is true for resizes caused by pointer or keyboard input * and false for other triggers (e.g. imperative API calls, initial mount, etc.) */ onLayoutChanged?: (layout: Layout, meta: LayoutChangedMeta) => void; /** * Minimum size of the resizable hit target area (either `Separator` or `Panel` edge) * This threshold ensures are large enough to avoid mis-clicks. * * - Coarse inputs (typically a finger on a touchscreen) have reduced accuracy; * to ensure accessibility and ease of use, hit targets should be larger to prevent mis-clicks. * - Fine inputs (typically a mouse) can be smaller * * ℹ️ [Apple interface guidelines](https://developer.apple.com/design/human-interface-guidelines/accessibility) suggest `20pt` (`27px`) on desktops and `28pt` (`37px`) for touch devices * In practice this seems to be much larger than many of their own applications use though. */ resizeTargetMinimumSize?: { coarse: number; fine: number; }; /** * Specifies the resizable orientation ("horizontal" or "vertical"); defaults to "horizontal" */ orientation?: "horizontal" | "vertical" | undefined; /** * CSS properties. * * ⚠️ The following styles cannot be overridden: `display`, `flex-direction`, `flex-wrap`, and `overflow`. */ style?: CSSProperties | undefined; }; export type OnGroupLayoutChange = GroupProps["onLayoutChange"]; export type OnGroupLayoutChanged = GroupProps["onLayoutChanged"];