import type { PrimitivePropsWithRef } from '../utils/primitive.cjs'; /** How many nodes a {@link Tree} allows selected at once: one, many, or no selection. */ export type SelectionMode = 'single' | 'multiple' | 'none'; /** What a plain click does to a multiple selection: toggle the node, or replace the selection with it. */ export type TreeSelectionBehavior = 'toggle' | 'replace'; /** Whether selecting a node also selects its subtree, turning the tree into a tri-state checkbox tree. */ export type TreeSelectionPropagation = 'none' | 'descendants'; /** A node's tri-state checkbox value, as reported on `aria-checked`. */ export type TreeCheckedState = boolean | 'mixed'; /** What a `TreeItem`'s `disabled` takes away: `'all'` makes it inert and unreachable, `'selection'` keeps it reachable but refusing selection. */ export type TreeDisabledBehavior = 'all' | 'selection'; /** Payload of {@link TreeProps.onLoadError} — the branch whose `loadChildren` rejected, and why. */ export interface TreeLoadErrorDetail { /** The node whose `loadChildren` call rejected. */ nodeId: string; /** Whatever the rejected promise rejected with. Unknown by construction: `loadChildren` is the consumer's. */ error: unknown; } /** Node's position among siblings (reported as `aria-posinset` / `aria-setsize`, 1-based). */ export interface TreeNodePosition { posinset: number; setsize: number; } /** @internal Shared state distributed from {@link Tree} to its parts via context. Not part of the public API. */ export interface TreeContextValue { selectionMode: SelectionMode; selectionBehavior: TreeSelectionBehavior; selectionPropagation: TreeSelectionPropagation; expandedIds: Set; selectedIds: Set; /** Node ids whose `loadChildren` call is still pending. */ loadingIds: Set; activeNodeId: string | null; setActiveNodeId: (nodeId: string) => void; disabledBehavior: TreeDisabledBehavior; /** Whether branch clicks toggle expansion and selection. */ expandOnClick: boolean; onExpandedChange: (nodeId: string, expanded: boolean) => void; /** Toggles selection or checked state (depending on checked axis). Under selectionBehavior="replace" a non-additive call replaces the selection instead. */ onSelectedChange: (nodeId: string, options?: { additive?: boolean | undefined; }) => void; /** Fires root's `onActivate` (Enter and click, not Space). */ onActivate: (nodeId: string) => void; /** Node's checked value (from `checkedIds` or `selectedIds`; tri-state with propagation). */ checkedState: (nodeId: string) => TreeCheckedState; /** Node's `aria-posinset` / `aria-setsize` (measured from DOM; undefined until first measurement). */ positionOf: (nodeId: string) => TreeNodePosition | undefined; /** Called by each `TreeItem` on mount (lets root walk hierarchy without touching DOM). */ registerNode: (nodeId: string, parentId: string | null) => void; unregisterNode: (nodeId: string) => void; } /** @internal Per-node state distributed from {@link TreeItem} to its descendants (e.g. `TreeGroup`, `TreeItemIndicator`) via context. Not part of the public API. */ export interface TreeItemContextValue { nodeId: string; depth: number; expanded: boolean; selected: boolean; disabled: boolean; hasChildren: boolean; /** Whether this node's `loadChildren` call is still pending. */ loading: boolean; } /** Collection node; `nodeId` must be unique and keys all state. */ export interface TreeNode { /** Node identity in `expandedIds`, `selectedIds`, `checkedIds`, active node and `loadChildren`. */ nodeId: string; /** * Text rendered as node content (falls back to `nodeId`). * @default undefined */ label?: string | undefined; /** * Excludes node from selection and activation (like `TreeItem`'s `disabled`). * @default false */ disabled?: boolean | undefined; /** * Declares node as branch with unloaded children (reports `aria-expanded="false"`). * @default undefined — inferred from `children`. */ hasChildren?: boolean | undefined; /** * Text typeahead matches against (falls back to rendered text). * @default undefined */ textValue?: string | undefined; /** * Child nodes (rendered in collapsible `TreeGroup`). * @default undefined — leaf. */ children?: TreeNode[] | undefined; } export interface TreeProps extends Omit, 'defaultValue' | 'ref'> { /** * Hierarchical data the tree renders from (becomes source of truth; `children` ignored). * @default undefined — renders `children`. */ collection?: readonly TreeNode[] | undefined; /** * Renders collection node content (ignored without `collection`). * @default undefined — renders `label ?? nodeId`. */ renderLabel?: ((node: TreeNode) => React.ReactNode) | undefined; /** * Selection mode (`'single'`, `'multiple'`, or `'none'` for expand/collapse only). * @default 'single' */ selectionMode?: SelectionMode | undefined; /** * What a plain click does while `selectionMode="multiple"`: `'toggle'` adds or removes the node (checkbox convention); `'replace'` selects only that node, and Shift-, Ctrl- or Cmd-click toggles it into the selection (editor convention). Keyboard bindings are unchanged. * @default 'toggle' */ selectionBehavior?: TreeSelectionBehavior | undefined; /** * Expanded ids for controlled usage; pair with `onExpandedIdsChange`. * @default undefined — uncontrolled. */ expandedIds?: string[] | undefined; /** * Initial expanded ids for uncontrolled usage. * @default [] */ defaultExpandedIds?: string[] | undefined; /** * Fires with new expanded ids on expand/collapse. * @default undefined — no-op. */ onExpandedIdsChange?: ((ids: string[]) => void) | undefined; /** * Selected ids for controlled usage; pair with `onSelectedIdsChange`. * @default undefined — uncontrolled. */ selectedIds?: string[] | undefined; /** * Initial selected ids for uncontrolled usage. * @default [] */ defaultSelectedIds?: string[] | undefined; /** * Fires with new selected ids on selection change. * @default undefined — no-op. */ onSelectedIdsChange?: ((ids: string[]) => void) | undefined; /** * Checked ids for controlled usage (second axis from `selectedIds`); pair with `onCheckedIdsChange`. * @default undefined — uncontrolled; `aria-checked` derives from `selectedIds`. */ checkedIds?: string[] | undefined; /** * Initial checked ids for uncontrolled usage (gives tree own checked axis). * @default undefined — no checked axis. */ defaultCheckedIds?: string[] | undefined; /** * Fires with new checked ids on check/uncheck. * @default undefined — no-op. */ onCheckedIdsChange?: ((ids: string[]) => void) | undefined; /** * Tri-state checkbox tree with selection propagation (toggle node selects/deselects subtree). * @default 'none' */ selectionPropagation?: TreeSelectionPropagation | undefined; /** * Loads node's children on first expansion (marks node busy until settled). * @default undefined — children eager. */ loadChildren?: ((nodeId: string) => void | Promise) | undefined; /** * Formats announcement when lazily-loaded children arrive. * @default defaultGetLoadedLabel — e.g. `"src loaded"`. */ getLoadedLabel?: ((nodeId: string) => string) | undefined; /** * Fires when `loadChildren` rejects (swallowed silently if not fired). * @default undefined — no-op. */ onLoadError?: ((detail: TreeLoadErrorDetail) => void) | undefined; /** * Fires on Enter or click (not Space). * @default undefined — no-op. */ onActivate?: ((nodeId: string) => void) | undefined; /** * What node's `disabled` takes away (see {@link TreeDisabledBehavior}}). * @default 'all' */ disabledBehavior?: TreeDisabledBehavior | undefined; /** * Whether branch clicks toggle expansion and selection. * @default true */ expandOnClick?: boolean | undefined; } /** Imperative Tree contract (assigned on root `
`). */ export interface TreeHandle { /** Clears load marker (or node + subtree with `nodeId`) for refetch on next expansion. */ invalidate(nodeId?: string): void; } /** {@link Tree}}'s forwarded ref (root `
` plus `invalidate()`). */ export type TreeElementHandle = HTMLDivElement & TreeHandle; /** Props for a single {@link TreeItem} node. */ export interface TreeItemProps extends PrimitivePropsWithRef<'div'> { /** Unique identifier tracked in the tree's expanded/selected/active state. */ nodeId: string; /** * Excludes node from selection and activation (not inherited by descendants). * @default false */ disabled?: boolean | undefined; /** * Typeahead text (published as `data-text-value`; falls back to rendered text). * @default undefined */ textValue?: string | undefined; /** * Declares node as branch, overriding `TreeGroup` scan (true makes it expandable before group mounts). * @default undefined — inferred from `TreeGroup`. */ hasChildren?: boolean | undefined; } /** Props for {@link TreeGroup}}, collapsible container of `TreeItem`'s children. */ export interface TreeGroupProps extends PrimitivePropsWithRef<'div'> { /** * Keep group mounted while parent `TreeItem` collapsed. * @default false */ forceMount?: boolean | undefined; } /** Props for {@link TreeItemIndicator}}, the expand/collapse affordance. */ export interface TreeItemIndicatorProps extends PrimitivePropsWithRef<'span'> { }