0
# Drag and Drop Hooks
1
2
React hooks for implementing drag-and-drop behavior on editor nodes. These hooks provide the core functionality for making blocks draggable and droppable, with support for visual feedback, state management, and event handling.
3
4
## Capabilities
5
6
### useDndNode
7
8
Combined hook that provides both drag and drop functionality for editor nodes. This is the main hook used for implementing drag-and-drop on blocks.
9
10
```typescript { .api }
11
/**
12
* Combined drag and drop hook for editor nodes
13
* Provides both dragging and dropping functionality with preview support
14
* @param options - Configuration options for drag and drop behavior
15
* @returns Object with drag ref, dragging state, and hover state
16
*/
17
export function useDndNode(options: UseDndNodeOptions): {
18
dragRef: ConnectDragSource;
19
isDragging: boolean;
20
isOver: boolean;
21
};
22
23
export type UseDndNodeOptions = Pick<UseDropNodeOptions, 'element'> &
24
Partial<Pick<UseDropNodeOptions, 'canDropNode' | 'nodeRef'>> &
25
Partial<Pick<UseDragNodeOptions, 'type'>> & {
26
/** Options passed to the drag hook */
27
drag?: Partial<Omit<UseDragNodeOptions, 'type'>>;
28
/** Options passed to the drop hook, excluding element, nodeRef */
29
drop?: Partial<
30
Omit<UseDropNodeOptions, 'canDropNode' | 'element' | 'nodeRef'>
31
>;
32
/** Orientation of the drag and drop interaction */
33
orientation?: 'horizontal' | 'vertical';
34
/** Preview configuration */
35
preview?: {
36
/** Whether to disable the preview */
37
disable?: boolean;
38
/** The reference to the preview element */
39
ref?: any;
40
};
41
/** Custom drop handler function */
42
onDropHandler?: (
43
editor: PlateEditor,
44
props: {
45
id: string;
46
dragItem: DragItemNode;
47
monitor: DropTargetMonitor<DragItemNode, unknown>;
48
nodeRef: any;
49
}
50
) => boolean | void;
51
};
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
import { useDndNode } from "@udecode/plate-dnd";
58
import { useElement } from "@udecode/plate/react";
59
60
function DraggableBlock({ children }) {
61
const element = useElement();
62
const nodeRef = useRef<HTMLDivElement>(null);
63
64
const { dragRef, isDragging, isOver } = useDndNode({
65
element,
66
nodeRef,
67
orientation: 'vertical'
68
});
69
70
return (
71
<div
72
ref={dragRef}
73
style={{
74
opacity: isDragging ? 0.5 : 1,
75
backgroundColor: isOver ? '#f0f0f0' : 'transparent'
76
}}
77
>
78
{children}
79
</div>
80
);
81
}
82
83
// With custom drop handler
84
function CustomDraggableBlock({ children }) {
85
const element = useElement();
86
87
const { dragRef, isDragging } = useDndNode({
88
element,
89
onDropHandler: (editor, { id, dragItem }) => {
90
console.log('Custom drop handling for block:', id);
91
// Return true to prevent default drop behavior
92
return false;
93
}
94
});
95
96
return (
97
<div ref={dragRef} style={{ opacity: isDragging ? 0.5 : 1 }}>
98
{children}
99
</div>
100
);
101
}
102
```
103
104
### useDragNode
105
106
Hook specifically for making nodes draggable. Provides fine-grained control over drag behavior.
107
108
```typescript { .api }
109
/**
110
* Hook for making editor nodes draggable
111
* Manages drag state and provides drag source functionality
112
* @param editor - The Plate editor instance
113
* @param options - Drag configuration options
114
* @returns Tuple with drag state, drag ref, and preview ref
115
*/
116
export function useDragNode(
117
editor: PlateEditor,
118
options: UseDragNodeOptions
119
): [{ isDragging: boolean }, ConnectDragSource, ConnectDragPreview];
120
121
export interface UseDragNodeOptions
122
extends DragSourceHookSpec<DragItemNode, unknown, { isDragging: boolean }> {
123
/** The editor element to make draggable */
124
element: TElement;
125
}
126
```
127
128
**Usage Examples:**
129
130
```typescript
131
import { useDragNode } from "@udecode/plate-dnd";
132
import { useEditorRef, useElement } from "@udecode/plate/react";
133
134
function CustomDraggable({ children }) {
135
const editor = useEditorRef();
136
const element = useElement();
137
138
const [{ isDragging }, dragRef, preview] = useDragNode(editor, {
139
element,
140
type: 'custom-block',
141
item: { customData: 'example' }
142
});
143
144
// Custom preview setup
145
useEffect(() => {
146
const img = new Image();
147
img.src = 'data:image/png;base64,iV...'; // Custom drag image
148
preview(img);
149
}, [preview]);
150
151
return (
152
<div ref={dragRef} style={{ opacity: isDragging ? 0.3 : 1 }}>
153
{children}
154
</div>
155
);
156
}
157
```
158
159
### useDropNode
160
161
Hook specifically for making nodes accept drops. Provides drop zone functionality with hover detection.
162
163
```typescript { .api }
164
/**
165
* Hook for making editor nodes accept drops
166
* Manages drop state and handles drop operations
167
* @param editor - The Plate editor instance
168
* @param options - Drop configuration options
169
* @returns Tuple with drop state and drop target ref
170
*/
171
export function useDropNode(
172
editor: PlateEditor,
173
options: UseDropNodeOptions
174
): [{ isOver: boolean }, ConnectDropTarget];
175
176
export interface UseDropNodeOptions
177
extends DropTargetHookSpec<DragItemNode, unknown, { isOver: boolean }> {
178
/** The node to which the drop line is attached */
179
element: TElement;
180
/** The reference to the node being dragged */
181
nodeRef: any;
182
/** Intercepts the drop handling */
183
canDropNode?: CanDropCallback;
184
/** Orientation of drop operations */
185
orientation?: 'horizontal' | 'vertical';
186
/** Custom drop handler function */
187
onDropHandler?: (
188
editor: PlateEditor,
189
props: {
190
id: string;
191
dragItem: DragItemNode;
192
monitor: DropTargetMonitor<DragItemNode, unknown>;
193
nodeRef: any;
194
}
195
) => boolean | void;
196
}
197
198
export type CanDropCallback = (args: {
199
dragEntry: NodeEntry<TElement>;
200
dragItem: DragItemNode;
201
dropEntry: NodeEntry<TElement>;
202
editor: PlateEditor;
203
}) => boolean;
204
```
205
206
**Usage Examples:**
207
208
```typescript
209
import { useDropNode } from "@udecode/plate-dnd";
210
import { useEditorRef, useElement } from "@udecode/plate/react";
211
212
function DropZone({ children }) {
213
const editor = useEditorRef();
214
const element = useElement();
215
const nodeRef = useRef<HTMLDivElement>(null);
216
217
const [{ isOver }, dropRef] = useDropNode(editor, {
218
accept: ['block', 'custom-block'],
219
element,
220
nodeRef,
221
canDropNode: ({ dragItem, dropEntry }) => {
222
// Custom validation logic
223
return dragItem.id !== dropEntry[0].id;
224
}
225
});
226
227
// Combine refs
228
const combinedRef = useCallback((el: HTMLDivElement) => {
229
nodeRef.current = el;
230
dropRef(el);
231
}, [dropRef]);
232
233
return (
234
<div
235
ref={combinedRef}
236
style={{
237
border: isOver ? '2px dashed #007acc' : '2px solid transparent',
238
minHeight: '40px'
239
}}
240
>
241
{children}
242
</div>
243
);
244
}
245
```
246
247
## Types
248
249
```typescript { .api }
250
export type DragItemNode = ElementDragItemNode | FileDragItemNode;
251
252
export interface ElementDragItemNode {
253
id: string;
254
element: TElement;
255
[key: string]: unknown;
256
}
257
258
export interface FileDragItemNode {
259
dataTransfer: DataTransfer[];
260
files: FileList;
261
items: DataTransferItemList;
262
}
263
```