Vue DnD Kit

repository·master·Indexed 42 days ago

https://github.com/zizigy/vue-dnd-kit

A headless drag-and-drop toolkit for Vue 3 providing logic for sorting, multi-drag, and nested zones without imposing markup or styles. It features a provider-based state management system via DnDProvider and a composables API including makeDraggable, makeDroppable, makeSelectionArea, and makeConstraintArea. The ecosystem includes @vue-dnd-kit/core, @vue-dnd-kit/utilities for reactive helpers, and @vue-dnd-kit/nuxt for Nuxt 4.0.0+ integration with SSR support and auto-imports.

Tokens
91.5K
Snippets
257
Records
341
Agent score
53%

What's inside Vue DnD Kit

  1. What is @vue-dnd-kit/utilities?

    master

    The @vue-dnd-kit/utilities package is a lightweight add-on for @vue-dnd-kit/core. It provides composable helpers that return reactive state, allowing you to easily track drag-and-drop interactions in your Vue templates or watchers.

    Key features:

    • Reactive State: Helpers return ComputedRef<boolean> (e.g., to check if an element is currently being dragged or is selected).
    • Lightweight: A thin layer over the core package with no additional runtime overhead.
    • TypeScript Support: Fully typed for better developer experience.
  2. Overview of vue-dnd-kit packages

    master

    The project is split into two main packages:

    • @vue-dnd-kit/core: The primary library containing the core composables (makeDraggable, makeDroppable), the DnDProvider component, and the DragPreview component.
    • @vue-dnd-kit/utilities: Contains additional utility helpers to extend functionality.
  3. Compare Vue DnD Kit with other Vue DnD libraries

    master

    When choosing a drag and drop library for Vue 3, consider your primary use case:

    • Use Vue DnD Kit v2 if you need universal primitives (drag, drop, selection, and constraints) with no external dependencies (Vue only). It is ideal for complex scenarios involving arbitrary drop zones, constraint boxes, or multi-selection, and includes built-in keyboard and touch support.
    • Use vue.draggable.next or vue-draggable-plus if you primarily need sortable lists. These are wrappers around Sortable.js and are optimized for list-based sorting but lack dedicated primitives for constraints or box selection.
    • Use vue3-dnd if you want a React DnD-inspired approach, though it requires React DnD backends and more manual setup for features like selection or constraints.
  4. Understand the Vue DnD Kit ecosystem

    master
    Vue DnD Kit is a modular ecosystem of drag and drop libraries for Vue 3. It is designed to be high-performance, accessible (ARIA/keyboard support), and highly customizable. The ecosystem is split into specialized packages to allow you to keep your bundle size minimal by only installing what you need.
  5. Implement Tree/Nesting behavior with a single drop handler

    master

    To create a draggable file tree that supports both sorting (moving between siblings) and nesting (dropping into a folder), you must distinguish between where the pointer lands during a drop event.

    • Sort: If the pointer lands on a sibling node row, e.hoveredDraggable will be populated. Use e.hoveredDraggable.items to get the sibling array.
    • Nest: If the pointer lands on an empty folder zone, e.hoveredDraggable will be undefined, but e.dropZone will be populated. Use e.dropZone.items to get the folder's children array.

    You can resolve the target array using this pattern:

    const tgtArr = e.hoveredDraggable?.items ?? e.dropZone?.items;
  6. How grouping works in vue-dnd-kit

    master

    The grouping system implements a permission-based mechanism to control which draggable elements can be dropped into specific drop zones. Compatibility is determined by a shared group membership.

    Group Matching Rules

    • Compatibility: A draggable element and a drop zone are compatible if they share at least one group string.
    • Multiple Groups: Elements can be assigned multiple groups (e.g., ['a', 'b']) to increase flexibility.
    • Universal Compatibility: If either a draggable element or a drop zone has no groups (an empty array [] or the groups prop is not specified), it is considered "universal".
      • A draggable without groups can be dropped into any drop zone.
      • A drop zone without groups can accept any draggable element.

    Reactive States for Visual Feedback

    When using useDroppable, you can access reactive states to provide visual cues to the user:

    • isOvered: true when a draggable element is currently hovering over the drop zone.
    • isAllowed: true when the currently hovered draggable element is compatible with the drop zone (shares at least one group).
    <!-- Example of group matching logic -->
    <Droppable :groups="['files', 'images']"> <!-- Accepts anything with 'files' OR 'images' -->
      Images Folder
    </Droppable>
    
    <Draggable :groups="['images']"> <!-- Compatible with the folder above -->
      Photo.jpg
    </Draggable>
    
    <Draggable> <!-- Universal: Compatible with everything -->
      Universal Item
    </Draggable>
  7. Internal pointer logic for dual-role elements

    master

    When an element is registered as both draggable and droppable, the internal hover.ts mechanism manages which role is active based on the pointer's position relative to the placementMargins:

    Pointer positionActive sidehoveredDraggabledropZone
    Top / bottom edgeDraggableset to nodenull
    CenterDroppablenullset to node

    This ensures that a single element doesn't trigger conflicting drag/drop behaviors simultaneously.

  8. How useSelection works

    master

    The useSelection composable interacts with the library's internal selection store to manage the state of multiple draggable items.

    Core Capabilities:

    • Track Selection State: Monitors which elements are currently selected.
    • Toggle Selection: Provides handleToggleSelect to switch an element's selection status.
    • Read Selection Status: Provides isSelected (a reactive property) to reflect the current state.
    • Group Dragging: Automatically enables the ability to drag all selected items simultaneously.

    Interaction Behaviors:

    • Escape Key: Cancels the current drag operation but does not clear the current selection.
    • Enter Key: Confirms the drop operation, completing the drag and drop process.
    • Persistence: Selection is maintained throughout the drag lifecycle until the items are successfully dropped.
  9. Optimize SVG Morphing Performance and Compatibility

    master

    When using GSAP's MorphSVG plugin with Vue DnD Kit, consider the following for production-ready implementations:

    SVG Path Compatibility

    For the smoothest morphing transitions, ensure that the source and target SVG paths have a similar number of points. While MorphSVG handles varying point counts, highly complex transformations may require manual path optimization to prevent visual artifacts.

    Performance Best Practices

    SVG morphing is computationally expensive. To maintain high frame rates:

    • Simplify Paths: Reduce the number of points in your SVG paths.
    • Use requestAnimationFrame: Ensure animations are synchronized with the browser's refresh rate.
    • Test on Low-end Devices: Verify performance on target hardware to ensure smooth interactions.

    GSAP MorphSVG Options

    You can extend the gsap.to() call with additional MorphSVG-specific properties:

    • shape: The target shape to morph to.
    • shapeIndex: Used for targeting specific sub-paths within a complex SVG.
    • type: Specifies different morphing algorithms.
    • Standard GSAP properties: ease, delay, duration, etc.
  10. How Groups work in vue-dnd-kit

    master

    Groups allow draggable items and droppable zones to declare specific "channels" of compatibility. This mechanism restricts drag-and-drop operations to compatible parties, while incompatible zones are silently ignored.

    Compatibility Rules

    • Global Compatibility: An empty array [] on either the draggable item or the droppable zone acts as a "global" tag, meaning it is compatible with everything.
    • Restricted Compatibility: If both the draggable item and the droppable zone have non-empty arrays for groups, they are only compatible if they share at least one common group.
    Draggable groupsZone groupsResult
    [] (global)[] (global)✓ accepted
    [] (global)['dev']✓ accepted
    ['dev'][] (global)✓ accepted
    ['dev']['dev']✓ accepted
    ['dev']['design']✗ blocked
    // Draggable item tagged as 'dev'
    makeDraggable(itemRef, {
      groups: ['dev'],
    });
    
    // Zone that only accepts 'dev' items
    makeDroppable(devZoneRef, {
      groups: ['dev'],
    });
  11. Core concepts of Vue DnD Kit

    master

    To build drag and drop interfaces with Vue DnD Kit, you need to understand these five fundamental abstractions:

    • Draggable: The elements that the user can pick up and move.
    • Droppable: The target zones where draggable elements can be dropped.
    • Sensors: The mechanism that detects and normalizes user input (such as mouse, touch, or keyboard interactions).
    • Collision Detection: The logic used to determine when and where elements intersect during a drag operation.
    • DnD Store: The central state management that tracks drag operations across your component tree.
  12. Use payload for list-based drag and drop

    master

    When performing drag-and-drop operations on lists (sorting, swapping, or copying), provide a payload function to makeDraggable. This allows the library to build the event.draggedItems array with the correct context.

    Payload Signature

    type TDraggablePayload<T = any> = () => [index: number, items: T[]];

    When the payload is provided, the IDragItem in the event object will contain:

    • index: The position of the item in the list.
    • item: The actual data at that index.
    • items: The full source array.

    For arbitrary custom data that is not part of a list, use the data option instead. You can use both: payload for list helpers and data for metadata.

    const items = ref([1, 2, 3]);
    
    // Providing payload for list context
    makeDraggable(itemRef, { dragHandle: '.handle' }, () => [0, items.value]);
    
    // In an onDrop event handler:
    // e.draggedItems[0].index, e.draggedItems[0].item, e.draggedItems[0].items