组件 · 数据展示

Tree 树形目录

展示组织、目录和分类的层级结构,支持搜索、行选择、意图复选及异步分支加载;焦点、选择和勾选相互独立。

引入

从 @selrux/ui/tree 引入 Tree 及所需公开类型,如 TreeNode、TreeCheckedState、TreeApi。组件子路径自动引入局部 CSS,无需额外引入全量样式。主题、语言、方向按需使用 @selrux/ui/config-provider。资料为本地 preview,先核对实际安装版本与来源状态。

本地实现子路径:@selrux/ui/tree、@selrux/headless/tree。UI 不直接引用 Headless 源码,类型从 Headless 公开入口复用。

  • UI Tree<T>:组合搜索区、状态区与树视口。
  • Headless TreeRoot<T>:持有模型和行为,不带品牌样式;children 为组合内容。
  • TreeSearch:绑定查询、输入组合态与输入键盘行为。
  • TreeViewport<T>:提供窗口化与非窗口化渲染,接收节点内容 render prop。
  • TreeItem:管理交互壳、稳定 id、ARIA 与节点状态;不由消费者手写 keydown。
  • TreeBranchAction:父行内的加载、分页和重试入口,支持 Alt+Enter 进入。
  • useTreeContext<T>:读取快照与动作;调用方不能直接修改 Map/Set。

何时使用

Tree 用于组织、目录、分类等层级数据,支持展开、搜索、行选择和复选。扁平选项使用 Select;在输入框中选择层级值需应用组合弹层,Tree 不提供 TreeSelect。

默认启用连接线和固定行高虚拟列表;filterable 开启搜索,checkable 开启复选。data、searchFields、getSearchData 保持稳定引用;更新节点时传入新不可变数据,不在每次受控输入更新时重建整棵树。

默认对 name 与 String(value) 做忽略大小写的子串匹配,采用 NFC 规范化。多个空白分隔词之间 AND,启用字段之间 OR;不自动拼音、正则或简繁转换。额外字段必须加入 searchFields,例如 searchData.fullName、searchData.alias。

getSearchData 必须纯函数,在索引建立时运行。context.pathNames() 只含祖先名称,当前节点自行拼接;静态 node.searchData 与派生结果合并,同名派生值优先。可以直接传入服务端预处理的 searchData.fullName,两种方式均不替换节点名称。

filter 模式保留匹配项及祖先并临时展开路径,清空后恢复原展开及滚动位置;highlight 保留当前视图,只标记匹配。filterNode 可覆盖本地匹配,仍须纯函数,不自动在 Worker 执行用户函数。

输入法组合完成后提交查询,默认合并等待 120ms;计算期间保留旧结果并暂停选择提交。隐藏字段命中只显示“匹配”,不会伪造名称高亮或输出完整隐藏路径。

本地搜索仅覆盖已提供及已加载数据,不自动遍历下载未知分支。全量服务端搜索使用 remote 与 searchResult,快照与取消约束见异步加载章节。

勾选意图与提交完整性

级联时最近祖先(含自身)的显式规则生效;没有规则即未选。比如 includeKeys=[部门]、excludeKeys=[小组] 表示选择部门其余成员,未来加载到该小组下的成员仍排除。小组内再显式 include 某成员允许恢复该成员;不能把后代 include 误判为冗余。key 同时出现在两数组是非法输入,不能靠遍历顺序裁决。

用户切换节点时清理该节点可达范围内被此次规则覆盖的旧规则;保留禁用隔离子树内的规则。未知 key 的祖先关系不可猜测,保留为 unresolved。strict 模式不继承,不产生半选,excludeKeys 必须为空;运行时切换 checkStrictly 要由调用方明确重置/转换状态,不隐式改写。

disabled / disableCheckbox 节点阻断祖先级联,父级汇总排除该隔离分支;其后代可由自己的显式规则独立选择。checkable=false 只隐藏自身复选,允许向子节点传导。所有可勾选后代均被隔离时,普通可勾选父节点按自身规则处理;纯结构节点没有可操作后代则不产生勾选。

父节点展示由后代结果派生:全部已知可勾选范围选中且分支完整则全选,部分选中则半选。未完整分支只有明确覆盖未来的 include 意图才能显示全选,否则已选部分显示半选并附“子节点未全部加载”;全未选仍未选。祖先派生全选不自动写成新的 include 意图,避免无意选择未来节点。

