CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-shopify--draggable

The JavaScript Drag & Drop library your grandparents warned you about.

Pending
Overview
Eval results
Files

events.mddocs/

Events

Shopify Draggable provides a comprehensive event system with drag events, specialized events for each draggable type, and plugin-specific events. All events extend AbstractEvent and support cancellation.

Capabilities

Base Event Class

All events inherit from AbstractEvent providing common functionality.

/**
 * Base class for all draggable events
 */
abstract class AbstractEvent<TData = {[key: string]: any}> {
  readonly type: string;
  readonly cancelable: boolean;
  constructor(data: TData);
  cancel(): void;
  canceled(): boolean;
  clone(data?: Partial<TData>): AbstractEvent<TData>;
}

export {AbstractEvent as BaseEvent};

Usage Example:

draggable.on('drag:start', (event) => {
  console.log('Event type:', event.type);
  console.log('Can cancel:', event.cancelable);
  
  // Cancel the event if needed
  if (shouldCancelDrag()) {
    event.cancel();
  }
  
  // Check if event was canceled
  if (event.canceled()) {
    console.log('Event was canceled');
  }
});

Core Drag Events

Base drag events that fire during drag operations for all draggable types.

class DragEvent extends AbstractEvent {
  readonly source: HTMLElement;
  readonly originalSource: HTMLElement;
  readonly mirror: HTMLElement;
  readonly sourceContainer: HTMLElement;
  readonly sensorEvent: SensorEvent;
  readonly originalEvent: Event;
}

class DragStartEvent extends DragEvent {}
class DragMoveEvent extends DragEvent {}
class DragStopEvent extends DragEvent {}
class DragStoppedEvent extends DragEvent {}

class DragOverEvent extends DragEvent {
  readonly overContainer: HTMLElement;
  readonly over: HTMLElement;
}

class DragOutEvent extends DragEvent {
  readonly overContainer: HTMLElement;
  readonly over: HTMLElement;
}

class DragOverContainerEvent extends DragEvent {
  readonly overContainer: HTMLElement;
}

class DragOutContainerEvent extends DragEvent {
  readonly overContainer: HTMLElement;
}

class DragPressureEvent extends DragEvent {
  readonly pressure: number;
}

Event Names:

type DraggableEventNames =
  | 'draggable:initialize'
  | 'draggable:destroy'
  | 'drag:start'
  | 'drag:move'
  | 'drag:over'
  | 'drag:over:container'
  | 'drag:out'
  | 'drag:out:container'
  | 'drag:stop'
  | 'drag:stopped'
  | 'drag:pressure'
  | MirrorEventNames;

Usage Example:

draggable.on('drag:start', (event) => {
  console.log('Started dragging:', event.source);
  console.log('Original element:', event.originalSource);
  console.log('From container:', event.sourceContainer);
});

draggable.on('drag:over', (event) => {
  console.log('Dragging over:', event.over);
  console.log('In container:', event.overContainer);
});

draggable.on('drag:pressure', (event) => {
  console.log('Pressure level:', event.pressure);
  // Handle force touch or pressure-sensitive input
  if (event.pressure > 0.8) {
    showContextMenu(event.source);
  }
});

Draggable Lifecycle Events

Events specific to Draggable instance lifecycle.

class DraggableEvent extends AbstractEvent {
  readonly draggable: Draggable;
}

class DraggableInitializedEvent extends DraggableEvent {}
class DraggableDestroyEvent extends DraggableEvent {}

Usage Example:

draggable.on('draggable:initialize', (event) => {
  console.log('Draggable initialized:', event.draggable);
  setupDragCallbacks(event.draggable);
});

draggable.on('draggable:destroy', (event) => {
  console.log('Draggable being destroyed');
  cleanupDragCallbacks();
});

Sortable Events

Events specific to sortable operations.

class SortableEvent extends AbstractEvent {
  readonly dragEvent: DragEvent;
}

class SortableStartEvent extends SortableEvent {
  readonly startIndex: number;
  readonly startContainer: HTMLElement;
}

class SortableSortEvent extends SortableEvent {
  readonly oldIndex: number;
  readonly newIndex: number;
  readonly oldContainer: HTMLElement;
  readonly newContainer: HTMLElement;
}

class SortableSortedEvent extends SortableEvent {
  readonly oldIndex: number;
  readonly newIndex: number;
  readonly oldContainer: HTMLElement;
  readonly newContainer: HTMLElement;
}

class SortableStopEvent extends SortableEvent {
  readonly oldIndex: number;
  readonly newIndex: number;
  readonly oldContainer: HTMLElement;
  readonly newContainer: HTMLElement;
}

Event Names:

type SortableEventNames =
  | 'sortable:start'
  | 'sortable:sort'
  | 'sortable:sorted'
  | 'sortable:stop'
  | DraggableEventNames;

Droppable Events

Events specific to droppable operations.

class DroppableEvent extends AbstractEvent {
  readonly dragEvent: DragEvent;
}

class DroppableStartEvent extends DroppableEvent {
  dropzone: HTMLElement;
}

