Skip to content

Commit

Permalink
feat: 优化拖拽
Browse files Browse the repository at this point in the history
  • Loading branch information
kolarorz committed Dec 31, 2024
1 parent 87e8228 commit bc328e4
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 68 deletions.
2 changes: 1 addition & 1 deletion docs/demos/tree/Drag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const expandedKeys = ref<number[]>([1, 100, 102]);
:fieldNames="customFieldNames"
:indent="20"
:iconSize="14"
:filter-method="filterMethod"
:filterMethod="filterMethod"
:itemGap="4"
:draggable="draggable"
@dragstart="onDragstart"
Expand Down
155 changes: 155 additions & 0 deletions docs/demos/tree/DragList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<script lang="ts" setup>
import { onMounted, ref, shallowRef } from 'vue';
import { VirtTree, type TreeNode } from 'vue-virt-list';
type ItemData = {
id: string | number;
title: string;
children?: ItemData[];
// 禁止拖入
disableDragIn?: boolean;
// 禁止托出
disableDragOut?: boolean;
};
const customFieldNames = {
key: 'id',
};
const list = ref<ItemData[]>([]);
list.value = Array.from({ length: 40 }).map((_, i) => ({
id: i + 1,
title: `Node-${i}`,
disableDragIn: true,
}));
const virtTreeRef = ref<typeof VirtTree>();
const filterMethod = (query: string, node: any) => {
return node.name.includes(query);
};
function onDragstart() {
console.log('onDragstart');
}
function onDragEnd(data: any) {
if (data) {
console.log('drag success', data);
} else {
console.warn('drag fail: Invalid');
}
}
const draggable = ref(true);
const expandedKeys = ref<number[]>([1, 100, 102]);
const toggleExpand = (key: number) => {
virtTreeRef.value?.expandNode(key, !expandedKeys.value.includes(key));
};
function onDragStart(e: MouseEvent | Event) {
if (virtTreeRef.value?.onDragstart) virtTreeRef.value?.onDragstart(e);
}
</script>

<template>
<div class="demo-tree">
<div class="virt-tree-wrapper">
<VirtTree
ref="virtTreeRef"
v-model:expandedKeys="expandedKeys"
:list="list"
:fieldNames="customFieldNames"
:indent="0"
:iconSize="14"
:filterMethod="filterMethod"
:itemGap="4"
@dragstart="onDragstart"
@dragend="onDragEnd"
dragOnly
dragGhostClass="drag-ghost-class"
dragClass="drag-class"
expandOnClickNode
default-expand-all
>
<template #default="{ node }">
<div
:style="{
display: 'flex',
alignItems: 'center',
height: '32px',
}"
@click="toggleExpand(node.data.id)"
>
<!-- drag handler -->
<div
style="cursor: move; margin-right: 8px"
draggable="true"
@dragstart="onDragStart"
>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5 3C5 2.44772 5.44772 2 6 2C6.55228 2 7 2.44772 7 3C7 3.55228 6.55228 4 6 4C5.44772 4 5 3.55228 5 3ZM9 3C9 2.44772 9.44772 2 10 2C10.5523 2 11 2.44772 11 3C11 3.55228 10.5523 4 10 4C9.44772 4 9 3.55228 9 3ZM5 8C5 7.44772 5.44772 7 6 7C6.55228 7 7 7.44772 7 8C7 8.55228 6.55228 9 6 9C5.44772 9 5 8.55228 5 8ZM9 8C9 7.44772 9.44772 7 10 7C10.5523 7 11 7.44772 11 8C11 8.55228 10.5523 9 10 9C9.44772 9 9 8.55228 9 8ZM5 13C5 12.4477 5.44772 12 6 12C6.55228 12 7 12.4477 7 13C7 13.5523 6.55228 14 6 14C5.44772 14 5 13.5523 5 13ZM9 13C9 12.4477 9.44772 12 10 12C10.5523 12 11 12.4477 11 13C11 13.5523 10.5523 14 10 14C9.44772 14 9 13.5523 9 13Z"
fill="var(--virt-tree-color-icon)"
/>
</svg>
</div>
<!-- content -->
<div>
{{ node.title }}
</div>
</div>
</template>