resolveChecked 为 O(N) 枚举,只在提交等明确动作调用;complete=false 时不得把部分叶子当完整清单。

不提供含义模糊的 checkedKeys 快捷状态。表单/提交调用 resolveChecked({strategy:'all'|'leaf'|'parent'}),返回已加载有效节点的 keys、values、unresolvedKeys、incompleteBranchKeys 和 complete。leaf 只含确认叶子;parent 是可安全压缩的完整勾选分支,遇到排除或未完整数据不能假装完成。该方法不发请求;只有 complete=true 才能当作完整枚举清单。提交意图结构则由业务后端另行约定,Tree 不宣称它是授权清单。

活动焦点、selectedKeys 行选择、checkedState 复选是独立状态。点击名称选择行,展开图标只展开,复选指示只勾选;方向键只导航。搜索不会缩小父级勾选范围,隐藏后代仍受影响;仅选命中项时逐项勾选或使用 strict。

同一状态只能选受控或非受控,生命周期中不可切换。受控回调只提议,父级回传前仍以 props 为准;相同状态不回调。禁用、加载中重复操作及取消事件不得产生重复提交。默认值只初始化,不随新加载节点重新执行。

data 引用或 dataVersion 变化均取消旧请求并清空旧分支缓存,保留 key 级选择意图为 unresolved 直到重新定位;因此普通输入重渲染必须保持 data 引用稳定。需保留加载数据时,调用方先从快照导出完整已加载结构再替换。显式切换业务数据集时调用方一并重置受控选择,不能跨租户沿用状态。

演练场

默认搜索 name/value;行选择与复选独立。方向键导航,Space 勾选,Enter 选择;异步示例支持 Alt+Enter 进入分页或重试按钮。

正在加载
正在加载

代码演示

基础树与连接线

正在加载
正在加载

连接线开关

正在加载
正在加载

五级目录与多分支

正在加载
正在加载

六档尺寸

正在加载
正在加载

搜索 name、value 与隐藏路径

试试:前端、FE-001、研发中心 前端、Web。清空恢复原展开;不存在的词会显示零匹配。

正在加载
正在加载

自定义匹配与仅高亮

正在加载
正在加载

受控展开、行选择与复选

正在加载
正在加载

行选择:无;展开:team;包含:无;排除:无

父选子排除与半选

正在加载
正在加载
includeKeys = ["engineering"];excludeKeys = ["contractors"]

严格复选与结构节点

正在加载
正在加载

节点级限制

正在加载
正在加载

异步分页与重试

正在加载
正在加载

自定义内容

正在加载
正在加载

四级目录与多行标题

正在加载
正在加载

空态与禁用

正在查询目录
正在查询目录
正在加载
正在加载

无效数据与恢复

正在加载
正在加载

表单校验

正在加载
正在加载

定位、展开与快照

正在加载
正在加载
先尝试定位折叠项,再展开并定位。

英文、暗色与 RTL

Loading
Loading

Headless 无样式组合

十万节点虚拟列表

正在加载
正在加载

API

Tree

开箱即用的数据驱动树;默认固定行高虚拟化。

