or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

external-dragging.mdindex.mdinteraction-types.mdthird-party-integration.md
tile.json

third-party-integration.mddocs/

Third-Party Drag Integration

Third-party drag integration allows you to bridge existing drag-and-drop libraries (like Sortable.js, dragula, or custom implementations) with FullCalendar. This provides flexibility to use your preferred drag system while maintaining FullCalendar's drop handling.

Capabilities

ThirdPartyDraggable Class

Bridges third-party drag-and-drop systems with FullCalendar by detecting drag operations and translating them into FullCalendar-compatible events.

/**
 * Bridges third-party drag-n-drop systems with FullCalendar
 * Must be instantiated and destroyed by caller
 * @param containerOrSettings - Event target container or settings object
 * @param settings - Configuration options when first param is container
 */
export class ThirdPartyDraggable {
  constructor(
    containerOrSettings?: EventTarget | ThirdPartyDraggableSettings,
    settings?: ThirdPartyDraggableSettings
  );
  
  /** Destroys the third-party draggable instance and removes listeners */
  destroy(): void;
  
  /** Internal dragging implementation (read-only) */
  readonly dragging: InferredElementDragging;
}

Usage Examples:

import { ThirdPartyDraggable } from '@fullcalendar/interaction';

// Global drag detection (detects drags anywhere in document)
const globalDraggable = new ThirdPartyDraggable({
  itemSelector: '[data-event]',
  eventData: (el) => ({
    title: el.dataset.title,
    duration: el.dataset.duration
  })
});

// Container-specific drag detection
const containerEl = document.getElementById('external-container');
const containerDraggable = new ThirdPartyDraggable(containerEl, {
  itemSelector: '.draggable-event',
  mirrorSelector: '.drag-mirror',
  eventData: {
    title: 'Third-party Event',
    backgroundColor: '#ff9f43'
  }
});

// Cleanup
globalDraggable.destroy();
containerDraggable.destroy();

ThirdPartyDraggableSettings

Configuration interface for third-party drag integration.

interface ThirdPartyDraggableSettings {
  /** Event data or function to generate event data when dropped */
  eventData?: DragMetaGenerator;
  
  /** CSS selector for draggable items */
  itemSelector?: string;
  
  /** CSS selector for drag mirror element */
  mirrorSelector?: string;
}

Property Details:

  • eventData: Same as ExternalDraggable - defines event data for dropped elements
  • itemSelector: CSS selector to identify draggable items (defaults to [data-event] for document container)
  • mirrorSelector: CSS selector to identify the drag mirror/ghost element created by third-party library

Constructor Variations

The ThirdPartyDraggable constructor supports multiple patterns:

// Pattern 1: Settings only (uses document as container)
new ThirdPartyDraggable(settings);

// Pattern 2: Container and settings
new ThirdPartyDraggable(container, settings);

// Pattern 3: Document container explicitly
new ThirdPartyDraggable(document, settings);

Pattern Examples:

// Document-wide detection with default selector
const docDraggable = new ThirdPartyDraggable({
  eventData: { title: 'Document Event' }
}); // Uses [data-event] selector by default

// Specific container
const listContainer = document.getElementById('sortable-list');
const listDraggable = new ThirdPartyDraggable(listContainer, {
  itemSelector: '.list-item',
  eventData: (el) => ({ title: el.textContent })
});

// Document with custom selector
const customDraggable = new ThirdPartyDraggable(document, {
  itemSelector: '.custom-draggable',
  eventData: { title: 'Custom Event' }
});

Integration with Popular Libraries

Sortable.js Integration:

import Sortable from 'sortablejs';
import { ThirdPartyDraggable } from '@fullcalendar/interaction';

// Set up Sortable.js
const listEl = document.getElementById('sortable-list');
const sortable = Sortable.create(listEl, {
  group: {
    name: 'shared',
    pull: 'clone',
    put: false
  },
  sort: false,
  ghostClass: 'sortable-ghost'
});

// Bridge with FullCalendar
const thirdPartyDraggable = new ThirdPartyDraggable(listEl, {
  itemSelector: '.sortable-item',
  mirrorSelector: '.sortable-ghost',
  eventData: (el) => ({
    title: el.textContent,
    backgroundColor: el.dataset.color,
    duration: el.dataset.duration || '01:00'
  })
});

Custom Drag Library Integration:

// Assuming a custom drag library that adds 'dragging' class
const customDraggable = new ThirdPartyDraggable({
  itemSelector: '.my-draggable',
  mirrorSelector: '.dragging', // Element with dragging class
  eventData: (el) => {
    const config = JSON.parse(el.dataset.eventConfig || '{}');
    return {
      title: config.title || el.textContent,
      backgroundColor: config.color || '#007bff',
      extendedProps: config.props || {}
    };
  }
});

Advanced Usage Patterns

Multiple Third-Party Sources:

// Different drag sources with different configurations
const sidebarDraggable = new ThirdPartyDraggable(
  document.getElementById('sidebar'),
  {
    itemSelector: '.sidebar-item',
    eventData: (el) => ({
      title: el.dataset.title,
      backgroundColor: '#28a745',
      extendedProps: { source: 'sidebar' }
    })
  }
);

const toolboxDraggable = new ThirdPartyDraggable(
  document.getElementById('toolbox'),
  {
    itemSelector: '.tool-item',
    eventData: (el) => ({
      title: el.dataset.name,
      backgroundColor: '#dc3545',
      extendedProps: { 
        source: 'toolbox',
        toolType: el.dataset.type
      }
    })
  }
);

Dynamic Event Data with Complex Logic:

const complexDraggable = new ThirdPartyDraggable({
  itemSelector: '.complex-item',
  eventData: (el) => {
    // Complex logic based on element state
    const isUrgent = el.classList.contains('urgent');
    const category = el.dataset.category;
    const baseColor = el.style.backgroundColor;
    
    return {
      title: el.querySelector('.item-title').textContent,
      backgroundColor: isUrgent ? '#ff0000' : baseColor,
      borderColor: isUrgent ? '#cc0000' : 'transparent',
      duration: isUrgent ? '00:30' : '01:00',
      extendedProps: {
        category,
        urgent: isUrgent,
        originalId: el.id,
        metadata: JSON.parse(el.dataset.metadata || '{}')
      }
    };
  }
});

Integration with FullCalendar Events

Third-party dragging integrates with the same FullCalendar event system:

const calendar = new Calendar(calendarEl, {
  plugins: [interactionPlugin],
  
  drop: (info) => {
    console.log('Third-party element dropped:', info.draggedEl);
    // info.draggedEl is the original dragged element
    // info.jsEvent is the drop event
    // info.date, info.dateStr, info.allDay provide drop location info
  },
  
  eventReceive: (info) => {
    console.log('Event created from third-party drag:', info.event.title);
    // info.event is the newly created event
    // info.relatedEvents are any related events
    // info.revert() can undo the operation
  }
});

Important Considerations

Pointer Events: The third-party drag system automatically disables pointer event checking for drag mirrors, since third-party libraries may not follow FullCalendar's pointer event conventions.

Mirror Detection: If your third-party library creates drag mirrors/ghosts, specify the mirrorSelector to help FullCalendar identify the visual feedback element.

Performance: For document-wide detection, consider using specific containers when possible to reduce event listener overhead.

Cleanup: Always call destroy() when removing third-party draggable instances to prevent memory leaks.