<template #empty>
<div style="padding: 16px">暂无数据</div>
</template>
</VirtTree>
</div>
</div>
</template>

<style scoped lang="scss">
.demo-tree {
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
.tree-btn-container {
display: flex;
flex: 1;
flex-direction: row-reverse;
justify-content: space-between;
padding: 12px 8px;
gap: 8px;
.input-label {
font-size: 14px;
}
.btn-item {
padding: 4px 12px;
cursor: pointer;
border: 1px solid #ececec;
border-radius: 4px;
font-size: 14px;
}
.input-container {
display: flex;
gap: 8px;
align-items: center;
input {
height: 100%;
border: 1px solid #ececec;
border-radius: 4px;
padding: 0 8px;
}
}
}
}
</style>
14 changes: 9 additions & 5 deletions docs/en/examples/virt-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,22 @@ Focus 状态切换完全交由外部处理,内部仅给Node节点加上`.is-fo

## draggable

<!<< @/demos/tree/Drag.vue

## dragover placement
拖拽后不直接修改数据,而是提供 dragend 事件,由业务自行判定并修改数据,数据更改后,通过响应式更新树。

拖拽悬浮生效区域判定
位置判定说明:每个元素会被切割为3份,上面一份判定为拖入该元素上方,下面一份判定为拖入该元素下方。如果该元素被禁止拖入,则会把该元素一分为二,去掉中间的区域。

<!<< @/demos/tree/DragoverPlacement.vue
<!<< @/demos/tree/Drag.vue

## drag handler

自定义拖拽生效图标,而不是整个节点

<!<< @/demos/tree/DragHandler.vue

通常这种形式用在替代 VueDraggable 组件

<!<< @/demos/tree/DragList.vue

## css variable

```css
Expand Down
14 changes: 9 additions & 5 deletions docs/zh/examples/virt-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,22 @@ Focus 状态切换完全交由外部处理,内部仅给Node节点加上`.is-fo

## draggable

<!<< @/demos/tree/Drag.vue

## dragover placement
拖拽后不直接修改数据,而是提供 dragend 事件,由业务自行判定并修改数据,数据更改后,通过响应式更新树。

拖拽悬浮生效区域判定
位置判定说明:每个元素会被切割为3份,上面一份判定为拖入该元素上方,下面一份判定为拖入该元素下方。如果该元素被禁止拖入,则会把该元素一分为二,去掉中间的区域。

<!<< @/demos/tree/DragoverPlacement.vue
<!<< @/demos/tree/Drag.vue

## drag handler

自定义拖拽生效图标,而不是整个节点

<!<< @/demos/tree/DragHandler.vue

通常这种形式用在替代 VueDraggable 组件

<!<< @/demos/tree/DragList.vue

## css variable

```css
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-virt-list",
"version": "1.5.7",
"version": "1.5.8",
"description": "Tiny & Virtual scroll list & Huge amount data & High performance (support vue2.x&vue3.x)",
"author": "kolarorz",
"license": "MIT",
Expand Down
60 changes: 30 additions & 30 deletions src/components/virt-tree/useDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
findAncestorWithClass,
getPrevSibling,
getNextSibling,
getDragoverPlacement,
} from './utils';