ts
<T = unknown>(props: TreeProps<T>) => JSX.Element
属性类型可选默认值 / 证据说明来源
datareadonly TreeNode<T>[]否未声明;见说明/约束不可变数据基线;引用变化重置加载代次。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
showLineboolean是未声明;见说明/约束默认开启连接线,关闭后保持缩进。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
size"xs" | "sm" | "md" | "lg" | "xl" | "2xl"是md(继承配置域尺寸,默认 md。)继承配置域尺寸,默认 md。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
renderNode((node: TreeNode<T>, context: TreeNodeRenderContext) => ReactNode)是未声明;见说明/约束默认展示 name,可替换内容但不能覆盖 role 或焦点。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
renderIcon((node: TreeNode<T>, context: TreeRenderContext) => ReactNode)是未声明;见说明/约束装饰图标,不承担展开动作。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
classNamesPartial<Record<TreeSemanticSlot, string>>是未声明;见说明/约束语义 class 覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
stylesPartial<Record<TreeSemanticSlot, CSSProperties>>是未声明;见说明/约束语义内联样式覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
emptyTextstring是未声明;见说明/约束空数据文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
noMatchTextstring是未声明;见说明/约束零匹配文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
loadingTextstring是未声明;见说明/约束搜索/加载中的文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
classNamestring是未声明;见说明/约束react-native: @types/react/index.d.ts
aria-describedbystring是未声明;见说明/约束Identifies the element (or elements) that describes the object.react-native: @types/react/index.d.ts
aria-labelstring是未声明;见说明/约束Defines a string value that labels the current element.react-native: @types/react/index.d.ts
onClickMouseEventHandler<HTMLDivElement>是未声明;见说明/约束react-native: @types/react/index.d.ts
dataVersionstring | number是未声明;见说明/约束业务数据源重置标记。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
disabledboolean是未声明;见说明/约束禁止用户操作。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
invalidboolean是未声明;见说明/约束错误语义。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
expandedKeysreadonly string[]是未声明;见说明/约束受控用户展开集合。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultExpandedKeysreadonly string[]是未声明;见说明/约束初始展开集合。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultExpandDepthnumber是未声明;见说明/约束初始展开层数,不自动请求。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onExpandedChange((keys: readonly string[], details: TreeExpandedDetails) => void)是未声明;见说明/约束展开提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
selectionMode"none" | "single" | "multiple"是未声明;见说明/约束行选择与复选独立。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
selectedKeysreadonly string[]是未声明;见说明/约束受控行选择。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultSelectedKeysreadonly string[]是未声明;见说明/约束初始行选择。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onSelectionChange((keys: readonly string[], details: TreeSelectionDetails) => void)是未声明;见说明/约束行选择提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkableboolean是未声明;见说明/约束显示复选能力。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkStrictlyboolean是未声明;见说明/约束独立复选,不向父子传导。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkedStateTreeCheckedState是未声明;见说明/约束受控包含/排除规则。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultCheckedStateTreeCheckedState是未声明;见说明/约束初始复选规则。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onCheckedChange((state: TreeCheckedState, details: TreeCheckedDetails) => void)是未声明;见说明/约束用户复选提议,加载完成不触发。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
filterableboolean是未声明;见说明/约束UI 可据此组合搜索框。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchValuestring是未声明;见说明/约束受控查询,输入即时回调,计算延迟合并。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultSearchValuestring是未声明;见说明/约束初始查询。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onSearch((query: string) => void)是未声明;见说明/约束搜索值提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchMode"filter" | "highlight"是未声明;见说明/约束过滤或仅标记当前视图。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
remoteboolean是未声明;见说明/约束由外部提供搜索快照。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchResultTreeSearchResult<T>是未声明;见说明/约束外部带代次的搜索结果。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadChildrenTreeLoadChildren<T>是未声明;见说明/约束网络请求适配。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadConcurrencynumber是4(并发上限,默认 4。)并发上限,默认 4。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadTimeoutnumber是30000(请求超时毫秒,默认 30000。)请求超时毫秒,默认 30000。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onLoadError((error: Error, details: TreeErrorDetails) => void)是未声明;见说明/约束分支错误观察。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onDataError((error: Error, details: TreeErrorDetails) => void)是未声明;见说明/约束数据/搜索错误观察。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
virtualboolean是true(固定行高虚拟化,默认 true。)固定行高虚拟化,默认 true。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
itemSizenumber是32(固定行高,默认 32。)固定行高,默认 32。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
heightnumber是320(视口高度,默认 320。)视口高度,默认 320。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
overscannumber是6(额外窗口行,默认 6。)额外窗口行,默认 6。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onContentScroll((details: VirtualListScrollDetails) => void)是未声明;见说明/约束滚动几何变化。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
apiRefRef<TreeApi<T>>是未声明;见说明/约束实例动作,与 DOM ref 分离。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
refRef<HTMLDivElement>是未声明;见说明/约束根 DOM。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchFieldsreadonly string[]是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts
getSearchData((node: TreeNode<T>, context: TreePathContext<T>) => TreeSearchData)是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts
filterNode((query: string, node: TreeNode<T>, context: TreePathContext<T>) => boolean)是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts

其余 272 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeApi

Tree 对外实例接口。

