function useDraggable<E extends HTMLElement>(
options: DraggableOptions<E>
): DraggableImplementation<E>;
This is not a drag and drop hook.
The useDraggable hook is used to drag elements through mouse, touch, and
keyboard events and was created for to support the
WindowSplitter and Slider
components.
Here's a quick example using one of the useDraggable tests.
function Example() {
const [value, setValue] = useState(20);
const [dragging, setDragging] = useState(false);
const {
draggableRef,
keyboardEventHandlers,
minimum,
maximum,
increment,
decrement,
} = useDraggable({
min: 0,
max: 100,
dragging,
value,
setValue,
setDragging,
});
const percentage = getPercentage({ min: 0, max: 100, value });
return (
<>
<Button
aria-valuenow={Math.ceil(percentage * 100)}
ref={draggableRef}
{...keyboardEventHandlers}
className={cnb(dragging && "dragging")}
>
Button
</Button>
<Button onClick={minimum}>Minimum</Button>
<Button onClick={maximum}>Maximum</Button>
<Button onClick={increment}>Increment</Button>
<Button onClick={decrement}>Decrement</Button>
</>
);
}
options - An object with the following definition:export type DraggableOptions<E extends HTMLElement = HTMLElement> =
BaseDraggableOptions<E> & DraggableStateOptions;
export interface BaseDraggableOptions<E extends HTMLElement>
extends DraggableEventHandlers<E>, ControllableDraggableStateOptions {
/**
* An optional ref to merge with the returned
* {@link DraggableImplementation.draggableRef}.
*/
ref?: Ref<E>;
/**
* The minimum number of pixels allowed for the draggable element. This must
* be a number greater than or equal to 0.
*
* When {@link withinOffsetParent} is set to `true`, this is the minimum value
* allowed instead of pixels.
*/
min: number;
/**
* The maximum number of pixels allowed for the draggable element. This must
* be a number greater than the {@link min} and usually a number less than the
* viewport size.
*
* When {@link withinOffsetParent} is set to `true`, this is the maximum value
* allowed instead of pixels.
*/
max: number;
/**
* The amount to increment or decrement the value with arrow keys.
*
* @defaultValue `1`
*/
step?: number;
/**
* This was added to support range sliders where there are two (or more)
* draggable elements within the same container element and their values
* cannot pass each other. Without these overrides, the range would keep
* changing as the other values change, so the drag percentage would be
* incorrect.
*
* @example Range Slider
* ```ts
* const min = 0;
* const max = 100;
* const minValue = 3;
* const maxValue = 80;
*
* const minValueDraggable = useDraggable({
* min,
* max,
* rangeMax: maxValue,
* });
* const maxValueDraggable = useDraggable({
* min,
* max,
* rangeMin: minValue,
* });
* ```
*
* @defaultValue `min`
*/
rangeMin?: number;
/**
* @see {@link rangeMin} for an explanation of this option.
* @defaultValue `max`
*/
rangeMax?: number;
/**
* Set this to `true` to enable dragging vertically instead of horizontally.
*
* @defaultValue `false`
*/
vertical?: boolean;
/**
* The default drag behavior is to increase the value when:
*
* - dragging `"right"` and the writing direction is `"ltr"`
* - dragging `"left"` and the writing direction is `"rtl"`
* - dragging `"upwards"`
*
* When this is set to `true`, the value when increase when:
*
* - dragging `"left"` and the writing direction is `"ltr"`
* - dragging `"right"` and the writing direction is `"rtl"`
* - dragging `"downwards"`
*
* @defaultValue `false`
*/
reversed?: boolean;
/**
* Set this to `true` to disable all drag behavior. This will still call any
* of the provided {@link DraggableEventHandlers}.
*
* @defaultValue `false`
*/
disabled?: boolean;
/**
* Set this to `true` if the dragging calculations should be to the
* `draggableRef.current.offsetParent` instead of the entire window. The main
* use case for this is sliders.
*
* @defaultValue `false`
*/
withinOffsetParent?: boolean;
/**
* Set this to `true` to prevent the `document.documentElement` from gaining
* the `.rmd-dragging` class names while dragging.
*
* This should normally remain as `false` to improve performance and prevent
* other mouse events from firing while dragging.
*
* @defaultValue `false`
*/
disableDraggingClassName?: boolean;
/**
* Set this to `true` to prevent the vertical or horizontal cursor from
* appearing while dragging.
*
* @defaultValue `false`
*/
disableDraggingCursorClassName?: boolean;
}
export interface DraggableImplementation<
E extends HTMLElement = HTMLElement,
> extends Required<DraggableEventHandlers<E>> {
mouseEventHandlers: Required<DraggableMouseEventHandlers<E>>;
touchEventHandlers: Required<DraggableTouchEventHandlers<E>>;
keyboardEventHandlers: Required<DraggableKeyboardEventHandlers<E>>;
/**
* Set the {@link value} to {@link DraggableOptions.min}.
*/
minimum: () => void;
/**
* Set the {@link value} to {@link DraggableOptions.max}.
*/
maximum: () => void;
/**
* Increment the {@link value} by {@link DraggableOptions.step}.
*/
increment: () => void;
/**
* Decrement the {@link value} by {@link DraggableOptions.step}.
*/
decrement: () => void;
/**
* The current percentage the `value` is within the range.
*
* ```ts
* const percentage =
* dragging && withinOffsetParent
* ? : dragPercentage
* : getPercentage({ min, max, value });
* ```
*/
percentage: number;
/**
* A ref that **Must** be passed to the element that should be draggable.
*/
draggableRef: RefCallback<E>;
/**
* This value will only update while dragging with a mouse or touch and should
* be used for the positioning styles while dragging.
*
* Note: The {@link percentage} will use this value while dragging.
*/
dragPercentage: number;
/**
* Flag to determine if the user has dragged at least once. Used internally
* for manually persisting the value into local storage once the user has
* stopped dragging.
*/
draggedOnce: NonNullRef<boolean>;
value: number;
setValue: UseStateSetter<number>;
dragging: boolean;
setDragging: UseStateSetter<boolean>;
}