refactor: tree

pull/4577/head
tangjinzhou 2021-08-18 10:48:16 +08:00
parent 106b2609be
commit 9cbaeabbd6
6 changed files with 77 additions and 61 deletions

View File

@ -11,7 +11,7 @@ import {
arrDel,
posToArr,
} from './util';
import type { Key, FlattenNode, EventDataNode, NodeInstance, ScrollTo } from './interface';
import type { Key, FlattenNode, EventDataNode, ScrollTo, DragNodeEvent } from './interface';
import {
flattenTreeData,
convertTreeToData,
@ -106,7 +106,7 @@ export default defineComponent({
let dragStartMousePosition = null;
let dragNode = null;
let dragNode: DragNodeEvent = null;
const treeNodeRequiredProps = computed(() => {
return {
@ -251,7 +251,7 @@ export default defineComponent({
cleanDragState();
if (onDragend && !outsideTree) {
onDragend({ event, node: convertNodePropsToEventData(node.props) });
onDragend({ event, node: node.eventData.value });
}
dragNode = null;
@ -266,7 +266,7 @@ export default defineComponent({
const onNodeDragStart: NodeDragEventHandler = (event, node) => {
const { onDragstart } = props;
const { eventKey } = node.props;
const { eventKey, eventData } = node;
dragNode = node;
dragStartMousePosition = {
@ -274,17 +274,17 @@ export default defineComponent({
y: event.clientY,
};
const newExpandedKeys = arrDel(expandedKeys.value, eventKey);
const newExpandedKeys = arrDel(expandedKeys.value, eventKey.value);
dragState.dragging = true;
dragState.dragChildrenKeys = getDragChildrenKeys(eventKey, keyEntities.value);
dragState.dragChildrenKeys = getDragChildrenKeys(eventKey.value, keyEntities.value);
indent.value = listRef.value.getIndentWidth();
setExpandedKeys(newExpandedKeys);
window.addEventListener('dragend', onWindowDragEnd);
if (onDragstart) {
onDragstart({ event, node: convertNodePropsToEventData(node.props) });
onDragstart({ event, node: eventData.value });
}
};
@ -295,9 +295,8 @@ export default defineComponent({
* Better for use mouse move event to refresh drag state.
* But let's just keep it to avoid event trigger logic change.
*/
const onNodeDragEnter = (event: MouseEvent, node: NodeInstance) => {
const onNodeDragEnter = (event: MouseEvent, node: DragNodeEvent) => {
const { onDragenter, onExpand, allowDrop, direction } = props;
const { pos } = node.props;
const {
dropPosition,
@ -347,27 +346,27 @@ export default defineComponent({
clearTimeout(delayedDragEnterLogic[key]);
});
if (dragNode.props.eventKey !== node.props.eventKey) {
if (dragNode.eventKey.value !== node.eventKey.value) {
// hoist expand logic here
// since if logic is on the bottom
// it will be blocked by abstract dragover node check
// => if you dragenter from top, you mouse will still be consider as in the top node
(event as any).persist();
delayedDragEnterLogic[pos] = window.setTimeout(() => {
delayedDragEnterLogic[node.pos.value] = window.setTimeout(() => {
if (!dragState.dragging) return;
let newExpandedKeys = [...expandedKeys.value];
const entity = keyEntities[node.props.eventKey];
const entity = keyEntities[node.eventKey.value];
if (entity && (entity.children || []).length) {
newExpandedKeys = arrAdd(expandedKeys.value, node.props.eventKey);
newExpandedKeys = arrAdd(expandedKeys.value, node.eventKey.value);
}
setExpandedKeys(newExpandedKeys);
if (onExpand) {
onExpand(newExpandedKeys, {
node: convertNodePropsToEventData(node.props),
node: node.eventData.value,
expanded: true,
nativeEvent: (event as any).nativeEvent,
});
@ -376,7 +375,7 @@ export default defineComponent({
}
// Skip if drag node is self
if (dragNode.props.eventKey === dropTargetKey && dropLevelOffset === 0) {
if (dragNode.eventKey.value === dropTargetKey && dropLevelOffset === 0) {
Object.assign(dragState, {
dragOverNodeKey: null,
dropPosition: null,
@ -403,13 +402,13 @@ export default defineComponent({
if (onDragenter) {
onDragenter({
event,
node: convertNodePropsToEventData(node.props),
node: node.eventData.value,
expandedKeys: expandedKeys.value,
});
}
};
const onNodeDragOver = (event: MouseEvent, node: NodeInstance) => {
const onNodeDragOver = (event: MouseEvent, node: DragNodeEvent) => {
const { onDragover, allowDrop, direction } = props;
const {
@ -441,7 +440,7 @@ export default defineComponent({
// Update drag position
if (dragNode.props.eventKey === dropTargetKey && dropLevelOffset === 0) {
if (dragNode.eventKey.value === dropTargetKey && dropLevelOffset === 0) {
if (
!(
dragState.dropPosition === null &&
@ -486,7 +485,7 @@ export default defineComponent({
}
if (onDragover) {
onDragover({ event, node: convertNodePropsToEventData(node.props) });
onDragover({ event, node: node.eventData.value });
}
};
@ -494,7 +493,7 @@ export default defineComponent({
const { onDragleave } = props;
if (onDragleave) {
onDragleave({ event, node: convertNodePropsToEventData(node.props) });
onDragleave({ event, node: node.eventData.value });
}
};
const onNodeDrop = (event: MouseEvent, _node, outsideTree = false) => {
@ -527,8 +526,8 @@ export default defineComponent({
const dropResult = {
event,
node: convertNodePropsToEventData(abstractDropNodeProps),
dragNode: dragNode ? convertNodePropsToEventData(dragNode.props) : null,
dragNodesKeys: [dragNode.props.eventKey].concat(dragChildrenKeys),
dragNode: dragNode ? dragNode.eventData.value : null,
dragNodesKeys: [dragNode.eventKey.value].concat(dragChildrenKeys),
dropToGap: dropPosition !== 0,
dropPosition: dropPosition + Number(posArr[posArr.length - 1]),
};
@ -804,9 +803,10 @@ export default defineComponent({
// );
// flattenNodes.value = newFlattenTreeData;
})
.catch(() => {
.catch(e => {
const expandedKeysToRestore = arrDel(expandedKeys.value, key);
setExpandedKeys(expandedKeysToRestore);
Promise.reject(e);
});
}
}
@ -874,7 +874,13 @@ export default defineComponent({
onActiveChange(null);
}
};
const activeItemEventNode = computed(() => {
return convertNodePropsToEventData({
...getTreeNodeProps(activeKey.value, treeNodeRequiredProps.value),
data: activeItem.value.data,
active: true,
});
});
const onKeyDown = event => {
const { onKeyDown, checkable, selectable } = props;
@ -896,11 +902,7 @@ export default defineComponent({
const item = activeItem.value;
if (item && item.data) {
const expandable = item.data.isLeaf === false || !!(item.data.children || []).length;
const eventNode = convertNodePropsToEventData({
...getTreeNodeProps(activeKey.value, treeNodeRequiredProps.value),
data: item.data,
active: true,
});
const eventNode = activeItemEventNode.value;
switch (event.which) {
// >>> Expand

View File

@ -6,6 +6,7 @@ import { computed, defineComponent, getCurrentInstance, onMounted, onUpdated, re
import { treeNodeProps } from './props';
import classNames from '../_util/classNames';
import { warning } from '../vc-util/warning';
import { DragNodeEvent } from './interface';
const ICON_OPEN = 'open';
const ICON_CLOSE = 'close';
@ -18,14 +19,13 @@ export default defineComponent({
props: treeNodeProps,
isTreeNode: 1,
slots: ['title', 'icon', 'switcherIcon', 'checkable'],
setup(props, { attrs, slots }) {
setup(props, { attrs, slots, expose }) {
warning(
!('slots' in props.data),
'treeData slots is deprecated, please use `v-slot:icon` or `v-slot:title`, `v-slot:switcherIcon` instead',
);
const dragNodeHighlight = ref(false);
const context = useInjectTreeContext();
const instance = getCurrentInstance();
const selectHandle = ref();
const hasChildren = computed(() => {
@ -86,9 +86,19 @@ export default defineComponent({
return treeSelectable;
});
const eventData = computed(() => {
return convertNodePropsToEventData(props);
});
const dragNodeEvent: DragNodeEvent = {
eventData,
eventKey: computed(() => props.eventKey),
selectHandle,
pos: computed(() => props.pos),
};
expose(dragNodeEvent);
const onSelectorDoubleClick = (e: MouseEvent) => {
const { onNodeDoubleClick } = context.value;
onNodeDoubleClick(e, convertNodePropsToEventData(props));
onNodeDoubleClick(e, eventData.value);
};
const onSelect = (e: MouseEvent) => {
@ -96,7 +106,7 @@ export default defineComponent({
const { onNodeSelect } = context.value;
e.preventDefault();
onNodeSelect(e, convertNodePropsToEventData(props));
onNodeSelect(e, eventData.value);
};
const onCheck = (e: MouseEvent) => {
@ -109,13 +119,13 @@ export default defineComponent({
e.preventDefault();
const targetChecked = !checked;
onNodeCheck(e, convertNodePropsToEventData(props), targetChecked);
onNodeCheck(e, eventData.value, targetChecked);
};
const onSelectorClick = (e: MouseEvent) => {
// Click trigger before select/check operation
const { onNodeClick } = context.value;
onNodeClick(e, convertNodePropsToEventData(props));
onNodeClick(e, eventData.value);
if (isSelectable.value) {
onSelect(e);
@ -126,17 +136,17 @@ export default defineComponent({
const onMouseEnter = (e: MouseEvent) => {
const { onNodeMouseEnter } = context.value;
onNodeMouseEnter(e, convertNodePropsToEventData(props));
onNodeMouseEnter(e, eventData.value);
};
const onMouseLeave = (e: MouseEvent) => {
const { onNodeMouseLeave } = context.value;
onNodeMouseLeave(e, convertNodePropsToEventData(props));
onNodeMouseLeave(e, eventData.value);
};
const onContextmenu = (e: MouseEvent) => {
const { onNodeContextMenu } = context.value;
onNodeContextMenu(e, convertNodePropsToEventData(props));
onNodeContextMenu(e, eventData.value);
};
const onDragStart = (e: DragEvent) => {
@ -144,7 +154,7 @@ export default defineComponent({
e.stopPropagation();
dragNodeHighlight.value = true;
onNodeDragStart(e, instance.vnode);
onNodeDragStart(e, dragNodeEvent);
try {
// ie throw error
@ -160,7 +170,7 @@ export default defineComponent({
e.preventDefault();
e.stopPropagation();
onNodeDragEnter(e, instance.vnode);
onNodeDragEnter(e, dragNodeEvent);
};
const onDragOver = (e: DragEvent) => {
@ -168,14 +178,14 @@ export default defineComponent({
e.preventDefault();
e.stopPropagation();
onNodeDragOver(e, instance.vnode);
onNodeDragOver(e, dragNodeEvent);
};
const onDragLeave = (e: DragEvent) => {
const { onNodeDragLeave } = context.value;
e.stopPropagation();
onNodeDragLeave(e, instance.vnode);
onNodeDragLeave(e, dragNodeEvent);
};
const onDragEnd = (e: DragEvent) => {
@ -183,7 +193,7 @@ export default defineComponent({
e.stopPropagation();
dragNodeHighlight.value = false;
onNodeDragEnd(e, instance.vnode);
onNodeDragEnd(e, dragNodeEvent);
};
const onDrop = (e: DragEvent) => {
@ -192,14 +202,14 @@ export default defineComponent({
e.preventDefault();
e.stopPropagation();
dragNodeHighlight.value = false;
onNodeDrop(e, instance.vnode);
onNodeDrop(e, dragNodeEvent);
};
// Disabled item still can be switch
const onExpand = e => {
const { onNodeExpand } = context.value;
if (props.loading) return;
onNodeExpand(e, convertNodePropsToEventData(props));
onNodeExpand(e, eventData.value);
};
const renderSwitcherIconDom = (isLeaf: boolean) => {
@ -231,7 +241,7 @@ export default defineComponent({
// We needn't reload data when has children in sync logic
// It's only needed in node expanded
if (!hasChildren.value && !loaded) {
onNodeLoad(convertNodePropsToEventData(props));
onNodeLoad(eventData.value);
}
}
};
@ -240,7 +250,7 @@ export default defineComponent({
syncLoadData();
});
onUpdated(() => {
syncLoadData();
//syncLoadData();
});
// Switcher
@ -454,7 +464,7 @@ export default defineComponent({
'drag-over': !disabled && dragOver,
'drag-over-gap-top': !disabled && dragOverGapTop,
'drag-over-gap-bottom': !disabled && dragOverGapBottom,
'filter-node': filterTreeNode && filterTreeNode(convertNodePropsToEventData(props)),
'filter-node': filterTreeNode && filterTreeNode(eventData.value),
})}
style={attrs.style}
onDragenter={mergedDraggable ? onDragEnter : undefined}

View File

@ -13,7 +13,7 @@ import type {
Key,
DataEntity,
EventDataNode,
NodeInstance,
DragNodeEvent,
DataNode,
Direction,
} from './interface';
@ -30,7 +30,7 @@ export type NodeDragEventParams = {
export type NodeMouseEventHandler = (e: MouseEvent, node: EventDataNode) => void;
export type NodeDragEventHandler = (
e: MouseEvent,
node: NodeInstance,
node: DragNodeEvent,
outsideTree?: boolean,
) => void;

View File

@ -1,4 +1,4 @@
import type { CSSProperties, VNode } from 'vue';
import type { ComputedRef, CSSProperties, Ref, VNode } from 'vue';
export type { ScrollTo } from '../vc-virtual-list/List';
export interface DataNode {
@ -31,6 +31,8 @@ export interface EventDataNode extends DataNode {
dragOverGapBottom: boolean;
pos: string;
active: boolean;
dataRef: DataNode;
eventKey: Key; // v2 key
}
export type IconType = any;
@ -38,16 +40,17 @@ export type IconType = any;
export type Key = string | number;
export type NodeElement = VNode & {
selectHandle?: HTMLSpanElement;
type: {
isTreeNode: boolean;
};
};
export type NodeInstance = VNode & {
selectHandle?: HTMLSpanElement;
export type DragNodeEvent = {
eventData: ComputedRef<EventDataNode>;
eventKey: ComputedRef<Key>;
selectHandle: Ref<HTMLSpanElement>;
pos: ComputedRef<string>;
};
export interface Entity {
node: NodeElement;
index: number;

View File

@ -9,9 +9,9 @@ import type {
Key,
DataNode,
DataEntity,
NodeInstance,
FlattenNode,
Direction,
DragNodeEvent,
} from './interface';
import { warning } from '../vc-util/warning';
import type { AllowDrop, TreeNodeProps, TreeProps } from './props';
@ -79,8 +79,8 @@ export function isFirstChild(treeNodeEntity: DataEntity) {
// Only used when drag, not affect SSR.
export function calcDropPosition(
event: MouseEvent,
_dragNode: NodeInstance,
targetNode: NodeInstance,
_dragNode: DragNodeEvent,
targetNode: DragNodeEvent,
indent: number,
startMousePosition: {
x: number;
@ -108,7 +108,7 @@ export function calcDropPosition(
const rawDropLevelOffset = (horizontalMouseOffset - 12) / indent;
// find abstract drop node by horizontal offset
let abstractDropNodeEntity: DataEntity = keyEntities[targetNode.props.eventKey];
let abstractDropNodeEntity: DataEntity = keyEntities[targetNode.eventKey.value];
if (clientY < top + height / 2) {
// first half, set abstract drop node to previous node
@ -150,7 +150,7 @@ export function calcDropPosition(
dropNode: abstractDropDataNode,
dropPosition: -1,
}) &&
abstractDropNodeEntity.key === targetNode.props.eventKey
abstractDropNodeEntity.key === targetNode.eventKey.value
) {
// first half of first node in first level
dropPosition = -1;

View File

@ -393,8 +393,9 @@ export function convertNodePropsToEventData(props: TreeNodeProps): EventDataNode
dragOverGapBottom,
pos,
active,
dataRef: data,
eventKey: data.key,
};
if (!('props' in eventData)) {
Object.defineProperty(eventData, 'props', {
get() {