fix: declare Tree Component type (#3178)

* fix: declare Tree Component type
pull/3211/head
ajuner 2020-11-18 17:37:15 +08:00 committed by GitHub
parent 00455da385
commit de05f936c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 59 deletions

View File

@ -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]'

View File

@ -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';
@ -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,
@ -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 />;
@ -56,7 +56,7 @@ export default defineComponent({
children: null,
onDebounceExpand: null,
tree: null,
lastSelectedKey: [],
lastSelectedKey: '',
cachedSelectedKeys: [],
configProvider: inject('configProvider', defaultConfigProvider),
};
@ -100,7 +100,7 @@ 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);
@ -108,7 +108,7 @@ export default defineComponent({
return undefined;
},
handleClick(event, node) {
handleClick(event: MouseEvent, node: VNode) {
const { expandAction } = this.$props;
// Expand the tree
@ -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
@ -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;
@ -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;
@ -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) {
@ -201,7 +201,7 @@ export default defineComponent({
}
},
setUncontrolledState(state) {
setUncontrolledState(state: unknown) {
const newState = omit(
state,
Object.keys(getOptionProps(this)).map(p => `_${p}`),
@ -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);
},

View File

@ -1,4 +1,4 @@
import { defineComponent, inject } from 'vue';
import { defineComponent, inject, VNode, PropType } from 'vue';
import classNames from '../_util/classNames';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
@ -14,6 +14,46 @@ import { cloneElement } from '../_util/vnode';
import { defaultConfigProvider } from '../config-provider';
const TreeNode = VcTree.TreeNode;
export interface TreeDataItem {
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;
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,
@ -32,30 +72,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,
@ -77,7 +123,9 @@ function TreeProps() {
prefixCls: PropTypes.string,
filterTreeNode: PropTypes.func,
openAnimation: PropTypes.any,
treeData: PropTypes.array,
treeData: {
type: Array as PropType<TreeDataItem[]>,
},
/**
* @default{title,key,children}
* 替换treeNode中 title,key,children字段为treeData中对应的字段
@ -123,7 +171,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`} />;
@ -148,26 +196,19 @@ 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 };
return treeData.map(item => {
const key = item[replaceFields.key];
const children = item[replaceFields.children];
const { slots = {}, scopedSlots = {}, class: cls, style, ...restProps } = item;
const { slots = {}, class: cls, style, ...restProps } = item;
const treeNodeProps = {
...restProps,
icon: $slots[scopedSlots.icon] || $slots[slots.icon] || restProps.icon,
switcherIcon:
$slots[scopedSlots.switcherIcon] ||
$slots[slots.switcherIcon] ||
restProps.switcherIcon,
title:
$slots[scopedSlots.title] ||
$slots[slots.title] ||
$slots.title ||
restProps[replaceFields.title],
icon: $slots[slots.icon] || restProps.icon,
switcherIcon: $slots[slots.switcherIcon] || restProps.switcherIcon,
title: $slots[slots.title] || $slots.title || restProps[replaceFields.title],
dataRef: item,
key,
class: cls,
@ -179,18 +220,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);
},

View File

@ -1,5 +1,7 @@
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,
@ -7,11 +9,13 @@ enum Record {
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) {
@ -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;
@ -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;
}
@ -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);
@ -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;
}