ts
TreeApi<T>
属性类型可选默认值 / 证据说明来源
scrollToKey(key: string, options?: { align?: "start" | "center" | "end" | "auto"; }) => "visible" | "not-visible" | "not-found"否未声明;见说明/约束当前不可见时不隐式展开。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
revealKey(key: string, options?: { focus?: boolean; }) => "visible" | "pending" | "not-found"否未声明;见说明/约束提议展开已知祖先;受控父级尚未接受时返回 pending。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
reloadNode(key: string) => void否未声明;见说明/约束原子刷新分支,失败保留旧数据。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
retryLoad(key: string) => void否未声明;见说明/约束重试失败页。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
resolveChecked(options?: { strategy?: "all" | "leaf" | "parent"; }) => TreeCheckedResult否未声明;见说明/约束按需枚举,未加载范围通过 complete 标识。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
getSnapshot() => TreeSnapshot<T>否未声明;见说明/约束返回副本,不允许外部修改内部状态。headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeChangeDetails

一次操作的变更来源;加载完成不产生伪用户变更。

ts
TreeChangeDetails
属性类型可选默认值 / 证据说明来源
keystring否未声明;见说明/约束触发节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
reason"pointer" | "keyboard" | "api"否未声明;见说明/约束操作来源。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
originalEventEvent是未声明;见说明/约束原始 DOM 事件。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeCheckedDetails

复选状态变更。

ts
TreeCheckedDetails
属性类型可选默认值 / 证据说明来源
checkedboolean否未声明;见说明/约束目标勾选状态。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
keystring否未声明;见说明/约束触发节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
reason"pointer" | "keyboard" | "api"否未声明;见说明/约束操作来源。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
originalEventEvent是未声明;见说明/约束原始 DOM 事件。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeCheckedResult

显式枚举结果;complete=false 不可当作完整提交清单。

ts
TreeCheckedResult
属性类型可选默认值 / 证据说明来源
keysreadonly string[]否未声明;见说明/约束已加载的有效节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
valuesreadonly (string | number)[]否未声明;见说明/约束与 keys 顺序一致的业务值。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
unresolvedKeysreadonly string[]否未声明;见说明/约束尚无法定位的意图节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
incompleteBranchKeysreadonly string[]否未声明;见说明/约束受选择影响但尚未完整的分支。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
completeboolean否未声明;见说明/约束是否完成枚举。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeCheckedState

勾选规则的公开契约。

ts
TreeCheckedState
属性类型可选默认值 / 证据说明来源
includeKeysreadonly string[]否未声明;见说明/约束包含当前节点及未来可达子项。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
excludeKeysreadonly string[]否未声明;见说明/约束排除当前子分支;最近的显式规则优先。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeChildrenPage

当前父级的一页直接子节点。

ts
TreeChildrenPage<T>
属性类型可选默认值 / 证据说明来源
nodesreadonly TreeNode<T>[]否未声明;见说明/约束按返回顺序追加。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
nextCursornull | string否未声明;见说明/约束null 为终页。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeErrorDetails

错误上下文不包含整份业务数据。

ts
TreeErrorDetails
属性类型可选默认值 / 证据说明来源
phase"data" | "search" | "load"否未声明;见说明/约束出错阶段。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
keystring是未声明;见说明/约束出错节点;整体索引错误时省略。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeExpandedDetails

展开状态变更。

ts
TreeExpandedDetails
属性类型可选默认值 / 证据说明来源
expandedboolean否未声明;见说明/约束目标展开状态。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
keystring否未声明;见说明/约束触发节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
reason"pointer" | "keyboard" | "api"否未声明;见说明/约束操作来源。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
originalEventEvent是未声明;见说明/约束原始 DOM 事件。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeLoadChildren

调用方负责网络与授权,组件负责生命周期。

ts
(node: TreeNode<T>, context: { readonly cursor: string | null; readonly signal: AbortSignal; readonly reason: TreeLoadReason; }) => Promise<TreeChildrenPage<T>>

TreeLoadReason

分支请求的触发场景。

ts
"expand" | "more" | "retry" | "reload"

TreeNode

数据驱动节点;key 全树唯一,value 不承担身份。