class DroppableDroppedEvent extends DroppableEvent {
  dropzone: HTMLElement;
}

class DroppableReturnedEvent extends DroppableEvent {
  dropzone: HTMLElement;
}

class DroppableStopEvent extends DroppableEvent {
  dropzone: HTMLElement;
}

Event Names:

type DroppableEventNames =
  | 'droppable:start'
  | 'droppable:dropped'
  | 'droppable:returned'
  | 'droppable:stop'
  | DraggableEventNames;

Swappable Events

Events specific to swappable operations.

class SwappableEvent extends AbstractEvent {
  readonly dragEvent: DragEvent;
}

class SwappableStartEvent extends SwappableEvent {}

class SwappableSwapEvent extends SwappableEvent {
  readonly over: HTMLElement;
  readonly overContainer: HTMLElement;
}

class SwappableSwappedEvent extends SwappableEvent {
  readonly swappedElement: HTMLElement;
}

class SwappableStopEvent extends SwappableEvent {}

Event Names:

type SwappableEventNames =
  | 'swappable:start'
  | 'swappable:swap'
  | 'swappable:swapped'
  | 'swappable:stop'
  | DraggableEventNames;

Mirror Events

Events related to the mirror plugin that creates drag representations.

class MirrorEvent extends AbstractEvent {
  readonly source: HTMLElement;
  readonly originalSource: HTMLElement;
  readonly sourceContainer: HTMLElement;
  readonly sensorEvent: SensorEvent;
  readonly originalEvent: Event;
}

class MirrorCreateEvent extends MirrorEvent {}

class MirrorCreatedEvent extends MirrorEvent {
  readonly mirror: HTMLElement;
}

class MirrorAttachedEvent extends MirrorEvent {
  readonly mirror: HTMLElement;
}

class MirrorMoveEvent extends MirrorEvent {
  readonly mirror: HTMLElement;
  readonly passedThreshX: boolean;
  readonly passedThreshY: boolean;
}

class MirrorMovedEvent extends MirrorEvent {
  readonly mirror: HTMLElement;
  readonly passedThreshX: boolean;
  readonly passedThreshY: boolean;
}

class MirrorDestroyEvent extends MirrorEvent {
  readonly mirror: HTMLElement;
}

Event Names:

type MirrorEventNames =
  | 'mirror:create'
  | 'mirror:created'
  | 'mirror:attached'
  | 'mirror:move'
  | 'mirror:moved'
  | 'mirror:destroy';

Plugin Events

Events from additional plugins.

// Collidable Plugin Events
class CollidableEvent extends AbstractEvent {
  readonly dragEvent: DragEvent;
  readonly collidingElement: HTMLElement;
}

class CollidableInEvent extends CollidableEvent {}
class CollidableOutEvent extends CollidableEvent {}

type CollidableEventNames = 'collidable:in' | 'collidable:out';

// Snappable Plugin Events
class SnapEvent extends AbstractEvent {
  readonly dragEvent: DragEvent;
  readonly snappable: HTMLElement;
}

class SnapInEvent extends SnapEvent {}
class SnapOutEvent extends SnapEvent {}

type SnappableEventNames = 'snap:in' | 'snap:out';

Event Type Helper

TypeScript utility type for getting specific event types from event names.

type GetEventByEventName<TEventName> =
  TEventName extends 'draggable:initialize'
    ? DraggableInitializedEvent
    : TEventName extends 'drag:start'
    ? DragStartEvent
    : TEventName extends 'sortable:sorted'
    ? SortableSortedEvent
    : TEventName extends 'droppable:dropped'
    ? DroppableDroppedEvent
    : TEventName extends 'swappable:swapped'
    ? SwappableSwappedEvent
    : AbstractEvent;

Usage Example:

// TypeScript will correctly infer the event type
draggable.on('drag:start', (event) => {
  // event is correctly typed as DragStartEvent
  console.log(event.source, event.originalSource);
});

sortable.on('sortable:sorted', (event) => {
  // event is correctly typed as SortableSortedEvent
  console.log(event.oldIndex, event.newIndex);
});

Complete Event Example

import { Draggable } from "@shopify/draggable";

const draggable = new Draggable(containers, options);

// Set up comprehensive event logging
const events = [
  'draggable:initialize',
  'drag:start',
  'drag:move', 
  'drag:over',
  'drag:out',
  'drag:stop',
  'drag:stopped',
  'mirror:create',
  'mirror:created',
  'mirror:move',
  'mirror:destroy'
];

events.forEach(eventName => {
  draggable.on(eventName, (event) => {
    console.log(`Event: ${eventName}`, {
      type: event.type,
      cancelable: event.cancelable,
      canceled: event.canceled(),
      timestamp: Date.now()
    });
  });
});

// Handle cancellation scenarios
draggable.on('drag:start', (event) => {
  // Cancel drag if element is disabled
  if (event.source.hasAttribute('disabled')) {
    event.cancel();
    showMessage('This item cannot be moved');
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-shopify--draggable

docs

core-draggable.md

droppable.md

events.md

index.md

plugins.md

sensors.md

sortable.md

swappable.md

tile.json