UNPKG

react-optimized-dnd

Version:

A React package for building performant drag-and-drop interfaces. Provides context provider, hooks, and type definitions for flexible, optimized DnD in React apps.

198 lines (171 loc) 5.51 kB
# react-optimized-dnd A React package for building performant drag-and-drop interfaces. Provides context provider, hooks, and type definitions for flexible, optimized DnD in React apps. ## Features - Simple API: Provider and hooks for drag-and-drop - Optimized for React performance - TypeScript support - No external dependencies ## Installation ``` npm install react-optimized-dnd ``` or ``` yarn add react-optimized-dnd ``` ## Usage Example Below is a minimal Trello-like board with draggable cards and droppable columns. ```tsx import { useState } from 'react'; import { ReactOptimizedDndProvider, useDraggable, useDroppable } from 'react-optimized-dnd'; const columnsData = [ [ { title: 'Card 1', description: 'Desc 1', color: '#60a5fa' }, { title: 'Card 2', description: 'Desc 2', color: '#f59e42' }, ], [{ title: 'Card 3', description: 'Desc 3', color: '#10b981' }], [{ title: 'Card 4', description: 'Desc 4', color: '#f43f5e' }], ]; function Card({ index, columnIndex, title, description, color }) { const { droppableRef, isOver } = useDroppable({ data: { index, columnIndex, type: 'card' } }); const { handleRef, deltaPos, isDragging } = useDraggable({ data: { index, columnIndex, type: 'card' }, }); return ( <div ref={droppableRef} style={{ border: isDragging ? '1px solid #e5e7eb' : '1px solid transparent', background: isDragging ? '#fafaf9' : 'white', borderRadius: 6, }} > <div ref={handleRef} style={{ cursor: isDragging ? 'grabbing' : 'grab', background: isDragging ? '#93c5fd' : '#fff', borderRadius: 6, padding: 12, boxShadow: '0 1px 2px #0001', transform: `translate(${deltaPos.x}px, ${deltaPos.y}px)`, }} > <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div style={{ width: 24, height: 24, borderRadius: '50%', background: color, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, }} > {title.charAt(0)} </div> <div> <div style={{ fontWeight: 500 }}>{title}</div> <div style={{ fontSize: 12, color: '#6b7280' }}>{description}</div> </div> </div> </div> </div> ); } function Column({ children, index }) { const { droppableRef, isOver } = useDroppable({ data: { index, type: 'column' } }); return ( <div ref={droppableRef} style={{ width: 280, background: '#f3f4f6', borderRadius: 8, padding: 16, display: 'flex', flexDirection: 'column', gap: 8, }} > {children} {isOver && ( <div style={{ height: 48, background: '#dbeafe', border: '1px solid #bfdbfe', borderRadius: 6, }} /> )} </div> ); } export default function Example() { const [columns, setColumns] = useState(columnsData); return ( <div style={{ display: 'flex', gap: 16 }}> <ReactOptimizedDndProvider onDragStart={() => {}} onDragEnd={(state) => { const dragging = state.draggingElement.data; const over = state.overElement.data; if (!dragging || !over) return; if (over.type === 'card') { const newColumns = [...columns]; const targetCol = over.columnIndex; const targetIdx = over.index; const dragCol = dragging.columnIndex; const dragIdx = dragging.index; const item = newColumns[dragCol][dragIdx]; newColumns[dragCol] = newColumns[dragCol].filter((_, i) => i !== dragIdx); newColumns[targetCol].splice(targetIdx, 0, item); setColumns(newColumns); return; } if (over.type === 'column') { const newColumns = [...columns]; const targetCol = over.index; const dragCol = dragging.columnIndex; const dragIdx = dragging.index; const item = newColumns[dragCol][dragIdx]; newColumns[dragCol] = newColumns[dragCol].filter((_, i) => i !== dragIdx); newColumns[targetCol] = [...newColumns[targetCol], item]; setColumns(newColumns); return; } }} onDragOver={() => {}} > {columns.map((column, columnIndex) => ( <Column key={columnIndex} index={columnIndex}> {column.map((item, cardIndex) => ( <Card key={cardIndex} index={cardIndex} columnIndex={columnIndex} {...item} /> ))} </Column> ))} </ReactOptimizedDndProvider> </div> ); } ``` ## API ### `ReactOptimizedDndProvider` Wrap your app or a subtree to enable drag-and-drop. Accepts optional callbacks: - `onDragStart: (state) => void` - `onDragEnd: (state) => void` - `onDragOver: (state) => void` ### `useDraggable` Hook to make an element draggable. - `data`: Any object to identify the draggable (required) - Returns: `{ handleRef, deltaPos, isDragging }` ### `useDroppable` Hook to make an element a drop target. - `data`: Any object to identify the droppable (required) - Returns: `{ droppableRef, isOver }` ## License MIT