@avinash_ghosh/react-generic-dragdrop
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

React Generic Drag & Drop

A flexible, reusable React drag and drop component library with TypeScript support. Perfect for building Kanban boards, sortable lists, and any drag-and-drop interface.

✨ Features

  • 🎯 Generic & Flexible - Works with any data type extending DragDropItem
  • 🔄 Cross-Container Transfers - Drag items between different containers
  • 🎨 Customizable Styling - Full control over appearance and animations
  • 📱 TypeScript Support - Complete type safety and IntelliSense
  • 🚀 Performance Optimized - Efficient state management with React Context
  • 🎪 Visual Feedback - Beautiful drop indicators and hover effects
  • 🔧 Easy Integration - Simple HOC pattern with minimal setup

📦 Installation

npm install @avinash_ghosh/react-generic-dragdrop
yarn add @avinash_ghosh/react-generic-dragdrop

🚀 Quick Start

import React, { useState } from 'react';
import { DragDropProvider, DragDropContainer, DragDropItem } from '@avinash_ghosh/react-generic-dragdrop';

// Define your item type
interface TaskItem extends DragDropItem {
  title: string;
  description: string;
}

function App() {
  const [items, setItems] = useState<TaskItem[]>([
    { id: '1', title: 'Task 1', description: 'First task' },
    { id: '2', title: 'Task 2', description: 'Second task' },
  ]);

  // Custom render function
  const renderItem = (item: TaskItem, index: number, isDragging: boolean) => (
    <div style={{
      padding: '12px',
      backgroundColor: isDragging ? '#f0f0f0' : 'white',
      border: '1px solid #ddd',
      borderRadius: '4px',
      opacity: isDragging ? 0.8 : 1,
    }}>
      <h4>{item.title}</h4>
      <p>{item.description}</p>
    </div>
  );

  return (
    <DragDropProvider>
      <DragDropContainer
        id="my-container"
        items={items}
        renderItem={renderItem}
        onReorder={setItems}
        title="My Tasks"
        containerStyles={{
          backgroundColor: '#f8f9fa',
          padding: '16px',
          borderRadius: '8px',
        }}
      />
    </DragDropProvider>
  );
}

🎯 Advanced Example - Kanban Board

import React, { useState } from 'react';
import { DragDropProvider, DragDropContainer, DragDropItem } from '@avinash_ghosh/react-generic-dragdrop';

interface Task extends DragDropItem {
  title: string;
  description: string;
  priority: 'low' | 'medium' | 'high';
  assignee: string;
}

