CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-draggable

React components for adding drag-and-drop functionality to React applications with position management and boundary controls

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

draggable-core.mddocs/

DraggableCore Component

DraggableCore is a low-level, stateless React component that provides drag events without any position management or visual transforms. It gives complete control over drag behavior to the parent component, making it perfect for custom drag implementations, complex interactions, or when you need full control over positioning logic.

Capabilities

Core Component

The stateless draggable component that only provides drag events.

/**
 * A low-level draggable component with no internal state
 * Provides drag events but no position management
 * @param props - DraggableCore configuration properties
 */
function DraggableCore(props: DraggableCoreProps): React.ReactElement;

interface DraggableCoreProps {
  // Interaction controls
  handle?: string;
  cancel?: string;
  disabled?: boolean;
  allowAnyClick?: boolean;
  allowMobileScroll?: boolean;
  
  // Event handlers (required for functionality)
  onStart?: DraggableEventHandler;
  onDrag?: DraggableEventHandler;
  onStop?: DraggableEventHandler;
  onMouseDown?: (e: MouseEvent) => void;
  
  // Advanced options
  enableUserSelectHack?: boolean;
  offsetParent?: HTMLElement;
  grid?: [number, number];
  scale?: number;
  nodeRef?: React.RefObject<HTMLElement>;
  
  // Required
  children: React.ReactNode;
}

Usage Examples:

import React, { useState } from 'react';
import { DraggableCore } from 'react-draggable';

// Basic DraggableCore with manual positioning
function CustomDraggable() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [dragging, setDragging] = useState(false);
  
  const handleStart = (e, data) => {
    setDragging(true);
  };
  
  const handleDrag = (e, data) => {
    setPosition({
      x: position.x + data.deltaX,
      y: position.y + data.deltaY
    });
  };
  
  const handleStop = (e, data) => {
    setDragging(false);
  };
  
  return (
    <DraggableCore
      onStart={handleStart}
      onDrag={handleDrag}
      onStop={handleStop}
    >
      <div
        style={{
          transform: `translate(${position.x}px, ${position.y}px)`,
          backgroundColor: dragging ? '#ff0000' : '#0000ff',
          padding: '10px',
          cursor: dragging ? 'grabbing' : 'grab'
        }}
      >
        Custom draggable with manual positioning
      </div>
    </DraggableCore>
  );
}

Event Handling

DraggableCore relies entirely on event handlers for functionality since it maintains no internal state.

/**
 * Called when drag starts
 * Must be provided to handle drag initiation
 * Return false to cancel the drag
 */
onStart?: DraggableEventHandler;

/**
 * Called continuously during drag movement
 * Must be provided to handle position updates
 * Return false to stop the current drag
 */
onDrag?: DraggableEventHandler;

/**
 * Called when drag ends
 * Handle final positioning and cleanup
 */
onStop?: DraggableEventHandler;

/**
 * Called on mouse down events before drag consideration
 */
onMouseDown?: (e: MouseEvent) => void;

type DraggableEventHandler = (
  e: DraggableEvent,
  data: DraggableData
) => void | false;

interface DraggableData {
  node: HTMLElement;    // The dragged DOM element
  x: number;           // Absolute x position relative to offset parent
  y: number;           // Absolute y position relative to offset parent
  deltaX: number;      // Change in x since last drag event
  deltaY: number;      // Change in y since last drag event
  lastX: number;       // Previous absolute x position
  lastY: number;       // Previous absolute y position
}

Usage Examples:

import React, { useState, useRef } from 'react';
import { DraggableCore } from 'react-draggable';

// Complex drag behavior with momentum
function MomentumDraggable() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [velocity, setVelocity] = useState({ x: 0, y: 0 });
  const lastTime = useRef(Date.now());
  
  const handleDrag = (e, data) => {
    const now = Date.now();
    const dt = (now - lastTime.current) / 1000;
    
    // Calculate velocity
    const vx = data.deltaX / dt;
    const vy = data.deltaY / dt;
    setVelocity({ x: vx, y: vy });
    
    // Update position
    setPosition({
      x: position.x + data.deltaX,
      y: position.y + data.deltaY
    });
    
    lastTime.current = now;
  };
  
  const handleStop = (e, data) => {
    // Apply momentum after drag stops
    const momentumX = velocity.x * 0.1;
    const momentumY = velocity.y * 0.1;
    
    setPosition({
      x: position.x + momentumX,
      y: position.y + momentumY
    });
  };
  
  return (
    <DraggableCore onDrag={handleDrag} onStop={handleStop}>
      <div
        style={{
          transform: `translate(${position.x}px, ${position.y}px)`,
          transition: velocity.x !== 0 ? 'transform 0.3s ease-out' : 'none'
        }}
      >
        Momentum draggable
      </div>
    </DraggableCore>
  );
}

Interaction Controls

Configure drag handles, cancel zones, and interaction behavior (same as Draggable).

/**
 * CSS selector for elements that can initiate drag
 */
handle?: string;

/**
 * CSS selector for elements that prevent drag initiation
 */
