Skip to content

Commit de05f93

Browse files
authored
fix: declare Tree Component type (#3178)
* fix: declare Tree Component type
1 parent 00455da commit de05f93

File tree

4 files changed

+111
-59
lines changed

4 files changed

+111
-59
lines changed

components/menu/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const Menu = defineComponent({
165165

166166
// Fix SVGElement e.target.className.indexOf is not a function
167167
// https://github.com/ant-design/ant-design/issues/15699
168-
const { className } = e.target as (SVGAnimationElement | HTMLElement);
168+
const { className } = e.target as SVGAnimationElement | HTMLElement;
169169
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
170170
const classNameValue =
171171
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'

components/tree/DirectoryTree.tsx

+15-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineComponent, inject } from 'vue';
1+
import { defineComponent, inject, VNode } from 'vue';
22
import omit from 'omit.js';
33
import debounce from 'lodash-es/debounce';
44
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined';
@@ -7,7 +7,7 @@ import FileOutlined from '@ant-design/icons-vue/FileOutlined';
77
import PropTypes from '../_util/vue-types';
88
import classNames from '../_util/classNames';
99
import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util';
10-
import Tree, { TreeProps } from './Tree';
10+
import Tree, { CheckEvent, ExpendEvent, SelectEvent, TreeProps } from './Tree';
1111
import {
1212
calcRangeKeys,
1313
getFullKeyList,
@@ -25,11 +25,11 @@ import { defaultConfigProvider } from '../config-provider';
2525
// selectedKeys?: string[]; }
2626

2727
export interface DirectoryTreeState {
28-
_expandedKeys?: string[];
29-
_selectedKeys?: string[];
28+
_expandedKeys?: (string | number)[];
29+
_selectedKeys?: (string | number)[];
3030
}
3131

32-
function getIcon(props) {
32+
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
3333
const { isLeaf, expanded } = props;
3434
if (isLeaf) {
3535
return <FileOutlined />;
@@ -56,7 +56,7 @@ export default defineComponent({
5656
children: null,
5757
onDebounceExpand: null,
5858
tree: null,
59-
lastSelectedKey: [],
59+
lastSelectedKey: '',
6060
cachedSelectedKeys: [],
6161
configProvider: inject('configProvider', defaultConfigProvider),
6262
};
@@ -100,15 +100,15 @@ export default defineComponent({
100100
this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true });
101101
},
102102
methods: {
103-
handleExpand(expandedKeys, info) {
103+
handleExpand(expandedKeys: (string | number)[], info: ExpendEvent) {
104104
this.setUncontrolledState({ _expandedKeys: expandedKeys });
105105
this.$emit('update:expandedKeys', expandedKeys);
106106
this.$emit('expand', expandedKeys, info);
107107

108108
return undefined;
109109
},
110110

111-
handleClick(event, node) {
111+
handleClick(event: MouseEvent, node: VNode) {
112112
const { expandAction } = this.$props;
113113

114114
// Expand the tree
@@ -118,7 +118,7 @@ export default defineComponent({
118118
this.$emit('click', event, node);
119119
},
120120

121-
handleDoubleClick(event, node) {
121+
handleDoubleClick(event: MouseEvent, node: VNode) {
122122
const { expandAction } = this.$props;
123123

124124
// Expand the tree
@@ -130,7 +130,7 @@ export default defineComponent({
130130
this.$emit('dblclick', event, node);
131131
},
132132

133-
hanldeSelect(keys, event) {
133+
hanldeSelect(keys: (string | number)[], event: SelectEvent) {
134134
const { multiple } = this.$props;
135135
const children = this.children || [];
136136
const { _expandedKeys: expandedKeys = [] } = this.$data;
@@ -150,7 +150,7 @@ export default defineComponent({
150150
const shiftPick = nativeEvent.shiftKey;
151151

152152
// Generate new selected keys
153-
let newSelectedKeys;
153+
let newSelectedKeys: (string | number)[];
154154
if (multiple && ctrlPick) {
155155
// Control click
156156
newSelectedKeys = keys;
@@ -180,11 +180,11 @@ export default defineComponent({
180180

181181
this.setUncontrolledState(newState);
182182
},
183-
setTreeRef(node) {
183+
setTreeRef(node: VNode) {
184184
this.tree = node;
185185
},
186186

187-
expandFolderNode(event, node) {
187+
expandFolderNode(event: MouseEvent, node: { isLeaf: boolean } & VNode) {
188188
const { isLeaf } = node;
189189

190190
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
@@ -201,7 +201,7 @@ export default defineComponent({
201201
}
202202
},
203203

204-
setUncontrolledState(state) {
204+
setUncontrolledState(state: unknown) {
205205
const newState = omit(
206206
state,
207207
Object.keys(getOptionProps(this)).map(p => `_${p}`),
@@ -210,7 +210,7 @@ export default defineComponent({
210210
this.setState(newState);
211211
}
212212
},
213-
handleCheck(checkedObj, eventObj) {
213+
handleCheck(checkedObj: (string | number)[], eventObj: CheckEvent) {
214214
this.$emit('update:checkedKeys', checkedObj);
215215
this.$emit('check', checkedObj, eventObj);
216216
},

components/tree/Tree.tsx

+69-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineComponent, inject } from 'vue';
1+
import { defineComponent, inject, VNode, PropType } from 'vue';
22
import classNames from '../_util/classNames';
33
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
44
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
@@ -14,6 +14,46 @@ import { cloneElement } from '../_util/vnode';
1414
import { defaultConfigProvider } from '../config-provider';
1515

1616
const TreeNode = VcTree.TreeNode;
17+
18+
export interface TreeDataItem {
19+
key?: string | number;
20+
title?: string;
21+
isLeaf?: boolean;
22+
selectable?: boolean;
23+
children?: TreeDataItem[];
24+
disableCheckbox?: boolean;
25+
disabled?: boolean;
26+
class?: string;
27+
style?: any;
28+
checkable?: boolean;
29+
icon?: any;
30+
slots?: any;
31+
switcherIcon?: any;
32+
}
33+
34+
interface DefaultEvent {
35+
nativeEvent: MouseEvent;
36+
node: any;
37+
}
38+
39+
export interface CheckEvent extends DefaultEvent {
40+
checked: boolean;
41+
checkedNodes: VNode[];
42+
checkedNodesPositions: { node: VNode; pos: string | number }[];
43+
event: string;
44+
halfCheckedKeys: (string | number)[];
45+
}
46+
47+
export interface ExpendEvent extends DefaultEvent {
48+
expanded: boolean;
49+
}
50+
51+
export interface SelectEvent extends DefaultEvent {
52+
event: string;
53+
selected: boolean;
54+
selectedNodes: VNode[];
55+
}
56+
1757
function TreeProps() {
1858
return {
1959
showLine: PropTypes.looseBool,
@@ -32,30 +72,36 @@ function TreeProps() {
3272
/** 默认展开对应树节点 */
3373
defaultExpandParent: PropTypes.looseBool,
3474
/** 默认展开指定的树节点 */
35-
defaultExpandedKeys: PropTypes.array,
75+
defaultExpandedKeys: PropTypes.arrayOf(
76+
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
77+
),
3678
/** (受控)展开指定的树节点 */
37-
expandedKeys: PropTypes.array,
79+
expandedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
3880
/** (受控)选中复选框的树节点 */
3981
checkedKeys: PropTypes.oneOfType([
40-
PropTypes.array,
82+
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
4183
PropTypes.shape({
42-
checked: PropTypes.array,
43-
halfChecked: PropTypes.array,
84+
checked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
85+
halfChecked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
4486
}).loose,
4587
]),
4688
/** 默认选中复选框的树节点 */
47-
defaultCheckedKeys: PropTypes.array,
89+
defaultCheckedKeys: PropTypes.arrayOf(
90+
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
91+
),
4892
/** (受控)设置选中的树节点 */
49-
selectedKeys: PropTypes.array,
93+
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
5094
/** 默认选中的树节点 */
51-
defaultSelectedKeys: PropTypes.array,
95+
defaultSelectedKeys: PropTypes.arrayOf(
96+
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
97+
),
5298
selectable: PropTypes.looseBool,
5399

54100
/** filter some AntTreeNodes as you need. it should return true */
55101
filterAntTreeNode: PropTypes.func,
56102
/** 异步加载数据 */
57103
loadData: PropTypes.func,
58-
loadedKeys: PropTypes.array,
104+
loadedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
59105
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
60106
/** 响应右键点击 */
61107
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
@@ -77,7 +123,9 @@ function TreeProps() {
77123
prefixCls: PropTypes.string,
78124
filterTreeNode: PropTypes.func,
79125
openAnimation: PropTypes.any,
80-
treeData: PropTypes.array,
126+
treeData: {
127+
type: Array as PropType<TreeDataItem[]>,
128+
},
81129
/**
82130
* @default{title,key,children}
83131
* 替换treeNode中 title,key,children字段为treeData中对应的字段
@@ -123,7 +171,7 @@ export default defineComponent({
123171
},
124172
TreeNode,
125173
methods: {
126-
renderSwitcherIcon(prefixCls: string, switcherIcon, { isLeaf, loading, expanded }) {
174+
renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
127175
const { showLine } = this.$props;
128176
if (loading) {
129177
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
@@ -148,26 +196,19 @@ export default defineComponent({
148196
<CaretDownFilled class={switcherCls} />
149197
);
150198
},
151-
updateTreeData(treeData) {
199+
updateTreeData(treeData: TreeDataItem[]) {
152200
const { $slots } = this;
153201
const defaultFields = { children: 'children', title: 'title', key: 'key' };
154202
const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
155203
return treeData.map(item => {
156204
const key = item[replaceFields.key];
157205
const children = item[replaceFields.children];
158-
const { slots = {}, scopedSlots = {}, class: cls, style, ...restProps } = item;
206+
const { slots = {}, class: cls, style, ...restProps } = item;
159207
const treeNodeProps = {
160208
...restProps,
161-
icon: $slots[scopedSlots.icon] || $slots[slots.icon] || restProps.icon,
162-
switcherIcon:
163-
$slots[scopedSlots.switcherIcon] ||
164-
$slots[slots.switcherIcon] ||
165-
restProps.switcherIcon,
166-
title:
167-
$slots[scopedSlots.title] ||
168-
$slots[slots.title] ||
169-
$slots.title ||
170-
restProps[replaceFields.title],
209+
icon: $slots[slots.icon] || restProps.icon,
210+
switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
211+
title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
171212
dataRef: item,
172213
key,
173214
class: cls,
@@ -179,18 +220,18 @@ export default defineComponent({
179220
return treeNodeProps;
180221
});
181222
},
182-
setTreeRef(node) {
223+
setTreeRef(node: VNode) {
183224
this.tree = node;
184225
},
185-
handleCheck(checkedObj, eventObj) {
226+
handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
186227
this.$emit('update:checkedKeys', checkedObj);
187228
this.$emit('check', checkedObj, eventObj);
188229
},
189-
handleExpand(expandedKeys, eventObj) {
230+
handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
190231
this.$emit('update:expandedKeys', expandedKeys);
191232
this.$emit('expand', expandedKeys, eventObj);
192233
},
193-
handleSelect(selectedKeys: string[], eventObj) {
234+
handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
194235
this.$emit('update:selectedKeys', selectedKeys);
195236
this.$emit('select', selectedKeys, eventObj);
196237
},

components/tree/util.ts

+26-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import { VNode } from 'vue';
12
import { getNodeChildren, convertTreeToEntities } from '../vc-tree/src/util';
23
import { getSlot } from '../_util/props-util';
4+
import { TreeDataItem } from './Tree';
35

46
enum Record {
57
None,
68
Start,
79
End,
810
}
911

12+
type TreeKey = string | number;
13+
1014
// TODO: Move this logic into `rc-tree`
11-
function traverseNodesKey(rootChildren, callback) {
15+
function traverseNodesKey(rootChildren: VNode[], callback?: Function) {
1216
const nodeList = getNodeChildren(rootChildren) || [];
1317

14-
function processNode(node) {
18+
function processNode(node: VNode) {
1519
const { key } = node;
1620
const children = getSlot(node);
1721
if (callback(key, node) !== false) {
@@ -22,13 +26,18 @@ function traverseNodesKey(rootChildren, callback) {
2226
nodeList.forEach(processNode);
2327
}
2428

25-
export function getFullKeyList(children) {
29+
export function getFullKeyList(children: VNode[]) {
2630
const { keyEntities } = convertTreeToEntities(children);
2731
return [...keyEntities.keys()];
2832
}
2933

3034
/** 计算选中范围,只考虑expanded情况以优化性能 */
31-
export function calcRangeKeys(rootChildren, expandedKeys, startKey, endKey) {
35+
export function calcRangeKeys(
36+
rootChildren: VNode[],
37+
expandedKeys: TreeKey[],
38+
startKey: TreeKey,
39+
endKey: TreeKey,
40+
) {
3241
const keys = [];
3342
let record = Record.None;
3443

@@ -39,11 +48,11 @@ export function calcRangeKeys(rootChildren, expandedKeys, startKey, endKey) {
3948
return [];
4049
}
4150

42-
function matchKey(key) {
51+
function matchKey(key: TreeKey) {
4352
return key === startKey || key === endKey;
4453
}
4554

46-
traverseNodesKey(rootChildren, key => {
55+
traverseNodesKey(rootChildren, (key: TreeKey) => {
4756
if (record === Record.End) {
4857
return false;
4958
}
@@ -73,10 +82,10 @@ export function calcRangeKeys(rootChildren, expandedKeys, startKey, endKey) {
7382
return keys;
7483
}
7584

76-
export function convertDirectoryKeysToNodes(rootChildren, keys) {
85+
export function convertDirectoryKeysToNodes(rootChildren: VNode[], keys: TreeKey[]) {
7786
const restKeys = [...keys];
7887
const nodes = [];
79-
traverseNodesKey(rootChildren, (key, node) => {
88+
traverseNodesKey(rootChildren, (key: TreeKey, node: VNode) => {
8089
const index = restKeys.indexOf(key);
8190
if (index !== -1) {
8291
nodes.push(node);
@@ -88,13 +97,15 @@ export function convertDirectoryKeysToNodes(rootChildren, keys) {
8897
return nodes;
8998
}
9099

91-
export function getFullKeyListByTreeData(treeData: any, replaceFields: any = {}) {
100+
export function getFullKeyListByTreeData(treeData: TreeDataItem[], replaceFields: any = {}) {
92101
let keys = [];
93-
const { key = 'key', children = 'children' } = replaceFields(treeData || []).forEach(item => {
94-
keys.push(item[key]);
95-
if (item[children]) {
96-
keys = [...keys, ...getFullKeyListByTreeData(item[children], replaceFields)];
97-
}
98-
});
102+
const { key = 'key', children = 'children' } = replaceFields(treeData || []).forEach(
103+
(item: TreeDataItem) => {
104+
keys.push(item[key]);
105+
if (item[children]) {
106+
keys = [...keys, ...getFullKeyListByTreeData(item[children], replaceFields)];
107+
}
108+
},
109+
);
99110
return keys;
100111
}

0 commit comments

Comments
 (0)