@neodrag/react
Version:
React library to add dragging to your apps 😉
233 lines (229 loc) • 8.76 kB
TypeScript
import React from 'react';
type DragBoundsCoords = {
/** Number of pixels from left of the document */
left: number;
/** Number of pixels from top of the document */
top: number;
/** Number of pixels from the right side of document */
right: number;
/** Number of pixels from the bottom of the document */
bottom: number;
};
type DragAxis = 'both' | 'x' | 'y' | 'none';
type DragBounds = HTMLElement | Partial<DragBoundsCoords> | 'parent' | 'body' | (string & Record<never, never>);
type DragEventData = {
/** How much element moved from its original position horizontally */
offsetX: number;
/** How much element moved from its original position vertically */
offsetY: number;
/** The node on which the draggable is applied */
rootNode: HTMLElement;
/** The element being dragged */
currentNode: HTMLElement;
/** The pointer event that triggered the drag */
event: PointerEvent;
};
type DragOptions = {
/**
* Optionally limit the drag area
*
* Accepts `parent` as prefixed value, and limits it to its parent.
*
* Or, you can specify any selector and it will be bound to that.
*
* **Note**: We don't check whether the selector is bigger than the node element.
* You yourself will have to make sure of that, or it may lead to strange behavior
*
* Or, finally, you can pass an object of type `{ top: number; right: number; bottom: number; left: number }`.
* These mimic the css `top`, `right`, `bottom` and `left`, in the sense that `bottom` starts from the bottom of the window, and `right` from right of window.
* If any of these properties are unspecified, they are assumed to be `0`.
*/
bounds?: DragBounds;
/**
* When to recalculate the dimensions of the `bounds` element.
*
* By default, bounds are recomputed only on dragStart. Use this options to change that behavior.
*
* @default '{ dragStart: true, drag: false, dragEnd: false }'
*/
recomputeBounds?: {
dragStart?: boolean;
drag?: boolean;
dragEnd?: boolean;
};
/**
* Axis on which the element can be dragged on. Valid values: `both`, `x`, `y`, `none`.
*
* - `both` - Element can move in any direction
* - `x` - Only horizontal movement possible
* - `y` - Only vertical movement possible
* - `none` - No movement at all
*
* @default 'both'
*/
axis?: DragAxis;
/**
* If false, uses the new translate property instead of transform: translate(); to move the element around.
*
* At present this is true by default, but will be changed to false in a future major version.
*
* @default false
* @deprecated Use `transform` option instead for transform: translate() or any other custom transform. Will be removed in v3.
*/
legacyTranslate?: boolean;
/**
* If true, uses `translate3d` instead of `translate` to move the element around, and the hardware acceleration kicks in.
*
* `true` by default, but can be set to `false` if [blurry text issue](https://developpaper.com/question/why-does-the-use-of-css3-translate3d-result-in-blurred-display/) occur
*
* @default true
* @deprecated Use `transform` option instead with translate(x, y, 1px). 1px forces some browsers to use GPU acceleration. Will be removed in v3
*/
gpuAcceleration?: boolean;
/**
* Custom transform function. If provided, this function will be used to apply the DOM transformations to the root node to move it.
* Existing transform logic, including `gpuAcceleration` and `legacyTranslate`, will be ignored.
*
* You can return a string to apply to a `transform` property, or not return anything and apply your transformations using `rootNode.style.transform = VALUE`
*
* @default undefined
*/
transform?: ({ offsetX, offsetY, rootNode, }: {
offsetX: number;
offsetY: number;
rootNode: HTMLElement;
}) => string | undefined | void;
/**
* Applies `user-select: none` on `<body />` element when dragging,
* to prevent the irritating effect where dragging doesn't happen and the text is selected.
* Applied when dragging starts and removed when it stops.
*
* Can be disabled using this option
*
* @default true
*/
applyUserSelectHack?: boolean;
/**
* Ignores touch events with more than 1 touch.
* This helps when you have multiple elements on a canvas where you want to implement
* pinch-to-zoom behaviour.
*
* @default false
*
*/
ignoreMultitouch?: boolean;
/**
* Disables dragging altogether.
*
* @default false
*/
disabled?: boolean;
/**
* Applies a grid on the page to which the element snaps to when dragging, rather than the default continuous grid.
*
* `Note`: If you're programmatically creating the grid, do not set it to [0, 0] ever, that will stop drag at all. Set it to `undefined`.
*
* @default undefined
*/
grid?: [number, number];
/**
* Threshold for dragging to start. If the user moves the mouse/finger less than this distance, the dragging won't start.
*
* @default { delay: 0, distance: 3 }
*/
threshold?: {
/**
* Threshold in milliseconds for a pointer movement to be considered a drag
*
* @default 0
*/
delay?: number;
/**
* Threshold in pixels for movement to be considered a drag
*
* @default 3
*/
distance?: number;
};
/**
* Control the position manually with your own state
*
* By default, the element will be draggable by mouse/finger, and all options will work as default while dragging.
*
* But changing the `position` option will also move the draggable around. These parameters are reactive,
* so using Svelte's reactive variables as values for position will work like a charm.
*
*
* Note: If you set `disabled: true`, you'll still be able to move the draggable through state variables. Only the user interactions won't work
*
*/
position?: {
x: number;
y: number;
};
/**
* CSS Selector of an element or multiple elements inside the parent node(on which `use:draggable` is applied).
*
* Can be an element or elements too. If it is provided, Trying to drag inside the `cancel` element(s) will prevent dragging.
*
* @default undefined
*/
cancel?: string | HTMLElement | HTMLElement[];
/**
* CSS Selector of an element or multiple elements inside the parent node(on which `use:draggable` is applied). Can be an element or elements too.
*
* If it is provided, Only clicking and dragging on this element will allow the parent to drag, anywhere else on the parent won't work.
*
* @default undefined
*/
handle?: string | HTMLElement | HTMLElement[];
/**
* Class to apply on the element on which `use:draggable` is applied.
* Note that if `handle` is provided, it will still apply class on the element to which this action is applied, **NOT** the handle
*
*/
defaultClass?: string;
/**
* Class to apply on the element when it is dragging
*
* @default 'neodrag-dragging'
*/
defaultClassDragging?: string;
/**
* Class to apply on the element if it has been dragged at least once.
*
* @default 'neodrag-dragged'
*/
defaultClassDragged?: string;
/**
* Offsets your element to the position you specify in the very beginning.
* `x` and `y` should be in pixels
*
*/
defaultPosition?: {
x: number;
y: number;
};
/**
* Fires when dragging start
*/
onDragStart?: (data: DragEventData) => void;
/**
* Fires when dragging is going on
*/
onDrag?: (data: DragEventData) => void;
/**
* Fires when dragging ends
*/
onDragEnd?: (data: DragEventData) => void;
};
type HandleCancelType = string | HTMLElement | React.RefObject<HTMLElement> | (React.RefObject<HTMLElement> | HTMLElement)[] | undefined;
type ReactDragOptions = Omit<DragOptions, 'handle' | 'cancel'> & {
handle?: HandleCancelType;
cancel?: HandleCancelType;
};
declare function useDraggable<RefType extends HTMLElement = HTMLDivElement>(nodeRef: React.RefObject<RefType>, options?: ReactDragOptions): {
isDragging: boolean;
dragState: DragEventData | undefined;
};
export { type DragAxis, type DragBounds, type DragBoundsCoords, type DragEventData, type ReactDragOptions as DragOptions, useDraggable };