Drag and drop

Naive UI предоставляет встроенные возможности для реализации drag-and-drop интерфейсов через компоненты и директивы, что упрощает создание интерактивных элементов с перемещением внутри страницы. Основными инструментами являются компоненты NTransfer, NTable, NTree и использование событий для отслеживания drag и drop. Для работы с drag-and-drop важно понимать концепцию событий и слотов, которые позволяют точно контролировать поведение перетаскиваемого элемента и зоны, в которую он перемещается.


Подготовка компонентов для перетаскивания

Чтобы элемент был перетаскиваемым, необходимо:

  1. Добавить атрибут draggable="true" к элементу.
  2. Подписаться на событие @dragstart, чтобы определить данные, передаваемые при начале перетаскивания.
  3. При необходимости обработать событие @dragend для логики после завершения операции.

Пример базового элемента:

<template>
  <div 
    class="draggable-item" 
    draggable="true" 
    @dragstart="handleDragStart" 
    @dragend="handleDragEnd"
  >
    Перетащи меня
  </div>
</template>

<script setup>
import { ref } from 'vue';

const handleDragStart = (event) => {
  event.dataTransfer.setData('text/plain', 'item1');
};

const handleDragEnd = (event) => {
  console.log('Перетаскивание завершено');
};
</script>

<style>
.draggable-item {
  width: 150px;
  height: 50px;
  background-color: #f0f0f0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  border: 1px solid #ccc;
}
.draggable-item:active {
  cursor: grabbing;
}
</style>

Настройка зоны приема (drop zone)

Для зоны, которая принимает элементы, необходимо:

  • Использовать событие @dragover с вызовом event.preventDefault() — без этого элемент не сможет быть “брошен”.
  • Обработать @drop, чтобы получить данные и выполнить перемещение.

Пример:

<template>
  <div 
    class="drop-zone" 
    @dragover.prevent
    @drop="handleDrop"
  >
    Бросьте элемент сюда
  </div>
</template>

<script setup>
const handleDrop = (event) => {
  const data = event.dataTransfer.getData('text/plain');
  console.log('Элемент брошен:', data);
};
</script>

<style>
.drop-zone {
  width: 300px;
  height: 200px;
  border: 2px dashed #007acc;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 20px;
}
</style>

Интеграция с компонентами Naive UI

1. Перетаскивание внутри NTable

Компонент NTable поддерживает drag-and-drop через пользовательские слоты и события:

<NTable :columns="columns" :data="tableData">
  <template #body-cell="{ row, column }">
    <div 
      draggable="true"
      @dragstart="startDrag(row)"
      @drop="dropRow(row)"
      @dragover.prevent
    >
      {{ row[column.key] }}
    </div>
  </template>
</NTable>

Логика для перемещения строк:

const draggedRow = ref(null);

function startDrag(row) {
  draggedRow.value = row;
}

function dropRow(targetRow) {
  if (!draggedRow.value || draggedRow.value === targetRow) return;
  
  const indexFrom = tableData.indexOf(draggedRow.value);
  const indexTo = tableData.indexOf(targetRow);
  
  tableData.splice(indexFrom, 1);
  tableData.splice(indexTo, 0, draggedRow.value);
  draggedRow.value = null;
}

2. Использование NTree с перетаскиванием

NTree поддерживает drag-and-drop через свойства draggable и события onDrop:

<NTree
  :data="treeData"
  :draggable="true"
  @dragstart="onDragStart"
  @drop="onDrop"
/>

Пример обработки:

let draggedNode = null;

function onDragStart(node) {
  draggedNode = node;
}

function onDrop({ node: targetNode, dropPosition }) {
  if (!draggedNode || draggedNode === targetNode) return;

  // Удаляем из исходного родителя
  const parent = findParentNode(treeData, draggedNode);
  parent?.children?.splice(parent.children.indexOf(draggedNode), 1);

  // Вставляем в новый родитель
  if (!targetNode.children) targetNode.children = [];
  targetNode.children.splice(dropPosition === 'inside' ? 0 : targetNode.children.length, 0, draggedNode);

  draggedNode = null;
}

Рекомендации по UX

  • Визуальная обратная связь: использовать изменение стиля элемента при перетаскивании, например, подсветку drop zone или изменение прозрачности перетаскиваемого элемента.
  • Точность попадания: при работе с сложными структурами (например, NTree) следует точно учитывать позицию вставки (before, after, inside).
  • Мобильная поддержка: drag-and-drop на сенсорных устройствах требует отдельной обработки через события touchstart, touchmove и touchend, так как стандартные HTML5 drag/drop не поддерживаются.

Продвинутые техники

  • Перетаскивание нескольких элементов: можно хранить массив draggedItems и при drop вставлять все сразу.
  • Сортировка и фильтрация: при интеграции с NTable сортировка и фильтры не должны сбрасывать состояние drag-and-drop, поэтому лучше использовать уникальные ключи элементов.
  • Анимации при перемещении: библиотека Naive UI не предоставляет встроенных анимаций перемещения, но их можно реализовать через CSS-переходы или сторонние библиотеки (например, VueUse + transition-group).

Naive UI позволяет гибко сочетать стандартные HTML drag-and-drop возможности с реактивной логикой Vue, обеспечивая простую реализацию интерактивных интерфейсов для таблиц, деревьев и списков с минимальным количеством кода.