perf: tree, close #5551

pull/5564/head
tangjinzhou 2022-04-27 22:37:22 +08:00
parent 53b4c5d8b2
commit 9aeadaf877
11 changed files with 266 additions and 207 deletions

View File

@ -16,8 +16,8 @@ import PropTypes from '../_util/vue-types';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import devWarning from '../vc-util/devWarning'; import devWarning from '../vc-util/devWarning';
import getIcons from '../select/utils/iconUtil'; import getIcons from '../select/utils/iconUtil';
import type { SwitcherIconProps } from '../tree/utils/iconUtil';
import renderSwitcherIcon from '../tree/utils/iconUtil'; import renderSwitcherIcon from '../tree/utils/iconUtil';
import type { AntTreeNodeProps } from '../tree/Tree';
import { warning } from '../vc-util/warning'; import { warning } from '../vc-util/warning';
import { flattenChildren } from '../_util/props-util'; import { flattenChildren } from '../_util/props-util';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
@ -239,7 +239,7 @@ const TreeSelect = defineComponent({
multiple={multiple} multiple={multiple}
removeIcon={removeIcon} removeIcon={removeIcon}
clearIcon={clearIcon} clearIcon={clearIcon}
switcherIcon={(nodeProps: AntTreeNodeProps) => switcherIcon={(nodeProps: SwitcherIconProps) =>
renderSwitcherIcon(treePrefixCls.value, switcherIcon, treeLine, nodeProps) renderSwitcherIcon(treePrefixCls.value, switcherIcon, treeLine, nodeProps)
} }
showTreeIcon={treeIcon as any} showTreeIcon={treeIcon as any}

View File

@ -9,6 +9,7 @@ import type { DataNode, EventDataNode, FieldNames, Key } from '../vc-tree/interf
import type { TreeNodeProps } from '../vc-tree/props'; import type { TreeNodeProps } from '../vc-tree/props';
import { treeProps as vcTreeProps } from '../vc-tree/props'; import { treeProps as vcTreeProps } from '../vc-tree/props';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import type { SwitcherIconProps } from './utils/iconUtil';
import renderSwitcherIcon from './utils/iconUtil'; import renderSwitcherIcon from './utils/iconUtil';
import dropIndicatorRender from './utils/dropIndicator'; import dropIndicatorRender from './utils/dropIndicator';
import devWarning from '../vc-util/devWarning'; import devWarning from '../vc-util/devWarning';
@ -229,7 +230,7 @@ export default defineComponent({
icon, icon,
itemHeight, itemHeight,
}; };
const children = slots.default ? filterEmpty(slots.default()) : undefined;
return ( return (
<VcTree <VcTree
{...newProps} {...newProps}
@ -249,7 +250,7 @@ export default defineComponent({
direction={direction.value} direction={direction.value}
checkable={checkable} checkable={checkable}
selectable={selectable} selectable={selectable}
switcherIcon={(nodeProps: AntTreeNodeProps) => switcherIcon={(nodeProps: SwitcherIconProps) =>
renderSwitcherIcon(prefixCls.value, switcherIcon, showLine, nodeProps) renderSwitcherIcon(prefixCls.value, switcherIcon, showLine, nodeProps)
} }
onCheck={handleCheck} onCheck={handleCheck}
@ -260,7 +261,7 @@ export default defineComponent({
...slots, ...slots,
checkable: () => <span class={`${prefixCls.value}-checkbox-inner`} />, checkable: () => <span class={`${prefixCls.value}-checkbox-inner`} />,
}} }}
children={filterEmpty(slots.default?.())} children={children}
></VcTree> ></VcTree>
); );
}; };

View File

