Shopify Draggable

repository·main·Indexed 12 days ago

https://github.com/shopify/draggable

A JavaScript library that abstracts native browser drag and drop events into a comprehensive API. Version 1.2.1 provides a core engine for drag behavior with specialized modules for sorting (Sortable), dropping (Droppable), and swapping (Swappable) elements, as well as accessibility plugins for screen reader announcements.

Tokens
32.4K
Snippets
90
Records
191
Agent score
95%

What's inside Draggable

  1. Use built-in Draggable plugins

    main

    The @shopify/draggable library includes several plugins by default that extend the core dragging functionality. These plugins are available for use to add specific behaviors like announcement for accessibility, focus management, visual mirroring, or scroll handling during drag operations.

    Included plugins:

    • Announcement: Provides accessibility announcements (e.g., via ARIA live regions) about the drag state.
    • Focusable: Manages focus during drag interactions.
    • Mirror: Creates a visual mirror/ghost of the element being dragged.
    • Scrollable: Handles automatic scrolling when a dragged element is moved near the boundaries of a scrollable container.
  2. What is Swappable and how does it work?

    main

    Swappable is a specialized extension built on top of Draggable. It enables users to swap elements by dragging one element over another.

    Key distinction from Sortable: Unlike a Sortable implementation, Swappable does not maintain a specific order. Instead, any draggable element that is dragged over a target element will be swapped with the source element.

  3. What is Sortable and how does it work?

    main

    Sortable is a specialized implementation built on top of Draggable designed specifically for reordering elements. It maintains the order of elements internally and provides additional lifecycle events specifically for sorting operations.

    Requirement: For Sortable to function correctly, you must ensure that draggable elements are immediate children of their corresponding containers.

  4. Understand the DraggableEvent base interface

    main

    All events emitted by a Draggable instance are based on the DraggableEvent interface. This base event is not cancelable and provides access to the draggable instance itself.

    Key properties:

    • type: Always returns the string 'draggable'.
    • draggable: A read-only property that provides the current Draggable instance associated with the event.
  5. How the Scrollable plugin works

    main
    The Scrollable plugin enables auto-scrolling of the document or specific containers during a drag operation. It works by listening to the drag:start, drag:move, and drag:stop events of a Draggable instance to determine when the viewport or a container needs to scroll to keep the dragged element in view. This plugin is included by default in Draggable instances.
  6. Use the Snappable plugin

    main

    The Snappable plugin simulates a "snap" effect by hiding the mirror element and removing the 'source:dragging' class from the source element. It also applies the 'source:placed' class to the source for potential drop animations.

    Note: This plugin is not included by default; you must explicitly import and include it in your plugins array when initializing a Draggable or Sortable instance.

    import {Draggable, Plugins} from '@shopify/draggable';
    
    const draggable = new Draggable(document.querySelectorAll('ul'), {
      draggable: 'li',
      plugins: [Plugins.Snappable],
    });
  7. Use the Focusable plugin to enable keyboard accessibility

    main

    The Focusable plugin makes draggable containers and elements focusable by decorating them with tabindex attributes during initialization. It is designed to improve accessibility by allowing keyboard users to interact with draggable items.

    Important behavior: The plugin will not override any existing tabindex attributes already present on your elements.

  8. Use the DragSensor to handle native browser drag events

    main

    The DragSensor picks up native browser drag events and triggers drag:start, drag:move, and drag:stop events on the source container.

    Note: Draggable does not use this sensor by default. It relies on the native Drag and Drop API, which means Draggable does not create a custom mirror element, resulting in less control over the visual drag mirror compared to other sensors.

    Important Limitations:

    • The distance option is not supported when using this sensor.
    • It uses the native Drag and Drop API.
    import {Draggable, Sensors} from '@shopify/draggable';
    
    const draggable = new Draggable(containers, {
      sensors: [Sensors.DragSensor],
    });
    
    // Remove default mouse sensor to ensure only DragSensor is used
    draggable.removeSensor(Sensors.MouseSensor);
  9. Understand the SensorEvent base interface

    main

    The SensorEvent is the base interface for all sensor events emitted by the library. It provides normalized data for both mouse and touch interactions. Note that SensorEvent is not cancelable.

    Key properties available on all sensor events:

    • originalEvent: The underlying browser Event that triggered the sensor.
    • clientX: The current X coordinate of the pointer.
    • clientY: The current Y coordinate of the pointer.
    • target: The HTMLElement currently under the cursor or touch pointer.
    • container: The element that fired the sensor event.
    • originalSource: The element that was initially picked up to start the drag.
    • pressure: The amount of pressure applied (useful for pressure-sensitive touch devices).
  10. Understand the SortableEvent hierarchy

    main

    All sortable events emitted by the Sortable class inherit from the SortableEvent base interface. SortableEvent provides access to the original browser DragEvent that triggered the sortable action via the dragEvent property. Note that SortableEvent itself is not cancelable.

    // Accessing the underlying browser event from a sortable event
    const originalEvent = sortableEvent.dragEvent;