useDrag
Hooks

useDrag

Pointer drag handling with capture, window listeners, Escape-to-cancel, and teardown.

Signature

useDrag handles pointer drag gestures and cancellation with Escape. Window listeners are attached during dragging and removed on completion, cancellation, or unmount. Handlers receive coordinates from React and native pointer events.

SignatureTypeScript
interface DragState {
  /** Pointer position when the gesture started. */
  startX: number;
  startY: number;
  /** Pointer position now. */
  x: number;
  y: number;
  /** Distance travelled since the start, in px. */
  dx: number;
  dy: number;
  shiftKey: boolean;
  altKey: boolean;
  ctrlKey: boolean;
  metaKey: boolean;
}

function useDrag(options: {
  onStart?: (state: DragState) => void;
  onMove: (state: DragState) => void;
  onEnd?: (state: DragState) => void;
  /** Gesture abandoned — pointercancel, or Escape. Without it, a cancel ends. */
  onCancel?: () => void;
  /** Route later events to the element the gesture began on. Default true. */
  capture?: boolean;
  /** Escape abandons the gesture. Default true. */
  escapeCancels?: boolean;
  disabled?: boolean;
}): { dragging: boolean; onPointerDown: (e: PointerEvent) => void };

Usage

ExampleTSX
import { useRef } from "react";
import type { DragState } from "@solarius/onset";
import { useDrag } from "@solarius/onset";

function Slider({ onChange }: { onChange: (value: number) => void }) {
  const trackRef = useRef<HTMLDivElement>(null);

  const apply = ({ x }: DragState) => {
    const box = trackRef.current?.getBoundingClientRect();
    if (!box || box.width === 0) return;
    onChange(Math.max(0, Math.min(1, (x - box.left) / box.width)));
  };

  const { dragging, onPointerDown } = useDrag({
    onStart: apply,
    onMove: apply,
    escapeCancels: false, // every move is already committed; nothing to revert
  });

  return (
    <div
      ref={trackRef}
      data-dragging={dragging || undefined}
      onPointerDown={onPointerDown}
    />
  );
}