Skip to content

Commit

Permalink
fix(tree): add allowDrop API to limit drop action (#3206)
Browse files Browse the repository at this point in the history
* fix(tree): tree 组件添加 allowDrop 属性方法

* chore(tree): tree 组件,提供 allowDrop 属性的示例

* fix(tree): tree 组件,规范 allowDrop 属性的 api 配置

* fix(tree): 解决 src/common.ts 中丢失 LayoutEnum 定义的问题

* test(tree): tree update snapshot
  • Loading branch information
TabSpace committed Jun 20, 2024
1 parent b61e1c1 commit 48904a8
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 23 deletions.
9 changes: 8 additions & 1 deletion src/tree/_example/draggable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
transition
expand-all
draggable
:allow-drop="handleAllowDrop"
@drag-start="handleDragStart"
@drag-end="handleDragEnd"
@drag-over="handleDragOver"
Expand Down Expand Up @@ -71,7 +72,7 @@ export default {
},
{
value: '2.2',
label: '2.2',
label: '2.2 不允许拖放为 2.2 的子节点',
},
],
},
Expand All @@ -94,6 +95,12 @@ export default {
handleDrop(ctx) {
console.log('handleDrop', ctx);
},
handleAllowDrop(ctx) {
const { dropNode, dropPosition } = ctx;
if (dropNode.value === '2.2' && dropPosition === 0) {
return false;
}
},
},
};
</script>
21 changes: 13 additions & 8 deletions src/tree/hooks/useDragHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TreeNode } from '../adapt';
import {
TreeProps, TypDragEventState, TypeTreeState, TypeDragHandle,
} from '../tree-types';
import { DragPosition } from './useDraggable';
import { emitEvent } from '../util';

