Skip to content

Commit 2d2878e

Browse files
msidolphinsxzz
andauthored
feat(components): add virtual tree (element-plus#3398)
* feat(components): add virtual tree * perf: optimize compute performance * perf: optimize update checked states performance * feat(components): [tree-v2] supports filter method * feat(components): [el-tree-v2] exposes check api * feat(components): [el-tree-v2] exposes current api * feat(components): [el-tree-v2] support contextmenu event * style(components): [el-tree-v2] optimized code style * refactor(components): [el-tree-v2] using svg icon * refactor(components): [el-tree-v2] replace all PropType with buildProp replace all PropType with buildProp, support perfMode, expose setData * refactor: improve prop * docs: [el-tree-v2] improve documention * refactor(components): [el-tree-v2] optimized code Co-authored-by: Kevin <[email protected]>
1 parent 7ee4f44 commit 2d2878e

File tree

25 files changed

+2732
-2
lines changed

25 files changed

+2732
-2
lines changed

Diff for: .markdownlint.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"MD033": false
2+
"MD033": false,
3+
"MD013": false
34
}

Diff for: docs/.vitepress/crowdin/en-US/pages/component.json

+4
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@
205205
{
206206
"link": "/tree",
207207
"text": "Tree"
208+
},
209+
{
210+
"link": "/tree-v2",
211+
"text": "Virtualized Tree"
208212
}
209213
]
210214
},

Diff for: docs/en-US/component/tree-v2.md

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# <ElBadge value="beta">Tree V2 virtualized tree</ElBadge>
2+
3+
Tree view with blazing fast scrolling performance for any amount of data
4+
5+
## Basic usage
6+
7+
Basic tree structure.
8+
9+
:::demo
10+
11+
tree-v2/basic
12+
13+
:::
14+
15+
## Selectable
16+
17+
Used for node selection.
18+
19+
:::demo
20+
21+
tree-v2/selectable
22+
23+
:::
24+
25+
## Disabled checkbox
26+
27+
The checkbox of a node can be set as disabled.
28+
29+
:::demo In the example, `disabled` property is declared in defaultProps, and some nodes are set as `disabled: true`. The corresponding checkboxes are disabled and can't be clicked.
30+
31+
tree-v2/disabled
32+
33+
:::
34+
35+
## Default expanded and default checked
36+
37+
Tree nodes can be initially expanded or checked
38+
39+
:::demo Use `default-expanded-keys` and `default-checked-keys` to set initially expanded and initially checked nodes respectively.
40+
41+
tree-v2/default-state
42+
43+
:::
44+
45+
## Custom node content
46+
47+
The content of tree nodes can be customized, so you can add icons or buttons as you will
48+
49+
:::demo
50+
51+
tree-v2/custom-node
52+
53+
:::
54+
55+
## Tree node filtering
56+
57+
Tree nodes can be filtered
58+
59+
:::demo Invoke the `filter` method of the Tree instance to filter tree nodes. Its parameter is the filtering keyword. Note that for it to work, `filter-node-method` is required, and its value is the filtering method.
60+
61+
tree-v2/filter
62+
63+
:::
64+
65+
## Attributes
66+
67+
| Attribute | Description | Type | Default |
68+
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------- |
69+
| data | tree data | array ||
70+
| empty-text | text displayed when data is void | string ||
71+
| props | configuration options, see the following table | object ||
72+
| highlight-current | whether current node is highlighted | boolean | false |
73+
| expand-on-click-node | whether to expand or collapse node when clicking on the node, if false, then expand or collapse node only when clicking on the arrow icon. | boolean | true |
74+
| check-on-click-node | whether to check or uncheck node when clicking on the node, if false, the node can only be checked or unchecked by clicking on the checkbox. | boolean | false |
75+
| default-expanded-keys | array of keys of initially expanded nodes | array ||
76+
| show-checkbox | whether node is selectable | boolean | false |
77+
| check-strictly | whether checked state of a node not affects its father and child nodes when `show-checkbox` is `true` | boolean | false |
78+
| default-checked-keys | array of keys of initially checked nodes | array ||
79+
| current-node-key | key of initially selected node | string, number ||
80+
| filter-method | this function will be executed on each node when use filter method. if return `false`, tree node will be hidden. | Function(value, data) ||
81+
| indent | horizontal indentation of nodes in adjacent levels in pixels | number | 16 |
82+
| icon | custome tree node icon | string | - |
83+
84+
## props
85+
86+
| Attribute | Description | Type | Default |
87+
| --------- | ------------------------------------------------------------------------------------ | -------------- | -------- |
88+
| id | unique identity key name for nodes, its value should be unique across the whole tree | string, number | id |
89+
| label | specify which key of node object is used as the node's label | string | label |
90+
| children | specify which node object is used as the node's subtree | string | children |
91+
| disabled | specify which key of node object represents if node's checkbox is disabled | boolean | disabled |
92+
93+
## Method
94+
95+
`Tree` has the following method, which returns the currently selected array of nodes.
96+
| Method | Description | Parameters |
97+
| --------------- | ---------------------------------------- | ---------------------------------------- |
98+
| filter | filter all tree nodes, filtered nodes will be hidden | (query: string) |
99+
| getCheckedNodes | If the node can be selected (`show-checkbox` is `true`), it returns the currently selected array of nodes | (leafOnly: boolean) |
100+
| getCheckedKeys | If the node can be selected (`show-checkbox` is `true`), it returns the currently selected array of node's keys | (leafOnly: boolean) |
101+
| setCheckedKeys | set certain nodes to be checked | (keys: TreeKey[]) |
102+
| setChecked | set node to be checked or not | (key: TreeKey, checked: boolean) |
103+
| getHalfCheckedNodes | If the node can be selected (`show-checkbox` is `true`), it returns the currently half selected array of nodes | - |
104+
| getHalfCheckedKeys | If the node can be selected (`show-checkbox` is `true`), it returns the currently half selected array of node's keys | - |
105+
| getCurrentKey | return the highlight node's key (undefined if no node is highlighted) ||
106+
| getCurrentNode | return the highlight node's data (undefined if no node is highlighted) ||
107+
| setCurrentKey | set highlighted node by key | (key: TreeKey) |
108+
| setData | When the data is very large, using reactive data will cause the poor performance, so we provide a way to avoid this situation | (data: TreeData) |
109+
110+
## Events
111+
112+
| Event Name | Description | Parameters |
113+
| ---------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
114+
| node-click | triggers when a node is clicked | (data: TreeNodeData, node: TreeNode) |
115+
| node-contextmenu | triggers when a node is clicked by right button | (e: Event, data: TreeNodeData, node: TreeNode) |
116+
| check-change | triggers when the selected state of the node changes | (data: TreeNodeData, checked: boolean) |
117+
| check | triggers after clicking the checkbox of a node | (data: TreeNodeData, info: { checkedKeys: TreeKey[],checkedNodes: TreeData, halfCheckedKeys: TreeKey[], halfCheckedNodes: TreeData,}) |
118+
| current-change | triggers when current node changes | (data: TreeNodeData, node: TreeNode) |
119+
| node-expand | triggers when current node open | (data: TreeNodeData, node: TreeNode) |
120+
| node-collapse | triggers when current node close | (data: TreeNodeData, node: TreeNode) |
121+
122+
## Slots
123+
124+
| Name | Description |
125+
| ---- | -------------------------------------------------------------------------------------------- |
126+
|| Custom content for tree nodes. The scope parameter is { node: TreeNode, data: TreeNodeData } |