function KanbanBoard() {
  const [todoItems, setTodoItems] = useState<Task[]>([
    { id: '1', title: 'Design Homepage', description: 'Create wireframes', priority: 'high', assignee: 'John' },
    { id: '2', title: 'Setup Database', description: 'Configure PostgreSQL', priority: 'medium', assignee: 'Jane' },
  ]);

  const [inProgressItems, setInProgressItems] = useState<Task[]>([
    { id: '3', title: 'Implement Auth', description: 'JWT authentication', priority: 'high', assignee: 'Bob' },
  ]);

  const [doneItems, setDoneItems] = useState<Task[]>([]);

  const renderTask = (task: Task, index: number, isDragging: boolean) => {
    const priorityColors = {
      high: '#ff4444',
      medium: '#ffaa00',
      low: '#44aa44'
    };

    return (
      <div style={{
        padding: '12px',
        backgroundColor: 'white',
        borderRadius: '8px',
        border: '2px solid transparent',
        borderColor: isDragging ? '#007bff' : 'transparent',
        boxShadow: isDragging ? '0 8px 16px rgba(0,0,0,0.2)' : '0 2px 4px rgba(0,0,0,0.1)',
        transform: isDragging ? 'rotate(2deg) scale(1.02)' : 'none',
        transition: 'all 0.2s ease',
      }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
          <span style={{ fontWeight: 'bold' }}>{task.title}</span>
          <span style={{
            padding: '2px 6px',
            borderRadius: '12px',
            backgroundColor: priorityColors[task.priority],
            color: 'white',
            fontSize: '10px',
            textTransform: 'uppercase'
          }}>
            {task.priority}
          </span>
        </div>
        <p style={{ margin: '8px 0', color: '#666' }}>{task.description}</p>
        <div style={{ fontSize: '12px', color: '#888' }}>Assigned to: {task.assignee}</div>
      </div>
    );
  };

  const handleMoveItem = (item: Task, fromContainerId: string) => {
    // Remove from source
    if (fromContainerId === 'todo') setTodoItems(prev => prev.filter(i => i.id !== item.id));
    if (fromContainerId === 'inprogress') setInProgressItems(prev => prev.filter(i => i.id !== item.id));
    if (fromContainerId === 'done') setDoneItems(prev => prev.filter(i => i.id !== item.id));
  };

  const handleReceiveItem = (item: Task, fromContainerId: string, toIndex: number, toContainerId: string) => {
    // Add to destination
    if (toContainerId === 'todo') {
      setTodoItems(prev => {
        const newItems = [...prev];
        newItems.splice(toIndex, 0, item);
        return newItems;
      });
    }
    if (toContainerId === 'inprogress') {
      setInProgressItems(prev => {
        const newItems = [...prev];
        newItems.splice(toIndex, 0, item);
        return newItems;
      });
    }
    if (toContainerId === 'done') {
      setDoneItems(prev => {
        const newItems = [...prev];
        newItems.splice(toIndex, 0, item);
        return newItems;
      });
    }
  };

  return (
    <DragDropProvider>
      <div style={{ display: 'flex', gap: '20px', padding: '20px' }}>
        <DragDropContainer
          id="todo"
          items={todoItems}
          renderItem={renderTask}
          onReorder={setTodoItems}
          onMoveItemOut={handleMoveItem}
          onReceiveItemFromOutside={(item, fromId, toIndex) => handleReceiveItem(item, fromId, toIndex, 'todo')}
          title="📋 To Do"
          containerStyles={{
            backgroundColor: '#f8f9fa',
            border: '2px dashed #dee2e6',
            borderRadius: '12px',
            padding: '16px',
            minHeight: '400px',
            width: '300px'
          }}
          gap="12px"
          emptyMessage="Drop tasks here"
        />

        <DragDropContainer
          id="inprogress"
          items={inProgressItems}
          renderItem={renderTask}
          onReorder={setInProgressItems}
          onMoveItemOut={handleMoveItem}
          onReceiveItemFromOutside={(item, fromId, toIndex) => handleReceiveItem(item, fromId, toIndex, 'inprogress')}
          title="🚀 In Progress"
          containerStyles={{
            backgroundColor: '#fff3cd',
            border: '2px dashed #ffeaa7',
            borderRadius: '12px',
            padding: '16px',
            minHeight: '400px',
            width: '300px'
          }}
          gap="12px"
          emptyMessage="Drop tasks here"
        />

        <DragDropContainer
          id="done"
          items={doneItems}
          renderItem={renderTask}
          onReorder={setDoneItems}
          onMoveItemOut={handleMoveItem}
          onReceiveItemFromOutside={(item, fromId, toIndex) => handleReceiveItem(item, fromId, toIndex, 'done')}
          title="✅ Done"
          containerStyles={{
            backgroundColor: '#d4edda',
            border: '2px dashed #c3e6cb',
            borderRadius: '12px',
            padding: '16px',
            minHeight: '400px',
            width: '300px'
          }}
          gap="12px"
          emptyMessage="Drop completed tasks here"
        />
      </div>
    </DragDropProvider>
  );
}

📚 API Reference

DragDropProvider

Context provider that manages global drag state. Must wrap all DragDropContainer components.

<DragDropProvider>
  {/* Your drag drop containers */}
</DragDropProvider>

DragDropContainer<T>

Generic container component for drag and drop functionality.

Props

Prop Type Required Description
id string Unique identifier for the container
items T[] Array of items to display
renderItem (item: T, index: number, isDragging: boolean) => ReactNode Function to render each item
onReorder (items: T[]) => void Called when items are reordered within the same container
onMoveItemOut (item: T, fromContainerId: string) => void Called when an item is moved out of this container
onReceiveItemFromOutside (item: T, fromContainerId: string, toIndex: number) => void Called when an item is moved into this container
containerStyles ContainerStyles Custom styles for the container
gap string Gap between items (default: '8px')
className string CSS class name for the container
title string Title displayed at the top of the container
emptyMessage string Message shown when container is empty
disabled boolean Disable drag and drop functionality

Types

DragDropItem

Base interface that all draggable items must extend:

interface DragDropItem {
  id: string;
  [key: string]: any; // Allow additional properties
}

ContainerStyles

Styling options for containers:

interface ContainerStyles {
  backgroundColor?: string;
  border?: string;
  borderRadius?: string;
  padding?: string;
  minHeight?: string;
  gap?: string;
  [key: string]: any; // Allow any CSS properties
}

🎨 Styling

The component provides default styling but is fully customizable:

Container Styling

<DragDropContainer
  containerStyles={{
    backgroundColor: '#f8f9fa',
    border: '2px dashed #dee2e6',
    borderRadius: '12px',
    padding: '20px',
    minHeight: '300px',
    boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
  }}
/>

Item Styling

Control item appearance through your renderItem function:

const renderItem = (item, index, isDragging) => (
  <div style={{
    padding: '12px',
    backgroundColor: isDragging ? '#e3f2fd' : 'white',
    border: `2px solid ${isDragging ? '#2196f3' : '#e0e0e0'}`,
    borderRadius: '8px',
    transform: isDragging ? 'rotate(2deg) scale(1.05)' : 'none',
    opacity: isDragging ? 0.8 : 1,
    transition: 'all 0.2s ease',
    cursor: 'grab',
  }}>
    {/* Your item content */}
  </div>
);

🔧 Development

Building the Package

npm run build

Running Tests

npm test

Development Mode

npm run build:watch

📄 License

MIT © [Your Name]

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

🐛 Issues

If you find a bug or have a feature request, please open an issue on GitHub.

📈 Changelog

v1.0.0

  • Initial release
  • Generic drag and drop container
  • Cross-container transfers
  • TypeScript support
  • Customizable styling

Package Sidebar

Install

npm i @avinash_ghosh/react-generic-dragdrop

Weekly Downloads

7

Version

1.0.1

License

MIT

Unpacked Size

136 kB

Total Files

14

Last publish

Collaborators

  • avinash_ghosh