CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sortablejs

JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices

Pending
Overview
Eval results
Files

plugins.mddocs/

Plugins

Extensible plugin architecture for advanced functionality like multi-drag, auto-scroll, and element swapping.

Capabilities

Plugin Registration

Mount plugins to extend Sortable functionality before creating sortable instances.

/**
 * Mount one or more plugins to extend Sortable functionality
 * @param plugins - Plugin instances to register
 */
Sortable.mount(...plugins: SortablePlugin[]): void;

Usage Examples:

import Sortable, { MultiDrag, AutoScroll, Swap, OnSpill } from "sortablejs";

// Mount individual plugins
Sortable.mount(new MultiDrag());
Sortable.mount(new AutoScroll());

// Mount multiple plugins at once
Sortable.mount(new MultiDrag(), new AutoScroll(), new Swap());

// Now create sortables with plugin functionality
const sortable = Sortable.create(el, {
  multiDrag: true,        // MultiDrag plugin option
  selectedClass: 'selected',
  scroll: true,           // AutoScroll plugin option
  scrollSensitivity: 100
});

MultiDrag Plugin

Enables selection and simultaneous dragging of multiple elements.

/**
 * MultiDrag plugin options
 */
interface MultiDragOptions {
  /** Enable multi-drag functionality */
  multiDrag?: boolean;
  
  /** CSS class for selected elements */
  selectedClass?: string;
  
  /** Key to hold for multi-selection (e.g., 'ctrl', 'meta') */
  multiDragKey?: string | null;
  
  /** Avoid implicit deselection when clicking outside */
  avoidImplicitDeselect?: boolean;
}

Usage Examples:

import Sortable, { MultiDrag } from "sortablejs";

Sortable.mount(new MultiDrag());

const sortable = Sortable.create(el, {
  multiDrag: true,
  selectedClass: 'selected',
  multiDragKey: 'ctrl', // Ctrl+click to select multiple
  
  onSelect: (evt) => {
    console.log('Item selected:', evt.item);
  },
  
  onDeselect: (evt) => {
    console.log('Item deselected:', evt.item);
  }
});

// Programmatically select items
sortable.select(document.getElementById('item1'));
sortable.select(document.getElementById('item2'));

// Get selected items
const selected = sortable.selectedElements; // HTMLElement[]

// Deselect all
sortable.deselectAll();

AutoScroll Plugin

Automatically scrolls containers when dragging near edges.

/**
 * AutoScroll plugin options
 */
interface AutoScrollOptions {
  /** Enable auto-scrolling */
  scroll?: boolean;
  
  /** Auto-scroll container (defaults to window) */
  scrollSensitivity?: number;
  
  /** Auto-scroll speed */
  scrollSpeed?: number;
  
  /** Pixels from edge to trigger scrolling */
  bubbleScroll?: boolean;
}

Usage Examples:

import Sortable, { AutoScroll } from "sortablejs";

Sortable.mount(new AutoScroll());

const sortable = Sortable.create(el, {
  scroll: true,
  scrollSensitivity: 100, // 100px from edge triggers scroll
  scrollSpeed: 20,        // Scroll speed
  bubbleScroll: true      // Allow scrolling in nested containers
});

Swap Plugin

Enables element swapping instead of insertion-based reordering.

/**
 * Swap plugin options
 */
interface SwapOptions {
  /** Enable swap mode instead of insertion */
  swap?: boolean;
  
  /** CSS class applied to swap target */
  swapClass?: string;
}

Usage Examples:

import Sortable, { Swap } from "sortablejs";

Sortable.mount(new Swap());

const sortable = Sortable.create(el, {
  swap: true,
  swapClass: 'highlight-swap',
  
  onSwap: (evt) => {
    console.log('Elements swapped:', evt.item, evt.swapItem);
  }
});

OnSpill Plugin

Handles elements that are dragged outside valid drop zones.

/**
 * OnSpill plugin options
 */
interface OnSpillOptions {
  /** Remove element when dragged outside valid drop zone */
  removeOnSpill?: boolean;
  
  /** Revert element to original position when spilled */
  revertOnSpill?: boolean;
}

Usage Examples:

import Sortable, { OnSpill } from "sortablejs";

Sortable.mount(new OnSpill());

const sortable = Sortable.create(el, {
  removeOnSpill: true, // Remove element when dragged out
  
  onSpill: (evt) => {
    console.log('Element spilled:', evt.item);
    // Custom handling for spilled elements
  }
});

// Alternative: revert instead of remove
const revertSortable = Sortable.create(el2, {
  revertOnSpill: true, // Return to original position when spilled
});

Plugin Events

MultiDrag Events

Additional events available when MultiDrag plugin is active.

/** Called when an element is selected for multi-drag */
onSelect?: (evt: SortableEvent) => void;

/** Called when an element is deselected */
onDeselect?: (evt: SortableEvent) => void;

Swap Events

Additional events available when Swap plugin is active.

/** Called when two elements are swapped */
onSwap?: (evt: SortableEvent & { swapItem: HTMLElement }) => void;

OnSpill Events

Additional events available when OnSpill plugin is active.

/** Called when an element is dragged outside a valid drop zone */
onSpill?: (evt: SortableEvent) => void;

Custom Plugin Development

Plugin Interface

Structure for creating custom Sortable plugins.

interface SortablePlugin {
  /** Plugin name (must be unique) */
  name: string;
  
  /** Default options merged into Sortable defaults */
  defaults?: { [key: string]: any };
  