export default function useDragHandle(state: TypeTreeState) {
Expand Down Expand Up @@ -54,12 +55,21 @@ export default function useDragHandle(state: TypeTreeState) {
const { dragEvent, node, dropPosition } = state;
if (node.value === dragNode.value || node.getParents().some((_node) => _node.value === dragNode.value)) return;

const ctx = {
dropNode: node.getModel(),
dragNode: dragNode.getModel(),
dropPosition,
e: dragEvent,
};

if (props.allowDrop(ctx) === false) return;

const nodes = store.getNodes() as TreeNode[];
nodes.some((_node) => {
if (_node.value === node.value) {
if (dropPosition === 0) {
if (dropPosition === DragPosition.Inside) {
dragNode.appendTo(store, _node);
} else if (dropPosition < 0) {
} else if (dropPosition === DragPosition.Before) {
node.insertBefore(dragNode);
} else {
node.insertAfter(dragNode);
Expand All @@ -68,12 +78,7 @@ export default function useDragHandle(state: TypeTreeState) {
}
return false;
});
const ctx = {
dropNode: node.getModel(),
dragNode: dragNode.getModel(),
dropPosition,
e: dragEvent,
};

emitEvent<Parameters<TreeProps['onDrop']>>(props, context, 'drop', ctx);
};

Expand Down
20 changes: 13 additions & 7 deletions src/tree/hooks/useDraggable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ export interface TypeDragStates {
dropPosition: number;
}

export enum DragPosition {
Before = -1,
Inside = 0,
After = 1,
}

type TypeDrag = 'dragStart' | 'dragOver' | 'dragLeave' | 'dragEnd' | 'drop';

export default function useDraggable(state: TypeTreeItemState) {
const { treeItemRef } = state;
const dragStates = reactive({
isDragOver: false,
isDragging: false,
dropPosition: 0,
dropPosition: DragPosition.Inside,
});

const updateDropPosition = (dragEvent: DragEvent) => {
Expand All @@ -29,11 +35,11 @@ export default function useDraggable(state: TypeTreeItemState) {
const diff = pageY - offsetY;

if (diff < gapHeight) {
dragStates.dropPosition = -1;
dragStates.dropPosition = DragPosition.Before;
} else if (diff < rect.height - gapHeight) {
dragStates.dropPosition = 0;
dragStates.dropPosition = DragPosition.Inside;
} else {
dragStates.dropPosition = 1;
dragStates.dropPosition = DragPosition.After;
}
};

Expand All @@ -45,13 +51,13 @@ export default function useDraggable(state: TypeTreeItemState) {
switch (status) {
case 'dragStart':
dragStates.isDragging = true;
dragStates.dropPosition = 0;
dragStates.dropPosition = DragPosition.Inside;
drag.handleDragStart?.({ node, dragEvent });
break;
case 'dragEnd':
dragStates.isDragging = false;
dragStates.isDragOver = false;
dragStates.dropPosition = 0;
dragStates.dropPosition = DragPosition.Inside;
throttleUpdateDropPosition.cancel();
drag.handleDragEnd?.({ node, dragEvent });
break;
Expand All @@ -62,7 +68,7 @@ export default function useDraggable(state: TypeTreeItemState) {
break;
case 'dragLeave':
dragStates.isDragOver = false;
dragStates.dropPosition = 0;
dragStates.dropPosition = DragPosition.Inside;
throttleUpdateDropPosition.cancel();
drag.handleDragLeave?.({ node, dragEvent });
break;
Expand Down
4 changes: 4 additions & 0 deletions src/tree/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export default {
defaultActived: {
type: Array as PropType<TdTreeProps['defaultActived']>,
},
/** 判断节点是否可以执行 drop 操作,泛型 `T` 表示树节点 TS 类型 */
allowDrop: {
type: Function as PropType<TdTreeProps['allowDrop']>,
},
/** 是否允许在过滤时节点折叠节点 */
allowFoldNodeOnFilter: Boolean,
/** 透传属性到 checkbox 组件。参考 checkbox 组件 API */
Expand Down
3 changes: 3 additions & 0 deletions src/tree/tree.en-US.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
:: BASE_DOC ::

## API

### Tree Props

name | type | default | description | required
-- | -- | -- | -- | --
activable | Boolean | false | make nodes can be highlight | N
activeMultiple | Boolean | false | \- | N
actived | Array | - | `.sync` is supported。Typescript:`Array<TreeNodeValue>` | N
allowDrop | Function | - | Determine whether the node can execute the drop operation。Typescript:`(context: { e: DragEvent; dragNode: TreeNodeModel<T>; dropNode: TreeNodeModel<T>; dropPosition: number; }) => boolean` | N
allowFoldNodeOnFilter | Boolean | false | \- | N
checkProps | Object | - | Typescript:`CheckboxProps`[Checkbox API Documents](./checkbox?tab=api)[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/tree/type.ts) | N
checkStrictly | Boolean | false | \- | N
Expand Down Expand Up @@ -81,6 +83,7 @@ getPath | `(value: TreeNodeValue)` | `TreeNodeModel<T>[]` | required
getTreeData | `(value?: TreeNodeValue)` | `Array<T>` | required。get tree struct data
insertAfter | `(value: TreeNodeValue, newData: T)` | \- | required
insertBefore | `(value: TreeNodeValue, newData: T)` | \- | required
refresh | \- | \- | required。refresh tree state, used in tree search
remove | `(value: TreeNodeValue)` | \- | required
scrollTo | `(scrollToParams: ScrollToElementParams)` | \- | support scrolling to a specific node when virtual scrolling
setItem | `(value: TreeNodeValue, options: TreeNodeState)` | \- | required
Expand Down
11 changes: 7 additions & 4 deletions src/tree/tree.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
:: BASE_DOC ::

## API

### Tree Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
activable | Boolean | false | 节点是否可高亮 | N
activeMultiple | Boolean | false | 是否允许多个节点同时高亮 | N
actived | Array | - | 高亮的节点值。支持语法糖 `.sync`。TS 类型:`Array<TreeNodeValue>` | N
allowDrop | Function | - | 判断节点是否可以执行 drop 操作,泛型 `T` 表示树节点 TS 类型。TS 类型:`(context: { e: DragEvent; dragNode: TreeNodeModel<T>; dropNode: TreeNodeModel<T>; dropPosition: number; }) => boolean` | N
allowFoldNodeOnFilter | Boolean | false | 是否允许在过滤时节点折叠节点 | N
checkProps | Object | - | 透传属性到 checkbox 组件。参考 checkbox 组件 API。TS 类型:`CheckboxProps`[Checkbox API Documents](./checkbox?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/tree/type.ts) | N
checkStrictly | Boolean | false | 父子节点选中状态不再关联,可各自选中或取消 | N
Expand Down Expand Up @@ -81,13 +83,14 @@ getPath | `(value: TreeNodeValue)` | `TreeNodeModel<T>[]` | 必需。自下而
getTreeData | `(value?: TreeNodeValue)` | `Array<T>` | 必需。获取某节点的全部树形结构;参数为空,则表示获取整棵树的结构数据,泛型 `T` 表示树节点 TS 类型
insertAfter | `(value: TreeNodeValue, newData: T)` | \- | 必需。插入新节点到指定节点后面,泛型 `T` 表示树节点 TS 类型
insertBefore | `(value: TreeNodeValue, newData: T)` | \- | 必需。插入新节点到指定节点前面,泛型 `T` 表示树节点 TS 类型
refresh | \- | \- | 必需。刷新树节点状态,可用于搜索场景刷新
remove | `(value: TreeNodeValue)` | \- | 必需。移除指定节点
scrollTo | `(scrollToParams: ScrollToElementParams)` | \- | 虚拟滚动场景下 支持指定滚动到具体的节点
setItem | `(value: TreeNodeValue, options: TreeNodeState)` | \- | 必需。设置节点状态

### TreeNodeState

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
activable | Boolean | false | 节点是否允许被激活 | N
actived | Boolean | false | 节点是否被激活 | N
Expand All @@ -105,7 +108,7 @@ visible | Boolean | false | 节点是否可视 | N

### TreeNodeModel

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
actived | Boolean | - | 必需。当前节点是否处于高亮激活态 | Y
checked | Boolean | - | 必需。当前节点是否被选中 | Y
Expand Down Expand Up @@ -138,7 +141,7 @@ setData | `(data: T)` | \- | 必需。设置节点数据,数据变化可自动

### TScroll

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
bufferSize | Number | 20 | 表示除可视区域外,额外渲染的行数,避免快速滚动过程中,新出现的内容来不及渲染从而出现空白 | N
isFixedRowHeight | Boolean | false | 表示每行内容是否同一个固定高度,仅在 `scroll.type``virtual` 时有效,该属性设置为 `true` 时,可用于简化虚拟滚动内部计算逻辑,提升性能,此时则需要明确指定 `scroll.rowHeight` 属性的值 | N
Expand Down
13 changes: 13 additions & 0 deletions src/tree/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export interface TdTreeProps<T extends TreeOptionData = TreeOptionData> {
* 高亮的节点值,非受控属性
*/
defaultActived?: Array<TreeNodeValue>;
/**
* 判断节点是否可以执行 drop 操作,泛型 `T` 表示树节点 TS 类型
*/
allowDrop?: (context: {
e: DragEvent;
dragNode: TreeNodeModel<T>;
dropNode: TreeNodeModel<T>;
dropPosition: number;
}) => boolean;
/**
* 是否允许在过滤时节点折叠节点
* @default false
Expand Down Expand Up @@ -277,6 +286,10 @@ export interface TreeInstanceFunctions<T extends TreeOptionData = TreeOptionData
* 插入新节点到指定节点前面,泛型 `T` 表示树节点 TS 类型
*/
insertBefore: (value: TreeNodeValue, newData: T) => void;
/**
* 刷新树节点状态,可用于搜索场景刷新
*/
refresh: () => void;
/**
* 移除指定节点
*/
Expand Down
4 changes: 2 additions & 2 deletions test/snap/__snapshots__/csr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -121779,12 +121779,12 @@ exports[`csr snapshot test > csr test ./src/tree/_example/draggable.vue 1`] = `
/>
<span
class="t-tree__label"
title="2.2"
title="2.2 不允许拖放为 2.2 的子节点"
>
<span
style="position: relative;"
>
2.2
2.2 不允许拖放为 2.2 的子节点
</span>
</span>
</div>
Expand Down
Loading

0 comments on commit 48904a8

Please sign in to comment.