@ -7,12 +7,15 @@ import type { AntTreeNodeProps } from '../Tree';
import { isValidElement } from '../../_util/props-util'; import { isValidElement } from '../../_util/props-util';
import { cloneVNode } from 'vue'; import { cloneVNode } from 'vue';
export interface SwitcherIconProps extends AntTreeNodeProps {
expanded: boolean;
loading: boolean;
}
export default function renderSwitcherIcon( export default function renderSwitcherIcon(
prefixCls: string, prefixCls: string,
switcherIcon: any, switcherIcon: any,
showLine: boolean | { showLeafIcon: boolean } | undefined, showLine: boolean | { showLeafIcon: boolean } | undefined,
props: AntTreeNodeProps, props: SwitcherIconProps,
) { ) {
const { isLeaf, expanded, loading } = props; const { isLeaf, expanded, loading } = props;
let icon = switcherIcon; let icon = switcherIcon;

View File

@ -1,7 +1,5 @@
import TreeNode from './TreeNode'; import TreeNode from './TreeNode';
import type { FlattenNode } from './interface'; import type { FlattenNode } from './interface';
import type { TreeNodeRequiredProps } from './utils/treeUtil';
import { getTreeNodeProps } from './utils/treeUtil';
import { useInjectTreeContext } from './contextTypes'; import { useInjectTreeContext } from './contextTypes';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { import {
@ -28,7 +26,7 @@ export default defineComponent({
onMotionStart: Function, onMotionStart: Function,
onMotionEnd: Function, onMotionEnd: Function,
motionType: String, motionType: String,
treeNodeRequiredProps: { type: Object as PropType<TreeNodeRequiredProps> }, // treeNodeRequiredProps: { type: Object as PropType<TreeNodeRequiredProps> },
}, },
slots: ['title', 'icon', 'switcherIcon', 'checkable'], slots: ['title', 'icon', 'switcherIcon', 'checkable'],
setup(props, { attrs, slots }) { setup(props, { attrs, slots }) {
@ -73,8 +71,7 @@ export default defineComponent({
}); });
return () => { return () => {
const { motion, motionNodes, motionType, active, treeNodeRequiredProps, ...otherProps } = const { motion, motionNodes, motionType, active, eventKey, ...otherProps } = props;
props;
if (motionNodes) { if (motionNodes) {
return ( return (
<Transition <Transition
@ -94,17 +91,15 @@ export default defineComponent({
} = treeNode; } = treeNode;
delete restProps.children; delete restProps.children;
const treeNodeProps = getTreeNodeProps(key, treeNodeRequiredProps);
return ( return (
<TreeNode <TreeNode
v-slots={slots} v-slots={slots}
{...restProps} {...restProps}
{...treeNodeProps}
title={title} title={title}
active={active} active={active}
data={treeNode.data} data={treeNode.data}
key={key} key={key}
eventKey={key}
isStart={isStart} isStart={isStart}
isEnd={isEnd} isEnd={isEnd}
/> />
@ -122,6 +117,7 @@ export default defineComponent({
style={attrs.style} style={attrs.style}
{...otherProps} {...otherProps}
active={active} active={active}
eventKey={eventKey}
/> />
); );
}; };

View File

@ -4,12 +4,14 @@
import { computed, defineComponent, ref, shallowRef, watch } from 'vue'; import { computed, defineComponent, ref, shallowRef, watch } from 'vue';
import VirtualList from '../vc-virtual-list'; import VirtualList from '../vc-virtual-list';
import omit from '../_util/omit';
import { useInjectKeysState, useInjectTreeContext } from './contextTypes';
import type { FlattenNode, DataEntity, DataNode, ScrollTo } from './interface'; import type { FlattenNode, DataEntity, DataNode, ScrollTo } from './interface';
import MotionTreeNode from './MotionTreeNode'; import MotionTreeNode from './MotionTreeNode';
import type { NodeListProps } from './props'; import type { NodeListProps } from './props';
import { nodeListProps } from './props'; import { nodeListProps } from './props';
import { findExpandedKeys, getExpandRange } from './utils/diffUtil'; import { findExpandedKeys, getExpandRange } from './utils/diffUtil';
import { getTreeNodeProps, getKey } from './utils/treeUtil'; import { getKey } from './utils/treeUtil';
const HIDDEN_STYLE = { const HIDDEN_STYLE = {
width: 0, width: 0,
@ -97,6 +99,7 @@ export default defineComponent({
// =============================== Ref ================================ // =============================== Ref ================================
const listRef = ref(); const listRef = ref();
const indentMeasurerRef = ref(); const indentMeasurerRef = ref();
const { expandedKeys, flattenNodes } = useInjectKeysState();
expose({ expose({
scrollTo: scroll => { scrollTo: scroll => {
listRef.value.scrollTo(scroll); listRef.value.scrollTo(scroll);
@ -104,19 +107,21 @@ export default defineComponent({
getIndentWidth: () => indentMeasurerRef.value.offsetWidth, getIndentWidth: () => indentMeasurerRef.value.offsetWidth,
}); });
// ============================== Motion ============================== // ============================== Motion ==============================
const transitionData = shallowRef<FlattenNode[]>(props.data); const transitionData = shallowRef<FlattenNode[]>(flattenNodes.value);
const transitionRange = shallowRef([]); const transitionRange = shallowRef([]);
const motionType = ref<'show' | 'hide' | null>(null); const motionType = ref<'show' | 'hide' | null>(null);
function onMotionEnd() { function onMotionEnd() {
transitionData.value = props.data; transitionData.value = flattenNodes.value;
transitionRange.value = []; transitionRange.value = [];
motionType.value = null; motionType.value = null;
props.onListChangeEnd(); props.onListChangeEnd();
} }
const context = useInjectTreeContext();
watch( watch(
[() => [...props.expandedKeys], () => props.data], [() => expandedKeys.value.slice(), flattenNodes],
([expandedKeys, data], [prevExpandedKeys, prevData]) => { ([expandedKeys, data], [prevExpandedKeys, prevData]) => {
const diffExpanded = findExpandedKeys(prevExpandedKeys, expandedKeys); const diffExpanded = findExpandedKeys(prevExpandedKeys, expandedKeys);
if (diffExpanded.key !== null) { if (diffExpanded.key !== null) {
@ -160,7 +165,7 @@ export default defineComponent({
); );
// We should clean up motion if is changed by dragging // We should clean up motion if is changed by dragging
watch( watch(
() => props.dragging, () => context.value.dragging,
dragging => { dragging => {
if (!dragging) { if (!dragging) {
onMotionEnd(); onMotionEnd();
@ -169,27 +174,18 @@ export default defineComponent({
); );
const mergedData = computed(() => const mergedData = computed(() =>
props.motion === undefined ? transitionData.value : props.data, props.motion === undefined ? transitionData.value : flattenNodes.value,
); );
const onActiveChange = () => {
props.onActiveChange(null);
};
return () => { return () => {
const { const {
prefixCls, prefixCls,
data,
selectable, selectable,
checkable, checkable,
expandedKeys,
selectedKeys,
checkedKeys,
loadedKeys,
loadingKeys,
halfCheckedKeys,
keyEntities,
disabled, disabled,
dragging,
dragOverNodeKey,
dropPosition,
motion, motion,
height, height,
@ -204,25 +200,12 @@ export default defineComponent({
onKeydown, onKeydown,
onFocus, onFocus,
onBlur, onBlur,
onActiveChange,
onListChangeStart, onListChangeStart,
onListChangeEnd, onListChangeEnd,
...domProps ...domProps
} = { ...props, ...attrs } as NodeListProps; } = { ...props, ...attrs } as NodeListProps;
const treeNodeRequiredProps = {
expandedKeys,
selectedKeys,
loadedKeys,
loadingKeys,
checkedKeys,
halfCheckedKeys,
dragOverNodeKey,
dropPosition,
keyEntities,
};
return ( return (
<> <>
{focused && activeItem && ( {focused && activeItem && (
@ -262,7 +245,7 @@ export default defineComponent({
</div> </div>
<VirtualList <VirtualList
{...domProps} {...omit(domProps, ['onActiveChange'])}
data={mergedData.value} data={mergedData.value}
itemKey={itemKey as any} itemKey={itemKey as any}
height={height} height={height}
@ -293,15 +276,12 @@ export default defineComponent({
const mergedKey = getKey(key, pos); const mergedKey = getKey(key, pos);
delete restProps.key; delete restProps.key;
delete restProps.children; delete restProps.children;
const treeNodeProps = getTreeNodeProps(mergedKey, treeNodeRequiredProps);
return ( return (
<MotionTreeNode <MotionTreeNode
{...restProps} {...restProps}
{...treeNodeProps} eventKey={mergedKey}
title={title} title={title}
active={!!activeItem && key === activeItem.key} active={!!activeItem && key === activeItem.key}
pos={pos}
data={treeNode.data} data={treeNode.data}
isStart={isStart} isStart={isStart}
isEnd={isEnd} isEnd={isEnd}
@ -310,10 +290,7 @@ export default defineComponent({
motionType={motionType.value} motionType={motionType.value}
onMotionStart={onListChangeStart} onMotionStart={onListChangeStart}
onMotionEnd={onMotionEnd} onMotionEnd={onMotionEnd}
treeNodeRequiredProps={treeNodeRequiredProps} onMousemove={onActiveChange}
onMousemove={() => {
onActiveChange(null);
}}
/> />
); );
}, },

View File

@ -1,5 +1,5 @@
import type { NodeMouseEventHandler, NodeDragEventHandler } from './contextTypes'; import type { NodeMouseEventHandler, NodeDragEventHandler } from './contextTypes';
import { TreeContext } from './contextTypes'; import { useProvideKeysState, TreeContext } from './contextTypes';
import { import {
getDragChildrenKeys, getDragChildrenKeys,
parseCheckedKeys, parseCheckedKeys,
@ -11,6 +11,7 @@ import {
posToArr, posToArr,
} from './util'; } from './util';
import type { Key, FlattenNode, EventDataNode, ScrollTo, DragNodeEvent } from './interface'; import type { Key, FlattenNode, EventDataNode, ScrollTo, DragNodeEvent } from './interface';
import type { TreeNodeRequiredProps } from './utils/treeUtil';
import { import {
flattenTreeData, flattenTreeData,
convertTreeToData, convertTreeToData,
@ -78,12 +79,12 @@ export default defineComponent({
const destroyed = ref(false); const destroyed = ref(false);
let delayedDragEnterLogic: Record<Key, number> = {}; let delayedDragEnterLogic: Record<Key, number> = {};
const indent = ref(); const indent = ref();
const selectedKeys = shallowRef([]); const selectedKeys = shallowRef<Key[]>([]);
const checkedKeys = shallowRef([]); const checkedKeys = shallowRef<Key[]>([]);
const halfCheckedKeys = shallowRef([]); const halfCheckedKeys = shallowRef<Key[]>([]);
const loadedKeys = shallowRef([]); const loadedKeys = shallowRef<Key[]>([]);
const loadingKeys = shallowRef([]); const loadingKeys = shallowRef<Key[]>([]);
const expandedKeys = shallowRef([]); const expandedKeys = shallowRef<Key[]>([]);
const loadingRetryTimes: Record<Key, number> = {}; const loadingRetryTimes: Record<Key, number> = {};
const dragState = reactive({ const dragState = reactive({
draggingNodeKey: null, draggingNodeKey: null,
@ -112,7 +113,10 @@ export default defineComponent({
? toRaw(props.treeData).slice() ? toRaw(props.treeData).slice()
: convertTreeToData(toRaw(props.children)); : convertTreeToData(toRaw(props.children));
}, },
{ immediate: true, deep: true }, {
immediate: true,
deep: true,
},
); );
const keyEntities = shallowRef({}); const keyEntities = shallowRef({});
@ -131,19 +135,37 @@ export default defineComponent({
let currentMouseOverDroppableNodeKey = null; let currentMouseOverDroppableNodeKey = null;
const treeNodeRequiredProps = computed(() => { const treeNodeRequiredProps = computed<TreeNodeRequiredProps>(() => {
return { return {
expandedKeys: expandedKeys.value || [], expandedKeysSet: expandedKeysSet.value,
selectedKeys: selectedKeys.value || [], selectedKeysSet: selectedKeysSet.value,
loadedKeys: loadedKeys.value || [], loadedKeysSet: loadedKeysSet.value,
loadingKeys: loadingKeys.value || [], loadingKeysSet: loadingKeysSet.value,
checkedKeys: checkedKeys.value || [], checkedKeysSet: checkedKeysSet.value,
halfCheckedKeys: halfCheckedKeys.value || [], halfCheckedKeysSet: halfCheckedKeysSet.value,
dragOverNodeKey: dragState.dragOverNodeKey, dragOverNodeKey: dragState.dragOverNodeKey,
dropPosition: dragState.dropPosition, dropPosition: dragState.dropPosition,
keyEntities: keyEntities.value, keyEntities: keyEntities.value,
}; };
}); });
const expandedKeysSet = computed(() => {
return new Set(expandedKeys.value);
});
const selectedKeysSet = computed(() => {
return new Set(selectedKeys.value);
});
const loadedKeysSet = computed(() => {
return new Set(loadedKeys.value);
});
const loadingKeysSet = computed(() => {
return new Set(loadingKeys.value);
});
const checkedKeysSet = computed(() => {
return new Set(checkedKeys.value);
});
const halfCheckedKeysSet = computed(() => {
return new Set(halfCheckedKeys.value);
});
watchEffect(() => { watchEffect(() => {
if (treeData.value) { if (treeData.value) {
@ -388,7 +410,7 @@ export default defineComponent({
allowDrop, allowDrop,
flattenNodes.value, flattenNodes.value,
keyEntities.value, keyEntities.value,
expandedKeys.value, expandedKeysSet.value,
direction, direction,
); );
@ -485,7 +507,7 @@ export default defineComponent({
allowDrop, allowDrop,
flattenNodes.value, flattenNodes.value,
keyEntities.value, keyEntities.value,
expandedKeys.value, expandedKeysSet.value,
direction, direction,
); );
@ -558,7 +580,6 @@ export default defineComponent({
const onNodeDrop = (event: DragEvent, _node, outsideTree = false) => { const onNodeDrop = (event: DragEvent, _node, outsideTree = false) => {
const { dragChildrenKeys, dropPosition, dropTargetKey, dropTargetPos, dropAllowed } = const { dragChildrenKeys, dropPosition, dropTargetKey, dropTargetPos, dropAllowed } =
dragState; dragState;
if (!dropAllowed) return; if (!dropAllowed) return;
const { onDrop } = props; const { onDrop } = props;
@ -735,11 +756,7 @@ export default defineComponent({
// We need to get the latest state of loading/loaded keys // We need to get the latest state of loading/loaded keys
const { loadData, onLoad } = props; const { loadData, onLoad } = props;
if ( if (!loadData || loadedKeysSet.value.has(key) || loadingKeysSet.value.has(key)) {
!loadData ||
loadedKeys.value.indexOf(key) !== -1 ||
loadingKeys.value.indexOf(key) !== -1
) {
return null; return null;
} }
@ -977,7 +994,7 @@ export default defineComponent({
// >>> Expand // >>> Expand
case KeyCode.LEFT: { case KeyCode.LEFT: {
// Collapse if possible // Collapse if possible
if (expandable && expandedKeys.value.includes(activeKey.value)) { if (expandable && expandedKeysSet.value.has(activeKey.value)) {
onNodeExpand({} as MouseEvent, eventNode); onNodeExpand({} as MouseEvent, eventNode);
} else if (item.parent) { } else if (item.parent) {
onActiveChange(item.parent.key); onActiveChange(item.parent.key);
@ -987,7 +1004,7 @@ export default defineComponent({
} }
case KeyCode.RIGHT: { case KeyCode.RIGHT: {
// Expand if possible // Expand if possible
if (expandable && !expandedKeys.value.includes(activeKey.value)) { if (expandable && !expandedKeysSet.value.has(activeKey.value)) {
onNodeExpand({} as MouseEvent, eventNode); onNodeExpand({} as MouseEvent, eventNode);
} else if (item.children && item.children.length) { } else if (item.children && item.children.length) {
onActiveChange(item.children[0].key); onActiveChange(item.children[0].key);
@ -1005,11 +1022,7 @@ export default defineComponent({
eventNode.checkable !== false && eventNode.checkable !== false &&
!eventNode.disableCheckbox !eventNode.disableCheckbox
) { ) {
onNodeCheck( onNodeCheck({} as MouseEvent, eventNode, !checkedKeysSet.value.has(activeKey.value));
{} as MouseEvent,
eventNode,
!checkedKeys.value.includes(activeKey.value),
);
} else if ( } else if (
!checkable && !checkable &&
selectable && selectable &&
@ -1042,6 +1055,21 @@ export default defineComponent({
window.removeEventListener('dragend', onWindowDragEnd); window.removeEventListener('dragend', onWindowDragEnd);
destroyed.value = true; destroyed.value = true;
}); });
useProvideKeysState({
expandedKeys,
selectedKeys,
loadedKeys,
loadingKeys,
checkedKeys,
halfCheckedKeys,
expandedKeysSet,
selectedKeysSet,
loadedKeysSet,
loadingKeysSet,
checkedKeysSet,
halfCheckedKeysSet,
flattenNodes,
});
return () => { return () => {
const { const {
// focused, // focused,
@ -1123,6 +1151,7 @@ export default defineComponent({
dropTargetKey, dropTargetKey,
dropPosition, dropPosition,
dragOverNodeKey, dragOverNodeKey,
dragging: draggingNodeKey !== null,
indent: indent.value, indent: indent.value,
direction, direction,
dropIndicatorRender, dropIndicatorRender,
@ -1160,12 +1189,10 @@ export default defineComponent({
ref={listRef} ref={listRef}
prefixCls={prefixCls} prefixCls={prefixCls}
style={style} style={style}
data={flattenNodes.value}
disabled={disabled} disabled={disabled}
selectable={selectable} selectable={selectable}
checkable={!!checkable} checkable={!!checkable}
motion={motion} motion={motion}
dragging={draggingNodeKey !== null}
height={height} height={height}
itemHeight={itemHeight} itemHeight={itemHeight}
virtual={virtual} virtual={virtual}
@ -1181,7 +1208,6 @@ export default defineComponent({
onListChangeEnd={onListChangeEnd} onListChangeEnd={onListChangeEnd}
onContextmenu={onContextmenu} onContextmenu={onContextmenu}
onScroll={onScroll} onScroll={onScroll}
{...treeNodeRequiredProps.value}
{...domProps} {...domProps}
/> />
</div> </div>

View File

@ -1,6 +1,6 @@
import { useInjectTreeContext } from './contextTypes'; import { useInjectKeysState, useInjectTreeContext } from './contextTypes';
import Indent from './Indent'; import Indent from './Indent';
import { convertNodePropsToEventData } from './utils/treeUtil'; import { convertNodePropsToEventData, getTreeNodeProps } from './utils/treeUtil';
import { import {
computed, computed,
defineComponent, defineComponent,
@ -14,8 +14,8 @@ 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 type { DragNodeEvent, Key } from './interface'; import type { DragNodeEvent, Key } from './interface';
import pick from 'lodash-es/pick';
import pickAttrs from '../_util/pickAttrs'; import pickAttrs from '../_util/pickAttrs';
import eagerComputed from '../_util/eagerComputed';
const ICON_OPEN = 'open'; const ICON_OPEN = 'open';
const ICON_CLOSE = 'close'; const ICON_CLOSE = 'close';
@ -35,8 +35,43 @@ export default defineComponent({
key => '`v-slot:' + key + '` ', key => '`v-slot:' + key + '` ',
)}instead`, )}instead`,
); );
const dragNodeHighlight = ref(false); const dragNodeHighlight = ref(false);
const context = useInjectTreeContext(); const context = useInjectTreeContext();
const {
expandedKeysSet,
selectedKeysSet,
loadedKeysSet,
loadingKeysSet,
checkedKeysSet,
halfCheckedKeysSet,
} = useInjectKeysState();
const { dragOverNodeKey, dropPosition, keyEntities } = context.value;
const mergedTreeNodeProps = computed(() => {
return getTreeNodeProps(props.eventKey, {
expandedKeysSet: expandedKeysSet.value,
selectedKeysSet: selectedKeysSet.value,
loadedKeysSet: loadedKeysSet.value,
loadingKeysSet: loadingKeysSet.value,
checkedKeysSet: checkedKeysSet.value,
halfCheckedKeysSet: halfCheckedKeysSet.value,
dragOverNodeKey,
dropPosition,
keyEntities,
});
});
const expanded = eagerComputed(() => mergedTreeNodeProps.value.expanded);
const selected = eagerComputed(() => mergedTreeNodeProps.value.selected);
const checked = eagerComputed(() => mergedTreeNodeProps.value.checked);
const loaded = eagerComputed(() => mergedTreeNodeProps.value.loaded);
const loading = eagerComputed(() => mergedTreeNodeProps.value.loading);
const halfChecked = eagerComputed(() => mergedTreeNodeProps.value.halfChecked);
const dragOver = eagerComputed(() => mergedTreeNodeProps.value.dragOver);
const dragOverGapTop = eagerComputed(() => mergedTreeNodeProps.value.dragOverGapTop);
const dragOverGapBottom = eagerComputed(() => mergedTreeNodeProps.value.dragOverGapBottom);
const pos = eagerComputed(() => mergedTreeNodeProps.value.pos);
const selectHandle = ref(); const selectHandle = ref();
const hasChildren = computed(() => { const hasChildren = computed(() => {
@ -48,7 +83,7 @@ export default defineComponent({
}); });
const isLeaf = computed(() => { const isLeaf = computed(() => {
const { isLeaf, loaded } = props; const { isLeaf } = props;
const { loadData } = context.value; const { loadData } = context.value;
const has = hasChildren.value; const has = hasChildren.value;
@ -57,16 +92,14 @@ export default defineComponent({
return false; return false;
} }
return isLeaf || (!loadData && !has) || (loadData && loaded && !has); return isLeaf || (!loadData && !has) || (loadData && loaded.value && !has);
}); });
const nodeState = computed(() => { const nodeState = computed(() => {
const { expanded } = props;
if (isLeaf.value) { if (isLeaf.value) {
return null; return null;
} }
return expanded ? ICON_OPEN : ICON_CLOSE; return expanded.value ? ICON_OPEN : ICON_CLOSE;
}); });
const isDisabled = computed(() => { const isDisabled = computed(() => {
@ -97,24 +130,22 @@ export default defineComponent({
return treeSelectable; return treeSelectable;
}); });
const renderArgsData = computed(() => { const renderArgsData = computed(() => {
const { data, active, checkable, disableCheckbox, disabled, selectable } = props;
return { return {
...pick(props, [ active,
'active', checkable,
'checkable', disableCheckbox,
'checked', disabled,
'disableCheckbox', selectable,
'disabled', ...data,
'expanded', dataRef: data,
'isLeaf', data,
'loading',
'selectable',
'selected',
'halfChecked',
]),
...props.data,
dataRef: props.data,
data: props.data,
isLeaf: isLeaf.value, isLeaf: isLeaf.value,
checked: checked.value,
expanded: expanded.value,
loading: loading.value,
selected: selected.value,
halfChecked: halfChecked.value,
}; };
}); });
const instance = getCurrentInstance(); const instance = getCurrentInstance();
@ -122,13 +153,16 @@ export default defineComponent({
const { eventKey } = props; const { eventKey } = props;
const { keyEntities } = context.value; const { keyEntities } = context.value;
const { parent } = keyEntities[eventKey] || {}; const { parent } = keyEntities[eventKey] || {};
return { ...convertNodePropsToEventData(props), parent }; return {
...convertNodePropsToEventData(Object.assign({}, props, mergedTreeNodeProps.value)),
parent,
};
}); });
const dragNodeEvent: DragNodeEvent = reactive({ const dragNodeEvent: DragNodeEvent = reactive({
eventData, eventData,
eventKey: computed(() => props.eventKey), eventKey: computed(() => props.eventKey),
selectHandle, selectHandle,
pos: computed(() => props.pos), pos,
key: instance.vnode.key as Key, key: instance.vnode.key as Key,
}); });
expose(dragNodeEvent); expose(dragNodeEvent);
@ -148,13 +182,13 @@ export default defineComponent({
const onCheck = (e: MouseEvent) => { const onCheck = (e: MouseEvent) => {
if (isDisabled.value) return; if (isDisabled.value) return;
const { disableCheckbox, checked } = props; const { disableCheckbox } = props;
const { onNodeCheck } = context.value; const { onNodeCheck } = context.value;
if (!isCheckable.value || disableCheckbox) return; if (!isCheckable.value || disableCheckbox) return;
e.preventDefault(); e.preventDefault();
const targetChecked = !checked; const targetChecked = !checked.value;
onNodeCheck(e, eventData.value, targetChecked); onNodeCheck(e, eventData.value, targetChecked);
}; };
@ -244,7 +278,7 @@ export default defineComponent({
// 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 (loading.value) return;
onNodeExpand(e, eventData.value); onNodeExpand(e, eventData.value);
}; };
@ -279,18 +313,18 @@ export default defineComponent({
// Load data to avoid default expanded tree without data // Load data to avoid default expanded tree without data
const syncLoadData = () => { const syncLoadData = () => {
const { expanded, loading, loaded } = props; //const { expanded, loading, loaded } = props;
const { loadData, onNodeLoad } = context.value; const { loadData, onNodeLoad } = context.value;
if (loading) { if (loading.value) {
return; return;
} }
// read from state to avoid loadData at same time // read from state to avoid loadData at same time
if (loadData && expanded && !isLeaf.value) { if (loadData && expanded.value && !isLeaf.value) {
// 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.value) {
onNodeLoad(eventData.value); onNodeLoad(eventData.value);
} }
} }
@ -306,7 +340,6 @@ export default defineComponent({
// Switcher // Switcher
const renderSwitcher = () => { const renderSwitcher = () => {
const { expanded } = props;
const { prefixCls } = context.value; const { prefixCls } = context.value;
// if switcherIconDom is null, no render switcher span // if switcherIconDom is null, no render switcher span
const switcherIconDom = renderSwitcherIconDom(); const switcherIconDom = renderSwitcherIconDom();
@ -320,7 +353,7 @@ export default defineComponent({
const switcherCls = classNames( const switcherCls = classNames(
`${prefixCls}-switcher`, `${prefixCls}-switcher`,
`${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`, `${prefixCls}-switcher_${expanded.value ? ICON_OPEN : ICON_CLOSE}`,
); );
return switcherIconDom !== false ? ( return switcherIconDom !== false ? (
@ -332,7 +365,7 @@ export default defineComponent({
// Checkbox // Checkbox
const renderCheckbox = () => { const renderCheckbox = () => {
const { checked, halfChecked, disableCheckbox } = props; const { disableCheckbox } = props;
const { prefixCls } = context.value; const { prefixCls } = context.value;
const disabled = isDisabled.value; const disabled = isDisabled.value;
@ -344,8 +377,8 @@ export default defineComponent({
<span <span
class={classNames( class={classNames(
`${prefixCls}-checkbox`, `${prefixCls}-checkbox`,
checked && `${prefixCls}-checkbox-checked`, checked.value && `${prefixCls}-checkbox-checked`,
!checked && halfChecked && `${prefixCls}-checkbox-indeterminate`, !checked.value && halfChecked.value && `${prefixCls}-checkbox-indeterminate`,
(disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`, (disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`,
)} )}
onClick={onCheck} onClick={onCheck}
@ -356,7 +389,6 @@ export default defineComponent({
}; };
const renderIcon = () => { const renderIcon = () => {
const { loading } = props;
const { prefixCls } = context.value; const { prefixCls } = context.value;
return ( return (
@ -364,7 +396,7 @@ export default defineComponent({
class={classNames( class={classNames(
`${prefixCls}-iconEle`, `${prefixCls}-iconEle`,
`${prefixCls}-icon__${nodeState.value || 'docu'}`, `${prefixCls}-icon__${nodeState.value || 'docu'}`,
loading && `${prefixCls}-icon_loading`, loading.value && `${prefixCls}-icon_loading`,
)} )}
/> />
); );
@ -396,9 +428,9 @@ export default defineComponent({
// title = slots.title || // title = slots.title ||
// context.value.slots?.[props.data?.slots?.title] || // context.value.slots?.[props.data?.slots?.title] ||
// context.value.slots?.title, // context.value.slots?.title,
selected, // selected,
icon = slots.icon, icon = slots.icon,
loading, // loading,
data, data,
} = props; } = props;
const title = const title =
@ -430,7 +462,7 @@ export default defineComponent({
) : ( ) : (
renderIcon() renderIcon()
); );
} else if (loadData && loading) { } else if (loadData && loading.value) {
$icon = renderIcon(); $icon = renderIcon();
} }
@ -454,7 +486,9 @@ export default defineComponent({
class={classNames( class={classNames(
`${wrapClass}`, `${wrapClass}`,
`${wrapClass}-${nodeState.value || 'normal'}`, `${wrapClass}-${nodeState.value || 'normal'}`,
!disabled && (selected || dragNodeHighlight.value) && `${prefixCls}-node-selected`, !disabled &&
(selected.value || dragNodeHighlight.value) &&
`${prefixCls}-node-selected`,
)} )}
onMouseenter={onMouseEnter} onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave} onMouseleave={onMouseLeave}
@ -471,17 +505,9 @@ export default defineComponent({
return () => { return () => {
const { const {
eventKey, eventKey,
dragOver,
dragOverGapTop,
dragOverGapBottom,
isLeaf, isLeaf,
isStart, isStart,
isEnd, isEnd,
expanded,
selected,
checked,
halfChecked,
loading,
domRef, domRef,
active, active,
data, data,
@ -507,17 +533,17 @@ export default defineComponent({
const dragging = draggingNodeKey === eventKey; const dragging = draggingNodeKey === eventKey;
const ariaSelected = selectable !== undefined ? { 'aria-selected': !!selectable } : undefined; const ariaSelected = selectable !== undefined ? { 'aria-selected': !!selectable } : undefined;
// console.log(1);
return ( return (
<div <div
ref={domRef} ref={domRef}
class={classNames(attrs.class, `${prefixCls}-treenode`, { class={classNames(attrs.class, `${prefixCls}-treenode`, {
[`${prefixCls}-treenode-disabled`]: disabled, [`${prefixCls}-treenode-disabled`]: disabled,
[`${prefixCls}-treenode-switcher-${expanded ? 'open' : 'close'}`]: !isLeaf, [`${prefixCls}-treenode-switcher-${expanded.value ? 'open' : 'close'}`]: !isLeaf,
[`${prefixCls}-treenode-checkbox-checked`]: checked, [`${prefixCls}-treenode-checkbox-checked`]: checked.value,
[`${prefixCls}-treenode-checkbox-indeterminate`]: halfChecked, [`${prefixCls}-treenode-checkbox-indeterminate`]: halfChecked.value,
[`${prefixCls}-treenode-selected`]: selected, [`${prefixCls}-treenode-selected`]: selected.value,
[`${prefixCls}-treenode-loading`]: loading, [`${prefixCls}-treenode-loading`]: loading.value,
[`${prefixCls}-treenode-active`]: active, [`${prefixCls}-treenode-active`]: active,
[`${prefixCls}-treenode-leaf-last`]: isEndNode, [`${prefixCls}-treenode-leaf-last`]: isEndNode,
[`${prefixCls}-treenode-draggable`]: draggableWithoutDisabled, [`${prefixCls}-treenode-draggable`]: draggableWithoutDisabled,
@ -525,9 +551,9 @@ export default defineComponent({
dragging, dragging,
'drop-target': dropTargetKey === eventKey, 'drop-target': dropTargetKey === eventKey,
'drop-container': dropContainerKey === eventKey, 'drop-container': dropContainerKey === eventKey,
'drag-over': !disabled && dragOver, 'drag-over': !disabled && dragOver.value,
'drag-over-gap-top': !disabled && dragOverGapTop, 'drag-over-gap-top': !disabled && dragOverGapTop.value,
'drag-over-gap-bottom': !disabled && dragOverGapBottom, 'drag-over-gap-bottom': !disabled && dragOverGapBottom.value,
'filter-node': filterTreeNode && filterTreeNode(eventData.value), 'filter-node': filterTreeNode && filterTreeNode(eventData.value),
})} })}
style={attrs.style} style={attrs.style}

View File

@ -3,8 +3,8 @@
* When util.js imports the TreeNode for tree generate will cause treeContextTypes be empty. * When util.js imports the TreeNode for tree generate will cause treeContextTypes be empty.
*/ */
import type { ComputedRef, InjectionKey, PropType } from 'vue'; import type { ComputedRef, InjectionKey, PropType, ShallowRef } from 'vue';
import { inject, computed, defineComponent, provide } from 'vue'; import { shallowRef, inject, computed, defineComponent, provide } from 'vue';
import type { VueNode } from '../_util/type'; import type { VueNode } from '../_util/type';
import type { import type {
IconType, IconType,
@ -13,6 +13,7 @@ import type {
EventDataNode, EventDataNode,
DragNodeEvent, DragNodeEvent,
Direction, Direction,
FlattenNode,
} from './interface'; } from './interface';
import type { DraggableConfig } from './Tree'; import type { DraggableConfig } from './Tree';
@ -60,6 +61,7 @@ export interface TreeContextProps {
direction: Direction; direction: Direction;
}) => VueNode; }) => VueNode;
dragOverNodeKey: Key | null; dragOverNodeKey: Key | null;
dragging: boolean;
direction: Direction; direction: Direction;
loadData: (treeNode: EventDataNode) => Promise<void>; loadData: (treeNode: EventDataNode) => Promise<void>;
@ -108,3 +110,40 @@ export const useInjectTreeContext = () => {
computed(() => ({} as TreeContextProps)), computed(() => ({} as TreeContextProps)),
); );
}; };
type KeysStateKeyType = {
expandedKeysSet: ComputedRef<Set<Key>>;
selectedKeysSet: ComputedRef<Set<Key>>;
loadedKeysSet: ComputedRef<Set<Key>>;
loadingKeysSet: ComputedRef<Set<Key>>;
checkedKeysSet: ComputedRef<Set<Key>>;
halfCheckedKeysSet: ComputedRef<Set<Key>>;
expandedKeys: ShallowRef<Key[]>;
selectedKeys: ShallowRef<Key[]>;
loadedKeys: ShallowRef<Key[]>;
loadingKeys: ShallowRef<Key[]>;
checkedKeys: ShallowRef<Key[]>;
halfCheckedKeys: ShallowRef<Key[]>;
flattenNodes: ShallowRef<FlattenNode[]>;
};
const KeysStateKey: InjectionKey<KeysStateKeyType> = Symbol('KeysStateKey');
export const useProvideKeysState = (state: KeysStateKeyType) => {
provide(KeysStateKey, state);
};
export const useInjectKeysState = () => {
return inject(KeysStateKey, {
expandedKeys: shallowRef<Key[]>([]),
selectedKeys: shallowRef<Key[]>([]),
loadedKeys: shallowRef<Key[]>([]),
loadingKeys: shallowRef<Key[]>([]),
checkedKeys: shallowRef<Key[]>([]),
halfCheckedKeys: shallowRef<Key[]>([]),
expandedKeysSet: computed<Set<Key>>(() => new Set()),
selectedKeysSet: computed<Set<Key>>(() => new Set()),
loadedKeysSet: computed<Set<Key>>(() => new Set()),
loadingKeysSet: computed<Set<Key>>(() => new Set()),
checkedKeysSet: computed<Set<Key>>(() => new Set()),
halfCheckedKeysSet: computed<Set<Key>>(() => new Set()),
flattenNodes: shallowRef<FlattenNode[]>([]),
});
};

View File

@ -7,15 +7,7 @@ import type {
NodeMouseEventHandler, NodeMouseEventHandler,
NodeMouseEventParams, NodeMouseEventParams,
} from './contextTypes'; } from './contextTypes';
import type { import type { DataNode, Key, FlattenNode, EventDataNode, Direction, FieldNames } from './interface';
DataNode,
Key,
FlattenNode,
EventDataNode,
Direction,
FieldNames,
DataEntity,
} from './interface';
export interface CheckInfo { export interface CheckInfo {
event: 'check'; event: 'check';
@ -32,18 +24,18 @@ export const treeNodeProps = {
prefixCls: String, prefixCls: String,
// By parent // By parent
expanded: { type: Boolean, default: undefined }, // expanded: { type: Boolean, default: undefined },
selected: { type: Boolean, default: undefined }, // selected: { type: Boolean, default: undefined },
checked: { type: Boolean, default: undefined }, // checked: { type: Boolean, default: undefined },
loaded: { type: Boolean, default: undefined }, // loaded: { type: Boolean, default: undefined },
loading: { type: Boolean, default: undefined }, // loading: { type: Boolean, default: undefined },
halfChecked: { type: Boolean, default: undefined }, // halfChecked: { type: Boolean, default: undefined },
title: PropTypes.any, // dragOver: { type: Boolean, default: undefined },
dragOver: { type: Boolean, default: undefined }, // dragOverGapTop: { type: Boolean, default: undefined },
dragOverGapTop: { type: Boolean, default: undefined }, // dragOverGapBottom: { type: Boolean, default: undefined },
dragOverGapBottom: { type: Boolean, default: undefined }, // pos: String,
pos: String,
title: PropTypes.any,
/** New added in Tree for easy data access */ /** New added in Tree for easy data access */
data: { type: Object as PropType<DataNode>, default: undefined as DataNode }, data: { type: Object as PropType<DataNode>, default: undefined as DataNode },
parent: { type: Object as PropType<DataNode>, default: undefined as DataNode }, parent: { type: Object as PropType<DataNode>, default: undefined as DataNode },
@ -68,7 +60,7 @@ export type TreeNodeProps = Partial<ExtractPropTypes<typeof treeNodeProps>>;
export const nodeListProps = { export const nodeListProps = {
prefixCls: { type: String as PropType<string> }, prefixCls: { type: String as PropType<string> },
data: { type: Array as PropType<FlattenNode[]> }, // data: { type: Array as PropType<FlattenNode[]> },
motion: { type: Object as PropType<any> }, motion: { type: Object as PropType<any> },
focusable: { type: Boolean as PropType<boolean> }, focusable: { type: Boolean as PropType<boolean> },
activeItem: { type: Object as PropType<FlattenNode> }, activeItem: { type: Object as PropType<FlattenNode> },
@ -78,17 +70,17 @@ export const nodeListProps = {
selectable: { type: Boolean as PropType<boolean> }, selectable: { type: Boolean as PropType<boolean> },
disabled: { type: Boolean as PropType<boolean> }, disabled: { type: Boolean as PropType<boolean> },
expandedKeys: { type: Array as PropType<Key[]> }, // expandedKeys: { type: Array as PropType<Key[]> },
selectedKeys: { type: Array as PropType<Key[]> }, // selectedKeys: { type: Array as PropType<Key[]> },
checkedKeys: { type: Array as PropType<Key[]> }, // checkedKeys: { type: Array as PropType<Key[]> },
loadedKeys: { type: Array as PropType<Key[]> }, // loadedKeys: { type: Array as PropType<Key[]> },
loadingKeys: { type: Array as PropType<Key[]> }, // loadingKeys: { type: Array as PropType<Key[]> },
halfCheckedKeys: { type: Array as PropType<Key[]> }, // halfCheckedKeys: { type: Array as PropType<Key[]> },
keyEntities: { type: Object as PropType<Record<Key, DataEntity<DataNode>>> }, // keyEntities: { type: Object as PropType<Record<Key, DataEntity<DataNode>>> },
dragging: { type: Boolean as PropType<boolean> }, // dragging: { type: Boolean as PropType<boolean> },
dragOverNodeKey: { type: [String, Number] as PropType<Key> }, // dragOverNodeKey: { type: [String, Number] as PropType<Key> },
dropPosition: { type: Number as PropType<number> }, // dropPosition: { type: Number as PropType<number> },
// Virtual list // Virtual list
height: { type: Number as PropType<number> }, height: { type: Number as PropType<number> },

View File

@ -98,7 +98,7 @@ export function calcDropPosition<TreeDataType extends BasicDataNode = DataNode>(
allowDrop: AllowDrop<TreeDataType>, allowDrop: AllowDrop<TreeDataType>,
flattenedNodes: FlattenNode[], flattenedNodes: FlattenNode[],
keyEntities: Record<Key, DataEntity<TreeDataType>>, keyEntities: Record<Key, DataEntity<TreeDataType>>,
expandKeys: Key[], expandKeysSet: Set<Key>,
direction: Direction, direction: Direction,
): { ): {
dropPosition: -1 | 0 | 1; dropPosition: -1 | 0 | 1;
@ -138,7 +138,7 @@ export function calcDropPosition<TreeDataType extends BasicDataNode = DataNode>(
let dropLevelOffset = 0; let dropLevelOffset = 0;
// Only allow cross level drop when dragging on a non-expanded node // Only allow cross level drop when dragging on a non-expanded node
if (!expandKeys.includes(initialAbstractDropNodeKey)) { if (!expandKeysSet.has(initialAbstractDropNodeKey)) {
for (let i = 0; i < rawDropLevelOffset; i += 1) { for (let i = 0; i < rawDropLevelOffset; i += 1) {
if (isLastChild(abstractDropNodeEntity)) { if (isLastChild(abstractDropNodeEntity)) {
abstractDropNodeEntity = abstractDropNodeEntity.parent; abstractDropNodeEntity = abstractDropNodeEntity.parent;
@ -164,10 +164,7 @@ export function calcDropPosition<TreeDataType extends BasicDataNode = DataNode>(
) { ) {
// first half of first node in first level // first half of first node in first level
dropPosition = -1; dropPosition = -1;
} else if ( } else if ((abstractDragOverEntity.children || []).length && expandKeysSet.has(dragOverNodeKey)) {
(abstractDragOverEntity.children || []).length &&
expandKeys.includes(dragOverNodeKey)
) {
// drop on expanded node // drop on expanded node
// only allow drop inside // only allow drop inside
if ( if (

View File

@ -361,12 +361,12 @@ export function convertDataToEntities(
} }
export interface TreeNodeRequiredProps<TreeDataType extends BasicDataNode = DataNode> { export interface TreeNodeRequiredProps<TreeDataType extends BasicDataNode = DataNode> {
expandedKeys: Key[]; expandedKeysSet: Set<Key>;
selectedKeys: Key[]; selectedKeysSet: Set<Key>;
loadedKeys: Key[]; loadedKeysSet: Set<Key>;
loadingKeys: Key[]; loadingKeysSet: Set<Key>;
checkedKeys: Key[]; checkedKeysSet: Set<Key>;
halfCheckedKeys: Key[]; halfCheckedKeysSet: Set<Key>;
dragOverNodeKey: Key; dragOverNodeKey: Key;
dropPosition: number; dropPosition: number;
keyEntities: Record<Key, DataEntity<TreeDataType>>; keyEntities: Record<Key, DataEntity<TreeDataType>>;
@ -378,12 +378,12 @@ export interface TreeNodeRequiredProps<TreeDataType extends BasicDataNode = Data
export function getTreeNodeProps<TreeDataType extends BasicDataNode = DataNode>( export function getTreeNodeProps<TreeDataType extends BasicDataNode = DataNode>(
key: Key, key: Key,
{ {
expandedKeys, expandedKeysSet,
selectedKeys, selectedKeysSet,
loadedKeys, loadedKeysSet,
loadingKeys, loadingKeysSet,
checkedKeys, checkedKeysSet,
halfCheckedKeys, halfCheckedKeysSet,
dragOverNodeKey, dragOverNodeKey,
dropPosition, dropPosition,
keyEntities, keyEntities,
@ -393,12 +393,12 @@ export function getTreeNodeProps<TreeDataType extends BasicDataNode = DataNode>(
const treeNodeProps = { const treeNodeProps = {
eventKey: key, eventKey: key,
expanded: expandedKeys.indexOf(key) !== -1, expanded: expandedKeysSet.has(key),
selected: selectedKeys.indexOf(key) !== -1, selected: selectedKeysSet.has(key),
loaded: loadedKeys.indexOf(key) !== -1, loaded: loadedKeysSet.has(key),
loading: loadingKeys.indexOf(key) !== -1, loading: loadingKeysSet.has(key),
checked: checkedKeys.indexOf(key) !== -1, checked: checkedKeysSet.has(key),
halfChecked: halfCheckedKeys.indexOf(key) !== -1, halfChecked: halfCheckedKeysSet.has(key),
pos: String(entity ? entity.pos : ''), pos: String(entity ? entity.pos : ''),
parent: entity.parent, parent: entity.parent,
// [Legacy] Drag props // [Legacy] Drag props
@ -412,7 +412,9 @@ export function getTreeNodeProps<TreeDataType extends BasicDataNode = DataNode>(
return treeNodeProps; return treeNodeProps;
} }
export function convertNodePropsToEventData(props: TreeNodeProps): EventDataNode { export function convertNodePropsToEventData(
props: TreeNodeProps & ReturnType<typeof getTreeNodeProps>,
): EventDataNode {
const { const {
data, data,
expanded, expanded,