ui/packages/ui-dnd
2026-06-10 21:19:44 -07:00
..
.forgejo/workflows ci(workflows): 👷 Remove redundant build steps from publish workflows to improve efficiency 2026-04-20 01:16:37 -07:00
e2e chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 18:06:26 -08:00
playwright chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 18:01:04 -08:00
playwright-report chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 19:03:24 -08:00
src chore(ui-icons): 🔧 Regenerate test metadata/verification files and update test setups to align with tooling changes across UI packages 2026-02-11 22:50:06 -08:00
test-results chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 18:06:26 -08:00
.gitignore chore(ui): 🔧 Standardize build artifact and environment file exclusion in all UI packages to enforce consistent .gitignore patterns 2026-04-20 01:16:37 -07:00
package.json deps-upgrade(ui-packages): ⬆️ Update all UI packages to latest stable versions for security, performance, and compatibility 2026-06-10 21:19:44 -07:00
playwright-ct.config.ts chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 17:55:41 -08:00
README.md
tsconfig.json chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 18:06:26 -08:00
vitest.config.ts chore(shared): 🔧 Update shared configuration files and scripts 2026-01-16 17:55:41 -08:00

@lilith/ui-dnd

Drag and drop components for React with grid and list support. Uses framer-motion for smooth animations.

Features

  • SortableGrid - CSS Grid-based sortable container with automatic reordering
  • SortableList - Flex-based sortable list (vertical or horizontal)
  • useDraggable - Hook to make any element draggable
  • useDroppable - Hook to make any element a drop target
  • DndProvider - Context provider for custom drag-and-drop implementations

Installation

pnpm add @lilith/ui-dnd

Peer Dependencies

{
  "react": "^18.0.0 || ^19.0.0",
  "react-dom": "^18.0.0 || ^19.0.0",
  "styled-components": "^6.0.0"
}

Usage

SortableGrid

Perfect for dashboard widgets, image galleries, or any grid-based reorderable content.

import { SortableGrid } from '@lilith/ui-dnd';

const widgets = ['temperature', 'fan_speed', 'power_draw', 'clock_speed'];

function Dashboard() {
  const [order, setOrder] = useState(widgets);

  return (
    <SortableGrid
      items={order}
      getItemKey={(item) => item}
      renderItem={(item, index, isDragging) => (
        <WidgetCard
          title={item}
          style={{ opacity: isDragging ? 0.8 : 1 }}
        />
      )}
      onReorder={setOrder}
      grid={{
        columns: 4,  // or 'auto' for responsive
        gap: 16,
        minColumnWidth: 280,  // used when columns='auto'
      }}
    />
  );
}

SortableList

For task lists, navigation items, or any linear sortable content.

import { SortableList } from '@lilith/ui-dnd';

function TaskList() {
  const [tasks, setTasks] = useState([
    { id: '1', title: 'Task 1' },
    { id: '2', title: 'Task 2' },
    { id: '3', title: 'Task 3' },
  ]);

  return (
    <SortableList
      items={tasks}
      getItemKey={(task) => task.id}
      renderItem={(task, index, isDragging) => (
        <TaskCard task={task} />
      )}
      onReorder={setTasks}
      direction="vertical"
      gap={8}
    />
  );
}

Custom Drag and Drop with Hooks

For more complex scenarios, use the low-level hooks:

import { DndProvider, useDraggable, useDroppable } from '@lilith/ui-dnd';

function DraggableItem({ id, data }) {
  const { dragProps, isDragging, setNodeRef } = useDraggable({
    id,
    type: 'card',
    data,
  });

  return (
    <div
      ref={setNodeRef}
      {...dragProps}
      style={{ opacity: isDragging ? 0.5 : 1 }}
    >
      Drag me!
    </div>
  );
}

function DropZone({ id, onItemDrop }) {
  const { dropProps, isOver, canDrop, setNodeRef } = useDroppable({
    id,
    acceptTypes: ['card'],
    onDrop: (result) => onItemDrop(result.item),
  });

  return (
    <div
      ref={setNodeRef}
      {...dropProps}
      style={{
        background: isOver && canDrop ? 'lightgreen' : 'white',
      }}
    >
      Drop here
    </div>
  );
}

function App() {
  return (
    <DndProvider onDrop={(result) => console.log('Dropped:', result)}>
      <DraggableItem id="item-1" data={{ name: 'Item 1' }} />
      <DropZone id="zone-1" onItemDrop={handleDrop} />
    </DndProvider>
  );
}

API Reference

SortableGrid Props

Prop Type Default Description
items T[] required Array of items to render
getItemKey (item: T) => string required Function to get unique key
renderItem (item: T, index: number, isDragging: boolean) => ReactNode required Render function
onReorder (items: T[]) => void required Callback when order changes
grid GridConfig { columns: 4, gap: 16 } Grid configuration
disabled boolean false Disable drag and drop
className string - Additional CSS class

GridConfig

Prop Type Default Description
columns number | 'auto' 4 Number of columns or auto-fill
gap number | string 16 Gap between items (px or theme key)
minColumnWidth number 280 Min column width when columns='auto'

SortableList Props

Prop Type Default Description
items T[] required Array of items to render
getItemKey (item: T) => string required Function to get unique key
renderItem (item: T, index: number, isDragging: boolean) => ReactNode required Render function
onReorder (items: T[]) => void required Callback when order changes
direction 'vertical' | 'horizontal' 'vertical' List direction
gap number | string 8 Gap between items
disabled boolean false Disable drag and drop
className string - Additional CSS class

useDraggable Config

Prop Type Default Description
id string required Unique identifier
type string required Item type for filtering
data T - Custom data payload
index number - Index in sortable container
disabled boolean false Disable dragging
onDragStart (item) => void - Drag start callback
onDragEnd (item, dropped) => void - Drag end callback

useDroppable Config

Prop Type Default Description
id string required Unique identifier
acceptTypes string[] [] Accepted item types (empty = all)
disabled boolean false Disable dropping
onDragEnter (item) => void - Item enters zone
onDragLeave (item) => void - Item leaves zone
onDrop (result) => void - Item dropped

Types

import type {
  Position,
  DragItem,
  DragState,
  DropResult,
  GridConfig,
  SortableGridProps,
  SortableListProps,
} from '@lilith/ui-dnd';

License

MIT