1
- import type { ExtractPropTypes , PropType , VNode } from 'vue' ;
2
- import { defineComponent , inject } from 'vue' ;
3
- import omit from 'omit.js' ;
1
+ import { ExtractPropTypes , nextTick , onUpdated , PropType , ref , watch } from 'vue' ;
2
+ import { defineComponent } from 'vue' ;
4
3
import debounce from 'lodash-es/debounce' ;
5
4
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined' ;
6
5
import FolderOutlined from '@ant-design/icons-vue/FolderOutlined' ;
7
6
import FileOutlined from '@ant-design/icons-vue/FileOutlined' ;
8
- import PropTypes from '../_util/vue-types' ;
9
7
import classNames from '../_util/classNames' ;
10
- import { treeProps } from './Tree' ;
8
+ import { AntdTreeNodeAttribute , treeProps } from './Tree' ;
11
9
import Tree , { TreeProps } from './Tree' ;
12
- import {
13
- calcRangeKeys ,
14
- getFullKeyList ,
15
- convertDirectoryKeysToNodes ,
16
- getFullKeyListByTreeData ,
17
- } from './util' ;
18
- import { getOptionProps , getComponent , getSlot } from '../_util/props-util' ;
19
10
import initDefaultProps from '../_util/props-util/initDefaultProps' ;
20
- import { defaultConfigProvider } from '../config-provider' ;
11
+ import { convertDataToEntities , convertTreeToData } from '../vc-tree/utils/treeUtil' ;
12
+ import { DataNode , EventDataNode , Key } from '../vc-tree/interface' ;
13
+ import { conductExpandParent } from '../vc-tree/util' ;
14
+ import { calcRangeKeys , convertDirectoryKeysToNodes } from './utils/dictUtil' ;
15
+ import useConfigInject from '../_util/hooks/useConfigInject' ;
16
+ import { filterEmpty } from '../_util/props-util' ;
21
17
22
- export type ExpandAction = false | 'click' | 'doubleClick' | 'dblclick' ;
23
-
24
- function getIcon ( props : { isLeaf : boolean ; expanded : boolean } & VNode ) {
25
- const { isLeaf, expanded } = props ;
26
- if ( isLeaf ) {
27
- return < FileOutlined /> ;
28
- }
29
- return expanded ? < FolderOpenOutlined /> : < FolderOutlined /> ;
30
- }
18
+ export type ExpandAction = false | 'click' | 'doubleclick' | 'dblclick' ;
31
19
32
20
const directoryTreeProps = {
33
21
...treeProps ( ) ,
@@ -36,16 +24,249 @@ const directoryTreeProps = {
36
24
37
25
export type DirectoryTreeProps = Partial < ExtractPropTypes < typeof directoryTreeProps > > ;
38
26
27
+ function getIcon ( props : AntdTreeNodeAttribute ) {
28
+ const { isLeaf, expanded } = props ;
29
+ if ( isLeaf ) {
30
+ return < FileOutlined /> ;
31
+ }
32
+ return expanded ? < FolderOpenOutlined /> : < FolderOutlined /> ;
33
+ }
34
+
39
35
export default defineComponent ( {
40
36
name : 'ADirectoryTree' ,
41
37
inheritAttrs : false ,
42
38
props : initDefaultProps ( directoryTreeProps , {
43
39
showIcon : true ,
44
40
expandAction : 'click' ,
45
41
} ) ,
46
- setup ( ) {
42
+ slots : [ 'icon' , 'title' , 'switcherIcon' ] ,
43
+ emits : [
44
+ 'update:selectedKeys' ,
45
+ 'update:checkedKeys' ,
46
+ 'update:expandedKeys' ,
47
+ 'expand' ,
48
+ 'select' ,
49
+ 'check' ,
50
+ 'doubleclick' ,
51
+ 'dblclick' ,
52
+ 'click' ,
53
+ ] ,
54
+ setup ( props , { attrs, slots, emit } ) {
55
+ // convertTreeToData 兼容 a-tree-node 历史写法,未来a-tree-node移除后,删除相关代码,不要再render中调用 treeData,否则死循环
56
+ const treeData = ref < DataNode [ ] > (
57
+ props . treeData || convertTreeToData ( filterEmpty ( slots . default ?.( ) ) ) ,
58
+ ) ;
59
+ watch (
60
+ ( ) => props . treeData ,
61
+ ( ) => {
62
+ treeData . value = props . treeData ;
63
+ } ,
64
+ ) ;
65
+ onUpdated ( ( ) => {
66
+ nextTick ( ( ) => {
67
+ if ( props . treeData === undefined && slots . default ) {
68
+ treeData . value = convertTreeToData ( filterEmpty ( slots . default ?.( ) ) ) ;
69
+ }
70
+ } ) ;
71
+ } ) ;
72
+ // Shift click usage
73
+ const lastSelectedKey = ref < Key > ( ) ;
74
+
75
+ const cachedSelectedKeys = ref < Key [ ] > ( ) ;
76
+
77
+ const treeRef = ref ( ) ;
78
+
79
+ const getInitExpandedKeys = ( ) => {
80
+ const { keyEntities } = convertDataToEntities ( treeData . value ) ;
81
+
82
+ let initExpandedKeys : any ;
83
+
84
+ // Expanded keys
85
+ if ( props . defaultExpandAll ) {
86
+ initExpandedKeys = Object . keys ( keyEntities ) ;
87
+ } else if ( props . defaultExpandParent ) {
88
+ initExpandedKeys = conductExpandParent (
89
+ props . expandedKeys || props . defaultExpandedKeys ,
90
+ keyEntities ,
91
+ ) ;
92
+ } else {
93
+ initExpandedKeys = props . expandedKeys || props . defaultExpandedKeys ;
94
+ }
95
+ return initExpandedKeys ;
96
+ } ;
97
+
98
+ const selectedKeys = ref ( props . selectedKeys || props . defaultSelectedKeys || [ ] ) ;
99
+
100
+ const expandedKeys = ref < Key [ ] > ( getInitExpandedKeys ( ) ) ;
101
+
102
+ watch (
103
+ ( ) => props . selectedKeys ,
104
+ ( ) => {
105
+ if ( props . selectedKeys !== undefined ) {
106
+ selectedKeys . value = props . selectedKeys ;
107
+ }
108
+ } ,
109
+ { immediate : true } ,
110
+ ) ;
111
+
112
+ watch (
113
+ ( ) => props . expandedKeys ,
114
+ ( ) => {
115
+ if ( props . expandedKeys !== undefined ) {
116
+ expandedKeys . value = props . expandedKeys ;
117
+ }
118
+ } ,
119
+ { immediate : true } ,
120
+ ) ;
121
+
122
+ const expandFolderNode = ( event : MouseEvent , node : any ) => {
123
+ const { isLeaf } = node ;
124
+
125
+ if ( isLeaf || event . shiftKey || event . metaKey || event . ctrlKey ) {
126
+ return ;
127
+ }
128
+ // Call internal rc-tree expand function
129
+ // https://github.com/ant-design/ant-design/issues/12567
130
+ treeRef . value ! . onNodeExpand ( event as any , node ) ;
131
+ } ;
132
+ const onDebounceExpand = debounce ( expandFolderNode , 200 , {
133
+ leading : true ,
134
+ } ) ;
135
+ const onExpand = (
136
+ keys : Key [ ] ,
137
+ info : {
138
+ node : EventDataNode ;
139
+ expanded : boolean ;
140
+ nativeEvent : MouseEvent ;
141
+ } ,
142
+ ) => {
143
+ if ( props . expandedKeys === undefined ) {
144
+ expandedKeys . value = keys ;
145
+ }
146
+ // Call origin function
147
+ emit ( 'update:expandedKeys' , keys ) ;
148
+ emit ( 'expand' , keys , info ) ;
149
+ } ;
150
+
151
+ const onClick = ( event : MouseEvent , node : EventDataNode ) => {
152
+ const { expandAction } = props ;
153
+
154
+ // Expand the tree
155
+ if ( expandAction === 'click' ) {
156
+ onDebounceExpand ( event , node ) ;
157
+ }
158
+ emit ( 'click' , event , node ) ;
159
+ } ;
160
+
161
+ const onDoubleClick = ( event : MouseEvent , node : EventDataNode ) => {
162
+ const { expandAction } = props ;
163
+ // Expand the tree
164
+ if ( expandAction === 'dblclick' || expandAction === 'doubleclick' ) {
165
+ onDebounceExpand ( event , node ) ;
166
+ }
167
+
168
+ emit ( 'doubleclick' , event , node ) ;
169
+ emit ( 'dblclick' , event , node ) ;
170
+ } ;
171
+
172
+ const onSelect = (
173
+ keys : Key [ ] ,
174
+ event : {
175
+ event : 'select' ;
176
+ selected : boolean ;
177
+ node : any ;
178
+ selectedNodes : DataNode [ ] ;
179
+ nativeEvent : MouseEvent ;
180
+ } ,
181
+ ) => {
182
+ const { multiple } = props ;
183
+ const { node, nativeEvent } = event ;
184
+ const { key = '' } = node ;
185
+
186
+ // const newState: DirectoryTreeState = {};
187
+
188
+ // We need wrap this event since some value is not same
189
+ const newEvent : any = {
190
+ ...event ,
191
+ selected : true , // Directory selected always true
192
+ } ;
193
+
194
+ // Windows / Mac single pick
195
+ const ctrlPick : boolean = nativeEvent . ctrlKey || nativeEvent . metaKey ;
196
+ const shiftPick : boolean = nativeEvent . shiftKey ;
197
+
198
+ // Generate new selected keys
199
+ let newSelectedKeys : Key [ ] ;
200
+ if ( multiple && ctrlPick ) {
201
+ // Control click
202
+ newSelectedKeys = keys ;
203
+ lastSelectedKey . value = key ;
204
+ cachedSelectedKeys . value = newSelectedKeys ;
205
+ newEvent . selectedNodes = convertDirectoryKeysToNodes ( treeData . value , newSelectedKeys ) ;
206
+ } else if ( multiple && shiftPick ) {
207
+ // Shift click
208
+ newSelectedKeys = Array . from (
209
+ new Set ( [
210
+ ...( cachedSelectedKeys . value || [ ] ) ,
211
+ ...calcRangeKeys ( {
212
+ treeData : treeData . value ,
213
+ expandedKeys : expandedKeys . value ,
214
+ startKey : key ,
215
+ endKey : lastSelectedKey . value ,
216
+ } ) ,
217
+ ] ) ,
218
+ ) ;
219
+ newEvent . selectedNodes = convertDirectoryKeysToNodes ( treeData . value , newSelectedKeys ) ;
220
+ } else {
221
+ // Single click
222
+ newSelectedKeys = [ key ] ;
223
+ lastSelectedKey . value = key ;
224
+ cachedSelectedKeys . value = newSelectedKeys ;
225
+ newEvent . selectedNodes = convertDirectoryKeysToNodes ( treeData . value , newSelectedKeys ) ;
226
+ }
227
+
228
+ emit ( 'update:selectedKeys' , newSelectedKeys ) ;
229
+ emit ( 'select' , newSelectedKeys , newEvent ) ;
230
+ if ( props . selectedKeys === undefined ) {
231
+ selectedKeys . value = newSelectedKeys ;
232
+ }
233
+ } ;
234
+
235
+ const onCheck : TreeProps [ 'onCheck' ] = ( checkedObjOrKeys , eventObj ) => {
236
+ emit ( 'update:checkedKeys' , checkedObjOrKeys ) ;
237
+ emit ( 'check' , checkedObjOrKeys , eventObj ) ;
238
+ } ;
239
+
240
+ const { prefixCls, direction } = useConfigInject ( 'tree' , props ) ;
241
+
47
242
return ( ) => {
48
- return null ;
243
+ const connectClassName = classNames (
244
+ `${ prefixCls . value } -directory` ,
245
+ {
246
+ [ `${ prefixCls . value } -directory-rtl` ] : direction . value === 'rtl' ,
247
+ } ,
248
+ attrs . class ,
249
+ ) ;
250
+ const { icon = slots . icon , ...otherProps } = props ;
251
+ return (
252
+ < Tree
253
+ { ...attrs }
254
+ icon = { icon || getIcon }
255
+ ref = { treeRef }
256
+ blockNode
257
+ { ...otherProps }
258
+ prefixCls = { prefixCls . value }
259
+ class = { connectClassName }
260
+ expandedKeys = { expandedKeys . value }
261
+ selectedKeys = { selectedKeys . value }
262
+ onSelect = { onSelect }
263
+ onClick = { onClick }
264
+ onDblclick = { onDoubleClick }
265
+ onExpand = { onExpand }
266
+ onCheck = { onCheck }
267
+ v-slots = { slots }
268
+ />
269
+ ) ;
49
270
} ;
50
271
} ,
51
272
} ) ;
0 commit comments