https://treemate.vercel.app/zh-CN.html
组件开发中树形数据结构的一站式解决方案。
帮助人们操作树形数据结构(可以用于 Tree,Select,Dropdown,Table,Menu 等组件)。
- 勾选节点,取消勾选节点
- 展开节点,折叠节点
- 在节点中移动
- 获取打平为数组的树
- 查询节点
- 支持 group 节点(聚集了一些同层级节点的节点)
- 支持 ignored 节点(在移动时忽略)
- 节点的元信息
- 获取到对应的原始数据引用
- 不完整数据中的勾选
- ...
npm i -D treemate
在开始之前,我强烈建议阅读这一节,了解一些 treemate 的基本概念。
在 treemate 中,一个树由节点(可选的 group 节点和可选的 ignored 节点)组成。
一个普通节点包括一个 key,可能还包含一个 children 属性,里面是它的子节点。
Node {
key,
children?
}
如果你不需要 Group 节点,可以跨过这一节。
一个 Group 节点包含一个 key,一个名为 type 的属性,值为 group
,还有一个 children 属性包含了它的子节点(不能包含 Group 节点)。
GroupNode {
key,
type: 'group',
children
}
在不同节点的移动过程中,Group 节点自身会被忽略,Group 节点的子节点会被看作为和 Group 在同一层的节点。
例如在下面的树中,group node 1 是 node 1 的下一个节点,group child 2 的下一个节点是 node 2.
- node 1
- group node 1
- group child 1
- group child 2
- node 2
有的时候你需要一些仅需渲染的节点。例如
- node 1
- 分割线 (render only)
- node 2
在数据层面,分割线节点没有实际意义。你可以把它作为一个 Ignored 节点。这个节点在节点移动的过程中会被忽略。(node 2 会被看作 node 1 的下一个节点。)同时 getNode
方法也不会返回 Ignored 节点。
Ignored 节点也应该包含一个 key(为现代前端框架的 diff 过程使用)。
IgnoredNode {
key,
type: 'ignored'
}
createTreeMate
方法接受一个节点的数组作为输入数据,返回一个 treemate 的实例。
在 javascript 中使用:
import { createTreeMate } from 'treemate'
const data = [
// 非叶节点
{
key: 1,
children: [
{
key: 2
}
]
},
// 叶节点
{
key: 3
},
// ignored 节点
{
key: 4,
type: 'ignored'
},
// group 节点
{
key: 5,
type: 'group',
children: [
{
key: 6
}
]
}
]
const treeMate = createTreeMate(data)
在 typescript 中,data 没什么区别。但是 createTreeMate
接受 3 个可选的泛型参数,这些参数为普通节点、Group 节点和 Ignored 节点的类型。
interface BaseNode {
key: string | number
children?: Array<BaseNode | GroupNode | IgnoredNode>
}
interface GroupNode {
key: string | number
type: 'group'
children: Array<BaseNode | IgnoredNode>
}
interface IgnoredNode {
key: string | number
type: 'ignored'
}
// 1. 指名左右节点的类型
const treeMate = createTreeMate<BaseNode, GroupNode, IgnoredNode>(data)
// 2. 等价于 createTreeMate<BaseNode, GroupNode, BaseNode>()
// 也就是说在数据中不会出现 Ignored 节点
const treeMate = createTreeMate<BaseNode, GroupNode>(data)
// 3. 等价于 createTreeMate<BaseNode, BaseNode, BaseNode>()
// 也就是说 Ignored 节点和 Group 节点不会出现在数据中
const treeMate = createTreeMate<BaseNode>(data)
// 4. 不写任何泛型参数
// 它会使用一个内置的类型作为基本节点的类型
// RawNode {
// key: string | number
// children?: RawNode[]
// disabled?: boolean
// isLeaf? boolean
// }
// Ignored 节点和 Group 节点不会出现在数据中
const treeMate = createTreeMate(data)
如果希望通过其他方式来判定一个节点的 key 和 disabled、group、ignored 的状态,你可以给 create treemate 传递一个选项。
const treeMate = createTreeMate(data, {
getKey: (node) => Key,
getDisabled: (node) => boolean,
getIsGroup: (node) => boolean,
getIgnored: (node) => boolean
})
假设我们有一个 treemate 的实例。
const tmNode = treeMate.getNode(key) // 如果不存在该节点会返回 null
// 注意:getNode 不会返回 Group 节点和 Ignored 节点!
// 如果你需要获取这些节点,你可以使用 treeNodeMap,例如:
treeMate.treeNodeMap.get(key)
TreeMateNode {
key,
rawNode, // 对于原始数据节点的引用,很可能有用
level, // 从 0 开始
index, // 它在父节点(或者根数组)中的 index
siblings,
isFirstChild,
isLastChild,
parent, // 父树节点(不是数据节点)
isShallowLoaded, // 在局部加载数据时会用到
isLeaf,
isGroup,
ignored, // boolean
disabled, // disabled
children?, // 它的子树节点(不是原始数据节点)
getPrev(), // 方法
getNext(), // 方法
getParent(), // 方法
getChild() // 方法
}
获取树的勾选状态。
disabled = true
的节点会阻止关联勾选的检查传播。
参数 checkedKeys
有两种形式:
Key[] // 1. 当前勾选的节点
// 2. 当前的综合勾选状态
interface InputMergedKeys {
checkedKeys?: Key[] | null
indeterminateKeys?: Key[] | null // half checked
}
// 也可以是
null | undefined
// 会被当作空数组看待
选项 options
形如:
interface CheckOptions {
cascade?: boolean // 是否关联勾选,子级勾选会影响父级状态,默认为 true
leafOnly?: boolean // 是否只允许叶节点被勾选,默认为 false
checkStrategy?: string // 勾选显示策略 'all' | 'parent' | 'child'
}
返回值形如:
interface MergedKeys {
checkedKeys: Key[]
indeterminateKeys: Key[] // 半选
}
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys([1])
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys([1], {
cascade: false
})
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys({
checkedKeys: [1],
indeterminateKeys: [2]
})
// ...
获取树在勾选一些节点后新的勾选状态。
keysToCheck
可以为 Key | Key[] | null | undefined
。
checkedKeys
,options
和返回值参考 getCheckedKeys(checkedKeys, options?)
。
获取树在取消勾选一些节点后新的勾选状态。
keysToCheck
可以为 Key | Key[] | null | undefined
。
checkedKeys
, options
和返回值参考 getCheckedKeys(checkedKeys, options?)
。
获取该 key
对应节点的前一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
options
形如 { loop?: boolean }
,默认 loop
为 false
,不会循环寻找。
获取该 key
对应节点的后一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
options
形如 { loop?: boolean }
,默认 loop
为 false
,不会循环寻找。
获取该 key
对应节点的父级 TreeMateNode
,寻找过程中 group
节点自身会被忽略,不存在时返回 null
。
获取该 key
对应节点第一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
获取该节点的前一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
options
形如 { loop?: boolean }
,默认 loop
为 false
,不会循环寻找。
获取该节点的后一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
options
形如 { loop?: boolean }
,默认 loop
为 false
,不会循环寻找。
获取该节点的父级 TreeMateNode
,寻找过程中 group
节点自身会被忽略,不存在时返回 null
。
获取该节点第一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
展开状态会影响树的展平状态。展平节点对于虚拟列表至关重要。
获取树对应于 expandedKeys
的展平节点。如果 expandedKeys
没有传入,treemate 会当作所有节点全部处于展开状态。
从展平节点创建一个索引的 getter 函数。
import { createIndexGetter } from 'treemate'
const getIndex = createIndexGetter(flattenedNodes)
getIndex(flattenedNodes[0].key) === 0
获取从根到该 key
对应节点的路径。返回值形如
interface MergedPath {
keyPath: Key[]
treeNodePath: TreeMateNode[]
treeNode: TreeMateNode | null
}
其中 keyPath
为路径中各个节点的 key
。其中 treeNodePath
为节点路径。treeNode
该 key
对应的 TreeMateNode
。
options
形如 { includeSelf?: boolean, includeGroup?: boolean }
,默认 includeSelf
为 true,includeGroup
为 false。
可以用于获取选择菜单的默认选项。
获取整个树第一个非 disabled
的 TreeMateNode
,寻找过程中 group | ignored
节点自身会被忽略,不存在时返回 null
。
原数据对应的 TreeMateNode
数组,结构完全对应于原数据。
key
到节点的 Map。包含全部节点,group | ignored
节点包含在内。