events

events

Listen to drag start, drag, and drag end events

The events plugin provides callbacks that fire during different phases of dragging. Get notified when dragging starts, during movement, and when it ends - perfect for updating UI state, logging, or triggering animations.

events({
  onDragStart: (data) => console.log('Started dragging'),
  onDrag: (data) => console.log('Moving:', data.offset),
  onDragEnd: (data) => console.log('Finished dragging'),
});

Basic Usage

import { createSignal } from 'solid-js';
import { events, useDraggable } from '@neodrag/solid';

function EventExample() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );
  const [dragStatus, setDragStatus] = createSignal('idle');
  const [position, setPosition] = createSignal({ x: 0, y: 0 });

  useDraggable(element, [
    events({
      onDragStart: () => {
        setDragStatus('dragging');
      },

      onDrag: (data) => {
        setPosition({ x: data.offset.x, y: data.offset.y });
      },

      onDragEnd: () => {
        setDragStatus('idle');
      },
    }),
  ]);

  return (
    <div ref={setElement}>
      Status: {dragStatus()}
      <br />
      Position: {position().x}, {position().y}
    </div>
  );
}

Event Data

All event callbacks receive a DragEventData object with:

  • offset - Current position { x: number, y: number }
  • rootNode - Element with draggable applied
  • currentNode - Element being dragged (can change mid-drag)
  • event - The pointer event that triggered this callback
import { createSignal } from 'solid-js';
import { events, useDraggable } from '@neodrag/solid';

function EventDataExample() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );

  const logEventData = (data) => {
    console.log('Position:', data.offset);
    console.log('Root element:', data.rootNode);
    console.log('Current element:', data.currentNode);
    console.log('Pointer event:', data.event);
  };

  useDraggable(element, [
    events({
      onDragStart: logEventData,
      onDrag: logEventData,
      onDragEnd: logEventData,
    }),
  ]);

  return <div ref={setElement}>Check console for event data</div>;
}

Real-World Examples

Position Tracking with Velocity

import { createSignal } from 'solid-js';
import { events, useDraggable } from '@neodrag/solid';

function VelocityTracker() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );
  const [position, setPosition] = createSignal({ x: 0, y: 0 });
  const [velocity, setVelocity] = createSignal({ x: 0, y: 0 });

  let lastTime = 0;
  let lastPosition = { x: 0, y: 0 };

  useDraggable(element, [
    events({
      onDrag: (data) => {
        const now = Date.now();
        const deltaTime = now - lastTime;

        if (deltaTime > 0) {
          const velocityX = Math.round(
            ((data.offset.x - lastPosition.x) / deltaTime) * 1000,
          );
          const velocityY = Math.round(
            ((data.offset.y - lastPosition.y) / deltaTime) * 1000,
          );
          setVelocity({ x: velocityX, y: velocityY });
        }

        setPosition({ ...data.offset });
        lastPosition = { ...data.offset };
        lastTime = now;
      },
    }),
  ]);

  return (
    <div ref={setElement}>
      Position: ({position().x}, {position().y})
      <br />
      Velocity: ({velocity().x}px/s, {velocity().y}px/s)
    </div>
  );
}

Save Position to Storage

import { createSignal } from 'solid-js';
import { events, useDraggable } from '@neodrag/solid';

function SavePosition() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );

  useDraggable(element, [
    events({
      onDragEnd: (data) => {
        localStorage.setItem(
          'elementPosition',
          JSON.stringify({
            x: data.offset.x,
            y: data.offset.y,
            timestamp: Date.now(),
          }),
        );
        console.log('Position saved!');
      },
    }),
  ]);

  return (
    <div ref={setElement}>Position saves when you stop dragging</div>
  );
}

Analytics Tracking

// Track user interactions for analytics
events({
  onDragStart: (data) => {
    analytics.track('drag_started', {
      element: data.rootNode.id,
      timestamp: Date.now(),
    });
  },

  onDragEnd: (data) => {
    analytics.track('drag_completed', {
      element: data.rootNode.id,
      finalPosition: data.offset,
      timestamp: Date.now(),
    });
  },
});

Progress Indicator

// Show progress based on how far element has moved
const maxDistance = 300;

events({
  onDragStart: () => {
    progressBar.style.display = 'block';
  },

  onDrag: (data) => {
    const distance = Math.sqrt(
      data.offset.x ** 2 + data.offset.y ** 2,
    );
    const progress = Math.min((distance / maxDistance) * 100, 100);
    progressBar.style.width = `${progress}%`;
  },

  onDragEnd: () => {
    progressBar.style.display = 'none';
  },
});

How It Works

The events plugin:

  1. Stores event data in its state during each hook
  2. Schedules callbacks using ctx.effect.immediate() for fast execution
  3. Provides consistent data across all three event types
  4. Non-cancelable - Always runs regardless of other plugin cancellations

The callbacks fire in this order:

  • onDragStartonDrag (repeatedly) → onDragEnd

API Reference

function events(options?: {
  onDragStart?: (data: DragEventData) => void;
  onDrag?: (data: DragEventData) => void;
  onDragEnd?: (data: DragEventData) => void;
}): Plugin;

Options:

  • onDragStart - Called when dragging begins
  • onDrag - Called repeatedly during dragging
  • onDragEnd - Called when dragging ends

DragEventData:

  • offset - Current position { x: number, y: number }
  • rootNode - Element with draggable applied
  • currentNode - Element being dragged
  • event - The pointer event that triggered this callback

Returns: A plugin object for use with draggable.