export const useDrag = ({
Expand Down Expand Up @@ -92,9 +91,8 @@ export const useDrag = ({
let scrollElementRect: DOMRect | undefined = undefined;
let clientElementRect: DOMRect | undefined = undefined;

const [topPlacement, bottomPlacement] = getDragoverPlacement(
props.dragoverPlacement,
);
let topPlacement = 0.33;
let bottomPlacement = 0.66;

const dragBox = document.createElement('div');
dragBox.classList.add('virt-tree-drag-box');
Expand Down Expand Up @@ -253,28 +251,11 @@ export const useDrag = ({
const hoverElement = document.elementFromPoint(mouseX, mouseY);
if (!hoverElement) return;
hoverTreeItem = findAncestorWithClass(hoverElement, 'virt-tree-item');
if (!hoverTreeItem) {
return;
}

if (!hoverTreeItem) return;
const hoverTreeId = hoverTreeItem?.dataset?.id;
if (!hoverTreeId) return;
const hoverTreeNode = getTreeNode(hoverTreeId);
if (!hoverTreeNode) return;

// 操作元素和hover元素不能一致
if (hoverTreeItem === sourceTreeItem) {
// 移除 line
if (hasStyleTreeItem?.contains(dragLine)) {
hasStyleTreeItem?.removeChild(dragLine);
}
// 移除 box
if (hasStyleTreeItem?.contains(dragBox)) {
hasStyleTreeItem?.removeChild(dragBox);
}
dragEffect = false;
return;
}
const hoverTreeItemRect = hoverTreeItem?.getBoundingClientRect();
if (!hoverTreeItemRect) return;

Expand All @@ -285,6 +266,15 @@ export const useDrag = ({
// 计算鼠标相对于元素高度的比例
const positionRatio = relativeY / elementHeight;

if (hoverTreeNode.data.disableDragIn) {
// 如果禁止拖入,就不需要中间区域判断了
topPlacement = 0.5;
bottomPlacement = 0.5;
} else {
topPlacement = 0.33;
bottomPlacement = 0.66;
}

if (positionRatio < topPlacement) {
placement = 'top';
} else if (positionRatio > bottomPlacement) {
Expand Down Expand Up @@ -312,6 +302,20 @@ export const useDrag = ({
lastPlacement = placement;
lastHoverTreeItem = hoverTreeItem;

// 操作元素和hover元素不能一致
if (hoverTreeItem === sourceTreeItem) {
// 移除 line
if (hasStyleTreeItem?.contains(dragLine)) {
hasStyleTreeItem?.removeChild(dragLine);
}
// 移除 box
if (hasStyleTreeItem?.contains(dragBox)) {
hasStyleTreeItem?.removeChild(dragBox);
}
dragEffect = false;
return;
}

// 一旦发生变化立马清除定时器
if (hoverExpandTimer) {
clearTimeout(hoverExpandTimer);
Expand All @@ -322,11 +326,7 @@ export const useDrag = ({
dragEffect = false;
// console.log('鼠标在中部', dragEffect);
// 判断是否能够进入disableDragIn
const id = hoverTreeItem?.dataset?.id;
if (!id) return;
const node = getTreeNode(id);
if (!node) return;
parentNode = node;
parentNode = hoverTreeNode;
prevNode = undefined;
if (hasStyleTreeItem?.contains(dragLine)) {
hasStyleTreeItem?.removeChild(dragLine);
Expand All @@ -345,7 +345,7 @@ export const useDrag = ({
// }

// 被禁用
if (node.data?.disableDragIn) return;
if (hoverTreeNode.data?.disableDragIn) return;

// 添加 box
hoverTreeItem?.appendChild(dragBox);
Expand All @@ -358,11 +358,11 @@ export const useDrag = ({
clearTimeout(hoverExpandTimer);
hoverExpandTimer = null;
}
const isExpanded = hasExpanded(node);
const isExpanded = hasExpanded(hoverTreeNode);
if (!isExpanded) {
if (!hoverExpandTimer) {
hoverExpandTimer = setTimeout(() => {
expandNode(id, true);
expandNode(hoverTreeId, true);
if (hoverExpandTimer) {
clearTimeout(hoverExpandTimer);
hoverExpandTimer = null;
Expand Down
Loading

0 comments on commit bc328e4

Please sign in to comment.