  /** Event options configuration for plugin events */
  eventOptions?: { [key: string]: any };
  
  /** Option change listeners for dynamic configuration */
  optionListeners?: { [optionName: string]: (value: any) => any };
  
  /** Plugin utilities to extend Sortable.utils */
  utils?: { [key: string]: any };
  
  /** Custom event properties injected into SortableEvent */
  eventProperties?: (eventName: string) => { [key: string]: any };
  
  /** Initialize plugin on sortable instance creation */
  initializePlugin?: (sortable: Sortable) => void;
  
  /** Plugin-specific methods available on sortable instance */
  [key: string]: any;
}

Custom Plugin Example:

// Comprehensive custom plugin demonstrating all interface features
const AdvancedLoggerPlugin = {
  name: 'advancedLogger',
  
  // Default options merged into Sortable defaults
  defaults: {
    logLevel: 'info',
    logToConsole: true,
    logToServer: false,
    serverEndpoint: '/api/drag-logs'
  },
  
  // Custom utilities added to Sortable.utils
  utils: {
    formatLogMessage: (level, action, item) => {
      return `[${new Date().toISOString()}] ${level.toUpperCase()}: ${action} - ${item.textContent}`;
    }
  },
  
  // Custom event properties injected into all events
  eventProperties: function(eventName) {
    return {
      logTimestamp: Date.now(),
      sessionId: this.sessionId
    };
  },
  
  // Option change listeners for dynamic reconfiguration
  optionListeners: {
    logLevel: function(newLevel) {
      console.log(`Logger level changed to: ${newLevel}`);
      return newLevel;
    }
  },
  
  // Initialize plugin on sortable instance
  initializePlugin(sortable) {
    // Generate unique session ID
    this.sessionId = Math.random().toString(36).substr(2, 9);
    
    // Store reference for event handlers
    const plugin = this;
    const options = sortable.options;
    
    // Enhanced logging methods on sortable instance
    sortable.logDragAction = function(action, item) {
      const message = plugin.utils.formatLogMessage(options.logLevel, action, item);
      
      if (options.logToConsole) {
        console.log(message);
      }
      
      if (options.logToServer) {
        fetch(options.serverEndpoint, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            sessionId: plugin.sessionId,
            action,
            timestamp: Date.now(),
            item: item.textContent
          })
        });
      }
    };
    
    // Hook into events
    const originalOnStart = options.onStart;
    const originalOnEnd = options.onEnd;
    
    options.onStart = (evt) => {
      sortable.logDragAction('DRAG_START', evt.item);
      originalOnStart && originalOnStart(evt);
    };
    
    options.onEnd = (evt) => {
      sortable.logDragAction('DRAG_END', evt.item);
      originalOnEnd && originalOnEnd(evt);
    };
  }
};

// Mount and use the advanced plugin
Sortable.mount(AdvancedLoggerPlugin);

const sortable = Sortable.create(el, {
  logLevel: 'debug',
  logToServer: true,
  serverEndpoint: '/api/custom-logs',
  
  onAdd: (evt) => {
    // Custom event properties are automatically available
    console.log('Session ID:', evt.sessionId);
    console.log('Log timestamp:', evt.logTimestamp);
  }
});

// Plugin methods are available on sortable instance
sortable.logDragAction('CUSTOM_ACTION', document.getElementById('item1'));

// Plugin utilities are available globally
console.log(Sortable.utils.formatLogMessage('warn', 'TEST', { textContent: 'Test Item' }));

Plugin Combinations

Comprehensive Setup

Example showing multiple plugins working together.

import Sortable, { MultiDrag, AutoScroll, Swap, OnSpill } from "sortablejs";

// Mount all plugins
Sortable.mount(new MultiDrag(), new AutoScroll(), new Swap(), new OnSpill());

const sortable = Sortable.create(el, {
  // MultiDrag options
  multiDrag: true,
  selectedClass: 'selected',
  multiDragKey: 'ctrl',
  
  // AutoScroll options
  scroll: true,
  scrollSensitivity: 100,
  scrollSpeed: 20,
  
  // Swap options (mutually exclusive with normal sorting)
  // swap: true,
  // swapClass: 'highlight-swap',
  
  // OnSpill options
  revertOnSpill: true,
  
  // Event handlers for plugin functionality
  onSelect: (evt) => console.log('Selected:', evt.item),
  onDeselect: (evt) => console.log('Deselected:', evt.item),
  onSpill: (evt) => console.log('Spilled:', evt.item)
});

Available Plugins Summary

/**
 * MultiDrag - Select and drag multiple elements simultaneously
 */
class MultiDrag implements SortablePlugin {
  name: 'multiDrag';
  select(element: HTMLElement): void;
  deselect(element: HTMLElement): void;
  deselectAll(): void;
  selectedElements: HTMLElement[];
}

/**
 * AutoScroll - Automatic scrolling when dragging near container edges
 */
class AutoScroll implements SortablePlugin {
  name: 'autoScroll';
}

/**
 * Swap - Element swapping instead of insertion-based reordering
 */
class Swap implements SortablePlugin {
  name: 'swap';
}

/**
 * OnSpill - Handle elements dragged outside valid drop zones
 */
class OnSpill implements SortablePlugin {
  name: 'onSpill';
}

Install with Tessl CLI

npx tessl i tessl/npm-sortablejs

docs

configuration.md

core-api.md

events.md

index.md

plugins.md

utilities.md

tile.json