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.
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
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}
/>
);
}