stateMarker

stateMarker

Add data attributes to track drag state

The stateMarker plugin adds data attributes that track drag state. These attributes enable CSS styling based on drag status and provide hooks for debugging and analytics.

stateMarker(); // Adds data-neodrag-state and data-neodrag-count attributes

Attributes added:

  • data-neodrag="" - Marks element as draggable
  • data-neodrag-state="idle|dragging" - Current drag state
  • data-neodrag-count="0" - Number of completed drags

Note: StateMarker is included by default and is non-cancelable, so it always runs regardless of other plugin cancellations.

Basic Usage

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

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

  useDraggable(element, [stateMarker()]);

  return (
    <div ref={setElement}>
      Drag me - watch the data attributes change
    </div>
  );
}

Reading State Attributes

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

function StateReader() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );
  const [currentState, setCurrentState] = createSignal('idle');
  const [dragCount, setDragCount] = createSignal(0);

  const updateStateInfo = () => {
    const el = element();
    if (el) {
      const state = el.getAttribute('data-neodrag-state') || 'idle';
      const count = parseInt(
        el.getAttribute('data-neodrag-count') || '0',
      );
      setCurrentState(state);
      setDragCount(count);
    }
  };

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

  return (
    <div ref={setElement}>
      State: {currentState()}
      <br />
      Drag Count: {dragCount()}
    </div>
  );
}

Monitoring State Changes

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

interface Notification {
  id: number;
  message: string;
  timestamp: string;
}

function StateMonitor() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );
  const [notifications, setNotifications] = createSignal<
    Notification[]
  >([]);

  const addNotification = (message: string) => {
    const notification = {
      id: Date.now(),
      message,
      timestamp: new Date().toLocaleTimeString(),
    };

    setNotifications((prev) => [...prev, notification]);

    // Remove after 3 seconds
    setTimeout(() => {
      setNotifications((prev) =>
        prev.filter((n) => n.id !== notification.id),
      );
    }, 3000);
  };

  useDraggable(element, [
    stateMarker(),
    events({
      onDragStart: () => addNotification('Drag started'),
      onDragEnd: (data) => {
        const count = data.rootNode.getAttribute(
          'data-neodrag-count',
        );
        addNotification(`Drag ended (Total: ${count})`);
      },
    }),
  ]);

  return (
    <div>
      <div ref={setElement}>Drag me for notifications</div>

      <div class="notifications">
        <For each={notifications()}>
          {(notification) => (
            <div>
              {notification.timestamp}: {notification.message}
            </div>
          )}
        </For>
      </div>
    </div>
  );
}

Analytics Integration

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

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

  const trackDragAnalytics = (eventName: string, data: any) => {
    // Send to analytics service
    analytics.track(eventName, {
      elementId: data.rootNode.id,
      dragCount: data.rootNode.getAttribute('data-neodrag-count'),
      position: data.offset,
      timestamp: Date.now(),
    });
  };

  useDraggable(element, [
    stateMarker(),
    events({
      onDragStart: (data) => trackDragAnalytics('drag_started', data),
      onDragEnd: (data) => trackDragAnalytics('drag_completed', data),
    }),
  ]);

  return <div ref={setElement}>Analytics-tracked element</div>;
}

Debug Inspector

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

interface DebugInfo {
  state: string;
  count: number;
  isDragging: boolean;
  lastEventTime: string | null;
}

function DebugInspector() {
  const [element, setElement] = createSignal<HTMLElement | null>(
    null,
  );
  const [debugInfo, setDebugInfo] = createSignal<DebugInfo>({
    state: 'idle',
    count: 0,
    isDragging: false,
    lastEventTime: null,
  });

  const updateDebugInfo = (eventType: string, data: any) => {
    setDebugInfo({
      state:
        data.rootNode.getAttribute('data-neodrag-state') || 'idle',
      count: parseInt(
        data.rootNode.getAttribute('data-neodrag-count') || '0',
      ),
      isDragging: eventType === 'drag' || eventType === 'dragStart',
      lastEventTime: new Date().toLocaleTimeString(),
    });
  };

  useDraggable(element, [
    stateMarker(),
    events({
      onDragStart: (data) => updateDebugInfo('dragStart', data),
      onDrag: (data) => updateDebugInfo('drag', data),
      onDragEnd: (data) => updateDebugInfo('dragEnd', data),
    }),
  ]);

  return (
    <div>
      <div ref={setElement}>Drag me to see debug info</div>

      <div class="debug-panel">
        <h3>Debug Information</h3>
        <p>State: {debugInfo().state}</p>
        <p>Total Drags: {debugInfo().count}</p>
        <p>
          Currently Dragging: {debugInfo().isDragging ? 'Yes' : 'No'}
        </p>
        <p>Last Event: {debugInfo().lastEventTime || 'None'}</p>
      </div>
    </div>
  );
}

How It Works

The stateMarker plugin manages data attributes through the drag lifecycle:

  1. Setup phase:

    • Adds data-neodrag="" to mark element
    • Sets data-neodrag-state="idle"
    • Initializes data-neodrag-count="0"
  2. Start phase:

    • Updates data-neodrag-state="dragging"
  3. End phase:

    • Resets data-neodrag-state="idle"
    • Increments data-neodrag-count
  4. Non-cancelable:

    • Always runs regardless of other plugin cancellations
    • Ensures consistent state tracking
// Simplified internal implementation
setup(ctx) {
  setNodeDataset(ctx.rootNode, 'neodrag', '');
  setNodeDataset(ctx.rootNode, 'neodrag-state', 'idle');
  setNodeDataset(ctx.rootNode, 'neodrag-count', '0');
},

start(ctx) {
  setNodeDataset(ctx.rootNode, 'neodrag-state', 'dragging');
},

end(ctx, state) {
  setNodeDataset(ctx.rootNode, 'neodrag-state', 'idle');
  setNodeDataset(ctx.rootNode, 'neodrag-count', ++state.count);
}

API Reference

function stateMarker(): Plugin;

Parameters: None

Data attributes added:

  • data-neodrag="" - Marks element as draggable
  • data-neodrag-state="idle|dragging" - Current drag state
  • data-neodrag-count="0" - Number of completed drags

Behavior:

  • Non-cancelable - always runs
  • Updates attributes automatically during drag lifecycle
  • Increments count only on successful drag completion
  • Provides hooks for CSS styling and JavaScript integration

Returns: A plugin object for use with draggable.