ts
TreeNode<T>
属性类型可选默认值 / 证据说明来源
keystring否未声明;见说明/约束稳定非空标识。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
namestring否未声明;见说明/约束展示与可访问名称。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
valuestring | number否未声明;见说明/约束可搜索的业务值,允许重复。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
isLeafboolean是未声明;见说明/约束false 表示尚可异步展开;true 必须没有子项。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
childrenState"unloaded" | "partial" | "complete"是未声明;见说明/约束已知子项的完整性。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
nextCursorstring是未声明;见说明/约束部分加载的下一页游标。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
disabledboolean是未声明;见说明/约束禁止当前节点交互,阻断祖先级联。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
selectableboolean是未声明;见说明/约束是否允许行选择。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
checkableboolean是未声明;见说明/约束false 隐藏自身复选但级联可穿透。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
disableCheckboxboolean是未声明;见说明/约束禁用复选并隔离级联。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
searchDataReadonly<Record<string, string | readonly string[]>>是未声明;见说明/约束不自动渲染的搜索文档。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
dataT是未声明;见说明/约束业务原始数据。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
childrenreadonly TreeNode<T>[]是未声明;见说明/约束已知直接子项。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeNodeRenderContext

用户渲染得到默认内容,保留原交互壳。

ts
TreeNodeRenderContext
属性类型可选默认值 / 证据说明来源
defaultNodeReactNode否未声明;见说明/约束ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
depthnumber否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
expandedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
selectedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
checkedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
mixedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
loadingboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
matchedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
activeboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
checkDisabledboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreePathContext

祖先路径按需读取,避免常驻每个节点的完整路径副本。

ts
TreePathContext<T>
属性类型可选默认值 / 证据说明来源
pathNames() => readonly string[]否未声明;见说明/约束不包含当前节点的祖先名称。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts
ancestors() => readonly TreeNode<T>[]否未声明;见说明/约束不包含当前节点的祖先数据。headless: @selrux/headless/dist/types/components/tree/contracts/node.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeProps

可搜索的树:支持虚拟化、连接线、自定义内容与异步勾选。

