Skip to content

fix: declare Tree Component type #3178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ const Menu = defineComponent({

// Fix SVGElement e.target.className.indexOf is not a function
// https://github.com/ant-design/ant-design/issues/15699
const { className } = e.target as (SVGAnimationElement | HTMLElement);
const { className } = e.target as SVGAnimationElement | HTMLElement;
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
const classNameValue =
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'
Expand Down
30 changes: 15 additions & 15 deletions components/tree/DirectoryTree.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, inject } from 'vue';
import { defineComponent, inject, VNode } from 'vue';
import omit from 'omit.js';
import debounce from 'lodash-es/debounce';
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined';
Expand All @@ -7,7 +7,7 @@ import FileOutlined from '@ant-design/icons-vue/FileOutlined';
import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames';
import { conductExpandParent, convertTreeToEntities } from '../vc-tree/src/util';
import Tree, { TreeProps } from './Tree';
import Tree, { CheckEvent, ExpendEvent, SelectEvent, TreeProps } from './Tree';
import {
calcRangeKeys,
getFullKeyList,
Expand All @@ -25,11 +25,11 @@ import { defaultConfigProvider } from '../config-provider';
// selectedKeys?: string[]; }

export interface DirectoryTreeState {
_expandedKeys?: string[];
_selectedKeys?: string[];
_expandedKeys?: (string | number)[];
_selectedKeys?: (string | number)[];
}

function getIcon(props) {
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
const { isLeaf, expanded } = props;
if (isLeaf) {
return <FileOutlined />;
Expand All @@ -56,7 +56,7 @@ export default defineComponent({
children: null,
onDebounceExpand: null,
tree: null,
lastSelectedKey: [],
lastSelectedKey: '',
cachedSelectedKeys: [],
configProvider: inject('configProvider', defaultConfigProvider),
};
Expand Down Expand Up @@ -100,15 +100,15 @@ export default defineComponent({
this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true });
},
methods: {
handleExpand(expandedKeys, info) {
handleExpand(expandedKeys: (string | number)[], info: ExpendEvent) {
this.setUncontrolledState({ _expandedKeys: expandedKeys });
this.$emit('update:expandedKeys', expandedKeys);
this.$emit('expand', expandedKeys, info);

return undefined;
},

handleClick(event, node) {
handleClick(event: MouseEvent, node: VNode) {
const { expandAction } = this.$props;

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

handleDoubleClick(event, node) {
handleDoubleClick(event: MouseEvent, node: VNode) {
const { expandAction } = this.$props;

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

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

// Generate new selected keys
let newSelectedKeys;
let newSelectedKeys: (string | number)[];
if (multiple && ctrlPick) {
// Control click
newSelectedKeys = keys;
Expand Down Expand Up @@ -180,11 +180,11 @@ export default defineComponent({

this.setUncontrolledState(newState);
},
setTreeRef(node) {
setTreeRef(node: VNode) {
this.tree = node;
},

expandFolderNode(event, node) {
expandFolderNode(event: MouseEvent, node: { isLeaf: boolean } & VNode) {
const { isLeaf } = node;

if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
Expand All @@ -201,7 +201,7 @@ export default defineComponent({
}
},

setUncontrolledState(state) {
setUncontrolledState(state: unknown) {
const newState = omit(
state,
Object.keys(getOptionProps(this)).map(p => `_${p}`),
Expand All @@ -210,7 +210,7 @@ export default defineComponent({
this.setState(newState);
}
},
handleCheck(checkedObj, eventObj) {
handleCheck(checkedObj: (string | number)[], eventObj: CheckEvent) {
this.$emit('update:checkedKeys', checkedObj);
this.$emit('check', checkedObj, eventObj);
},
Expand Down
79 changes: 63 additions & 16 deletions components/tree/Tree.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, inject } from 'vue';
import { defineComponent, inject, VNode } from 'vue';
import classNames from '../_util/classNames';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
Expand All @@ -14,6 +14,47 @@ import { cloneElement } from '../_util/vnode';
import { defaultConfigProvider } from '../config-provider';

const TreeNode = VcTree.TreeNode;

export interface TreeDataItem {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

很多字段是可选的吧

key?: string | number;
title?: string;
isLeaf?: boolean;
selectable?: boolean;
children?: TreeDataItem[];
disableCheckbox?: boolean;
disabled?: boolean;
class?: string;
style?: any;
checkable?: boolean;
icon?: any;
slots?: any;
scopedSlots?: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

没有 scopedSlots 这个属性了吧

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看逻辑上这个属性还是在的,文档上是没了 我给他去掉

switcherIcon?: any;
}

interface DefaultEvent {
nativeEvent: MouseEvent;
node: any;
}

export interface CheckEvent extends DefaultEvent {
checked: boolean;
checkedNodes: VNode[];
checkedNodesPositions: { node: VNode; pos: string | number }[];
event: string;
halfCheckedKeys: (string | number)[];
}

export interface ExpendEvent extends DefaultEvent {
expanded: boolean;
}

export interface SelectEvent extends DefaultEvent {
event: string;
selected: boolean;
selectedNodes: VNode[];
}

function TreeProps() {
return {
showLine: PropTypes.looseBool,
Expand All @@ -32,30 +73,36 @@ function TreeProps() {
/** 默认展开对应树节点 */
defaultExpandParent: PropTypes.looseBool,
/** 默认展开指定的树节点 */
defaultExpandedKeys: PropTypes.array,
defaultExpandedKeys: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
),
/** (受控)展开指定的树节点 */
expandedKeys: PropTypes.array,
expandedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
/** (受控)选中复选框的树节点 */
checkedKeys: PropTypes.oneOfType([
PropTypes.array,
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
PropTypes.shape({
checked: PropTypes.array,
halfChecked: PropTypes.array,
checked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
halfChecked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
}).loose,
]),
/** 默认选中复选框的树节点 */
defaultCheckedKeys: PropTypes.array,
defaultCheckedKeys: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
),
/** (受控)设置选中的树节点 */
selectedKeys: PropTypes.array,
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
/** 默认选中的树节点 */
defaultSelectedKeys: PropTypes.array,
defaultSelectedKeys: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
),
selectable: PropTypes.looseBool,

/** filter some AntTreeNodes as you need. it should return true */
filterAntTreeNode: PropTypes.func,
/** 异步加载数据 */
loadData: PropTypes.func,
loadedKeys: PropTypes.array,
loadedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
/** 响应右键点击 */
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
Expand Down Expand Up @@ -123,7 +170,7 @@ export default defineComponent({
},
TreeNode,
methods: {
renderSwitcherIcon(prefixCls: string, switcherIcon, { isLeaf, loading, expanded }) {
renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
const { showLine } = this.$props;
if (loading) {
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
Expand All @@ -148,7 +195,7 @@ export default defineComponent({
<CaretDownFilled class={switcherCls} />
);
},
updateTreeData(treeData) {
updateTreeData(treeData: TreeDataItem[]) {
const { $slots } = this;
const defaultFields = { children: 'children', title: 'title', key: 'key' };
const replaceFields = { ...defaultFields, ...this.$props.replaceFields };
Expand Down Expand Up @@ -179,18 +226,18 @@ export default defineComponent({
return treeNodeProps;
});
},
setTreeRef(node) {
setTreeRef(node: VNode) {
this.tree = node;
},
handleCheck(checkedObj, eventObj) {
handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
this.$emit('update:checkedKeys', checkedObj);
this.$emit('check', checkedObj, eventObj);
},
handleExpand(expandedKeys, eventObj) {
handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
this.$emit('update:expandedKeys', expandedKeys);
this.$emit('expand', expandedKeys, eventObj);
},
handleSelect(selectedKeys: string[], eventObj) {
handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
this.$emit('update:selectedKeys', selectedKeys);
this.$emit('select', selectedKeys, eventObj);
},
Expand Down
41 changes: 26 additions & 15 deletions components/tree/util.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { VNode } from 'vue';
import { getNodeChildren, convertTreeToEntities } from '../vc-tree/src/util';
import { getSlot } from '../_util/props-util';
import { TreeDataItem } from './Tree';

enum Record {
None,
Start,
End,
}

type TreeKey = string | number;

// TODO: Move this logic into `rc-tree`
function traverseNodesKey(rootChildren, callback) {
function traverseNodesKey(rootChildren: VNode[], callback?: Function) {
const nodeList = getNodeChildren(rootChildren) || [];

function processNode(node) {
function processNode(node: VNode) {
const { key } = node;
const children = getSlot(node);
if (callback(key, node) !== false) {
Expand All @@ -22,13 +26,18 @@ function traverseNodesKey(rootChildren, callback) {
nodeList.forEach(processNode);
}

export function getFullKeyList(children) {
export function getFullKeyList(children: VNode[]) {
const { keyEntities } = convertTreeToEntities(children);
return [...keyEntities.keys()];
}

/** 计算选中范围,只考虑expanded情况以优化性能 */
export function calcRangeKeys(rootChildren, expandedKeys, startKey, endKey) {
export function calcRangeKeys(
rootChildren: VNode[],
expandedKeys: TreeKey[],
startKey: TreeKey,
endKey: TreeKey,
) {
const keys = [];
let record = Record.None;

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

function matchKey(key) {
function matchKey(key: TreeKey) {
return key === startKey || key === endKey;
}

traverseNodesKey(rootChildren, key => {
traverseNodesKey(rootChildren, (key: TreeKey) => {
if (record === Record.End) {
return false;
}
Expand Down Expand Up @@ -73,10 +82,10 @@ export function calcRangeKeys(rootChildren, expandedKeys, startKey, endKey) {
return keys;
}

export function convertDirectoryKeysToNodes(rootChildren, keys) {
export function convertDirectoryKeysToNodes(rootChildren: VNode[], keys: TreeKey[]) {
const restKeys = [...keys];
const nodes = [];
traverseNodesKey(rootChildren, (key, node) => {
traverseNodesKey(rootChildren, (key: TreeKey, node: VNode) => {
const index = restKeys.indexOf(key);
if (index !== -1) {
nodes.push(node);
Expand All @@ -88,13 +97,15 @@ export function convertDirectoryKeysToNodes(rootChildren, keys) {
return nodes;
}

export function getFullKeyListByTreeData(treeData: any, replaceFields: any = {}) {
export function getFullKeyListByTreeData(treeData: TreeDataItem[], replaceFields: any = {}) {
let keys = [];
const { key = 'key', children = 'children' } = replaceFields(treeData || []).forEach(item => {
keys.push(item[key]);
if (item[children]) {
keys = [...keys, ...getFullKeyListByTreeData(item[children], replaceFields)];
}
});
const { key = 'key', children = 'children' } = replaceFields(treeData || []).forEach(
(item: TreeDataItem) => {
keys.push(item[key]);
if (item[children]) {
keys = [...keys, ...getFullKeyListByTreeData(item[children], replaceFields)];
}
},
);
return keys;
}