cancel?: string;

/**
 * Completely disable drag functionality
 */
disabled?: boolean;

/**
 * Allow dragging with any mouse button
 */
allowAnyClick?: boolean;

/**
 * Allow normal mobile scrolling behavior
 */
allowMobileScroll?: boolean;

Usage Examples:

// DraggableCore with handle and cancel
function HandleDraggableCore() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  
  const handleDrag = (e, data) => {
    setPosition({
      x: position.x + data.deltaX,
      y: position.y + data.deltaY
    });
  };
  
  return (
    <DraggableCore
      handle=".drag-handle"
      cancel=".no-drag"
      onDrag={handleDrag}
    >
      <div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
        <div className="drag-handle" style={{ cursor: 'grab' }}>
          ✋ Drag Handle
        </div>
        <div>
          <button className="no-drag">Button (not draggable)</button>
          <p>Content area</p>
        </div>
      </div>
    </DraggableCore>
  );
}

Grid and Scaling

Apply grid snapping and scaling to drag calculations.

/**
 * Snap drag movements to grid positions
 * [x, y] - grid cell size in pixels
 */
grid?: [number, number];

/**
 * Scale factor for drag distance calculations
 * Useful for zoomed/scaled containers
 */
scale?: number;

Usage Examples:

// Grid-snapped DraggableCore
function GridDraggableCore() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  
  const handleDrag = (e, data) => {
    // Note: grid snapping is applied to the data values automatically
    setPosition({
      x: data.x,  // Already snapped to grid
      y: data.y   // Already snapped to grid  
    });
  };
  
  return (
    <DraggableCore grid={[25, 25]} onDrag={handleDrag}>
      <div
        style={{
          transform: `translate(${position.x}px, ${position.y}px)`,
          width: '50px',
          height: '50px',
          backgroundColor: '#4CAF50'
        }}
      >
        Grid Snap
      </div>
    </DraggableCore>
  );
}

Advanced Configuration

Fine-tune behavior for specific use cases and React compatibility.

/**
 * Add user-select: none during drag to prevent text selection
 * Default: true
 */
enableUserSelectHack?: boolean;

/**
 * Custom offset parent for position calculations
 */
offsetParent?: HTMLElement;

/**
 * React ref for the draggable element
 * Recommended for React Strict Mode compatibility
 */
nodeRef?: React.RefObject<HTMLElement>;

Usage Examples:

import React, { useRef, useState } from 'react';
import { DraggableCore } from 'react-draggable';

// React Strict Mode compatible DraggableCore
function StrictModeDraggableCore() {
  const nodeRef = useRef(null);
  const [position, setPosition] = useState({ x: 0, y: 0 });
  
  const handleDrag = (e, data) => {
    setPosition({
      x: position.x + data.deltaX,
      y: position.y + data.deltaY
    });
  };
  
  return (
    <DraggableCore nodeRef={nodeRef} onDrag={handleDrag}>
      <div
        ref={nodeRef}
        style={{
          transform: `translate(${position.x}px, ${position.y}px)`
        }}
      >
        Strict Mode Compatible
      </div>
    </DraggableCore>
  );
}

Common Use Cases

DraggableCore is ideal for scenarios requiring custom drag behavior:

Custom Positioning Logic:

// Custom boundary checking
function BoundedDraggableCore() {
  const [position, setPosition] = useState({ x: 100, y: 100 });
  
  const handleDrag = (e, data) => {
    // Custom boundary logic
    const newX = Math.max(0, Math.min(400, position.x + data.deltaX));
    const newY = Math.max(0, Math.min(300, position.y + data.deltaY));
    
    setPosition({ x: newX, y: newY });
  };
  
  return (
    <DraggableCore onDrag={handleDrag}>
      <div style={{ transform: `translate(${position.x}px, ${position.y}px)` }}>
        Custom bounded
      </div>
    </DraggableCore>
  );
}

Multi-element Coordination:

// Dragging multiple elements simultaneously
function MultiElementDrag() {
  const [positions, setPositions] = useState([
    { x: 0, y: 0 }, { x: 100, y: 0 }, { x: 200, y: 0 }
  ]);
  
  const handleDrag = (index) => (e, data) => {
    setPositions(prev => prev.map((pos, i) => 
      i === index 
        ? { x: pos.x + data.deltaX, y: pos.y + data.deltaY }
        : { x: pos.x + data.deltaX * 0.5, y: pos.y + data.deltaY * 0.5 } // Follow drag
    ));
  };
  
  return (
    <div>
      {positions.map((pos, index) => (
        <DraggableCore key={index} onDrag={handleDrag(index)}>
          <div
            style={{
              transform: `translate(${pos.x}px, ${pos.y}px)`,
              width: '50px',
              height: '50px',
              backgroundColor: index === 0 ? '#ff0000' : '#cccccc'
            }}
          >
            {index === 0 ? 'Leader' : 'Follower'}
          </div>
        </DraggableCore>
      ))}
    </div>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-react-draggable

docs

draggable-core.md

draggable.md

index.md

tile.json