ts
TreeProps<T>
属性类型可选默认值 / 证据说明来源
datareadonly TreeNode<T>[]否未声明;见说明/约束不可变数据基线;引用变化重置加载代次。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
showLineboolean是未声明;见说明/约束默认开启连接线,关闭后保持缩进。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
size"xs" | "sm" | "md" | "lg" | "xl" | "2xl"是md(继承配置域尺寸,默认 md。)继承配置域尺寸,默认 md。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
renderNode((node: TreeNode<T>, context: TreeNodeRenderContext) => ReactNode)是未声明;见说明/约束默认展示 name,可替换内容但不能覆盖 role 或焦点。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
renderIcon((node: TreeNode<T>, context: TreeRenderContext) => ReactNode)是未声明;见说明/约束装饰图标,不承担展开动作。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
classNamesPartial<Record<TreeSemanticSlot, string>>是未声明;见说明/约束语义 class 覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
stylesPartial<Record<TreeSemanticSlot, CSSProperties>>是未声明;见说明/约束语义内联样式覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
emptyTextstring是未声明;见说明/约束空数据文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
noMatchTextstring是未声明;见说明/约束零匹配文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
loadingTextstring是未声明;见说明/约束搜索/加载中的文案覆盖。ui: @selrux/ui/dist/types/components/tree/tree-props.d.ts
classNamestring是未声明;见说明/约束react-native: @types/react/index.d.ts
aria-describedbystring是未声明;见说明/约束Identifies the element (or elements) that describes the object.react-native: @types/react/index.d.ts
aria-labelstring是未声明;见说明/约束Defines a string value that labels the current element.react-native: @types/react/index.d.ts
onClickMouseEventHandler<HTMLDivElement>是未声明;见说明/约束react-native: @types/react/index.d.ts
dataVersionstring | number是未声明;见说明/约束业务数据源重置标记。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
disabledboolean是未声明;见说明/约束禁止用户操作。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
invalidboolean是未声明;见说明/约束错误语义。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
expandedKeysreadonly string[]是未声明;见说明/约束受控用户展开集合。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultExpandedKeysreadonly string[]是未声明;见说明/约束初始展开集合。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultExpandDepthnumber是未声明;见说明/约束初始展开层数,不自动请求。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onExpandedChange((keys: readonly string[], details: TreeExpandedDetails) => void)是未声明;见说明/约束展开提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
selectionMode"none" | "single" | "multiple"是未声明;见说明/约束行选择与复选独立。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
selectedKeysreadonly string[]是未声明;见说明/约束受控行选择。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultSelectedKeysreadonly string[]是未声明;见说明/约束初始行选择。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onSelectionChange((keys: readonly string[], details: TreeSelectionDetails) => void)是未声明;见说明/约束行选择提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkableboolean是未声明;见说明/约束显示复选能力。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkStrictlyboolean是未声明;见说明/约束独立复选,不向父子传导。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
checkedStateTreeCheckedState是未声明;见说明/约束受控包含/排除规则。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultCheckedStateTreeCheckedState是未声明;见说明/约束初始复选规则。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onCheckedChange((state: TreeCheckedState, details: TreeCheckedDetails) => void)是未声明;见说明/约束用户复选提议,加载完成不触发。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
filterableboolean是未声明;见说明/约束UI 可据此组合搜索框。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchValuestring是未声明;见说明/约束受控查询,输入即时回调,计算延迟合并。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
defaultSearchValuestring是未声明;见说明/约束初始查询。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onSearch((query: string) => void)是未声明;见说明/约束搜索值提议。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchMode"filter" | "highlight"是未声明;见说明/约束过滤或仅标记当前视图。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
remoteboolean是未声明;见说明/约束由外部提供搜索快照。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchResultTreeSearchResult<T>是未声明;见说明/约束外部带代次的搜索结果。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadChildrenTreeLoadChildren<T>是未声明;见说明/约束网络请求适配。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadConcurrencynumber是4(并发上限,默认 4。)并发上限,默认 4。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
loadTimeoutnumber是30000(请求超时毫秒,默认 30000。)请求超时毫秒,默认 30000。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onLoadError((error: Error, details: TreeErrorDetails) => void)是未声明;见说明/约束分支错误观察。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onDataError((error: Error, details: TreeErrorDetails) => void)是未声明;见说明/约束数据/搜索错误观察。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
virtualboolean是true(固定行高虚拟化,默认 true。)固定行高虚拟化,默认 true。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
itemSizenumber是32(固定行高,默认 32。)固定行高,默认 32。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
heightnumber是320(视口高度,默认 320。)视口高度,默认 320。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
overscannumber是6(额外窗口行,默认 6。)额外窗口行,默认 6。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
onContentScroll((details: VirtualListScrollDetails) => void)是未声明;见说明/约束滚动几何变化。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
apiRefRef<TreeApi<T>>是未声明;见说明/约束实例动作,与 DOM ref 分离。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
refRef<HTMLDivElement>是未声明;见说明/约束根 DOM。headless: @selrux/headless/dist/types/components/tree/contracts/props.d.ts
searchFieldsreadonly string[]是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts
getSearchData((node: TreeNode<T>, context: TreePathContext<T>) => TreeSearchData)是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts
filterNode((query: string, node: TreeNode<T>, context: TreePathContext<T>) => boolean)是未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/search/search-types.d.ts

其余 272 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeRenderContext

内容渲染只能接触状态,不能替换交互壳。

ts
TreeRenderContext
属性类型可选默认值 / 证据说明来源
depthnumber否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
expandedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
selectedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
checkedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
mixedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
loadingboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
matchedboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
activeboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts
checkDisabledboolean否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/primitives/context-types.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeSearchData

Tree 的节点身份、业务数据与派生搜索字段。

ts
{ readonly [x: string]: string | readonly string[]; }

TreeSearchResult

外部搜索快照,必须与当前 query 和最新 revision 一致。

ts
TreeSearchResult<T>
属性类型可选默认值 / 证据说明来源
querystring否未声明;见说明/约束对应的原始查询。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
revisionnumber否未声明;见说明/约束同一查询的单调递增版本。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
status"loading" | "error" | "success"否未声明;见说明/约束查询状态。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
nodesreadonly TreeNode<T>[]否未声明;见说明/约束包含祖先上下文的结果树。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
matchedKeysreadonly string[]否未声明;见说明/约束真正命中的 key,不含仅作上下文的祖先。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
completeboolean否未声明;见说明/约束查询是否完整。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
nextCursornull | string是未声明;见说明/约束外部后续页游标。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts
errorError是未声明;见说明/约束可呈现的失败信息。headless: @selrux/headless/dist/types/components/tree/contracts/loading.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeSelectionDetails

