Привет, Хабр! Недавно я представил вам Vue DnD Kit — библиотеку для создания интерфейсов с перетаскиванием в Vue 3. Сегодня хочу рассказать о новом пакете vue-dnd-kit/components, который значительно упрощает разработку сложных drag & drop интерфейсов.
Важно: Эта публикация — краткая новость о выходе пакета компонентов. Для полноценных примеров и подробной документации рекомендую перейти на официальный сайт документации.
⚠️ Статус проекта: Пакет находится в активной разработке (бета-версия). API может изменяться между минорными версиями. Не рекомендуется для продакшена до версии 1.0.0.
Что такое vue-dnd-kit/components?
vue-dnd-kit/components
— это пакет готовых компонентов, построенных поверх основного пакета vue-dnd-kit/core
. Он предоставляет готовые решения для типовых сценариев использования drag & drop, таких как:
? Сортируемые таблицы с перетаскиванием строк и столбцов
? Канбан-доски для управления задачами
? Древовидные структуры с неограниченной вложенностью
? Интерактивные дашборды с перетаскиваемыми виджетами
Ключевые особенности
1. CLI для быстрой установки компонентов
Одна из самых крутых фич — встроенный CLI, который работает по принципу shadcn/ui:
# Посмотреть доступные компоненты
pnpm dlx @vue-dnd-kit/components list
# Добавить компонент в проект
pnpm dlx @vue-dnd-kit/components add Table
# Добавить компонент в конкретную папку
pnpm dlx @vue-dnd-kit/components add Kanban --dir src/shared/components
Важно: CLI клонирует компоненты прямо в ваш проект, давая полный контроль над кодом и стилизацией. Вы можете свободно модифицировать, кастомизировать и адаптировать компоненты под свои нужды — это не зависимость, а ваш собственный код!
2. Минимальная стилизация
Все компоненты поставляются с минимальной стилизацией, что позволяет легко интегрировать их в любой дизайн-систему:
<template>
<Table
:rows="users"
:columns="columns"
>
<template #caption>Список пользователей</template>
<template #default="props">
<TableRow
v-for="(user, index) in users"
:key="user.id"
:row="user"
:row-index="index"
v-bind="props"
/>
</template>
</Table>
</template>
<style>
/* Ваши собственные стили */
.vue-dnd-table {
@apply w-full border-collapse;
}
.vue-dnd-table-row {
@apply hover:bg-gray-50 transition-colors;
}
</style>
3. Гибкая система слотов
Каждый компонент предоставляет множество слотов для кастомизации:
<template>
<Kanban
:columns="columns"
v-slot="{ columns }"
>
<KanbanColumn
v-for="(column, index) in columns"
:key="column.id"
:column="column"
:columns="columns"
:column-index="index"
:body-source="column.tasks"
>
<!-- Кастомный заголовок колонки -->
<template #header>
<div class="flex items-center gap-2">
<span class="font-semibold">{{ column.title }}</span>
<span class="text-sm text-gray-500">({{ column.tasks.length }})</span>
</div>
</template>
<!-- Кастомные карточки задач -->
<KanbanItem
v-for="(task, taskIndex) in column.tasks"
:key="task.id"
:item="task"
:items="column.tasks"
:item-index="taskIndex"
>
<div class="p-3 bg-white rounded-lg shadow-sm">
<h4 class="font-medium">{{ task.title }}</h4>
<p class="text-sm text-gray-600 mt-1">{{ task.description }}</p>
</div>
</KanbanItem>
<!-- Кастомный футер колонки -->
<template #footer>
<button class="w-full p-2 text-gray-500 hover:bg-gray-50 rounded">
+ Добавить задачу
</button>
</template>
</KanbanColumn>
</Kanban>
</template>
Доступные компоненты
? Table — Сортируемые таблицы
Полнофункциональная таблица с возможностью перетаскивания строк и столбцов:
<script setup lang="ts">
import { ref } from 'vue';
import { Table, TableRow, type ITableColumn } from './components/Table';
interface IUser {
id: number;
name: string;
email: string;
role: string;
}
const users = ref<IUser[]>([
{ id: 1, name: 'Иван Петров', email: 'ivan@example.com', role: 'Админ' },
{
id: 2,
name: 'Мария Сидорова',
email: 'maria@example.com',
role: 'Менеджер',
},
{
id: 3,
name: 'Алексей Козлов',
email: 'alex@example.com',
role: 'Разработчик',
},
]);
const columns = ref<ITableColumn<IUser>[]>([
{ label: 'ID', key: 'id' },
{ label: 'Имя', key: 'name' },
{ label: 'Email', key: 'email' },
{ label: 'Роль', key: 'role' },
]);
</script>
<template>
<Table
:rows="users"
:columns="columns"
>
<template #caption>Список сотрудников</template>
<template #default="props">
<TableRow
v-for="(user, index) in users"
:key="user.id"
:row="user"
:row-index="index"
v-bind="props"
/>
</template>
</Table>
</template>
Особенности:
Перетаскивание строк для изменения порядка
Перетаскивание столбцов для изменения их позиции
Кастомные заголовки столбцов через слоты
Поддержка статусов (success, processing, failed)
Визуальные индикаторы при перетаскивании
? Kanban — Канбан-доски
Гибкая канбан-доска для управления задачами и проектами:
<script setup lang="ts">
import { ref } from 'vue';
import { Kanban, KanbanColumn, KanbanItem } from './components/Kanban';
interface ITask {
id: number;
title: string;
description: string;
priority: 'low' | 'medium' | 'high';
}
interface IColumn {
id: number;
title: string;
tasks: ITask[];
}
const columns = ref<IColumn[]>([
{
id: 1,
title: 'К выполнению',
tasks: [
{
id: 1,
title: 'Изучить документацию',
description: 'Прочитать API docs',
priority: 'medium',
},
{
id: 2,
title: 'Написать тесты',
description: 'Покрыть код тестами',
priority: 'high',
},
],
},
{
id: 2,
title: 'В работе',
tasks: [
{
id: 3,
title: 'Разработать UI',
description: 'Создать компоненты',
priority: 'high',
},
],
},
{
id: 3,
title: 'Готово',
tasks: [
{
id: 4,
title: 'Настройка проекта',
description: 'Инициализация репозитория',
priority: 'low',
},
],
},
]);
</script>
<template>
<Kanban
:columns="columns"
v-slot="{ columns }"
>
<KanbanColumn
v-for="(column, index) in columns"
:key="column.id"
:column="column"
:columns="columns"
:column-index="index"
:body-source="column.tasks"
>
<template #header>
<div class="flex items-center justify-between">
<span class="font-semibold">{{ column.title }}</span>
<span class="text-sm bg-gray-100 px-2 py-1 rounded-full">
{{ column.tasks.length }}
</span>
</div>
</template>
<KanbanItem
v-for="(task, taskIndex) in column.tasks"
:key="task.id"
:item="task"
:items="column.tasks"
:item-index="taskIndex"
>
<div
class="p-3 bg-white rounded-lg shadow-sm border-l-4"
:class="{
'border-red-500': task.priority === 'high',
'border-yellow-500': task.priority === 'medium',
'border-green-500': task.priority === 'low',
}"
>
<h4 class="font-medium text-sm">{{ task.title }}</h4>
<p class="text-xs text-gray-600 mt-1">{{ task.description }}</p>
</div>
</KanbanItem>
</KanbanColumn>
</Kanban>
</template>
Особенности:
Перетаскивание задач между колонками
Перетаскивание колонок для изменения порядка
Кастомные заголовки и футеры колонок
Гибкая система слотов для кастомизации
? Tree — Древовидные структуры
Иерархические структуры с неограниченной вложенностью:
<script setup lang="ts">
import { ref } from 'vue';
import { Tree } from './components/Tree';
interface IFile {
id: number;
name: string;
type: 'file' | 'folder';
children?: IFile[];
}
const files = ref<IFile[]>([
{
id: 1,
name: 'Проект',
type: 'folder',
children: [
{
id: 2,
name: 'src',
type: 'folder',
children: [
{ id: 3, name: 'main.ts', type: 'file' },
{ id: 4, name: 'App.vue', type: 'file' },
],
},
{
id: 5,
name: 'docs',
type: 'folder',
children: [{ id: 6, name: 'README.md', type: 'file' }],
},
],
},
]);
</script>
<template>
<Tree
:data="files"
item-key="id"
nesting-key="children"
v-slot="{ item }"
>
<div class="flex items-center gap-2 py-1">
<span
class="text-lg"
:class="item.type === 'folder' ? 'text-blue-500' : 'text-gray-500'"
>
{{ item.type === 'folder' ? '?' : '?' }}
</span>
<span class="font-medium">{{ item.name }}</span>
</div>
</Tree>
</template>
Особенности:
Неограниченная вложенность
Разворачивание/сворачивание узлов
Перетаскивание элементов между уровнями
Визуальные индикаторы для элементов с детьми
Подсветка зон для сброса
? Dashboard — Интерактивные дашборды
Гибкая система виджетов с перетаскиванием:
<script setup lang="ts">
import { ref } from 'vue';
import { Dashboard, DashboardItem } from './components/Dashboard';
import ChartCard from './components/Dashboard/Example/ChartCard.vue';
import StatCard from './components/Dashboard/Example/StatCard.vue';
import TaskList from './components/Dashboard/Example/TaskList.vue';
interface IDashboardItem {
id: number;
component: Component;
}
const dashboard = ref<IDashboardItem[]>([
{ id: 1, component: ChartCard },
{ id: 2, component: TaskList },
{ id: 3, component: StatCard },
]);
</script>
<template>
<Dashboard
:data="dashboard"
class="dashboard"
>
<TransitionGroup
name="dashboard"
appear
>
<DashboardItem
v-for="(item, index) in dashboard"
:key="item.id"
:index="index"
:source="dashboard"
class="dashboard-item"
>
<component :is="item.component" />
</DashboardItem>
</TransitionGroup>
</Dashboard>
</template>
<style>
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
gap: 20px;
padding: 20px;
}
.dashboard-item {
border-radius: 12px;
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.dashboard-move {
transition: all 0.3s ease;
}
</style>
Особенности:
CSS Grid для адаптивной раскладки
Плавные анимации при перетаскивании
Поддержка TransitionGroup
Кастомные виджеты через слоты
Как начать использовать
Установка
# npm
npm install @vue-dnd-kit/components @vue-dnd-kit/core
# yarn
yarn add @vue-dnd-kit/components @vue-dnd-kit/core
# pnpm
pnpm add @vue-dnd-kit/components @vue-dnd-kit/core
Быстрый старт с CLI
# Добавить таблицу
pnpm dlx @vue-dnd-kit/components add Table
# Добавить канбан-доску
pnpm dlx @vue-dnd-kit/components add Kanban
# Добавить дерево
pnpm dlx @vue-dnd-kit/components add Tree
# Добавить дашборд
pnpm dlx @vue-dnd-kit/components add Dashboard
Подключение как плагин
import { createApp } from 'vue';
import App from './App.vue';
import VueDnDKitPlugin from '@vue-dnd-kit/core';
const app = createApp(App);
app.use(VueDnDKitPlugin);
app.mount('#app');
Преимущества перед другими решениями
1. Простота использования
CLI для быстрой установки компонентов
Интуитивный API на основе слотов
Минимальная конфигурация
2. Гибкость
Полный контроль над стилизацией
Кастомные слоты для любой кастомизации
Отсутствие привязки к конкретным UI-фреймворкам
3. Производительность
Оптимизированные компоненты
Минимальные перерисовки
Эффективная работа с большими списками
4. Доступность
Полная поддержка клавиатурной навигации
Совместимость со скринридерами
Семантическая разметка
Планы на будущее
Проект активно развивается! В разработке находятся дополнительные компоненты:
SortableList — простые сортируемые списки
FormBuilder — конструктор форм с перетаскиванием
Tabs — перетаскиваемые вкладки
FileExplorer — файловый менеджер
Grid — адаптивная сетка
Roadmap:
[x] Базовые drag & drop компоненты
[x] Table компонент
[x] Kanban доска
[x] Tree компонент
[ ] SortableList
[ ] FormBuilder
[ ] Dashboard
[ ] Tabs
[ ] FileExplorer
[ ] Grid
[ ] Тесты
[ ] Стабильный API (версия 1.0.0)
Заключение
vue-dnd-kit/components
— это мощный инструмент для быстрой разработки сложных интерфейсов с перетаскиванием. Благодаря CLI, который работает по принципу shadcn/ui, гибкой системе слотов и минимальной стилизации, вы можете создавать профессиональные drag & drop интерфейсы за считанные минуты.
Ключевое преимущество: Компоненты клонируются в ваш проект, а не устанавливаются как зависимости. Это означает полную свободу в кастомизации и отсутствие привязки к конкретным версиям библиотеки.
Библиотека с открытым исходным кодом, активно развивается, и я буду рад любому вкладу от сообщества!
Полезные ссылки:
Буду рад вашим отзывам и предложениям в комментариях!
Теги: vue.js, drag-and-drop, dnd-kit, frontend-разработка, components, ui-library
Хабы: VueJS, Frontend-разработка
GlennMiller1991
Не доработаны чутка примеры. На той же странице в документации рассказывается о предотвращении подобного поведения, но автор сам забыл похоже)
ZiZIGY Автор
Спасибо за полезный комментарий, я просто реально забыл xD) Спасибо)