Diff for: docs/examples/tree-v2/basic.vue

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<template>
2+
<el-tree-v2 :data="data" :props="props" :height="208"></el-tree-v2>
3+
</template>
4+
<script lang="ts">
5+
import { defineComponent, ref } from 'vue'
6+
7+
const getKey = (prefix, id) => {
8+
return `${prefix}-${id}`
9+
}
10+
11+
const createData = (
12+
maxDeep,
13+
maxChildren,
14+
minNodesNumber,
15+
deep = 1,
16+
key = 'node'
17+
) => {
18+
let id = 0
19+
return new Array(minNodesNumber).fill(deep).map(() => {
20+
const childrenNumber =
21+
deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren)
22+
const nodeKey = getKey(key, ++id)
23+
return {
24+
id: nodeKey,
25+
label: nodeKey,
26+
children: childrenNumber
27+
? createData(maxDeep, maxChildren, childrenNumber, deep + 1, nodeKey)
28+
: undefined,
29+
}
30+
})
31+
}
32+
export default defineComponent({
33+
setup() {
34+
return {
35+
data: createData(4, 30, 40),
36+
props: ref({
37+
id: 'id',
38+
label: 'label',
39+
children: 'children',
40+
}),
41+
}
42+
},
43+
})
44+
</script>