行选择差异。

ts
TreeSelectionDetails
属性类型可选默认值 / 证据说明来源
addedKeysreadonly string[]否未声明;见说明/约束新增节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
removedKeysreadonly string[]否未声明;见说明/约束移除节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
keystring否未声明;见说明/约束触发节点。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
reason"pointer" | "keyboard" | "api"否未声明;见说明/约束操作来源。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts
originalEventEvent是未声明;见说明/约束原始 DOM 事件。headless: @selrux/headless/dist/types/components/tree/contracts/selection.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

TreeSemanticSlot

可覆盖的语义外观槽。

ts
"content" | "search" | "status" | "root" | "viewport" | "item" | "switcher" | "checkIndicator" | "icon" | "line" | "empty" | "loading" | "error"

TreeSize

六档尺寸沿用库内控件比例。

ts
"xs" | "sm" | "md" | "lg" | "xl" | "2xl"

TreeSnapshot

可序列化的已加载状态快照。

ts
TreeSnapshot<T>
属性类型可选默认值 / 证据说明来源
datareadonly TreeNode<T>[]否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
searchValuestring否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
expandedKeysreadonly string[]否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
selectedKeysreadonly string[]否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts
checkedStateTreeCheckedState否未声明;见说明/约束headless: @selrux/headless/dist/types/components/tree/contracts/api.d.ts

其余 0 个 React 原生属性按继承汇总;关键原生属性已展开。

节点数据约束

name 是非空展示文本及可访问名称;value 可重复,搜索默认转换为字符串。searchData 只参与已启用字段的搜索,data 原样留给业务渲染。完整字段由公开 TreeNode 声明生成。

key 不使用路径名称、数组下标或 value 的隐式字符串转换生成。这样允许节点改名、移动及 value 重复,也不会混淆数字 1 与字符串 "1"。选择和展开回调均使用 key,业务 value 通过解析后的节点获取。

省略状态时:有 children(包括空数组)视为完整;无 children 且 isLeaf=false 为未加载;其他情况视为叶子。异步空分支必须明确 isLeaf=false,加载返回空且完整后转为叶子。partial 必须有 nextCursor,complete 不得附 nextCursor,isLeaf=true 不得同时有子项或不完整状态。根级分页由应用负责传入 data,不冒充节点子页。

重复 key、循环引用、多个父节点引用同一 key、非法字段与状态冲突按数据错误处理。错误指出冲突 key,生产同样拒绝有问题的数据版本/加载页;保留上一份合法快照,不静默去重或挂接到错误父级。初次数据非法显示错误态并禁用数据操作。

异步加载与远程快照

loadChildren(node, { cursor, signal, reason }) 返回 { nodes, nextCursor };第一页 cursor=null,终页 nextCursor=null。reason 是 expand/more/retry/reload;把 signal 传给实际网络请求。默认并发 4、超时 30000ms,加载/更多/重试位于父行尾部,不占业务节点编号。

每个父级仅一个在途请求,标识为 dataGeneration + parentKey + cursor + requestId。分页只按游标顺序追加;重试同页不重复提交,重复/冲突 key 使整页失败。空页且还有游标允许继续手动加载,但游标重复或循环必须报错,不自动无限翻页。

折叠取消尚未开始的排队请求;在途请求默认允许完成缓存,不自动重新展开。删除、刷新或新数据代次取消在途请求;即使 loader 无视 signal,旧结果也不能提交。队列上限 32,超限分支保持未加载并给出可重试状态,不留下永久 loading。

加载状态为 idle/queued/loading/partial/loaded/error,错误不清空已成功子页,不自动重试。reloadNode 中止当前请求后请求第一页,成功后原子替换当前子树,失败保留旧数据并显示重试按钮。缓存只含当前数据代次已加载分支,不做后台 TTL 自动刷新。

