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

component-utilities.mddocs/

Component Utilities

Pre-built components and hooks for common drag-and-drop UI patterns. These utilities provide higher-level abstractions for implementing draggable elements and visual drop indicators in your Plate editor components.

Capabilities

useDraggable

Hook that provides a simplified interface for making elements draggable with pre-configured behavior and state management.

/**
 * Simplified hook for making elements draggable
 * Provides pre-configured draggable state and refs
 * @param props - Configuration options for draggable behavior
 * @returns Draggable state with refs and status
 */
export function useDraggable(props: UseDndNodeOptions): DraggableState;

export type DraggableState = {
  /** Whether the element is currently being dragged */
  isDragging: boolean;
  /** Reference to the draggable preview element */
  previewRef: React.RefObject<HTMLDivElement | null>;
  /** Reference function for the drag handle element */
  handleRef: (
    elementOrNode:
      | Element
      | React.ReactElement<any>
      | React.RefObject<any>
      | null
  ) => void;
};

Usage Examples:

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

function DraggableBlockWithHandle({ children }) {
  const element = useElement();
  
  const { isDragging, previewRef, handleRef } = useDraggable({
    element,
    orientation: 'vertical'
  });

  return (
    <div 
      ref={previewRef}
      style={{ opacity: isDragging ? 0.5 : 1 }}
    >
      <div 
        ref={handleRef}
        style={{ 
          cursor: 'grab',
          padding: '4px',
          background: '#f0f0f0',
          marginBottom: '4px'
        }}
      >
        ⋮⋮ Drag Handle
      </div>
      {children}
    </div>
  );
}

// With custom drop handler
function CustomDraggableBlock({ children }) {
  const element = useElement();
  
  const { isDragging, previewRef, handleRef } = useDraggable({
    element,
    onDropHandler: (editor, { id }) => {
      console.log('Block dropped:', id);
      return false; // Allow default behavior
    }
  });

  return (
    <div ref={previewRef}>
      <button ref={handleRef} disabled={isDragging}>
        {isDragging ? 'Dragging...' : 'Drag me'}
      </button>
      {children}
    </div>
  );
}

useDropLine

Hook for managing visual drop line indicators that show where dragged items will be dropped.

/**
 * Hook for managing drop line visual indicators
 * Shows visual feedback for drop zones during drag operations
 * @param options - Configuration for drop line behavior
 * @returns Object containing the current drop line direction
 */
export function useDropLine(options?: {
  /** The id of the element to show the dropline for */
  id?: string;
  /** Orientation of the drop line */
  orientation?: 'horizontal' | 'vertical';
}): {
  dropLine?: DropLineDirection;
};

export type DropLineDirection = '' | 'bottom' | 'left' | 'right' | 'top';

Usage Examples:

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

function BlockWithDropLine({ children }) {
  const element = useElement();
  const { dropLine } = useDropLine({ 
    id: element.id as string,
    orientation: 'vertical' 
  });

  return (
    <div style={{ position: 'relative' }}>
      {/* Drop line indicator */}
      {dropLine && (
        <div
          style={{
            position: 'absolute',
            height: '2px',
            backgroundColor: '#007acc',
            left: 0,
            right: 0,
            [dropLine]: dropLine === 'top' ? '-1px' : 
                      dropLine === 'bottom' ? 'calc(100% - 1px)' : '50%'
          }}
        />
      )}
      {children}
    </div>
  );
}

// Horizontal drop lines
function HorizontalBlock({ children }) {
  const element = useElement();
  const { dropLine } = useDropLine({ 
    orientation: 'horizontal' 
  });

  return (
    <div style={{ 
      position: 'relative',
      display: 'inline-block',
      margin: '0 4px'
    }}>
      {dropLine && (
        <div
          style={{
            position: 'absolute',
            width: '2px',
            backgroundColor: '#007acc',
            top: 0,
            bottom: 0,
            [dropLine]: dropLine === 'left' ? '-1px' : 
                      dropLine === 'right' ? 'calc(100% - 1px)' : '50%'
          }}
        />
      )}
      {children}
    </div>
  );
}

// Custom drop line styling
function CustomDropLine({ children }) {
  const { dropLine } = useDropLine();
  
  const getDropLineStyle = () => {
    if (!dropLine) return {};
    
    const baseStyle = {
      position: 'absolute' as const,
      backgroundColor: '#ff6b6b',
      zIndex: 1000,
      borderRadius: '2px'
    };
    
    switch (dropLine) {
      case 'top':
        return { ...baseStyle, top: '-2px', left: 0, right: 0, height: '4px' };
      case 'bottom':
        return { ...baseStyle, bottom: '-2px', left: 0, right: 0, height: '4px' };
      case 'left':
        return { ...baseStyle, left: '-2px', top: 0, bottom: 0, width: '4px' };
      case 'right':
        return { ...baseStyle, right: '-2px', top: 0, bottom: 0, width: '4px' };
      default:
        return {};
    }
  };

  return (
    <div style={{ position: 'relative' }}>
      {dropLine && <div style={getDropLineStyle()} />}
      {children}
    </div>
  );
}

Integration Example

Combining both utilities for a complete drag-and-drop block component:

import { useDraggable, useDropLine } from "@udecode/plate-dnd";
import { useElement } from "@udecode/plate/react";

function CompleteDragDropBlock({ children }) {
  const element = useElement();
  
  // Draggable functionality
  const { isDragging, previewRef, handleRef } = useDraggable({
    element,
    orientation: 'vertical'
  });
  
  // Drop line indicators
  const { dropLine } = useDropLine({
    id: element.id as string,
    orientation: 'vertical'
  });

  return (
    <div 
      ref={previewRef}
      style={{ 
        position: 'relative',
        opacity: isDragging ? 0.5 : 1,
        padding: '8px',
        border: '1px solid #ddd',
        borderRadius: '4px',
        margin: '4px 0'
      }}
    >
      {/* Drop line indicator */}
      {dropLine === 'top' && (
        <div style={{
          position: 'absolute',
          top: '-2px',
          left: 0,
          right: 0,
          height: '4px',
          backgroundColor: '#007acc',
          borderRadius: '2px'
        }} />
      )}
      
      {dropLine === 'bottom' && (
        <div style={{
          position: 'absolute',
          bottom: '-2px',
          left: 0,
          right: 0,
          height: '4px',
          backgroundColor: '#007acc',
          borderRadius: '2px'
        }} />
      )}
      
      {/* Drag handle */}
      <div 
        ref={handleRef}
        style={{
          position: 'absolute',
          left: '-30px',
          top: '50%',
          transform: 'translateY(-50%)',
          cursor: isDragging ? 'grabbing' : 'grab',
          padding: '4px',
          backgroundColor: '#f9f9f9',
          border: '1px solid #ddd',
          borderRadius: '4px',
          fontSize: '12px'
        }}
      >
        ⋮⋮
      </div>
      
      {children}
    </div>
  );
}

Types

export interface UseDndNodeOptions {
  element: TElement;
  nodeRef?: any;
  canDropNode?: CanDropCallback;
  type?: string;
  drag?: Partial<UseDragNodeOptions>;
  drop?: Partial<UseDropNodeOptions>;
  orientation?: 'horizontal' | 'vertical';
  preview?: {
    disable?: boolean;
    ref?: any;
  };
  onDropHandler?: (
    editor: PlateEditor,
    props: {
      id: string;
      dragItem: DragItemNode;
      monitor: DropTargetMonitor<DragItemNode, unknown>;
      nodeRef: any;
    }
  ) => boolean | void;
}

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