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

View File

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

View File

@ -13,7 +13,7 @@ import type {
Key, Key,
DataEntity, DataEntity,
EventDataNode, EventDataNode,
NodeInstance, DragNodeEvent,
DataNode, DataNode,
Direction, Direction,
} from './interface'; } from './interface';
@ -30,7 +30,7 @@ export type NodeDragEventParams = {
export type NodeMouseEventHandler = (e: MouseEvent, node: EventDataNode) => void; export type NodeMouseEventHandler = (e: MouseEvent, node: EventDataNode) => void;
export type NodeDragEventHandler = ( export type NodeDragEventHandler = (
e: MouseEvent, e: MouseEvent,
node: NodeInstance, node: DragNodeEvent,
outsideTree?: boolean, outsideTree?: boolean,
) => void; ) => 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 type { ScrollTo } from '../vc-virtual-list/List';
export interface DataNode { export interface DataNode {
@ -31,6 +31,8 @@ export interface EventDataNode extends DataNode {
dragOverGapBottom: boolean; dragOverGapBottom: boolean;
pos: string; pos: string;
active: boolean; active: boolean;
dataRef: DataNode;
eventKey: Key; // v2 key
} }
export type IconType = any; export type IconType = any;
@ -38,16 +40,17 @@ export type IconType = any;
export type Key = string | number; export type Key = string | number;
export type NodeElement = VNode & { export type NodeElement = VNode & {
selectHandle?: HTMLSpanElement;
type: { type: {
isTreeNode: boolean; isTreeNode: boolean;
}; };
}; };
export type NodeInstance = VNode & { export type DragNodeEvent = {
selectHandle?: HTMLSpanElement; eventData: ComputedRef<EventDataNode>;
eventKey: ComputedRef<Key>;
selectHandle: Ref<HTMLSpanElement>;
pos: ComputedRef<string>;
}; };
export interface Entity { export interface Entity {
node: NodeElement; node: NodeElement;
index: number; index: number;

View File

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

View File

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