← 返回
未分类 中文

@shopify/draggable — Drag & Drop Interaction Skill

Implement drag-and-drop interactions with @shopify/draggable. Supports Draggable (basic drag), Sortable (reordering), Droppable (drop zones), Swappable (swap...
Implement drag-and-drop interactions with @shopify/draggable. Supports Draggable (basic drag), Sortable (reordering), Droppable (drop zones), Swappable (swap...
openlark openlark 来源
未分类 clawhub v1.0.0 1 版本 100000 Key: 无需
★ 0
Stars
📥 122
下载
💾 0
安装
1
版本
#latest

概述

@shopify/draggable — Drag & Drop Interaction Skill

> Built on Shopify's @shopify/draggable (MIT license, v1.2.1).

> Pure JS library, zero runtime dependencies, supports ES modules and UMD.

Use cases

sortable lists, droppable panels, custom drag interactions, kanban/grid drag-and-drop reorganization. No longer maintained, now community-maintained.

Install

npm install @shopify/draggable

Or via CDN (recommended for prototyping):

<script type="module">
import { Draggable, Sortable, Droppable, Swappable } from 'https://cdn.jsdelivr.net/npm/@shopify/draggable/build/esm/index.mjs';
</script>

Module Overview

ModuleClassPurpose
------------------------
BaseDraggableCore drag engine, manages mirror, sensors, and events
SortingSortableReorder on drag, tracks old and new indices
Drop ZoneDroppableDrag elements into/out of specific dropzones
SwapSwappableSwap two element positions on drag (no sorting)

Quick Start

Basic Drag

import Draggable from '@shopify/draggable';

const draggable = new Draggable(document.querySelectorAll('.container'), {
  draggable: '.draggable-source',
});

draggable.on('drag:start', (event) => {
  console.log('Started dragging:', event.source);
});

draggable.on('drag:stop', () => {
  console.log('Drag ended');
});

Sortable List

import Sortable from '@shopify/draggable';

const sortable = new Sortable(document.querySelectorAll('.list'), {
  draggable: '.list-item',
  delay: { mouse: 200, touch: 300 },
});

sortable.on('sortable:stop', (event) => {
  console.log(`Moved from index ${event.oldIndex} to ${event.newIndex}`);
});

Drag into Drop Zone

import Droppable from '@shopify/draggable';

const droppable = new Droppable(document.querySelectorAll('.source-container'), {
  draggable: '.card',
  dropzone: '.dropzone',
});

droppable.on('droppable:dropped', (event) => {
  console.log('Dropped into dropzone:', event.dropzone);
});

droppable.on('droppable:returned', (event) => {
  console.log('Returned to original position');
});

Element Swap

import Swappable from '@shopify/draggable';

const swappable = new Swappable(document.querySelectorAll('.grid'), {
  draggable: '.grid-item',
});

swappable.on('swappable:swapped', (event) => {
  console.log('Swapped element:', event.swappedElement);
});

Configuration Options

OptionTypeDefaultDescription
------------------------------------
draggableString'.draggable-source'CSS selector for draggable elements
handle`String\null`nullCSS selector for drag handle
delayObject{}Delay before drag starts ({ mouse: ms, touch: ms })
distanceNumber0Minimum pixels to move before dragging
placedTimeoutNumber800Delay before removing placed CSS classes (ms)
pluginsArray[]Additional plugins
sensorsArray[]Additional sensors
classesObjectsee belowCustom CSS class names
announcementsObjectsee belowAccessibility announcements

CSS Class Map

const defaultClasses = {
  'container:dragging': 'draggable-container--is-dragging',
  'source:dragging':    'draggable-source--is-dragging',
  'source:placed':      'draggable-source--placed',
  'container:placed':   'draggable-container--placed',
  'body:dragging':      'draggable--is-dragging',
  'draggable:over':     'draggable--over',
  'container:over':     'draggable-container--over',
  'source:original':    'draggable--original',
  mirror:               'draggable-mirror',
};

Droppable additional classes:

IdentifierDefault ClassDescription
----------------------------------------
droppable:activedraggable-dropzone--activeAccepting drop zones
droppable:occupieddraggable-dropzone--occupiedOccupied drop zones

Excluding Default Plugins/Sensors

new Draggable(containers, {
  exclude: {
    plugins: [],     // List of plugin constructors
    sensors: [],     // List of sensor constructors
  },
});

Events API

Base Events (All Modules)

EventTrigger
----------------
drag:startDrag started
drag:moveDrag moving
drag:overHovering over another draggable element
drag:over:containerHovering over another container
drag:outMoved out of an element
drag:out:containerMoved out of a container
drag:stopDrag stopped
drag:pressurePressure change (Force Touch)
drag:stoppedDrag fully ended

Common Event Properties

draggable.on('drag:start', (event) => {
  event.source;           // Cloned source element
  event.originalSource;   // Original element (display:none)
  event.sourceContainer;  // Source container
  event.sensorEvent;      // Original sensor event
  event.cancel();         // Cancel the drag
});

Sortable Events

EventAdditional Properties
------------------------------
sortable:startstartIndex, startContainer
sortable:sortcurrentIndex, source, over
sortable:sortedoldIndex, newIndex, oldContainer, newContainer
sortable:stopoldIndex, newIndex, oldContainer, newContainer

Droppable Events

EventAdditional Properties
------------------------------
droppable:startdragEvent, dropzone
droppable:droppeddragEvent, dropzone
droppable:returneddragEvent, dropzone
droppable:stopdragEvent, dropzone

Swappable Events

EventAdditional Properties
------------------------------
swappable:startdragEvent
swappable:swapdragEvent, over, overContainer
swappable:swappeddragEvent, swappedElement
swappable:stopdragEvent

Plugins

PluginClassFunction
-------------------------
AnnouncementDraggable.Plugins.AnnouncementLive accessibility announcements during drag
FocusableDraggable.Plugins.FocusableKeyboard focus management
MirrorDraggable.Plugins.MirrorShows mirror element while dragging (enabled by default)
ScrollableDraggable.Plugins.ScrollableAuto-scroll container when dragging near edge
CollidablePlugins.CollidableCollision detection (requires separate import)
ResizeMirrorPlugins.ResizeMirrorAuto-resize mirror element
SnappablePlugins.SnappableSnap to specific positions
SwapAnimationPlugins.SwapAnimationSwap animation
SortAnimationPlugins.SortAnimationSort animation

Collidable

import { Plugins } from '@shopify/draggable';
import Collidable from '@shopify/draggable/build/esm/Plugins/Collidable';

new Draggable(containers, {
  plugins: [Collidable],
});

Collidable events: collidable:in (entered collision), collidable:out (left collision).

Snappable

import Snappable from '@shopify/draggable/build/esm/Plugins/Snappable';
new Draggable(containers, {
  plugins: [Snappable],
});

Sensors

SensorDescriptionDefault
------------------------------
MouseSensorMouse drag
TouchSensorTouch drag
ForceTouchSensorForce Touch pressure
DragSensorNative HTML5 Drag & Drop
import { Draggable } from '@shopify/draggable';
import ForceTouchSensor from '@shopify/draggable/build/esm/Draggable/Sensors/ForceTouchSensor';

new Draggable(containers, {
  sensors: [ForceTouchSensor],
});

Instance Methods

MethodDescription
---------------------
addPlugin(...plugins)Add plugins
removePlugin(...plugins)Remove plugins
addSensor(...sensors)Add sensors
removeSensor(...sensors)Remove sensors
addContainer(...containers)Dynamically add containers
removeContainer(...containers)Dynamically remove containers
on(type, ...callbacks)Bind event listener
off(type, callback)Unbind event listener
trigger(event)Trigger an event
isDragging()Check if currently dragging
getDraggableElements()Get all draggable elements
cancel()Immediately cancel current drag
destroy()Destroy the instance

Common Patterns

Drag with Handle

new Sortable(document.querySelectorAll('.list'), {
  draggable: '.list-item',
  handle: '.drag-handle',   // Only .drag-handle can trigger drag
});

Delay & Minimum Distance (Prevent Accidental Drag)

new Draggable(containers, {
  delay: { mouse: 100, touch: 200 },
  distance: 5,            // Must move 5px before dragging
});

Cross-Container Sorting

const sortable = new Sortable(document.querySelectorAll('.column'), {
  draggable: '.card',
  delay: { touch: 200 },
});
// Cross-container drag is supported automatically

Prevent Specific Drags

draggable.on('drag:start', (event) => {
  if (event.source.dataset.draggable === 'false') {
    event.cancel();
  }
});

Custom Mirror Style

import Draggable from '@shopify/draggable';

const draggable = new Draggable(containers, {
  classes: {
    mirror: 'my-custom-mirror',
  },
});

// Or customize via CSS
// .draggable-mirror { opacity: 0.7; transform: scale(1.05); }

Lifecycle

constructor()
  ↓
draggable:initialized
  ↓
drag:start ──→ drag:move ──→ drag:stop ──→ drag:stopped
  ↓              ↓              ↓
Plugin events  sortable:sorted  sortable:stop
  ↓           droppable:       droppable:stop
cancel()      dropped          swappable:stop
              swappable:
              swapped
  ↓
destroy()

Notes

  • Draggable does not perform sorting by itself — Sortable, Droppable, and Swappable are its subclasses
  • The source element is set to display: none during drag; the mirror takes its visual place
  • No longer maintained by the original Shopify authors; now community-maintained. Evaluate risk for production use
  • TypeScript type definitions are bundled — no need to install @types separately
  • Does not support IE11; targets ES6 modern browsers
  • Use jsdelivr for CDN; avoid unpkg (incompatible paths)

版本历史

共 1 个版本

  • v1.0.0 当前
    2026-06-03 13:43 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

content-creation

Toutiao Graphic Publisher

openlark
通过浏览器自动化在头条发布图文内容,支持智能排版、自动生成热门标签等功能。
★ 2 📥 1,036
dev-programming

Github

steipete
使用 `gh` CLI 与 GitHub 交互,通过 `gh issue`、`gh pr`、`gh run` 和 `gh api` 管理议题、PR、CI 运行及高级查询。
★ 686 📥 331,137
dev-programming

Mcporter

steipete
使用 mcporter CLI 直接列出、配置、认证及调用 MCP 服务器/工具(支持 HTTP 或 stdio),涵盖临时服务器、配置编辑及 CLI/类型生成功能。
★ 198 📥 68,243