CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-udecode--plate-dnd

React drag and drop plugin for Plate rich-text editor enabling block rearrangement and file drops

Pending
Overview
Eval results
Files

editor-transforms.mddocs/

Editor Transforms

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.

Capabilities

Block Selection Functions

Functions for selecting and focusing specific blocks within the editor during drag-and-drop operations.

selectBlocksBySelectionOrId

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);
}

selectBlockById

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>
  );
}

focusBlockStartById

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);
}

Block Removal Functions

Functions for removing blocks while maintaining proper editor focus and state.

removeBlocksAndFocus

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)
  });
}

Drop Operation Functions

Core functions that handle the drag-and-drop logic, including path calculation and node movement.

onDropNode

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);
    }
  });
}

getDropPath

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;
}

onHoverNode

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');
  }
}

Types

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

docs

auto-scrolling.md

component-utilities.md

drag-drop-hooks.md

editor-transforms.md

index.md

plugin-configuration.md

utility-functions.md

tile.json