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

drag-drop-hooks.mddocs/

Drag and Drop Hooks

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.

Capabilities

useDndNode

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.

/**
 * Combined drag and drop hook for editor nodes
 * Provides both dragging and dropping functionality with preview support
 * @param options - Configuration options for drag and drop behavior
 * @returns Object with drag ref, dragging state, and hover state
 */
export function useDndNode(options: UseDndNodeOptions): {
  dragRef: ConnectDragSource;
  isDragging: boolean;
  isOver: boolean;
};

export type UseDndNodeOptions = Pick<UseDropNodeOptions, 'element'> &
  Partial<Pick<UseDropNodeOptions, 'canDropNode' | 'nodeRef'>> &
  Partial<Pick<UseDragNodeOptions, 'type'>> & {
    /** Options passed to the drag hook */
    drag?: Partial<Omit<UseDragNodeOptions, 'type'>>;
    /** Options passed to the drop hook, excluding element, nodeRef */
    drop?: Partial<
      Omit<UseDropNodeOptions, 'canDropNode' | 'element' | 'nodeRef'>
    >;
    /** Orientation of the drag and drop interaction */
    orientation?: 'horizontal' | 'vertical';
    /** Preview configuration */
    preview?: {
      /** Whether to disable the preview */
      disable?: boolean;
      /** The reference to the preview element */
      ref?: any;
    };
    /** Custom drop handler function */
    onDropHandler?: (
      editor: PlateEditor,
      props: {
        id: string;
        dragItem: DragItemNode;
        monitor: DropTargetMonitor<DragItemNode, unknown>;
        nodeRef: any;
      }
    ) => boolean | void;
  };

Usage Examples:

import { useDndNode } from "@udecode/plate-dnd";
import { useElement } from "@udecode/plate/react";

function DraggableBlock({ children }) {
  const element = useElement();
  const nodeRef = useRef<HTMLDivElement>(null);
  
  const { dragRef, isDragging, isOver } = useDndNode({
    element,
    nodeRef,
    orientation: 'vertical'
  });

  return (
    <div 
      ref={dragRef}
      style={{
        opacity: isDragging ? 0.5 : 1,
        backgroundColor: isOver ? '#f0f0f0' : 'transparent'
      }}
    >
      {children}
    </div>
  );
}

// With custom drop handler
function CustomDraggableBlock({ children }) {
  const element = useElement();
  
  const { dragRef, isDragging } = useDndNode({
    element,
    onDropHandler: (editor, { id, dragItem }) => {
      console.log('Custom drop handling for block:', id);
      // Return true to prevent default drop behavior
      return false;
    }
  });

  return (
    <div ref={dragRef} style={{ opacity: isDragging ? 0.5 : 1 }}>
      {children}
    </div>
  );
}

useDragNode

Hook specifically for making nodes draggable. Provides fine-grained control over drag behavior.

/**
 * Hook for making editor nodes draggable
 * Manages drag state and provides drag source functionality
 * @param editor - The Plate editor instance
 * @param options - Drag configuration options
 * @returns Tuple with drag state, drag ref, and preview ref
 */
export function useDragNode(
  editor: PlateEditor,
  options: UseDragNodeOptions
): [{ isDragging: boolean }, ConnectDragSource, ConnectDragPreview];

export interface UseDragNodeOptions
  extends DragSourceHookSpec<DragItemNode, unknown, { isDragging: boolean }> {
  /** The editor element to make draggable */
  element: TElement;
}

Usage Examples:

import { useDragNode } from "@udecode/plate-dnd";
import { useEditorRef, useElement } from "@udecode/plate/react";

function CustomDraggable({ children }) {
  const editor = useEditorRef();
  const element = useElement();
  
  const [{ isDragging }, dragRef, preview] = useDragNode(editor, {
    element,
    type: 'custom-block',
    item: { customData: 'example' }
  });

  // Custom preview setup
  useEffect(() => {
    const img = new Image();
    img.src = 'data:image/png;base64,iV...'; // Custom drag image
    preview(img);
  }, [preview]);

  return (
    <div ref={dragRef} style={{ opacity: isDragging ? 0.3 : 1 }}>
      {children}
    </div>
  );
}

useDropNode

Hook specifically for making nodes accept drops. Provides drop zone functionality with hover detection.

/**
 * Hook for making editor nodes accept drops
 * Manages drop state and handles drop operations
 * @param editor - The Plate editor instance
 * @param options - Drop configuration options
 * @returns Tuple with drop state and drop target ref
 */
export function useDropNode(
  editor: PlateEditor,
  options: UseDropNodeOptions
): [{ isOver: boolean }, ConnectDropTarget];

export interface UseDropNodeOptions
  extends DropTargetHookSpec<DragItemNode, unknown, { isOver: boolean }> {
  /** The node to which the drop line is attached */
  element: TElement;
  /** The reference to the node being dragged */
  nodeRef: any;
  /** Intercepts the drop handling */
  canDropNode?: CanDropCallback;
  /** Orientation of drop operations */
  orientation?: 'horizontal' | 'vertical';
  /** Custom drop handler function */
  onDropHandler?: (
    editor: PlateEditor,
    props: {
      id: string;
      dragItem: DragItemNode;
      monitor: DropTargetMonitor<DragItemNode, unknown>;
      nodeRef: any;
    }
  ) => boolean | void;
}

export type CanDropCallback = (args: {
  dragEntry: NodeEntry<TElement>;
  dragItem: DragItemNode;
  dropEntry: NodeEntry<TElement>;
  editor: PlateEditor;
}) => boolean;

Usage Examples:

import { useDropNode } from "@udecode/plate-dnd";
import { useEditorRef, useElement } from "@udecode/plate/react";

function DropZone({ children }) {
  const editor = useEditorRef();
  const element = useElement();
  const nodeRef = useRef<HTMLDivElement>(null);
  
  const [{ isOver }, dropRef] = useDropNode(editor, {
    accept: ['block', 'custom-block'],
    element,
    nodeRef,
    canDropNode: ({ dragItem, dropEntry }) => {
      // Custom validation logic
      return dragItem.id !== dropEntry[0].id;
    }
  });

  // Combine refs
  const combinedRef = useCallback((el: HTMLDivElement) => {
    nodeRef.current = el;
    dropRef(el);
  }, [dropRef]);

  return (
    <div 
      ref={combinedRef}
      style={{
        border: isOver ? '2px dashed #007acc' : '2px solid transparent',
        minHeight: '40px'
      }}
    >
      {children}
    </div>
  );
}

Types

export type DragItemNode = ElementDragItemNode | FileDragItemNode;

export interface ElementDragItemNode {
  id: string;
  element: TElement;
  [key: string]: unknown;
}

export interface FileDragItemNode {
  dataTransfer: DataTransfer[];
  files: FileList;
  items: DataTransferItemList;
}

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