feat: tree
parent
98755f332c
commit
54cdc3ff40
|
@ -6,6 +6,7 @@ import { computed, defineComponent, ref, shallowRef, watch } from 'vue';
|
|||
import VirtualList from '../vc-virtual-list';
|
||||
import type { FlattenNode, DataEntity, DataNode, ScrollTo } from './interface';
|
||||
import MotionTreeNode from './MotionTreeNode';
|
||||
import type { NodeListProps } from './props';
|
||||
import { nodeListProps } from './props';
|
||||
import { findExpandedKeys, getExpandRange } from './utils/diffUtil';
|
||||
import { getTreeNodeProps, getKey } from './utils/treeUtil';
|
||||
|
@ -35,6 +36,7 @@ export const MotionEntity: DataEntity = {
|
|||
index: 0,
|
||||
pos: '0',
|
||||
node: MotionNode,
|
||||
nodes: [MotionNode],
|
||||
};
|
||||
|
||||
const MotionFlattenData: FlattenNode = {
|
||||
|
@ -208,7 +210,7 @@ export default defineComponent({
|
|||
onListChangeEnd,
|
||||
|
||||
...domProps
|
||||
} = { ...props, ...attrs };
|
||||
} = { ...props, ...attrs } as NodeListProps;
|
||||
|
||||
const treeNodeRequiredProps = {
|
||||
expandedKeys,
|
||||
|
@ -269,6 +271,15 @@ export default defineComponent({
|
|||
itemHeight={itemHeight}
|
||||
prefixCls={`${prefixCls}-list`}
|
||||
ref={listRef}
|
||||
onVisibleChange={(originList, fullList) => {
|
||||
const originSet = new Set(originList);
|
||||
const restList = fullList.filter(item => !originSet.has(item));
|
||||
|
||||
// Motion node is not render. Skip motion
|
||||
if (restList.some(item => itemKey(item) === MOTION_KEY)) {
|
||||
onMotionEnd();
|
||||
}
|
||||
}}
|
||||
v-slots={{
|
||||
default: (treeNode: FlattenNode) => {
|
||||
const {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type { NodeMouseEventHandler, NodeDragEventHandler } from './contextTypes';
|
||||
import { TreeContext } from './contextTypes';
|
||||
import {
|
||||
getDataAndAria,
|
||||
getDragChildrenKeys,
|
||||
parseCheckedKeys,
|
||||
conductExpandParent,
|
||||
|
@ -34,11 +33,19 @@ import {
|
|||
watchEffect,
|
||||
} from 'vue';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import type { CheckInfo } from './props';
|
||||
import type { CheckInfo, DraggableFn } from './props';
|
||||
import { treeProps } from './props';
|
||||
import { warning } from '../vc-util/warning';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
import classNames from '../_util/classNames';
|
||||
import pickAttrs from '../_util/pickAttrs';
|
||||
|
||||
const MAX_RETRY_TIMES = 10;
|
||||
|
||||
export type DraggableConfig = {
|
||||
icon?: any;
|
||||
nodeDraggable?: DraggableFn;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Tree',
|
||||
|
@ -74,9 +81,9 @@ export default defineComponent({
|
|||
const loadedKeys = shallowRef([]);
|
||||
const loadingKeys = shallowRef([]);
|
||||
const expandedKeys = shallowRef([]);
|
||||
|
||||
const loadingRetryTimes: Record<Key, number> = {};
|
||||
const dragState = reactive({
|
||||
dragging: false,
|
||||
draggingNodeKey: null,
|
||||
dragChildrenKeys: [],
|
||||
|
||||
// dropTargetKey is the key of abstract-drop-node
|
||||
|
@ -111,6 +118,8 @@ export default defineComponent({
|
|||
|
||||
let dragNode: DragNodeEvent = null;
|
||||
|
||||
let currentMouseOverDroppableNodeKey = null;
|
||||
|
||||
const treeNodeRequiredProps = computed(() => {
|
||||
return {
|
||||
expandedKeys: expandedKeys.value || [],
|
||||
|
@ -219,6 +228,17 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
|
||||
const resetDragState = () => {
|
||||
Object.assign(dragState, {
|
||||
dragOverNodeKey: null,
|
||||
dropPosition: null,
|
||||
dropLevelOffset: null,
|
||||
dropTargetKey: null,
|
||||
dropContainerKey: null,
|
||||
dropTargetPos: null,
|
||||
dropAllowed: false,
|
||||
});
|
||||
};
|
||||
const scrollTo: ScrollTo = scroll => {
|
||||
listRef.value.scrollTo(scroll);
|
||||
};
|
||||
|
@ -231,9 +251,9 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const cleanDragState = () => {
|
||||
if (dragState.dragging) {
|
||||
if (dragState.draggingNodeKey !== null) {
|
||||
Object.assign(dragState, {
|
||||
dragging: false,
|
||||
draggingNodeKey: null,
|
||||
dropPosition: null,
|
||||
dropContainerKey: null,
|
||||
dropTargetKey: null,
|
||||
|
@ -243,6 +263,7 @@ export default defineComponent({
|
|||
});
|
||||
}
|
||||
dragStartMousePosition = null;
|
||||
currentMouseOverDroppableNodeKey = null;
|
||||
};
|
||||
// if onNodeDragEnd is called, onWindowDragEnd won't be called since stopPropagation() is called
|
||||
const onNodeDragEnd: NodeDragEventHandler = (event, node, outsideTree = false) => {
|
||||
|
@ -277,7 +298,7 @@ export default defineComponent({
|
|||
|
||||
const newExpandedKeys = arrDel(expandedKeys.value, eventKey);
|
||||
|
||||
dragState.dragging = true;
|
||||
dragState.draggingNodeKey = eventKey;
|
||||
dragState.dragChildrenKeys = getDragChildrenKeys(eventKey, keyEntities.value);
|
||||
indent.value = listRef.value.getIndentWidth();
|
||||
|
||||
|
@ -296,9 +317,18 @@ 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: DragNodeEvent) => {
|
||||
const onNodeDragEnter = (event: DragEvent, node: DragNodeEvent) => {
|
||||
const { onDragenter, onExpand, allowDrop, direction } = props;
|
||||
const { pos, eventKey } = node;
|
||||
// record the key of node which is latest entered, used in dragleave event.
|
||||
if (currentMouseOverDroppableNodeKey !== eventKey) {
|
||||
currentMouseOverDroppableNodeKey = eventKey;
|
||||
}
|
||||
|
||||
if (!dragNode) {
|
||||
resetDragState();
|
||||
return;
|
||||
}
|
||||
const {
|
||||
dropPosition,
|
||||
dropLevelOffset,
|
||||
|
@ -321,21 +351,12 @@ export default defineComponent({
|
|||
);
|
||||
|
||||
if (
|
||||
!dragNode ||
|
||||
// don't allow drop inside its children
|
||||
dragState.dragChildrenKeys.indexOf(dropTargetKey) !== -1 ||
|
||||
// don't allow drop when drop is not allowed caculated by calcDropPosition
|
||||
!dropAllowed
|
||||
) {
|
||||
Object.assign(dragState, {
|
||||
dragOverNodeKey: null,
|
||||
dropPosition: null,
|
||||
dropLevelOffset: null,
|
||||
dropTargetKey: null,
|
||||
dropContainerKey: null,
|
||||
dropTargetPos: null,
|
||||
dropAllowed: false,
|
||||
});
|
||||
resetDragState();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -352,8 +373,8 @@ export default defineComponent({
|
|||
// 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
|
||||
delayedDragEnterLogic[node.pos] = window.setTimeout(() => {
|
||||
if (!dragState.dragging) return;
|
||||
delayedDragEnterLogic[pos] = window.setTimeout(() => {
|
||||
if (dragState.draggingNodeKey === null) return;
|
||||
|
||||
let newExpandedKeys = [...expandedKeys.value];
|
||||
const entity = keyEntities.value[node.eventKey];
|
||||
|
@ -375,15 +396,7 @@ export default defineComponent({
|
|||
|
||||
// Skip if drag node is self
|
||||
if (dragNode.eventKey === dropTargetKey && dropLevelOffset === 0) {
|
||||
Object.assign(dragState, {
|
||||
dragOverNodeKey: null,
|
||||
dropPosition: null,
|
||||
dropLevelOffset: null,
|
||||
dropTargetKey: null,
|
||||
dropContainerKey: null,
|
||||
dropTargetPos: null,
|
||||
dropAllowed: false,
|
||||
});
|
||||
resetDragState();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -407,9 +420,12 @@ export default defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
const onNodeDragOver = (event: MouseEvent, node: DragNodeEvent) => {
|
||||
const onNodeDragOver = (event: DragEvent, node: DragNodeEvent) => {
|
||||
const { onDragover, allowDrop, direction } = props;
|
||||
|
||||
if (!dragNode) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
dropPosition,
|
||||
dropLevelOffset,
|
||||
|
@ -431,7 +447,7 @@ export default defineComponent({
|
|||
direction,
|
||||
);
|
||||
|
||||
if (!dragNode || dragState.dragChildrenKeys.indexOf(dropTargetKey) !== -1 || !dropAllowed) {
|
||||
if (dragState.dragChildrenKeys.indexOf(dropTargetKey) !== -1 || !dropAllowed) {
|
||||
// don't allow drop inside its children
|
||||
// don't allow drop when drop is not allowed caculated by calcDropPosition
|
||||
return;
|
||||
|
@ -451,15 +467,7 @@ export default defineComponent({
|
|||
dragState.dragOverNodeKey === null
|
||||
)
|
||||
) {
|
||||
Object.assign(dragState, {
|
||||
dropPosition: null,
|
||||
dropLevelOffset: null,
|
||||
dropTargetKey: null,
|
||||
dropContainerKey: null,
|
||||
dropTargetPos: null,
|
||||
dropAllowed: false,
|
||||
dragOverNodeKey: null,
|
||||
});
|
||||
resetDragState();
|
||||
}
|
||||
} else if (
|
||||
!(
|
||||
|
@ -489,13 +497,23 @@ export default defineComponent({
|
|||
};
|
||||
|
||||
const onNodeDragLeave: NodeDragEventHandler = (event, node) => {
|
||||
// if it is outside the droppable area
|
||||
// currentMouseOverDroppableNodeKey will be updated in dragenter event when into another droppable receiver.
|
||||
if (
|
||||
currentMouseOverDroppableNodeKey === node.eventKey &&
|
||||
!(event.currentTarget as any).contains(event.relatedTarget as Node)
|
||||
) {
|
||||
resetDragState();
|
||||
currentMouseOverDroppableNodeKey = null;
|
||||
}
|
||||
|
||||
const { onDragleave } = props;
|
||||
|
||||
if (onDragleave) {
|
||||
onDragleave({ event, node: node.eventData });
|
||||
}
|
||||
};
|
||||
const onNodeDrop = (event: MouseEvent, _node, outsideTree = false) => {
|
||||
const onNodeDrop = (event: DragEvent, _node, outsideTree = false) => {
|
||||
const { dragChildrenKeys, dropPosition, dropTargetKey, dropTargetPos, dropAllowed } =
|
||||
dragState;
|
||||
|
||||
|
@ -666,11 +684,11 @@ export default defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
const onNodeLoad = (treeNode: EventDataNode) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const onNodeLoad = (treeNode: EventDataNode) => {
|
||||
const key = treeNode[fieldNames.value.key];
|
||||
const loadPromise = new Promise<void>((resolve, reject) => {
|
||||
// We need to get the latest state of loading/loaded keys
|
||||
const { loadData, onLoad } = props;
|
||||
const key = treeNode[fieldNames.value.key];
|
||||
|
||||
if (
|
||||
!loadData ||
|
||||
|
@ -705,12 +723,28 @@ export default defineComponent({
|
|||
.catch(e => {
|
||||
const newLoadingKeys = arrDel(loadingKeys.value, key);
|
||||
loadingKeys.value = newLoadingKeys;
|
||||
|
||||
// If exceed max retry times, we give up retry
|
||||
loadingRetryTimes[key] = (loadingRetryTimes[key] || 0) + 1;
|
||||
if (loadingRetryTimes[key] >= MAX_RETRY_TIMES) {
|
||||
warning(false, 'Retry for `loadData` many times but still failed. No more retry.');
|
||||
const newLoadedKeys = arrAdd(loadedKeys.value, key);
|
||||
if (props.loadedKeys === undefined) {
|
||||
loadedKeys.value = newLoadedKeys;
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
|
||||
reject(e);
|
||||
});
|
||||
|
||||
loadingKeys.value = arrAdd(loadingKeys.value, key);
|
||||
});
|
||||
// Not care warning if we ignore this
|
||||
loadPromise.catch(() => {});
|
||||
|
||||
return loadPromise;
|
||||
};
|
||||
const onNodeMouseEnter: NodeMouseEventHandler = (event, node) => {
|
||||
const { onMouseenter } = props;
|
||||
if (onMouseenter) {
|
||||
|
@ -968,7 +1002,7 @@ export default defineComponent({
|
|||
// focused,
|
||||
// flattenNodes,
|
||||
// keyEntities,
|
||||
dragging,
|
||||
draggingNodeKey,
|
||||
// activeKey,
|
||||
dropLevelOffset,
|
||||
dropContainerKey,
|
||||
|
@ -1003,7 +1037,27 @@ export default defineComponent({
|
|||
} = props;
|
||||
|
||||
const { class: className, style } = attrs;
|
||||
const domProps = getDataAndAria({ ...props, ...attrs });
|
||||
const domProps = pickAttrs(
|
||||
{ ...props, ...attrs },
|
||||
{
|
||||
aria: true,
|
||||
data: true,
|
||||
},
|
||||
);
|
||||
|
||||
// It's better move to hooks but we just simply keep here
|
||||
let draggableConfig: DraggableConfig;
|
||||
if (draggable) {
|
||||
if (typeof draggable === 'object') {
|
||||
draggableConfig = draggable;
|
||||
} else if (typeof draggable === 'function') {
|
||||
draggableConfig = {
|
||||
nodeDraggable: draggable,
|
||||
};
|
||||
} else {
|
||||
draggableConfig = {};
|
||||
}
|
||||
}
|
||||
return (
|
||||
<TreeContext
|
||||
value={{
|
||||
|
@ -1012,7 +1066,8 @@ export default defineComponent({
|
|||
showIcon,
|
||||
icon,
|
||||
switcherIcon,
|
||||
draggable,
|
||||
draggable: draggableConfig,
|
||||
draggingNodeKey,
|
||||
checkable,
|
||||
customCheckable: slots.checkable,
|
||||
checkStrictly,
|
||||
|
@ -1065,7 +1120,7 @@ export default defineComponent({
|
|||
selectable={selectable}
|
||||
checkable={!!checkable}
|
||||
motion={motion}
|
||||
dragging={dragging}
|
||||
dragging={draggingNodeKey !== null}
|
||||
height={height}
|
||||
itemHeight={itemHeight}
|
||||
virtual={virtual}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useInjectTreeContext } from './contextTypes';
|
||||
import { getDataAndAria } from './util';
|
||||
import Indent from './Indent';
|
||||
import { convertNodePropsToEventData } from './utils/treeUtil';
|
||||
import {
|
||||
|
@ -16,6 +15,7 @@ import classNames from '../_util/classNames';
|
|||
import { warning } from '../vc-util/warning';
|
||||
import type { DragNodeEvent, Key } from './interface';
|
||||
import pick from 'lodash-es/pick';
|
||||
import pickAttrs from '../_util/pickAttrs';
|
||||
|
||||
const ICON_OPEN = 'open';
|
||||
const ICON_CLOSE = 'close';
|
||||
|
@ -248,6 +248,20 @@ export default defineComponent({
|
|||
onNodeExpand(e, eventData.value);
|
||||
};
|
||||
|
||||
const isDraggable = () => {
|
||||
const { data } = props;
|
||||
const { draggable } = context.value;
|
||||
return !!(draggable && (!draggable.nodeDraggable || draggable.nodeDraggable(data)));
|
||||
};
|
||||
|
||||
// ==================== Render: Drag Handler ====================
|
||||
const renderDragHandler = () => {
|
||||
const { draggable, prefixCls } = context.value;
|
||||
return draggable?.icon ? (
|
||||
<span class={`${prefixCls}-draggable-icon`}>{draggable.icon}</span>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const renderSwitcherIconDom = () => {
|
||||
const {
|
||||
switcherIcon: switcherIconFromProps = slots.switcherIcon ||
|
||||
|
@ -368,9 +382,9 @@ export default defineComponent({
|
|||
dragOverNodeKey,
|
||||
direction,
|
||||
} = context.value;
|
||||
const mergedDraggable = draggable !== false;
|
||||
const rootDraggable = draggable !== false;
|
||||
// allowDrop is calculated in Tree.tsx, there is no need for calc it here
|
||||
const showIndicator = !disabled && mergedDraggable && dragOverNodeKey === eventKey;
|
||||
const showIndicator = !disabled && rootDraggable && dragOverNodeKey === eventKey;
|
||||
return showIndicator
|
||||
? dropIndicatorRender({ dropPosition, dropLevelOffset, indent, prefixCls, direction })
|
||||
: null;
|
||||
|
@ -396,12 +410,10 @@ export default defineComponent({
|
|||
prefixCls,
|
||||
showIcon,
|
||||
icon: treeIcon,
|
||||
draggable,
|
||||
loadData,
|
||||
// slots: contextSlots,
|
||||
} = context.value;
|
||||
const disabled = isDisabled.value;
|
||||
const mergedDraggable = typeof draggable === 'function' ? draggable(data) : draggable;
|
||||
|
||||
const wrapClass = `${prefixCls}-node-content-wrapper`;
|
||||
|
||||
|
@ -443,16 +455,12 @@ export default defineComponent({
|
|||
`${wrapClass}`,
|
||||
`${wrapClass}-${nodeState.value || 'normal'}`,
|
||||
!disabled && (selected || dragNodeHighlight.value) && `${prefixCls}-node-selected`,
|
||||
!disabled && mergedDraggable && 'draggable',
|
||||
)}
|
||||
draggable={(!disabled && mergedDraggable) || undefined}
|
||||
aria-grabbed={(!disabled && mergedDraggable) || undefined}
|
||||
onMouseenter={onMouseEnter}
|
||||
onMouseleave={onMouseLeave}
|
||||
onContextmenu={onContextmenu}
|
||||
onClick={onSelectorClick}
|
||||
onDblclick={onSelectorDoubleClick}
|
||||
onDragstart={mergedDraggable ? onDragStart : undefined}
|
||||
>
|
||||
{$icon}
|
||||
{$title}
|
||||
|
@ -478,15 +486,28 @@ export default defineComponent({
|
|||
active,
|
||||
data,
|
||||
onMousemove,
|
||||
selectable,
|
||||
...otherProps
|
||||
} = { ...props, ...attrs };
|
||||
const { prefixCls, filterTreeNode, draggable, keyEntities, dropContainerKey, dropTargetKey } =
|
||||
context.value;
|
||||
const {
|
||||
prefixCls,
|
||||
filterTreeNode,
|
||||
keyEntities,
|
||||
dropContainerKey,
|
||||
dropTargetKey,
|
||||
draggingNodeKey,
|
||||
} = context.value;
|
||||
const disabled = isDisabled.value;
|
||||
const dataOrAriaAttributeProps = getDataAndAria(otherProps);
|
||||
const dataOrAriaAttributeProps = pickAttrs(otherProps, { aria: true, data: true });
|
||||
const { level } = keyEntities[eventKey] || {};
|
||||
const isEndNode = isEnd[isEnd.length - 1];
|
||||
const mergedDraggable = typeof draggable === 'function' ? draggable(data) : draggable;
|
||||
|
||||
const mergedDraggable = isDraggable();
|
||||
const draggableWithoutDisabled = !disabled && mergedDraggable;
|
||||
|
||||
const dragging = draggingNodeKey === eventKey;
|
||||
const ariaSelected = selectable !== undefined ? { 'aria-selected': !!selectable } : undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={domRef}
|
||||
|
@ -499,7 +520,9 @@ export default defineComponent({
|
|||
[`${prefixCls}-treenode-loading`]: loading,
|
||||
[`${prefixCls}-treenode-active`]: active,
|
||||
[`${prefixCls}-treenode-leaf-last`]: isEndNode,
|
||||
[`${prefixCls}-treenode-draggable`]: draggableWithoutDisabled,
|
||||
|
||||
dragging,
|
||||
'drop-target': dropTargetKey === eventKey,
|
||||
'drop-container': dropContainerKey === eventKey,
|
||||
'drag-over': !disabled && dragOver,
|
||||
|
@ -508,15 +531,22 @@ export default defineComponent({
|
|||
'filter-node': filterTreeNode && filterTreeNode(eventData.value),
|
||||
})}
|
||||
style={attrs.style}
|
||||
// Draggable config
|
||||
draggable={draggableWithoutDisabled}
|
||||
aria-grabbed={dragging}
|
||||
onDragstart={draggableWithoutDisabled ? onDragStart : undefined}
|
||||
// Drop config
|
||||
onDragenter={mergedDraggable ? onDragEnter : undefined}
|
||||
onDragover={mergedDraggable ? onDragOver : undefined}
|
||||
onDragleave={mergedDraggable ? onDragLeave : undefined}
|
||||
onDrop={mergedDraggable ? onDrop : undefined}
|
||||
onDragend={mergedDraggable ? onDragEnd : undefined}
|
||||
onMousemove={onMousemove}
|
||||
{...ariaSelected}
|
||||
{...dataOrAriaAttributeProps}
|
||||
>
|
||||
<Indent prefixCls={prefixCls} level={level} isStart={isStart} isEnd={isEnd} />
|
||||
{renderDragHandler()}
|
||||
{renderSwitcher()}
|
||||
{renderCheckbox()}
|
||||
{renderSelector()}
|
||||
|
|
|
@ -12,22 +12,23 @@ import type {
|
|||
DataEntity,
|
||||
EventDataNode,
|
||||
DragNodeEvent,
|
||||
DataNode,
|
||||
Direction,
|
||||
} from './interface';
|
||||
|
||||
import type { DraggableConfig } from './Tree';
|
||||
|
||||
export type NodeMouseEventParams = {
|
||||
event: MouseEvent;
|
||||
node: EventDataNode;
|
||||
};
|
||||
export type NodeDragEventParams = {
|
||||
event: MouseEvent;
|
||||
event: DragEvent;
|
||||
node: EventDataNode;
|
||||
};
|
||||
|
||||
export type NodeMouseEventHandler = (e: MouseEvent, node: EventDataNode) => void;
|
||||
export type NodeDragEventHandler = (
|
||||
e: MouseEvent,
|
||||
e: DragEvent,
|
||||
node: DragNodeEvent,
|
||||
outsideTree?: boolean,
|
||||
) => void;
|
||||
|
@ -38,12 +39,13 @@ export interface TreeContextProps {
|
|||
showIcon: boolean;
|
||||
icon: IconType;
|
||||
switcherIcon: IconType;
|
||||
draggable: ((node: DataNode) => boolean) | boolean;
|
||||
draggable: DraggableConfig;
|
||||
draggingNodeKey?: Key;
|
||||
checkable: boolean;
|
||||
customCheckable: () => any;
|
||||
checkStrictly: boolean;
|
||||
disabled: boolean;
|
||||
keyEntities: Record<Key, DataEntity>;
|
||||
keyEntities: Record<Key, DataEntity<any>>;
|
||||
// for details see comment in Tree.state (Tree.tsx)
|
||||
dropLevelOffset?: number;
|
||||
dropContainerKey: Key | null;
|
||||
|
@ -79,8 +81,8 @@ export interface TreeContextProps {
|
|||
onNodeDragEnd: NodeDragEventHandler;
|
||||
onNodeDrop: NodeDragEventHandler;
|
||||
slots: {
|
||||
title?: (data: DataNode) => any;
|
||||
titleRender?: (data: DataNode) => any;
|
||||
title?: (data: any) => any;
|
||||
titleRender?: (data: any) => any;
|
||||
[key: string]: ((...args: any[]) => any) | undefined;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// base rc-tree 5.0.1
|
||||
// base rc-tree 5.3.7
|
||||
import type { TreeProps, TreeNodeProps } from './props';
|
||||
import Tree from './Tree';
|
||||
import TreeNode from './TreeNode';
|
||||
import type { BasicDataNode } from './interface';
|
||||
export { TreeNode };
|
||||
export type { TreeProps, TreeNodeProps };
|
||||
export type { TreeProps, TreeNodeProps, BasicDataNode };
|
||||
export default Tree;
|
||||
|
|
|
@ -2,15 +2,13 @@ import type { CSSProperties, VNode } from 'vue';
|
|||
import type { TreeNodeProps } from './props';
|
||||
export type { ScrollTo } from '../vc-virtual-list/List';
|
||||
|
||||
export interface DataNode {
|
||||
/** For fieldNames, we provides a abstract interface */
|
||||
export interface BasicDataNode {
|
||||
checkable?: boolean;
|
||||
children?: DataNode[];
|
||||
disabled?: boolean;
|
||||
disableCheckbox?: boolean;
|
||||
icon?: IconType;
|
||||
isLeaf?: boolean;
|
||||
key: string | number;
|
||||
title?: any;
|
||||
selectable?: boolean;
|
||||
switcherIcon?: IconType;
|
||||
|
||||
|
@ -21,6 +19,12 @@ export interface DataNode {
|
|||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface DataNode extends BasicDataNode {
|
||||
children?: DataNode[];
|
||||
key: string | number;
|
||||
title?: any;
|
||||
}
|
||||
|
||||
export interface EventDataNode extends DataNode {
|
||||
expanded?: boolean;
|
||||
selected?: boolean;
|
||||
|
@ -60,10 +64,12 @@ export interface Entity {
|
|||
children?: Entity[];
|
||||
}
|
||||
|
||||
export interface DataEntity extends Omit<Entity, 'node' | 'parent' | 'children'> {
|
||||
node: DataNode;
|
||||
parent?: DataEntity;
|
||||
children?: DataEntity[];
|
||||
export interface DataEntity<TreeDataType extends BasicDataNode = DataNode>
|
||||
extends Omit<Entity, 'node' | 'parent' | 'children'> {
|
||||
node: TreeDataType;
|
||||
nodes: TreeDataType[];
|
||||
parent?: DataEntity<TreeDataType>;
|
||||
children?: DataEntity<TreeDataType>[];
|
||||
level: number;
|
||||
}
|
||||
|
||||
|
@ -86,6 +92,8 @@ export type Direction = 'ltr' | 'rtl' | undefined;
|
|||
|
||||
export interface FieldNames {
|
||||
title?: string;
|
||||
/** @private Internal usage for `vc-tree-select`, safe to remove if no need */
|
||||
_title?: string[];
|
||||
key?: string;
|
||||
children?: string;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import type { BasicDataNode } from '.';
|
||||
import type { EventHandler } from '../_util/EventInterface';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import type {
|
||||
|
@ -10,10 +11,10 @@ import type {
|
|||
DataNode,
|
||||
Key,
|
||||
FlattenNode,
|
||||
DataEntity,
|
||||
EventDataNode,
|
||||
Direction,
|
||||
FieldNames,
|
||||
DataEntity,
|
||||
} from './interface';
|
||||
|
||||
export interface CheckInfo {
|
||||
|
@ -83,7 +84,7 @@ export const nodeListProps = {
|
|||
loadedKeys: { type: Array as PropType<Key[]> },
|
||||
loadingKeys: { type: Array as PropType<Key[]> },
|
||||
halfCheckedKeys: { type: Array as PropType<Key[]> },
|
||||
keyEntities: { type: Object as PropType<Record<Key, DataEntity>> },
|
||||
keyEntities: { type: Object as PropType<Record<Key, DataEntity<DataNode>>> },
|
||||
|
||||
dragging: { type: Boolean as PropType<boolean> },
|
||||
dragOverNodeKey: { type: [String, Number] as PropType<Key> },
|
||||
|
@ -106,8 +107,17 @@ export const nodeListProps = {
|
|||
};
|
||||
|
||||
export type NodeListProps = Partial<ExtractPropTypes<typeof nodeListProps>>;
|
||||
export type AllowDrop = (options: { dropNode: DataNode; dropPosition: -1 | 0 | 1 }) => boolean;
|
||||
|
||||
export interface AllowDropOptions<TreeDataType extends BasicDataNode = DataNode> {
|
||||
dragNode: EventDataNode;
|
||||
dropNode: TreeDataType;
|
||||
dropPosition: -1 | 0 | 1;
|
||||
}
|
||||
export type AllowDrop<TreeDataType extends BasicDataNode = DataNode> = (
|
||||
options: AllowDropOptions<TreeDataType>,
|
||||
) => boolean;
|
||||
|
||||
export type DraggableFn = (node: DataNode) => boolean;
|
||||
export const treeProps = () => ({
|
||||
prefixCls: String,
|
||||
focusable: { type: Boolean, default: undefined },
|
||||
|
@ -123,7 +133,7 @@ export const treeProps = () => ({
|
|||
multiple: { type: Boolean, default: undefined },
|
||||
checkable: { type: Boolean, default: undefined },
|
||||
checkStrictly: { type: Boolean, default: undefined },
|
||||
draggable: { type: [Function, Boolean] as PropType<((node: DataNode) => boolean) | boolean> },
|
||||
draggable: { type: [Function, Boolean] as PropType<DraggableFn | boolean> },
|
||||
defaultExpandParent: { type: Boolean, default: undefined },
|
||||
autoExpandParent: { type: Boolean, default: undefined },
|
||||
defaultExpandAll: { type: Boolean, default: undefined },
|
||||
|
|
|
@ -12,11 +12,13 @@ import type {
|
|||
FlattenNode,
|
||||
Direction,
|
||||
DragNodeEvent,
|
||||
BasicDataNode,
|
||||
} from './interface';
|
||||
import { warning } from '../vc-util/warning';
|
||||
import type { AllowDrop, TreeNodeProps, TreeProps } from './props';
|
||||
|
||||
export function arrDel(list: Key[], value: Key) {
|
||||
if (!list) return [];
|
||||
const clone = list.slice();
|
||||
const index = clone.indexOf(value);
|
||||
if (index >= 0) {
|
||||
|
@ -26,7 +28,7 @@ export function arrDel(list: Key[], value: Key) {
|
|||
}
|
||||
|
||||
export function arrAdd(list: Key[], value: Key) {
|
||||
const clone = list.slice();
|
||||
const clone = (list || []).slice();
|
||||
if (clone.indexOf(value) === -1) {
|
||||
clone.push(value);
|
||||
}
|
||||
|
@ -45,13 +47,16 @@ export function isTreeNode(node: NodeElement) {
|
|||
return node && node.type && (node.type as any).isTreeNode;
|
||||
}
|
||||
|
||||
export function getDragChildrenKeys(dragNodeKey: Key, keyEntities: Record<Key, DataEntity>): Key[] {
|
||||
export function getDragChildrenKeys<TreeDataType extends BasicDataNode = DataNode>(
|
||||
dragNodeKey: Key,
|
||||
keyEntities: Record<Key, DataEntity<TreeDataType>>,
|
||||
): Key[] {
|
||||
// not contains self
|
||||
// self for left or right drag
|
||||
const dragChildrenKeys = [];
|
||||
|
||||
const entity = keyEntities[dragNodeKey];
|
||||
function dig(list: DataEntity[] = []) {
|
||||
function dig(list: DataEntity<TreeDataType>[] = []) {
|
||||
list.forEach(({ key, children }) => {
|
||||
dragChildrenKeys.push(key);
|
||||
dig(children);
|
||||
|
@ -63,7 +68,9 @@ export function getDragChildrenKeys(dragNodeKey: Key, keyEntities: Record<Key, D
|
|||
return dragChildrenKeys;
|
||||
}
|
||||
|
||||
export function isLastChild(treeNodeEntity: DataEntity) {
|
||||
export function isLastChild<TreeDataType extends BasicDataNode = DataNode>(
|
||||
treeNodeEntity: DataEntity<TreeDataType>,
|
||||
) {
|
||||
if (treeNodeEntity.parent) {
|
||||
const posArr = posToArr(treeNodeEntity.pos);
|
||||
return Number(posArr[posArr.length - 1]) === treeNodeEntity.parent.children.length - 1;
|
||||
|
@ -71,24 +78,26 @@ export function isLastChild(treeNodeEntity: DataEntity) {
|
|||
return false;
|
||||
}
|
||||
|
||||
export function isFirstChild(treeNodeEntity: DataEntity) {
|
||||
export function isFirstChild<TreeDataType extends BasicDataNode = DataNode>(
|
||||
treeNodeEntity: DataEntity<TreeDataType>,
|
||||
) {
|
||||
const posArr = posToArr(treeNodeEntity.pos);
|
||||
return Number(posArr[posArr.length - 1]) === 0;
|
||||
}
|
||||
|
||||
// Only used when drag, not affect SSR.
|
||||
export function calcDropPosition(
|
||||
export function calcDropPosition<TreeDataType extends BasicDataNode = DataNode>(
|
||||
event: MouseEvent,
|
||||
_dragNode: DragNodeEvent,
|
||||
dragNode: DragNodeEvent,
|
||||
targetNode: DragNodeEvent,
|
||||
indent: number,
|
||||
startMousePosition: {
|
||||
x: number;
|
||||
y: number;
|
||||
},
|
||||
allowDrop: AllowDrop,
|
||||
allowDrop: AllowDrop<TreeDataType>,
|
||||
flattenedNodes: FlattenNode[],
|
||||
keyEntities: Record<Key, DataEntity>,
|
||||
keyEntities: Record<Key, DataEntity<TreeDataType>>,
|
||||
expandKeys: Key[],
|
||||
direction: Direction,
|
||||
): {
|
||||
|
@ -108,7 +117,7 @@ export function calcDropPosition(
|
|||
const rawDropLevelOffset = (horizontalMouseOffset - 12) / indent;
|
||||
|
||||
// find abstract drop node by horizontal offset
|
||||
let abstractDropNodeEntity: DataEntity = keyEntities[targetNode.eventKey];
|
||||
let abstractDropNodeEntity: DataEntity<TreeDataType> = keyEntities[targetNode.eventKey];
|
||||
|
||||
if (clientY < top + height / 2) {
|
||||
// first half, set abstract drop node to previous node
|
||||
|
@ -139,7 +148,7 @@ export function calcDropPosition(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const abstractDragDataNode = dragNode.eventData;
|
||||
const abstractDropDataNode = abstractDropNodeEntity.node;
|
||||
let dropAllowed = true;
|
||||
if (
|
||||
|
@ -147,6 +156,7 @@ export function calcDropPosition(
|
|||
abstractDropNodeEntity.level === 0 &&
|
||||
clientY < top + height / 2 &&
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: -1,
|
||||
}) &&
|
||||
|
@ -162,6 +172,7 @@ export function calcDropPosition(
|
|||
// only allow drop inside
|
||||
if (
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: 0,
|
||||
})
|
||||
|
@ -178,6 +189,7 @@ export function calcDropPosition(
|
|||
// 2. do not allow drop
|
||||
if (
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: 1,
|
||||
})
|
||||
|
@ -196,6 +208,7 @@ export function calcDropPosition(
|
|||
// 3. do not allow drop
|
||||
if (
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: 0,
|
||||
})
|
||||
|
@ -203,6 +216,7 @@ export function calcDropPosition(
|
|||
dropPosition = 0;
|
||||
} else if (
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: 1,
|
||||
})
|
||||
|
@ -220,6 +234,7 @@ export function calcDropPosition(
|
|||
// 2. do not allow drop
|
||||
if (
|
||||
allowDrop({
|
||||
dragNode: abstractDragDataNode,
|
||||
dropNode: abstractDropDataNode,
|
||||
dropPosition: 1,
|
||||
})
|
||||
|
@ -336,17 +351,3 @@ export function conductExpandParent(keyList: Key[], keyEntities: Record<Key, Dat
|
|||
|
||||
return [...expandedKeys];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the data- and aria- key/value pairs
|
||||
*/
|
||||
export function getDataAndAria(props: Partial<TreeProps | TreeNodeProps>) {
|
||||
const omitProps: Record<string, string> = {};
|
||||
Object.keys(props).forEach(key => {
|
||||
if (key.startsWith('data-') || key.startsWith('aria-')) {
|
||||
omitProps[key] = props[key];
|
||||
}
|
||||
});
|
||||
|
||||
return omitProps;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { warning } from '../../vc-util/warning';
|
||||
import type { Key, DataEntity, DataNode, GetCheckDisabled } from '../interface';
|
||||
import type { Key, DataEntity, DataNode, GetCheckDisabled, BasicDataNode } from '../interface';
|
||||
|
||||
interface ConductReturnType {
|
||||
checkedKeys: Key[];
|
||||
|
@ -16,17 +16,17 @@ function removeFromCheckedKeys(halfCheckedKeys: Set<Key>, checkedKeys: Set<Key>)
|
|||
return filteredKeys;
|
||||
}
|
||||
|
||||
export function isCheckDisabled(node: DataNode) {
|
||||
export function isCheckDisabled<TreeDataType>(node: TreeDataType) {
|
||||
const { disabled, disableCheckbox, checkable } = (node || {}) as DataNode;
|
||||
return !!(disabled || disableCheckbox) || checkable === false;
|
||||
}
|
||||
|
||||
// Fill miss keys
|
||||
function fillConductCheck(
|
||||
function fillConductCheck<TreeDataType extends BasicDataNode = DataNode>(
|
||||
keys: Set<Key>,
|
||||
levelEntities: Map<number, Set<DataEntity>>,
|
||||
levelEntities: Map<number, Set<DataEntity<TreeDataType>>>,
|
||||
maxLevel: number,
|
||||
syntheticGetCheckDisabled: GetCheckDisabled<DataNode>,
|
||||
syntheticGetCheckDisabled: GetCheckDisabled<TreeDataType>,
|
||||
): ConductReturnType {
|
||||
const checkedKeys = new Set<Key>(keys);
|
||||
const halfCheckedKeys = new Set<Key>();
|
||||
|
@ -98,12 +98,12 @@ function fillConductCheck(
|
|||
}
|
||||
|
||||
// Remove useless key
|
||||
function cleanConductCheck(
|
||||
function cleanConductCheck<TreeDataType extends BasicDataNode = DataNode>(
|
||||
keys: Set<Key>,
|
||||
halfKeys: Key[],
|
||||
levelEntities: Map<number, Set<DataEntity>>,
|
||||
levelEntities: Map<number, Set<DataEntity<TreeDataType>>>,
|
||||
maxLevel: number,
|
||||
syntheticGetCheckDisabled: GetCheckDisabled<DataNode>,
|
||||
syntheticGetCheckDisabled: GetCheckDisabled<TreeDataType>,
|
||||
): ConductReturnType {
|
||||
const checkedKeys = new Set<Key>(keys);
|
||||
let halfCheckedKeys = new Set<Key>(halfKeys);
|
||||
|
@ -182,15 +182,15 @@ function cleanConductCheck(
|
|||
* @param keyEntities key - dataEntity map
|
||||
* @param mode `fill` to fill missing key, `clean` to remove useless key
|
||||
*/
|
||||
export function conductCheck(
|
||||
export function conductCheck<TreeDataType extends BasicDataNode = DataNode>(
|
||||
keyList: Key[],
|
||||
checked: true | { checked: false; halfCheckedKeys: Key[] },
|
||||
keyEntities: Record<Key, DataEntity>,
|
||||
getCheckDisabled?: GetCheckDisabled<DataNode>,
|
||||
keyEntities: Record<Key, DataEntity<TreeDataType>>,
|
||||
getCheckDisabled?: GetCheckDisabled<TreeDataType>,
|
||||
): ConductReturnType {
|
||||
const warningMissKeys: Key[] = [];
|
||||
|
||||
let syntheticGetCheckDisabled: GetCheckDisabled<DataNode>;
|
||||
let syntheticGetCheckDisabled: GetCheckDisabled<TreeDataType>;
|
||||
if (getCheckDisabled) {
|
||||
syntheticGetCheckDisabled = getCheckDisabled;
|
||||
} else {
|
||||
|
@ -208,7 +208,7 @@ export function conductCheck(
|
|||
return hasEntity;
|
||||
}),
|
||||
);
|
||||
const levelEntities = new Map<number, Set<DataEntity>>();
|
||||
const levelEntities = new Map<number, Set<DataEntity<TreeDataType>>>();
|
||||
let maxLevel = 0;
|
||||
|
||||
// Convert entities by level for calculation
|
||||
|
@ -216,7 +216,7 @@ export function conductCheck(
|
|||
const entity = keyEntities[key];
|
||||
const { level } = entity;
|
||||
|
||||
let levelSet: Set<DataEntity> = levelEntities.get(level);
|
||||
let levelSet: Set<DataEntity<TreeDataType>> = levelEntities.get(level);
|
||||
if (!levelSet) {
|
||||
levelSet = new Set();
|
||||
levelEntities.set(level, levelSet);
|
||||
|
@ -237,7 +237,12 @@ export function conductCheck(
|
|||
|
||||
let result: ConductReturnType;
|
||||
if (checked === true) {
|
||||
result = fillConductCheck(keys, levelEntities, maxLevel, syntheticGetCheckDisabled);
|
||||
result = fillConductCheck<TreeDataType>(
|
||||
keys,
|
||||
levelEntities,
|
||||
maxLevel,
|
||||
syntheticGetCheckDisabled,
|
||||
);
|
||||
} else {
|
||||
result = cleanConductCheck(
|
||||
keys,
|
||||
|
|
|
@ -7,6 +7,7 @@ import type {
|
|||
EventDataNode,
|
||||
GetKey,
|
||||
FieldNames,
|
||||
BasicDataNode,
|
||||
} from '../interface';
|
||||
import { getPosition, isTreeNode } from '../util';
|
||||
import { warning } from '../../vc-util/warning';
|
||||
|
@ -23,11 +24,13 @@ export function getKey(key: Key, pos: string) {
|
|||
return pos;
|
||||
}
|
||||
|
||||
export function fillFieldNames(fieldNames?: FieldNames) {
|
||||
const { title, key, children } = fieldNames || {};
|
||||
export function fillFieldNames(fieldNames?: FieldNames): Required<FieldNames> {
|
||||
const { title, _title, key, children } = fieldNames || {};
|
||||
const mergedTitle = title || 'title';
|
||||
|
||||
return {
|
||||
title: title || 'title',
|
||||
title: mergedTitle,
|
||||
_title: _title || [mergedTitle],
|
||||
key: key || 'key',
|
||||
children: children || 'children',
|
||||
};
|
||||
|
@ -129,7 +132,11 @@ export function flattenTreeData(
|
|||
expandedKeys: Key[] | true,
|
||||
fieldNames: FieldNames,
|
||||
): FlattenNode[] {
|
||||
const { title: fieldTitle, key: fieldKey, children: fieldChildren } = fillFieldNames(fieldNames);
|
||||
const {
|
||||
_title: fieldTitles,
|
||||
key: fieldKey,
|
||||
children: fieldChildren,
|
||||
} = fillFieldNames(fieldNames);
|
||||
|
||||
const expandedKeySet = new Set(expandedKeys === true ? [] : expandedKeys);
|
||||
const flattenList: FlattenNode[] = [];
|
||||
|
@ -139,10 +146,19 @@ export function flattenTreeData(
|
|||
const pos: string = getPosition(parent ? parent.pos : '0', index);
|
||||
const mergedKey = getKey(treeNode[fieldKey], pos);
|
||||
|
||||
// Pick matched title in field title list
|
||||
let mergedTitle: any;
|
||||
for (let i = 0; i < fieldTitles.length; i += 1) {
|
||||
const fieldTitle = fieldTitles[i];
|
||||
if (treeNode[fieldTitle] !== undefined) {
|
||||
mergedTitle = treeNode[fieldTitle];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Add FlattenDataNode into list
|
||||
const flattenNode: FlattenNode = {
|
||||
...omit(treeNode, [fieldTitle, fieldKey, fieldChildren] as any),
|
||||
title: treeNode[fieldTitle],
|
||||
...omit(treeNode, [...fieldTitles, fieldKey, fieldChildren] as any),
|
||||
title: mergedTitle,
|
||||
key: mergedKey,
|
||||
parent,
|
||||
pos,
|
||||
|
@ -191,6 +207,7 @@ export function traverseDataNodes(
|
|||
key: Key;
|
||||
parentPos: string | number;
|
||||
level: number;
|
||||
nodes: DataNode[];
|
||||
}) => void,
|
||||
// To avoid too many params, let use config instead of origin param
|
||||
config?: TraverseDataNodesConfig | string,
|
||||
|
@ -227,9 +244,11 @@ export function traverseDataNodes(
|
|||
node: DataNode,
|
||||
index?: number,
|
||||
parent?: { node: DataNode; pos: string; level: number },
|
||||
pathNodes?: DataNode[],
|
||||
) {
|
||||
const children = node ? node[mergeChildrenPropName] : dataNodes;
|
||||
const pos = node ? getPosition(parent.pos, index) : '0';
|
||||
const connectNodes = node ? [...pathNodes, node] : [];
|
||||
|
||||
// Process node if is not root
|
||||
if (node) {
|
||||
|
@ -241,6 +260,7 @@ export function traverseDataNodes(
|
|||
key,
|
||||
parentPos: parent.node ? parent.pos : null,
|
||||
level: parent.level + 1,
|
||||
nodes: connectNodes,
|
||||
};
|
||||
|
||||
callback(data);
|
||||
|
@ -249,11 +269,16 @@ export function traverseDataNodes(
|
|||
// Process children node
|
||||
if (children) {
|
||||
children.forEach((subNode, subIndex) => {
|
||||
processNode(subNode, subIndex, {
|
||||
node,
|
||||
pos,
|
||||
level: parent ? parent.level + 1 : -1,
|
||||
});
|
||||
processNode(
|
||||
subNode,
|
||||
subIndex,
|
||||
{
|
||||
node,
|
||||
pos,
|
||||
level: parent ? parent.level + 1 : -1,
|
||||
},
|
||||
connectNodes,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -306,8 +331,8 @@ export function convertDataToEntities(
|
|||
traverseDataNodes(
|
||||
dataNodes,
|
||||
item => {
|
||||
const { node, index, pos, key, parentPos, level } = item;
|
||||
const entity: DataEntity = { node, index, key, pos, level };
|
||||
const { node, index, pos, key, parentPos, level, nodes } = item;
|
||||
const entity: DataEntity = { node, nodes, index, key, pos, level };
|
||||
|
||||
const mergedKey = getKey(key, pos);
|
||||
|
||||
|
@ -335,7 +360,7 @@ export function convertDataToEntities(
|
|||
return wrapper;
|
||||
}
|
||||
|
||||
export interface TreeNodeRequiredProps {
|
||||
export interface TreeNodeRequiredProps<TreeDataType extends BasicDataNode = DataNode> {
|
||||
expandedKeys: Key[];
|
||||
selectedKeys: Key[];
|
||||
loadedKeys: Key[];
|
||||
|
@ -344,13 +369,13 @@ export interface TreeNodeRequiredProps {
|
|||
halfCheckedKeys: Key[];
|
||||
dragOverNodeKey: Key;
|
||||
dropPosition: number;
|
||||
keyEntities: Record<Key, DataEntity>;
|
||||
keyEntities: Record<Key, DataEntity<TreeDataType>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TreeNode props with Tree props.
|
||||
*/
|
||||
export function getTreeNodeProps(
|
||||
export function getTreeNodeProps<TreeDataType extends BasicDataNode = DataNode>(
|
||||
key: Key,
|
||||
{
|
||||
expandedKeys,
|
||||
|
@ -362,7 +387,7 @@ export function getTreeNodeProps(
|
|||
dragOverNodeKey,
|
||||
dropPosition,
|
||||
keyEntities,
|
||||
}: TreeNodeRequiredProps,
|
||||
}: TreeNodeRequiredProps<TreeDataType>,
|
||||
) {
|
||||
const entity = keyEntities[key];
|
||||
|
||||
|
@ -418,6 +443,7 @@ export function convertNodePropsToEventData(props: TreeNodeProps): EventDataNode
|
|||
pos,
|
||||
active,
|
||||
eventKey,
|
||||
key: eventKey,
|
||||
};
|
||||
if (!('props' in eventData)) {
|
||||
Object.defineProperty(eventData, 'props', {
|
||||
|
|
|
@ -96,6 +96,7 @@ const List = defineComponent({
|
|||
onScroll: PropTypes.func,
|
||||
onMousedown: PropTypes.func,
|
||||
onMouseenter: PropTypes.func,
|
||||
onVisibleChange: Function as PropType<(visibleList: any[], fullList: any[]) => void>,
|
||||
},
|
||||
setup(props, { expose }) {
|
||||
// ================================= MISC =================================
|
||||
|
@ -400,6 +401,20 @@ const List = defineComponent({
|
|||
return cs;
|
||||
});
|
||||
|
||||
// ================================ Effect ================================
|
||||
/** We need told outside that some list not rendered */
|
||||
watch(
|
||||
[() => calRes.start, () => calRes.end, mergedData],
|
||||
() => {
|
||||
if (props.onVisibleChange) {
|
||||
const renderList = mergedData.value.slice(calRes.start, calRes.end + 1);
|
||||
|
||||
props.onVisibleChange(renderList, mergedData.value);
|
||||
}
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
return {
|
||||
state,
|
||||
mergedData,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// base rc-virtual-list 3.4.2
|
||||
import List from './List';
|
||||
|
||||
export default List;
|
||||
|
|
Loading…
Reference in New Issue