Getting Started with the DraggablePremium
The useDraggable
hook and Draggable
component enable the user to drag a native HTML Element or custom React Component.
The draggable
functionality provides the following callbacks which enable the developer to implement custom positioning, styling, or behavior based on the current application state and the event arguments:
- onPress—A callback indicating that the
draggable
item has been pressed, but not dragged yet. - onDragStart—A callback indicating that the
draggable
item has been dragged for the first time. - onDrag—A callback called on every
pointer
/touch
action, indicating that a drag is happening. - onDragEnd—A callback indicating that the
draggable
item is no longer being dragged. - onRelease—A callback indicating that the
draggable
item has been released.
Basic Usage
The following example demonstrates the draggable
functionality in action.
Registering for Drag
To register an element/component to the useDraggable
hook, pass it as the first parameter. For the Draggable
component, it is enough to provide a single children
prop to the component.
In the following section, you will learn how to:
- Pass a native HTML element to the
useDraggable
hook or theDraggable
component. - Pass a custom React component to the
useDraggable
hook or theDraggable
component.
Native HTML Element
- To pass a native HTML Element to the
useDraggable
hook, obtain theref
object by using theReact.useRef
hook.
You can use the same
ref
object for other parts of your application, as long as you do not mutate it directly.
const element = React.useRef();
// ...
useDraggable(element, {});
// ...
return <div ref={element}>Drag Me</div>;
- To pass a native HTML Element to the
Draggable
component, pass the desired element as a direct child.
render() {
return (
<Draggable>
<div>Drag Me</div>
</Draggable>
)
}
The Draggable
component will obtain the ref
of the child component. If you require a reference
, use the childRef.
element = React.createRef();
// ...
componentDidMount() {
// Sample code
element.current.focus();
}
// ...
render() {
return (
<Draggable childRef={this.element}>
<div>Drag Me</div>
</Draggable>
)
}
Custom React Component
There are two possible ways to pass a custom React Component to the useDraggable
hook:
-
If the Component is a Functional Component:
-
To obtain the
ref
, wrap the component inReact.forwardRef
.jsxconst CustomComponent = React.forwardRef((props, ref) => { return <div ref={ref}>DragMe</div>; });
Alternatively, if you are already using the
ref
and applying additional properties, add theelement
field to theref
, which will resolve to the HTML element underneath.jsxconst CustomComponent = React.forwardRef((props, ref) => { const element = React.useRef(); React.useImperativeHandle(ref, () => ({ element: element.current })); return <div ref={element}>DragMe</div>; });
-
Obtain the
ref
of yourCustomComponent
and pass it to theuseDraggable
hook.jsxconst component = React.useRef(); // ... useDraggable(component, {}); // ... return <CustomComponent ref={component}>Drag Me</CustomComponent>;
-
-
If the Component is a Class Component:
-
Provide a public
element
getter as a class field of the component.jsxclass CustomComponent extends React.Component { _element = React.createRef(); get element() { return this._element; } render() { return <div ref={this._element}>DragMe</div>; } }
-
Wrap your
CustomComponent
into aDraggable
.jsxrender() { return ( <Draggable> <CustomComponent>Drag Me</CustomComponent> </Draggable> ) }
-
Handling Drag Events
The KendoReact drag
callbacks provide the necessary information to apply correct positioning on the page. The drag
can be positioned relatively
to its parent or absolutely
in the document body.
Relative Positioning
To calculate the drag
position correctly, you need:
- The initial
clientX
/clientY
—Theclient
coordinates where thedragging
begins. - The current
clientX
/clientY
—Theclient
coordinates of the current positioning of thepointer
. - The
offsetX
/offsetY
—Theoffset
coordinates indicating where thepointer
was positioned relative to thedrag
. - The
scrollX
/scrollY
—Thescroll
coordinates required to account for the current scroll position of the parent.
To position the drag
relative to its parent:
-
Set the initial
offsetX
andoffsetY
values by handling theonPress
callback.jsxconst handlePress = (event) => { setOffset({ x: event.offsetX, y: event.offsetY }); };
-
Set the initial
clientX
andclientY
values by handling theonDragStart
callback.jsxconst handleDragStart = (event) => { setInitial({ x: event.clientX - offset.x, y: event.clientY - offset.y }); };
-
Calculate the current position of the
drag
by handling theonDrag
callback.jsxconst handleDrag = (event) => { setTranslate({ x: event.clientX - initial.x - offset.x + event.scrollX, y: event.clientY - initial.y - offset.y + event.scrollY }); };
-
Finally, apply the
transform
style to the element.jsxreturn ( <div style={{ transform: `translate(${translate.x}px, ${translate.y}px)`; }} > DragMe </div> )
Absolute Positioning
Absolute positioning is most-suitable for scenarios where the drag might happen between two different parent elements.
To calculate the drag
position correctly, you need:
- The initial
clientX
/clientY
—Theclient
coordinates where thedragging
begins. - The current
clientX
/clientY
—Theclient
coordinates of the current positioning of thepointer
. - The offsetX/offsetY—The
offset
coordinates indicating where thepointer
was positioned relative to thedrag
.
Due to the
drag
being positionedabsolutely
within thebody
element, there is no need to account for thescroll
coordinates of the parent.
To position the drag
element absolutely
:
-
Set the initial
offsetX
andoffsetY
values by handling theonPress
callback.jsxconst handlePress = (event) => { setOffset({ x: event.offsetX, y: event.offsetY }); };
-
Set the initial
clientX
andclientY
values by handling theonDragStart
callback.jsxconst handleDragStart = (event) => { setInitial({ x: event.clientX - offset.x, y: event.clientY - offset.y }); };
-
Calculate the current position of the
drag
by handling theonDrag
callback.jsxconst handleDrag = (event) => { setCoordinates({ x: event.clientX - initial.x - offset.x, y: event.clientY - initial.y - offset.y }); };
-
Finally, apply the
transform
style to the element.jsxreturn ReactDOM.createPortal( <div style={{ position: 'absolute', left: coordinates.x, top: coordinates.y }} > DragMe </div>, document.body );