React drag and drop plugin for Plate rich-text editor enabling block rearrangement and file drops
—
Functions for manipulating editor state during drag-and-drop operations. These transforms handle block selection, movement, focus management, and the core logic for processing drop operations within the Plate editor.
Functions for selecting and focusing specific blocks within the editor during drag-and-drop operations.
Intelligently selects blocks based on current selection or by specific ID, providing contextual selection behavior.
/**
* Select blocks by selection or by id
* If the block with id is not selected, select the block with id
* Else, select the blocks above the selection
* @param editor - The Plate editor instance
* @param id - The ID of the block to select
*/
export function selectBlocksBySelectionOrId(
editor: PlateEditor,
id: string
): void;Usage Examples:
import { selectBlocksBySelectionOrId } from "@udecode/plate-dnd";
// In a drag operation handler
function handleDragStart(editor: PlateEditor, blockId: string) {
// Select the dragged block or maintain current selection
selectBlocksBySelectionOrId(editor, blockId);
}
// In a drop handler
function handleBlockDrop(editor: PlateEditor, droppedBlockId: string) {
// Ensure proper selection after drop
selectBlocksBySelectionOrId(editor, droppedBlockId);
}Selects a specific block by its ID and focuses the editor.
/**
* Select the block above the selection by id and focus the editor
* @param editor - The editor instance
* @param id - The ID of the block to select
*/
export function selectBlockById(editor: Editor, id: string): void;Usage Examples:
import { selectBlockById } from "@udecode/plate-dnd";
// Select a specific block
function selectSpecificBlock(editor: Editor, blockId: string) {
selectBlockById(editor, blockId);
}
// In a component that needs to highlight a block
function HighlightableBlock({ blockId, children }) {
const editor = useEditorRef();
const handleClick = () => {
selectBlockById(editor, blockId);
};
return (
<div onClick={handleClick}>
{children}
</div>
);
}Focuses the start of a specific block by its ID.
/**
* Select the start of a block by id and focus the editor
* @param editor - The editor instance
* @param id - The ID of the block to focus
*/
export function focusBlockStartById(editor: Editor, id: string): void;Usage Examples:
import { focusBlockStartById } from "@udecode/plate-dnd";
// Focus at the beginning of a block after operation
function focusBlock(editor: Editor, blockId: string) {
focusBlockStartById(editor, blockId);
}
// In a navigation function
function navigateToBlock(editor: Editor, targetBlockId: string) {
focusBlockStartById(editor, targetBlockId);
}Functions for removing blocks while maintaining proper editor focus and state.
Removes blocks with specific IDs and maintains editor focus.
/**
* Remove blocks with an id and focus the editor
* @param editor - The editor instance
* @param options - Options for finding blocks to remove
*/
export function removeBlocksAndFocus<E extends Editor = Editor>(
editor: E,
options: EditorNodesOptions<ValueOf<E>>
): void;Usage Examples:
import { removeBlocksAndFocus } from "@udecode/plate-dnd";
// Remove selected blocks
function removeSelectedBlocks(editor: Editor) {
if (!editor.selection) return;
removeBlocksAndFocus(editor, {
at: editor.selection
});
}
// Remove blocks of a specific type
function removeBlocksByType(editor: Editor, blockType: string) {
removeBlocksAndFocus(editor, {
match: n => n.type === blockType
});
}
// In a delete handler
function handleDeleteBlocks(editor: Editor, blockIds: string[]) {
removeBlocksAndFocus(editor, {
match: n => blockIds.includes(n.id as string)
});
}Core functions that handle the drag-and-drop logic, including path calculation and node movement.
Handles the actual drop operation when a dragged node is dropped onto a target.
/**
* Handles drop operations for dragged nodes
* Calculates drop paths and moves nodes to new positions
* @param editor - The Plate editor instance
* @param options - Drop operation configuration
*/
export function onDropNode(
editor: PlateEditor,
options: OnDropNodeOptions
): void;
export interface OnDropNodeOptions {
/** Function to validate if drop is allowed */
canDropNode?: CanDropCallback;
/** The dragged item data */
dragItem: ElementDragItemNode;
/** The target element */
element: TElement;
/** React DnD monitor for drop state */
monitor: DropTargetMonitor<DragItemNode, unknown>;
/** Reference to the DOM node */
nodeRef: any;
/** Orientation of the drop operation */
orientation?: 'horizontal' | 'vertical';
}Usage Examples:
import { onDropNode } from "@udecode/plate-dnd";
// In a custom drop handler
function customDropHandler(
editor: PlateEditor,
dragItem: ElementDragItemNode,
targetElement: TElement,
monitor: DropTargetMonitor,
nodeRef: React.RefObject<HTMLElement>
) {
onDropNode(editor, {
dragItem,
element: targetElement,
monitor,
nodeRef,
orientation: 'vertical',
canDropNode: ({ dragEntry, dropEntry }) => {
// Custom validation: prevent dropping on itself
return dragEntry[0].id !== dropEntry[0].id;
}
});
}
// With custom validation
function restrictedDropHandler(editor: PlateEditor, options: OnDropNodeOptions) {
const restrictedTypes = ['heading', 'code-block'];
onDropNode(editor, {
...options,
canDropNode: ({ dragEntry, dropEntry }) => {
// Prevent moving restricted types
return !restrictedTypes.includes(dragEntry[0].type as string);
}
});
}Calculates the appropriate drop path for a drag operation, determining where the dragged item should be placed.
/**
* Calculates drop paths for drag operations
* Determines source and target paths for node movement
* @param editor - The Plate editor instance
* @param options - Path calculation options
* @returns Drop operation result with paths or null if invalid
*/
export function getDropPath(
editor: PlateEditor,
options: GetDropPathOptions
): DropPathResult | null;
export interface GetDropPathOptions {
canDropNode?: CanDropCallback;
dragItem: DragItemNode;
element: TElement;
monitor: DropTargetMonitor<DragItemNode, unknown>;
nodeRef: any;
orientation?: 'horizontal' | 'vertical';
}
export interface DropPathResult {
/** Source path of the dragged item */
from: Path;
/** Target path for the drop */
to: Path;
}Usage Examples:
import { getDropPath } from "@udecode/plate-dnd";
// Calculate drop path before performing operation
function calculateAndExecuteDrop(
editor: PlateEditor,
dragItem: DragItemNode,
targetElement: TElement,
monitor: DropTargetMonitor,
nodeRef: React.RefObject<HTMLElement>
) {
const result = getDropPath(editor, {
dragItem,
element: targetElement,
monitor,
nodeRef,
orientation: 'vertical'
});
if (result) {
// Perform custom operations before/after move
console.log('Moving from', result.from, 'to', result.to);
// Move the node
editor.tf.moveNodes({
at: result.from,
to: result.to
});
}
}
// Validation before drop
function validateDrop(
editor: PlateEditor,
dragItem: DragItemNode,
targetElement: TElement,
monitor: DropTargetMonitor,
nodeRef: React.RefObject<HTMLElement>
): boolean {
const result = getDropPath(editor, {
dragItem,
element: targetElement,
monitor,
nodeRef,
canDropNode: ({ dragEntry, dropEntry }) => {
// Custom validation logic
return dragEntry[0].type === dropEntry[0].type;
}
});
return result !== null;
}Handles hover events during drag operations, managing visual feedback and drop target state.
/**
* Handles hover events during drag operations
* Manages drop target state and visual feedback
* @param editor - The Plate editor instance
* @param options - Hover handling options
*/
export function onHoverNode(
editor: PlateEditor,
options: OnHoverNodeOptions
): void;
export interface OnHoverNodeOptions {
canDropNode?: CanDropCallback;
dragItem: DragItemNode;
element: TElement;
monitor: DropTargetMonitor<DragItemNode, unknown>;
nodeRef: any;
orientation?: 'horizontal' | 'vertical';
}Usage Examples:
import { onHoverNode } from "@udecode/plate-dnd";
// In a custom hover handler
function customHoverHandler(
editor: PlateEditor,
dragItem: DragItemNode,
targetElement: TElement,
monitor: DropTargetMonitor,
nodeRef: React.RefObject<HTMLElement>
) {
onHoverNode(editor, {
dragItem,
element: targetElement,
monitor,
nodeRef,
orientation: 'vertical'
});
// Additional custom hover logic
if (monitor.isOver({ shallow: true })) {
console.log('Hovering over target element');
}
}export type CanDropCallback = (args: {
dragEntry: NodeEntry<TElement>;
dragItem: DragItemNode;
dropEntry: NodeEntry<TElement>;
editor: PlateEditor;
}) => boolean;
export interface ElementDragItemNode {
id: string;
element: TElement;
[key: string]: unknown;
}
export type EditorNodesOptions<T> = {
at?: Location;
match?: (node: T) => boolean;
mode?: 'all' | 'highest' | 'lowest';
universal?: boolean;
reverse?: boolean;
voids?: boolean;
};Install with Tessl CLI
npx tessl i tessl/npm-udecode--plate-dnd