远程搜索 TreeSearchResult<T> 包含 query、revision、status(loading/success/error)、nodes(保留祖先路径的结果树)、matchedKeys、complete、nextCursor、error。只提交 query 与当前输入一致且 revision 最新的结果;父级负责取消请求、分页和错误重试。搜索快照与基础树隔离,clear 后恢复基础树;相同 key 的身份必须一致。远程搜索上下文中的父节点禁止级联勾选;strict 模式独立勾选具体节点(包括调用方提供的上下文节点)。首版不在远程搜索快照里调用 loadChildren,避免混入非查询结果。

实例动作

UI 使用单独 apiRef,保留 ref 指向根 DOM 的库内习惯。TreeApi 提供:

  • scrollToKey(key, {align}):仅定位当前可见 key,返回 visible/not-visible/not-found;不隐式展开或发请求。
  • revealKey(key, {focus}):仅对已知祖先路径提出展开请求,提交后定位;受控父级未接受时返回 pending;未知路径需消费方先加载,不能递归全树查找。
  • reloadNode(key) / retryLoad(key):明确的分支刷新/失败重试入口。
  • resolveChecked(options):显式枚举勾选结果;枚举成本为 O(N),只在明确提交动作调用。
  • getSnapshot():返回查询、展开、行选择、复选意图与已加载结构副本,不允许篡改内部集合。getSnapshot 会遍历已加载数据,只在保存等明确动作调用。

不自动注入十万个隐藏 input。原生 form reset、required 与提交序列化由应用表单适配;组件提供受控状态与 invalid/aria-describedby 透传。首版不宣称与原生 select 等价。

虚拟化与自定义内容

默认 virtual=true、height=320、overscan=6。Headless 默认行高 32px,UI 六档尺寸对应 24/28/32/36/40/48px。虚拟模式所有行必须同高,itemSize 应覆盖自定义内容高度;小数据自然多行可用 virtual=false 并设置 styles.content.whiteSpace='normal'。非虚拟模式不适用于十万节点。

renderNode 只替换内容,context.defaultNode 保留名称高亮;上下文包含深度、展开、选择、勾选、半选、加载、命中、活动及复选禁用状态。渲染槽不改变 name 的可访问名称;交互内容的边界见自定义渲染契约。

classNames/styles 提供 root、search、status、viewport、item、switcher、checkIndicator、icon、content、line、empty、loading、error 槽。开启连接线时使用方框加减号,叶子短横线从本级竖线接出,末项折线收口,祖先续线贯穿多级后代;关闭 showLine 改用箭头并保留缩进。自然多行以第一行对齐图标与短横线。

十万节点示例按按钮生成,不在打开页面时分配。虚拟化减少 DOM,不消除搜索、级联汇总和导出的遍历成本;长路径增加索引内存。内置字符预算超限通过 onDataError 报错,可减少字段、使用短别名或远程搜索。本批不重新证明十万节点的性能、内存峰值或全量性能门槛。

自定义渲染边界

renderNode 不能覆盖 role、id、ARIA、tabIndex 或焦点壳;默认仅非交互内容。额外按钮/菜单需要 Headless 组合与单独键盘设计,不以 stopPropagation 冒充完整可访问性。节点 name 始终是可访问名称,value 和隐藏 searchData 不自动读出。

键盘与无障碍

为每棵树提供 aria-label 或 aria-labelledby;placeholder 不替代可访问名称。焦点、行选择、复选分别通过 aria-activedescendant、aria-selected、aria-checked 表达。虚拟窗口额外挂载活动行,保证焦点目标存在;连接线不进入可访问树。

按键行为
Tab / Shift+Tab搜索框、树容器与外部控件间移动,不逐节点 Tab
↑ / ↓、Home / End前后或首尾可见项,不自动选择
→ / ←展开或进入子项,收起或返回父项;RTL 镜像方向
Enter行选择;selectionMode=none 时切换展开
Spacecheckable 时勾选,否则行选择;不可勾选行不回退选择
Shift+点击 / Shift+Spacemultiple 行选择扩展当前可见范围;启用复选时 Space 优先复选
名称前缀按 name 定位,不查隐藏字段
Escape / Alt+Enter搜索框 Escape 清空;树内 Alt+Enter 进入父行分页/重试,Escape 返回

自动化键盘与 DOM 语义检查不代表人工读屏兼容已验收;VoiceOver/Safari 与 NVDA/Chromium 尚未完成本批验证。

文档快照:edbed4e3949112ae4797ba611a523984