Diff for: docs/examples/tree-v2/custom-node.vue

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<el-tree-v2 :data="data" :props="props" :height="208">
3+
<template #default="{ node }">
4+
<span class="prefix" :class="{ 'is-leaf': node.isLeaf }"
5+
>[ElementPlus]</span
6+
>
7+
<span>{{ node.label }}</span>
8+
</template>
9+
</el-tree-v2>
10+
</template>
11+
<script lang="ts">
12+
import { defineComponent, ref } from 'vue'
13+
14+
const getKey = (prefix, id) => {
15+
return `${prefix}-${id}`
16+
}
17+
18+
const createData = (
19+
maxDeep,
20+
maxChildren,
21+
minNodesNumber,
22+
deep = 1,
23+
key = 'node'
24+
) => {
25+
let id = 0
26+
return new Array(minNodesNumber).fill(deep).map(() => {
27+
const childrenNumber =
28+
deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren)
29+
const nodeKey = getKey(key, ++id)
30+
return {
31+
id: nodeKey,
32+
label: nodeKey,
33+
children: childrenNumber
34+
? createData(maxDeep, maxChildren, childrenNumber, deep + 1, nodeKey)
35+
: undefined,
36+
}
37+
})
38+
}
39+
export default defineComponent({
40+
setup() {
41+
return {
42+
data: createData(4, 30, 40),
43+
props: ref({
44+
id: 'id',
45+
label: 'label',
46+
children: 'children',
47+
}),
48+
}
49+
},
50+
})
51+
</script>
52+
53+
<style lang="scss" scoped>
54+
.prefix {
55+
color: var(--el-color-primary);
56+
margin-right: 10px;
57+
&.is-leaf {
58+
color: var(--el-color-success);
59+
}
60+
}
61+
</style>

Diff for: docs/examples/tree-v2/default-state.vue

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<template>
2+
<el-tree-v2
3+
:data="data"
4+
:height="208"
5+
:props="props"
6+
show-checkbox
7+
:default-checked-keys="defaultCheckedKeys"
8+
:default-expanded-keys="defaultExpandedKeys"
9+
></el-tree-v2>
10+
</template>
11+
<script lang="ts">
12+
import { defineComponent, ref } from 'vue'
13+
14+
const getKey = (prefix, id) => {
15+
return `${prefix}-${id}`
16+
}
17+
18+
const createData = (
19+
maxDeep,
20+
maxChildren,
21+
minNodesNumber,
22+
deep = 1,
23+
key = 'node'
24+
) => {
25+
let id = 0
26+
return new Array(minNodesNumber).fill(deep).map(() => {
27+
const childrenNumber =
28+
deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren)
29+
const nodeKey = getKey(key, ++id)
30+
return {
31+
id: nodeKey,
32+
label: nodeKey,
33+
children: childrenNumber
34+
? createData(maxDeep, maxChildren, childrenNumber, deep + 1, nodeKey)
35+
: undefined,
36+
}
37+
})
38+
}
39+
export default defineComponent({
40+
setup() {
41+
const data = createData(4, 30, 40)
42+
const checkedKeys: any[] = []
43+
const expanedKeys: any[] = []
44+
for (let i = 0; i < data.length; ++i) {
45+
const children = data[i].children
46+
if (children) {
47+
expanedKeys.push(data[i].id)
48+
checkedKeys.push(children[0].id)
49+
break
50+
}
51+
}
52+
return {
53+
data,
54+
props: ref({
55+
id: 'id',
56+
label: 'label',
57+
children: 'children',
58+
}),
59+
defaultCheckedKeys: checkedKeys,
60+
defaultExpandedKeys: expanedKeys,
61+
}
62+
},
63+
})
64+
</script>

Diff for: docs/examples/tree-v2/disabled.vue

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<el-tree-v2
3+
:data="data"
4+
:props="props"
5+
show-checkbox
6+
:height="208"
7+
></el-tree-v2>
8+
</template>
9+
<script lang="ts">
10+
import { defineComponent, ref } from 'vue'
11+
12+
const getKey = (prefix, id) => {
13+
return `${prefix}-${id}`
14+
}
15+
16+
const createData = (
17+
maxDeep,
18+
maxChildren,
19+
minNodesNumber,
20+
deep = 1,
21+
key = 'node'
22+
) => {
23+
let id = 0
24+
return new Array(minNodesNumber).fill(deep).map(() => {
25+
const childrenNumber =
26+
deep === maxDeep ? 0 : Math.round(Math.random() * maxChildren)
27+
const nodeKey = getKey(key, ++id)
28+
return {
29+
id: nodeKey,
30+
label: nodeKey,
31+
disabled: Math.random() > 0.6,
32+
children: childrenNumber
33+
? createData(maxDeep, maxChildren, childrenNumber, deep + 1, nodeKey)
34+
: undefined,
35+
}
36+
})
37+
}
38+
export default defineComponent({
39+
setup() {
40+
return {
41+
data: createData(4, 30, 40),
42+
props: ref({
43+
id: 'id',
44+
label: 'label',
45+
children: 'children',
46+
disabled: 'disabled',
47+
}),
48+
}
49+
},
50+
})
51+
</script>

0 commit comments

Comments
 (0)