refactor: tree
parent
99f034deac
commit
ae36627763
|
@ -1,251 +0,0 @@
|
|||
import type { VNode } from 'vue';
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import omit from 'omit.js';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import FolderOpenOutlined from '@ant-design/icons-vue/FolderOpenOutlined';
|
||||
import FolderOutlined from '@ant-design/icons-vue/FolderOutlined';
|
||||
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 type { CheckEvent, ExpendEvent, SelectEvent } from './Tree';
|
||||
import Tree, { TreeProps } from './Tree';
|
||||
import {
|
||||
calcRangeKeys,
|
||||
getFullKeyList,
|
||||
convertDirectoryKeysToNodes,
|
||||
getFullKeyListByTreeData,
|
||||
} from './util';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
|
||||
// export type ExpandAction = false | 'click' | 'dblclick'; export interface
|
||||
// DirectoryTreeProps extends TreeProps { expandAction?: ExpandAction; }
|
||||
// export interface DirectoryTreeState { expandedKeys?: string[];
|
||||
// selectedKeys?: string[]; }
|
||||
|
||||
export interface DirectoryTreeState {
|
||||
_expandedKeys?: (string | number)[];
|
||||
_selectedKeys?: (string | number)[];
|
||||
}
|
||||
|
||||
function getIcon(props: { isLeaf: boolean; expanded: boolean } & VNode) {
|
||||
const { isLeaf, expanded } = props;
|
||||
if (isLeaf) {
|
||||
return <FileOutlined />;
|
||||
}
|
||||
return expanded ? <FolderOpenOutlined /> : <FolderOutlined />;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ADirectoryTree',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props: initDefaultProps(
|
||||
{
|
||||
...TreeProps(),
|
||||
expandAction: PropTypes.oneOf([false, 'click', 'doubleclick', 'dblclick']),
|
||||
},
|
||||
{
|
||||
showIcon: true,
|
||||
expandAction: 'click',
|
||||
},
|
||||
),
|
||||
setup() {
|
||||
return {
|
||||
children: null,
|
||||
onDebounceExpand: null,
|
||||
tree: null,
|
||||
lastSelectedKey: '',
|
||||
cachedSelectedKeys: [],
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const props = getOptionProps(this);
|
||||
const { defaultExpandAll, defaultExpandParent, expandedKeys, defaultExpandedKeys } = props;
|
||||
const children = getSlot(this);
|
||||
const { keyEntities } = convertTreeToEntities(children);
|
||||
const state: DirectoryTreeState = {};
|
||||
// Selected keys
|
||||
state._selectedKeys = props.selectedKeys || props.defaultSelectedKeys || [];
|
||||
|
||||
// Expanded keys
|
||||
if (defaultExpandAll) {
|
||||
if (props.treeData) {
|
||||
state._expandedKeys = getFullKeyListByTreeData(props.treeData, props.replaceFields);
|
||||
} else {
|
||||
state._expandedKeys = getFullKeyList(children);
|
||||
}
|
||||
} else if (defaultExpandParent) {
|
||||
state._expandedKeys = conductExpandParent(expandedKeys || defaultExpandedKeys, keyEntities);
|
||||
} else {
|
||||
state._expandedKeys = expandedKeys || defaultExpandedKeys;
|
||||
}
|
||||
return {
|
||||
_selectedKeys: [],
|
||||
_expandedKeys: [],
|
||||
...state,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
expandedKeys(val) {
|
||||
this.setState({ _expandedKeys: val });
|
||||
},
|
||||
selectedKeys(val) {
|
||||
this.setState({ _selectedKeys: val });
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.onDebounceExpand = debounce(this.expandFolderNode, 200, { leading: true });
|
||||
},
|
||||
methods: {
|
||||
handleExpand(expandedKeys: (string | number)[], info: ExpendEvent) {
|
||||
this.setUncontrolledState({ _expandedKeys: expandedKeys });
|
||||
this.$emit('update:expandedKeys', expandedKeys);
|
||||
this.$emit('expand', expandedKeys, info);
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
handleClick(event: MouseEvent, node: VNode) {
|
||||
const { expandAction } = this.$props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'click') {
|
||||
this.onDebounceExpand(event, node);
|
||||
}
|
||||
this.$emit('click', event, node);
|
||||
},
|
||||
|
||||
handleDoubleClick(event: MouseEvent, node: VNode) {
|
||||
const { expandAction } = this.$props;
|
||||
|
||||
// Expand the tree
|
||||
if (expandAction === 'dblclick' || expandAction === 'doubleclick') {
|
||||
this.onDebounceExpand(event, node);
|
||||
}
|
||||
|
||||
this.$emit('doubleclick', event, node);
|
||||
this.$emit('dblclick', event, node);
|
||||
},
|
||||
|
||||
hanldeSelect(keys: (string | number)[], event: SelectEvent) {
|
||||
const { multiple } = this.$props;
|
||||
const children = this.children || [];
|
||||
const { _expandedKeys: expandedKeys = [] } = this.$data;
|
||||
const { node, nativeEvent } = event;
|
||||
const { eventKey = '' } = node;
|
||||
|
||||
const newState: DirectoryTreeState = {};
|
||||
|
||||
// We need wrap this event since some value is not same
|
||||
const newEvent = {
|
||||
...event,
|
||||
selected: true, // Directory selected always true
|
||||
};
|
||||
|
||||
// Windows / Mac single pick
|
||||
const ctrlPick = nativeEvent.ctrlKey || nativeEvent.metaKey;
|
||||
const shiftPick = nativeEvent.shiftKey;
|
||||
|
||||
// Generate new selected keys
|
||||
let newSelectedKeys: (string | number)[];
|
||||
if (multiple && ctrlPick) {
|
||||
// Control click
|
||||
newSelectedKeys = keys;
|
||||
this.lastSelectedKey = eventKey;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
||||
} else if (multiple && shiftPick) {
|
||||
// Shift click
|
||||
newSelectedKeys = Array.from(
|
||||
new Set([
|
||||
...(this.cachedSelectedKeys || []),
|
||||
...calcRangeKeys(children, expandedKeys, eventKey, this.lastSelectedKey),
|
||||
]),
|
||||
);
|
||||
newEvent.selectedNodes = convertDirectoryKeysToNodes(children, newSelectedKeys);
|
||||
} else {
|
||||
// Single click
|
||||
newSelectedKeys = [eventKey];
|
||||
this.lastSelectedKey = eventKey;
|
||||
this.cachedSelectedKeys = newSelectedKeys;
|
||||
newEvent.selectedNodes = [event.node];
|
||||
}
|
||||
newState._selectedKeys = newSelectedKeys;
|
||||
|
||||
this.$emit('update:selectedKeys', newSelectedKeys);
|
||||
this.$emit('select', newSelectedKeys, newEvent);
|
||||
|
||||
this.setUncontrolledState(newState);
|
||||
},
|
||||
setTreeRef(node: VNode) {
|
||||
this.tree = node;
|
||||
},
|
||||
|
||||
expandFolderNode(event: MouseEvent, node: { isLeaf: boolean } & VNode) {
|
||||
const { isLeaf } = node;
|
||||
|
||||
if (isLeaf || event.shiftKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.tree.tree) {
|
||||
// Get internal vc-tree
|
||||
const internalTree = this.tree.tree;
|
||||
|
||||
// Call internal rc-tree expand function
|
||||
// https://github.com/ant-design/ant-design/issues/12567
|
||||
internalTree.onNodeExpand(event, node);
|
||||
}
|
||||
},
|
||||
|
||||
setUncontrolledState(state: unknown) {
|
||||
const newState = omit(
|
||||
state,
|
||||
Object.keys(getOptionProps(this)).map(p => `_${p}`),
|
||||
);
|
||||
if (Object.keys(newState).length) {
|
||||
this.setState(newState);
|
||||
}
|
||||
},
|
||||
handleCheck(checkedObj: (string | number)[], eventObj: CheckEvent) {
|
||||
this.$emit('update:checkedKeys', checkedObj);
|
||||
this.$emit('check', checkedObj, eventObj);
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
this.children = getSlot(this);
|
||||
const { prefixCls: customizePrefixCls, ...props } = getOptionProps(this);
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
const { _expandedKeys: expandedKeys, _selectedKeys: selectedKeys } = this.$data;
|
||||
const { class: className, ...restAttrs } = this.$attrs;
|
||||
const connectClassName = classNames(`${prefixCls}-directory`, className);
|
||||
const treeProps = {
|
||||
icon: getIcon,
|
||||
...restAttrs,
|
||||
...omit(props, ['onUpdate:selectedKeys', 'onUpdate:checkedKeys', 'onUpdate:expandedKeys']),
|
||||
prefixCls,
|
||||
expandedKeys,
|
||||
selectedKeys,
|
||||
switcherIcon: getComponent(this, 'switcherIcon'),
|
||||
ref: this.setTreeRef,
|
||||
class: connectClassName,
|
||||
onSelect: this.hanldeSelect,
|
||||
onClick: this.handleClick,
|
||||
onDblclick: this.handleDoubleClick,
|
||||
onExpand: this.handleExpand,
|
||||
onCheck: this.handleCheck,
|
||||
};
|
||||
return (
|
||||
<Tree {...treeProps} v-slots={omit(this.$slots, ['default'])}>
|
||||
{this.children}
|
||||
</Tree>
|
||||
);
|
||||
},
|
||||
});
|
|
@ -1,289 +0,0 @@
|
|||
import type { VNode, PropType, CSSProperties } from 'vue';
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import classNames from '../_util/classNames';
|
||||
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
||||
import FileOutlined from '@ant-design/icons-vue/FileOutlined';
|
||||
import CaretDownFilled from '@ant-design/icons-vue/CaretDownFilled';
|
||||
import MinusSquareOutlined from '@ant-design/icons-vue/MinusSquareOutlined';
|
||||
import PlusSquareOutlined from '@ant-design/icons-vue/PlusSquareOutlined';
|
||||
import VcTree, { TreeNode } from '../vc-tree';
|
||||
import animation from '../_util/openAnimation';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
|
||||
export interface TreeDataItem {
|
||||
key?: string | number;
|
||||
title?: string;
|
||||
isLeaf?: boolean;
|
||||
selectable?: boolean;
|
||||
children?: TreeDataItem[];
|
||||
disableCheckbox?: boolean;
|
||||
disabled?: boolean;
|
||||
class?: string;
|
||||
style?: CSSProperties;
|
||||
checkable?: boolean;
|
||||
icon?: VNode;
|
||||
|
||||
slots?: Record<string, string>;
|
||||
switcherIcon?: VNode;
|
||||
// support custom field
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface DefaultEvent {
|
||||
nativeEvent: MouseEvent;
|
||||
node: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface CheckEvent extends DefaultEvent {
|
||||
checked: boolean;
|
||||
checkedNodes: Array<Record<string, any>>;
|
||||
checkedNodesPositions: { node: Record<string, any>; 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: Array<Record<string, any>>;
|
||||
}
|
||||
|
||||
export interface TreeDragEvent {
|
||||
event: DragEvent;
|
||||
expandedKeys: (string | number)[];
|
||||
node: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface DropEvent {
|
||||
dragNode: Record<string, any>;
|
||||
dragNodesKeys: (string | number)[];
|
||||
dropPosition: number;
|
||||
dropToGap: boolean;
|
||||
event: DragEvent;
|
||||
node: Record<string, any>;
|
||||
}
|
||||
|
||||
function TreeProps() {
|
||||
return {
|
||||
showLine: PropTypes.looseBool,
|
||||
/** 是否支持多选 */
|
||||
multiple: PropTypes.looseBool,
|
||||
/** 是否自动展开父节点 */
|
||||
autoExpandParent: PropTypes.looseBool,
|
||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/
|
||||
checkStrictly: PropTypes.looseBool,
|
||||
/** 是否支持选中 */
|
||||
checkable: PropTypes.looseBool,
|
||||
/** 是否禁用树 */
|
||||
disabled: PropTypes.looseBool,
|
||||
/** 默认展开所有树节点 */
|
||||
defaultExpandAll: PropTypes.looseBool,
|
||||
/** 默认展开对应树节点 */
|
||||
defaultExpandParent: PropTypes.looseBool,
|
||||
/** 默认展开指定的树节点 */
|
||||
defaultExpandedKeys: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
),
|
||||
/** (受控)展开指定的树节点 */
|
||||
expandedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
/** (受控)选中复选框的树节点 */
|
||||
checkedKeys: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
PropTypes.shape({
|
||||
checked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
halfChecked: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
}).loose,
|
||||
]),
|
||||
/** 默认选中复选框的树节点 */
|
||||
defaultCheckedKeys: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
),
|
||||
/** (受控)设置选中的树节点 */
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
/** 默认选中的树节点 */
|
||||
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.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
// onLoaded: (loadedKeys: string[], info: { event: 'load', node: AntTreeNode; }) => void,
|
||||
/** 响应右键点击 */
|
||||
// onRightClick: (options: AntTreeNodeMouseEvent) => void,
|
||||
/** 设置节点可拖拽(IE>8)*/
|
||||
draggable: PropTypes.looseBool,
|
||||
// /** 开始拖拽时调用 */
|
||||
// onDragStart: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragenter 触发时调用 */
|
||||
// onDragEnter: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragover 触发时调用 */
|
||||
// onDragOver: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** dragleave 触发时调用 */
|
||||
// onDragLeave: (options: AntTreeNodeMouseEvent) => void,
|
||||
// /** drop 触发时调用 */
|
||||
// onDrop: (options: AntTreeNodeMouseEvent) => void,
|
||||
showIcon: PropTypes.looseBool,
|
||||
icon: PropTypes.func,
|
||||
switcherIcon: PropTypes.any,
|
||||
prefixCls: PropTypes.string,
|
||||
filterTreeNode: PropTypes.func,
|
||||
openAnimation: PropTypes.any,
|
||||
treeData: {
|
||||
type: Array as PropType<TreeDataItem[]>,
|
||||
},
|
||||
/**
|
||||
* @default{title,key,children}
|
||||
* 替换treeNode中 title,key,children字段为treeData中对应的字段
|
||||
*/
|
||||
replaceFields: PropTypes.object,
|
||||
blockNode: PropTypes.looseBool,
|
||||
/** 展开/收起节点时触发 */
|
||||
onExpand: PropTypes.func,
|
||||
/** 点击复选框触发 */
|
||||
onCheck: PropTypes.func,
|
||||
/** 点击树节点触发 */
|
||||
onSelect: PropTypes.func,
|
||||
/** 单击树节点触发 */
|
||||
onClick: PropTypes.func,
|
||||
/** 双击树节点触发 */
|
||||
onDoubleclick: PropTypes.func,
|
||||
onDblclick: PropTypes.func,
|
||||
'onUpdate:selectedKeys': PropTypes.func,
|
||||
'onUpdate:checkedKeys': PropTypes.func,
|
||||
'onUpdate:expandedKeys': PropTypes.func,
|
||||
};
|
||||
}
|
||||
|
||||
export { TreeProps };
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ATree',
|
||||
inheritAttrs: false,
|
||||
props: initDefaultProps(TreeProps(), {
|
||||
checkable: false,
|
||||
showIcon: false,
|
||||
openAnimation: {
|
||||
...animation,
|
||||
appear: null,
|
||||
},
|
||||
blockNode: false,
|
||||
}),
|
||||
setup() {
|
||||
return {
|
||||
tree: null,
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
};
|
||||
},
|
||||
TreeNode,
|
||||
methods: {
|
||||
renderSwitcherIcon(prefixCls: string, switcherIcon: VNode, { isLeaf, loading, expanded }) {
|
||||
const { showLine } = this.$props;
|
||||
if (loading) {
|
||||
return <LoadingOutlined class={`${prefixCls}-switcher-loading-icon`} />;
|
||||
}
|
||||
|
||||
if (isLeaf) {
|
||||
return showLine ? <FileOutlined class={`${prefixCls}-switcher-line-icon`} /> : null;
|
||||
}
|
||||
const switcherCls = `${prefixCls}-switcher-icon`;
|
||||
if (switcherIcon) {
|
||||
return cloneElement(switcherIcon, {
|
||||
class: switcherCls,
|
||||
});
|
||||
}
|
||||
return showLine ? (
|
||||
expanded ? (
|
||||
<MinusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||
) : (
|
||||
<PlusSquareOutlined class={`${prefixCls}-switcher-line-icon`} />
|
||||
)
|
||||
) : (
|
||||
<CaretDownFilled class={switcherCls} />
|
||||
);
|
||||
},
|
||||
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 = {}, class: cls, style, ...restProps } = item;
|
||||
const treeNodeProps = {
|
||||
...restProps,
|
||||
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,
|
||||
style,
|
||||
};
|
||||
if (children) {
|
||||
return { ...treeNodeProps, children: this.updateTreeData(children) };
|
||||
}
|
||||
return treeNodeProps;
|
||||
});
|
||||
},
|
||||
setTreeRef(node: VNode) {
|
||||
this.tree = node;
|
||||
},
|
||||
handleCheck(checkedObj: (number | string)[], eventObj: CheckEvent) {
|
||||
this.$emit('update:checkedKeys', checkedObj);
|
||||
this.$emit('check', checkedObj, eventObj);
|
||||
},
|
||||
handleExpand(expandedKeys: (number | string)[], eventObj: ExpendEvent) {
|
||||
this.$emit('update:expandedKeys', expandedKeys);
|
||||
this.$emit('expand', expandedKeys, eventObj);
|
||||
},
|
||||
handleSelect(selectedKeys: (number | string)[], eventObj: SelectEvent) {
|
||||
this.$emit('update:selectedKeys', selectedKeys);
|
||||
this.$emit('select', selectedKeys, eventObj);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const props = getOptionProps(this);
|
||||
const { prefixCls: customizePrefixCls, showIcon, treeNodes, blockNode } = props;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('tree', customizePrefixCls);
|
||||
const switcherIcon = getComponent(this, 'switcherIcon');
|
||||
const checkable = props.checkable;
|
||||
let treeData = props.treeData || treeNodes;
|
||||
if (treeData) {
|
||||
treeData = this.updateTreeData(treeData);
|
||||
}
|
||||
const { class: className, ...restAttrs } = this.$attrs;
|
||||
const vcTreeProps = {
|
||||
...props,
|
||||
prefixCls,
|
||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable,
|
||||
children: getSlot(this),
|
||||
switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, switcherIcon, nodeProps),
|
||||
ref: this.setTreeRef,
|
||||
...restAttrs,
|
||||
class: classNames(className, {
|
||||
[`${prefixCls}-icon-hide`]: !showIcon,
|
||||
[`${prefixCls}-block-node`]: blockNode,
|
||||
}),
|
||||
onCheck: this.handleCheck,
|
||||
onExpand: this.handleExpand,
|
||||
onSelect: this.handleSelect,
|
||||
} as Record<string, any>;
|
||||
if (treeData) {
|
||||
vcTreeProps.treeData = treeData;
|
||||
}
|
||||
return <VcTree {...vcTreeProps} />;
|
||||
},
|
||||
});
|
|
@ -1,110 +0,0 @@
|
|||
import type { VNode } from 'vue';
|
||||
import { getNodeChildren, convertTreeToEntities } from '../vc-tree/src/util';
|
||||
import { getSlot } from '../_util/props-util';
|
||||
import type { TreeDataItem } from './Tree';
|
||||
|
||||
enum Record {
|
||||
None,
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
type TreeKey = string | number;
|
||||
|
||||
// TODO: Move this logic into `rc-tree`
|
||||
function traverseNodesKey(rootChildren: VNode[], callback?: Function) {
|
||||
const nodeList = getNodeChildren(rootChildren) || [];
|
||||
|
||||
function processNode(node: VNode) {
|
||||
const { key } = node;
|
||||
const children = getSlot(node);
|
||||
if (callback(key, node) !== false) {
|
||||
traverseNodesKey(children, callback);
|
||||
}
|
||||
}
|
||||
|
||||
nodeList.forEach(processNode);
|
||||
}
|
||||
|
||||
export function getFullKeyList(children: VNode[]) {
|
||||
const { keyEntities } = convertTreeToEntities(children);
|
||||
return [...keyEntities.keys()];
|
||||
}
|
||||
|
||||
/** 计算选中范围,只考虑expanded情况以优化性能 */
|
||||
export function calcRangeKeys(
|
||||
rootChildren: VNode[],
|
||||
expandedKeys: TreeKey[],
|
||||
startKey: TreeKey,
|
||||
endKey: TreeKey,
|
||||
) {
|
||||
const keys = [];
|
||||
let record = Record.None;
|
||||
|
||||
if (startKey && startKey === endKey) {
|
||||
return [startKey];
|
||||
}
|
||||
if (!startKey || !endKey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function matchKey(key: TreeKey) {
|
||||
return key === startKey || key === endKey;
|
||||
}
|
||||
|
||||
traverseNodesKey(rootChildren, (key: TreeKey) => {
|
||||
if (record === Record.End) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchKey(key)) {
|
||||
// Match test
|
||||
keys.push(key);
|
||||
|
||||
if (record === Record.None) {
|
||||
record = Record.Start;
|
||||
} else if (record === Record.Start) {
|
||||
record = Record.End;
|
||||
return false;
|
||||
}
|
||||
} else if (record === Record.Start) {
|
||||
// Append selection
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
if (expandedKeys.indexOf(key) === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
export function convertDirectoryKeysToNodes(rootChildren: VNode[], keys: TreeKey[]) {
|
||||
const restKeys = [...keys];
|
||||
const nodes = [];
|
||||
traverseNodesKey(rootChildren, (key: TreeKey, node: VNode) => {
|
||||
const index = restKeys.indexOf(key);
|
||||
if (index !== -1) {
|
||||
nodes.push(node);
|
||||
restKeys.splice(index, 1);
|
||||
}
|
||||
|
||||
return !!restKeys.length;
|
||||
});
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function getFullKeyListByTreeData(treeData: TreeDataItem[], replaceFields: any = {}) {
|
||||
let keys = [];
|
||||
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;
|
||||
}
|
|
@ -3,7 +3,7 @@ import type { FlattenNode } from './interface';
|
|||
import type { TreeNodeRequiredProps } from './utils/treeUtil';
|
||||
import { getTreeNodeProps } from './utils/treeUtil';
|
||||
import { useInjectTreeContext } from './contextTypes';
|
||||
import { computed, getCurrentInstance, nextTick, PropType } from 'vue';
|
||||
import { computed, nextTick, PropType } from 'vue';
|
||||
import { defineComponent, onBeforeUnmount, onMounted, ref, Transition, watch } from 'vue';
|
||||
import { treeNodeProps } from './props';
|
||||
import { collapseMotion } from '../_util/transition';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { ComputedRef, CSSProperties, DefineComponent, Ref, VNode } from 'vue';
|
||||
import type { ComputedRef, CSSProperties, Ref, VNode } from 'vue';
|
||||
import { TreeNodeProps } from './props';
|
||||
export type { ScrollTo } from '../vc-virtual-list/List';
|
||||
|
||||
|
@ -40,11 +40,7 @@ export type IconType = any;
|
|||
|
||||
export type Key = string | number;
|
||||
|
||||
export type NodeElement = VNode<DefineComponent<TreeNodeProps>> & {
|
||||
type: {
|
||||
isTreeNode: boolean;
|
||||
};
|
||||
};
|
||||
export type NodeElement = VNode<TreeNodeProps>;
|
||||
|
||||
export type DragNodeEvent = {
|
||||
eventData: ComputedRef<EventDataNode>;
|
||||
|
|
|
@ -42,7 +42,7 @@ export function getPosition(level: string | number, index: number) {
|
|||
}
|
||||
|
||||
export function isTreeNode(node: NodeElement) {
|
||||
return node && node.type && node.type.isTreeNode;
|
||||
return node && node.type && (node.type as any).isTreeNode;
|
||||
}
|
||||
|
||||
export function getDragChildrenKeys(dragNodeKey: Key, keyEntities: Record<Key, DataEntity>): Key[] {
|
||||
|
@ -272,7 +272,6 @@ export function convertDataToTree(
|
|||
const list = Array.isArray(treeData) ? treeData : [treeData];
|
||||
return list.map(({ children, ...props }): NodeElement => {
|
||||
const childrenNodes = convertDataToTree(children, processor);
|
||||
|
||||
return <TreeNode {...processProps(props)}>{childrenNodes}</TreeNode>;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue