react-dragdrop-kit 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,70 +1,97 @@
1
1
  # react-dragdrop-kit
2
2
 
3
- A flexible and lightweight **drag-and-drop toolkit for React**. Build **sortable lists, grids, and Kanban boards** with a simple, fully-controlled API and customizable previews. Powered by Atlassian's pragmatic drag-and-drop under the hood.
3
+ A flexible, lightweight drag-and-drop toolkit for React.
4
+ Build sortable lists, grids, and Kanban boards with a controlled API and minimal overhead.
4
5
 
5
- <p>
6
- <a href="https://www.npmjs.com/package/react-dragdrop-kit"><img alt="npm" src="https://img.shields.io/npm/v/react-dragdrop-kit.svg?label=react-dragdrop-kit"></a>
7
- <a href="https://www.npmjs.com/package/react-dragdrop-kit"><img alt="downloads" src="https://img.shields.io/npm/dm/react-dragdrop-kit.svg"></a>
8
- <a href="https://github.com/Yourstruggle11/react-dragdrop-kit"><img alt="license" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
9
- </p>
6
+ [![npm version](https://img.shields.io/npm/v/react-dragdrop-kit.svg)](https://www.npmjs.com/package/react-dragdrop-kit)
7
+ [![npm downloads](https://img.shields.io/npm/dm/react-dragdrop-kit.svg)](https://www.npmjs.com/package/react-dragdrop-kit)
8
+ [![npm unpacked size](https://img.shields.io/npm/unpacked-size/react-dragdrop-kit)](https://www.npmjs.com/package/react-dragdrop-kit)
9
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Yourstruggle11/react-dragdrop-kit)
10
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Yourstruggle11/react-dragdrop-kit)
10
11
 
11
- ---
12
+ ## Why this library
12
13
 
13
- ## Features
14
+ `react-dragdrop-kit` is designed around a controlled data model:
14
15
 
15
- ### Drag & Drop Lists
16
- - 🔁 **Sortable** lists (vertical / horizontal)
17
- - 🎯 **Controlled**: you own the array, update on `onReorder`
18
- - 🧩 **Custom render** per item (cards, compact, detailed… anything)
19
- - 🧲 **Drop indicator** + optional **custom drag preview**
16
+ - You own state.
17
+ - The library reports drag intent and reorder results.
18
+ - You decide what to persist and render.
20
19
 
21
- ### 🆕 Kanban Boards (v1.2.0+)
22
- - 📋 **Full-featured Kanban board** with column and card management
23
- - 🔄 **Cross-column dragging** - Move cards between columns seamlessly
24
- - 🎨 **Headless architecture** - Complete styling control
25
- - **Accessible** - Screen reader announcements and keyboard support
26
- - 📱 **Touch-friendly** - Works on mobile devices
27
- - 🎯 **TypeScript-first** - Full type safety
20
+ This keeps behavior predictable and easy to integrate with app-specific rules.
21
+
22
+ ## Feature overview
23
+
24
+ ### List and grid drag-and-drop
25
+
26
+ - Vertical and horizontal list support
27
+ - Controlled reorder callback: `onReorder(newItems, orderUpdates)`
28
+ - Optional visual drop indicator
29
+ - Optional custom drag preview style/class
30
+ - Optional handle-only dragging via `dragHandle`
31
+ - Optional multi-item drag with `selectedIds` + `multiDragEnabled`
32
+ - Optional live list reordering during drag-over via `liveReorder`
33
+
34
+ ### Kanban module
35
+
36
+ - Card reorder within columns
37
+ - Card movement across columns
38
+ - Column reordering
39
+ - Headless rendering (`renderColumn`, `renderCard`)
40
+ - Accessibility helpers (`AnnouncerProvider`, `useAnnouncer`, `announcements`)
41
+ - Keyboard drag-reorder is planned, not fully shipped yet
42
+ - Live-reorder preview for Kanban is not shipped yet (drop-commit only)
28
43
 
29
44
  ### General
30
- - 🧪 **TypeScript** types included
31
- - ⚡ **Tiny bundles** (~5KB main, ~9KB Kanban)
32
- - 🎨 **Framework agnostic styling** - Works with Tailwind, MUI, Chakra, etc.
33
- - 📚 **Comprehensive documentation**
34
45
 
35
- ---
46
+ - TypeScript-first API
47
+ - Lightweight runtime and tree-shakeable exports
48
+ - Built on `@atlaskit/pragmatic-drag-and-drop`
36
49
 
37
- ## 📦 Install
50
+ ## Installation
38
51
 
39
52
  ```bash
40
- npm i react-dragdrop-kit
41
- # or
42
- yarn add react-dragdrop-kit
53
+ npm install react-dragdrop-kit
43
54
  # or
44
55
  pnpm add react-dragdrop-kit
56
+ # or
57
+ yarn add react-dragdrop-kit
45
58
  ```
46
59
 
47
- ---
60
+ ## Package entry points
48
61
 
49
- ## 🚀 Quick Start
62
+ ```ts
63
+ import { DragDropList } from "react-dragdrop-kit";
64
+ ```
50
65
 
51
- ### Sortable List
66
+ ```ts
67
+ import { KanbanBoard, applyDragResult } from "react-dragdrop-kit/kanban";
68
+ ```
69
+
70
+ ## Quick start: sortable list
52
71
 
53
72
  ```tsx
54
73
  import { useState } from "react";
55
74
  import { DragDropList } from "react-dragdrop-kit";
56
75
 
57
- function App() {
58
- const [items, setItems] = useState([
59
- { id: "1", position: 0, title: "Learn React" },
60
- { id: "2", position: 1, title: "Build awesome app" },
61
- { id: "3", position: 2, title: "Deploy to production" },
76
+ interface Todo {
77
+ id: string;
78
+ position: number;
79
+ title: string;
80
+ }
81
+
82
+ export default function TodoList() {
83
+ const [items, setItems] = useState<Todo[]>([
84
+ { id: "1", position: 0, title: "Design" },
85
+ { id: "2", position: 1, title: "Build" },
86
+ { id: "3", position: 2, title: "Ship" },
62
87
  ]);
63
88
 
64
89
  return (
65
90
  <DragDropList
66
91
  items={items}
67
- onReorder={(next) => setItems(next.map((it, i) => ({ ...it, position: i })))}
92
+ onReorder={(next) =>
93
+ setItems(next.map((item, index) => ({ ...item, position: index })))
94
+ }
68
95
  renderItem={(item) => (
69
96
  <div style={{ padding: 12, border: "1px solid #e5e7eb", borderRadius: 8 }}>
70
97
  {item.title}
@@ -77,261 +104,191 @@ function App() {
77
104
  }
78
105
  ```
79
106
 
80
- ### Kanban Board
107
+ ## Quick start: drag handle and multi-drag
108
+
109
+ ```tsx
110
+ <DragDropList
111
+ items={items}
112
+ onReorder={handleReorder}
113
+ renderItem={renderItem}
114
+ dragHandle="[data-drag-handle]"
115
+ selectedIds={selectedIds}
116
+ multiDragEnabled
117
+ liveReorder
118
+ />
119
+ ```
120
+
121
+ Behavior notes:
122
+
123
+ - `dragHandle` is optional. If provided, drag starts only from matching descendants.
124
+ - `multiDragEnabled` is opt-in. Without it, behavior remains single-item drag.
125
+ - `selectedIds` is consumed only when multi-drag is enabled.
126
+ - `liveReorder` is opt-in. Without it, reorder commits on drop (existing behavior).
127
+
128
+ ## Quick start: Kanban board
81
129
 
82
130
  ```tsx
83
- import { useState } from 'react';
131
+ import { useCallback, useState } from "react";
84
132
  import {
85
133
  KanbanBoard,
86
134
  applyDragResult,
87
- AnnouncerProvider,
88
- } from 'react-dragdrop-kit/kanban';
135
+ type DropResult,
136
+ type KanbanBoardState,
137
+ } from "react-dragdrop-kit/kanban";
89
138
 
90
- function App() {
91
- const [state, setState] = useState({
139
+ export default function Board() {
140
+ const [state, setState] = useState<KanbanBoardState>({
92
141
  columns: [
93
- { id: 'todo', title: 'To Do', cardIds: ['task-1', 'task-2'] },
94
- { id: 'done', title: 'Done', cardIds: [] },
142
+ { id: "todo", title: "To Do", cardIds: ["task-1", "task-2"] },
143
+ { id: "done", title: "Done", cardIds: [] },
95
144
  ],
96
145
  cards: {
97
- 'task-1': { id: 'task-1', title: 'Design landing page' },
98
- 'task-2': { id: 'task-2', title: 'Implement auth' },
146
+ "task-1": { id: "task-1", title: "Design landing page" },
147
+ "task-2": { id: "task-2", title: "Implement auth" },
99
148
  },
100
149
  });
101
150
 
102
- const handleDragEnd = (result) => {
103
- if (!result.destination) return;
104
- setState(applyDragResult(state, result));
105
- };
106
-
107
- return (
108
- <AnnouncerProvider>
109
- <KanbanBoard
110
- state={state}
111
- onDragEnd={handleDragEnd}
112
- renderColumn={(col) => <div style={{ padding: 16 }}>{col.title}</div>}
113
- renderCard={(card) => (
114
- <div style={{ padding: 12, background: '#fff', borderRadius: 8 }}>
115
- {card.title}
116
- </div>
117
- )}
118
- />
119
- </AnnouncerProvider>
151
+ const handleDragEnd = useCallback(
152
+ (result: DropResult, stateBefore: KanbanBoardState) => {
153
+ if (!result.destination) return;
154
+ setState(applyDragResult(stateBefore, result));
155
+ },
156
+ []
120
157
  );
121
- }
122
- ```
123
-
124
- ---
125
-
126
- ## 🎨 Styled Examples
127
-
128
- ### With Tailwind CSS
129
-
130
- ```tsx
131
- import { DragDropList } from "react-dragdrop-kit";
132
-
133
- export default function TailwindExample() {
134
- const [items, setItems] = useState([
135
- { id: "1", position: 0, name: "Dashboard", icon: "📊" },
136
- { id: "2", position: 1, name: "Projects", icon: "📁" },
137
- { id: "3", position: 2, name: "Team", icon: "👥" },
138
- ]);
139
158
 
140
159
  return (
141
- <DragDropList
142
- items={items}
143
- onReorder={(next) => setItems(next.map((it, i) => ({ ...it, position: i })))}
144
- containerClassName="bg-gray-50 rounded-xl p-6 space-y-2"
145
- itemClassName="bg-white rounded-lg shadow-sm hover:shadow-lg transition-all duration-200 cursor-move"
146
- showDropIndicator
147
- dropIndicatorClassName="bg-blue-500"
148
- renderItem={(item) => (
149
- <div className="flex items-center p-4 space-x-3">
150
- <span className="text-2xl">{item.icon}</span>
151
- <span className="font-medium text-gray-700">{item.name}</span>
152
- </div>
153
- )}
160
+ <KanbanBoard
161
+ state={state}
162
+ onDragEnd={handleDragEnd}
163
+ renderColumn={(column) => <div style={{ padding: 12 }}>{column.title}</div>}
164
+ renderCard={(card) => <div style={{ padding: 12 }}>{card.title}</div>}
154
165
  />
155
166
  );
156
167
  }
157
168
  ```
158
169
 
159
- ---
160
-
161
- ## 📚 API Reference
162
-
163
- ### DragDropList Component
164
-
165
- | Prop | Type | Default | Description |
166
- | ------------------------ | --------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------ |
167
- | `items` | `Array<DraggableItem & T>` | — | Items to render. Must include `{ id: string; position: number }`. |
168
- | `onReorder` | `(next: T[], updates: OrderUpdate[]) => void` | — | Called after drop. `next` is the new array; `updates` has id + newPosition. |
169
- | `renderItem` | `(item: T) => React.ReactNode` | — | Custom renderer for each item. |
170
- | `direction` | `"vertical" \| "horizontal"` | `"vertical"` | Drag axis + layout. |
171
- | `gap` | `number` | `0` | Gap (px) between items. |
172
- | `disabled` | `boolean` | `false` | Disable dragging. |
173
- | `showDropIndicator` | `boolean` | `false` | Show a drop indicator while dragging. |
174
- | `dropIndicatorPosition` | `"top" \| "bottom"` | `"bottom"` | Position of drop indicator relative to target item. |
175
- | `dropIndicatorClassName` | `string` | — | CSS class applied to the drop indicator element. |
176
- | `dragPreviewStyle` | `React.CSSProperties` | — | Inline styles for custom drag preview. |
177
- | `containerClassName` | `string` | — | Class applied to the container. |
178
- | `itemClassName` | `string` | — | Class applied to each item wrapper. |
179
-
180
- ### Kanban Components
181
-
182
- See the [Kanban Documentation](./docs/kanban.md) for complete API reference, including:
183
- - `KanbanBoard` - High-level component
184
- - `KanbanColumnView` - Headless column component
185
- - `KanbanCardView` - Headless card component
186
- - `AnnouncerProvider` - Accessibility provider
187
- - Helper utilities and types
188
-
189
- ---
190
-
191
- ## 🗂️ Kanban Board Features
192
-
193
- ### Core Functionality
194
- - ✅ **Drag cards** within and between columns
195
- - ✅ **Reorder columns** by dragging headers
196
- - ✅ **Empty column support** - Drop into columns with no cards
197
- - ✅ **Cancel drag** - Drop outside board to cancel
198
- - ✅ **Normalized state** - Efficient data structure
199
-
200
- ### Accessibility (a11y)
201
- - ♿ **Screen reader support** with live announcements
202
- - 🎹 **Keyboard navigation** (infrastructure ready)
203
- - 🏷️ **Proper ARIA attributes**
204
- - 📢 **Context-aware messages**
205
-
206
- ### Developer Experience
207
- - 📘 **Full TypeScript support**
208
- - 🎨 **Headless architecture** - Style with any framework
209
- - 🔧 **Helper utilities** - `applyDragResult`, reorder functions
210
- - 📖 **Migration guide** from react-beautiful-dnd
211
-
212
- ### Customization
213
- The Kanban board is completely headless, giving you full control:
214
- - Custom card designs
215
- - Custom column headers
216
- - Theme support
217
- - Animation styles
218
- - Responsive layouts
219
-
220
- ---
221
-
222
- ## 📂 Examples
223
-
224
- This repo includes comprehensive examples:
225
-
226
- ### Drag & Drop Lists
227
- * [Basic Example](./examples/basic-example.tsx)
228
- * [Tailwind Example](./examples/tailwind-example.tsx)
229
- * [Material UI Example](./examples/material-ui-example.tsx)
230
- * [Advanced Features](./examples/advanced-features.tsx)
231
- * [Styled Components](./examples/styled-components-example.tsx)
232
-
233
- ### Kanban Boards
234
- * [Basic Kanban](./examples/kanban/basic-kanban.tsx)
235
- * [Rich Cards with Tags & Avatars](./examples/kanban/rich-cards-kanban.tsx)
236
- * [Multi-theme Kanban](./examples/kanban/themed-kanban.tsx)
237
- * [Kanban with Accessibility](./examples/kanban/accessible-kanban.tsx)
238
-
239
- 👉 Explore the [`examples/`](./examples) folder for the complete code.
170
+ ## API reference: list module
240
171
 
241
- 🎮 **Live Demo**: Check out our [interactive demo app](https://react-dragdrop-kit.netlify.app/) with full Kanban showcase!
172
+ ### Core types
242
173
 
243
- ---
174
+ ```ts
175
+ type DraggableItem = {
176
+ id: string;
177
+ position: number;
178
+ [key: string]: any;
179
+ };
244
180
 
245
- ## 🧠 Advanced Usage
246
-
247
- ### TypeScript
248
-
249
- ```tsx
250
- import type { DraggableItem, OrderUpdate, KanbanBoardState, DropResult } from 'react-dragdrop-kit';
251
- import type { KanbanCard, KanbanColumn } from 'react-dragdrop-kit/kanban';
252
-
253
- // Extend with custom fields
254
- interface TodoItem extends DraggableItem {
255
- title: string;
256
- completed: boolean;
257
- }
258
-
259
- interface ProjectCard extends KanbanCard {
260
- priority: 'low' | 'medium' | 'high';
261
- assignee: string;
262
- tags: string[];
263
- }
181
+ type OrderUpdate = {
182
+ id: string;
183
+ newPosition: number;
184
+ moved?: boolean;
185
+ };
264
186
  ```
265
187
 
266
- ### React Strict Mode (React 19)
267
-
268
- In dev, React Strict Mode **double-mounts** to surface side effects. If you see duplicate `onReorder` calls in development, ensure your event listeners clean up correctly and keep callback identities stable with `useCallback`. Production builds call once.
269
-
270
- ---
271
-
272
- ## 📊 Bundle Size
273
-
274
- | Module | Size (Minified) |
275
- |--------|-----------------|
276
- | `react-dragdrop-kit` (Main) | ~5KB |
277
- | `react-dragdrop-kit/kanban` | ~9KB |
278
-
279
- Tree-shakeable exports mean you only pay for what you use!
280
-
281
- ---
282
-
283
- ## 🔄 Migration Guides
188
+ ### DragDropList props
189
+
190
+ | Prop | Type | Default | Description |
191
+ | --- | --- | --- | --- |
192
+ | `items` | `T[]` | Required | Controlled items. Each item must include `id` and `position`. |
193
+ | `onReorder` | `(newItems: T[], orderUpdates: OrderUpdate[]) => void` | Required | Fired after successful drop/reorder. |
194
+ | `renderItem` | `(item: T, index: number) => ReactNode` | Required | Item renderer. |
195
+ | `containerClassName` | `string` | `""` | Class name for container. |
196
+ | `containerStyle` | `React.CSSProperties` | `{}` | Inline style for container. |
197
+ | `itemClassName` | `string` | `""` | Class for each draggable wrapper. |
198
+ | `itemStyle` | `React.CSSProperties` | `{}` | Style for each draggable wrapper. |
199
+ | `dragPreviewClassName` | `string` | `""` | Class for generated drag preview. |
200
+ | `dragPreviewStyle` | `React.CSSProperties` | `{}` | Style for generated drag preview. |
201
+ | `onDragStart` | `(item: T, index: number) => void` | `undefined` | Callback on item drag start. |
202
+ | `onDragEnd` | `(item: T, index: number) => void` | `undefined` | Callback on item drag end. |
203
+ | `disabled` | `boolean` | `false` | Disables list drag/drop. |
204
+ | `gap` | `number \| string` | `undefined` | Gap applied to container. |
205
+ | `direction` | `"vertical" \| "horizontal"` | `"vertical"` | Layout and closest-edge interpretation. |
206
+ | `showDropIndicator` | `boolean` | `false` | Enables drop indicator line. |
207
+ | `dropIndicatorClassName` | `string` | `""` | Class for drop indicator. |
208
+ | `dropIndicatorStyle` | `React.CSSProperties` | `{}` | Style for drop indicator. |
209
+ | `dropIndicatorPosition` | `"top" \| "bottom"` | `"bottom"` | Indicator position for hovered target item. |
210
+ | `dragHandle` | `string` | `undefined` | CSS selector for handle-only dragging. |
211
+ | `selectedIds` | `string[]` | `[]` | Selected IDs used by multi-drag. |
212
+ | `multiDragEnabled` | `boolean` | `false` | Enables grouped drag behavior. |
213
+ | `liveReorder` | `boolean` | `false` | Reorders list in real time during drag-over. |
214
+
215
+ ## API reference: Kanban module
216
+
217
+ The full Kanban API is documented in [docs/kanban.md](./docs/kanban.md).
218
+
219
+ Exports:
220
+
221
+ - Components: `KanbanBoard`, `KanbanColumnView`, `KanbanCardView`
222
+ - Hooks: `useKanbanDnd`, `useAutoscroll`
223
+ - A11y: `AnnouncerProvider`, `useAnnouncer`, `announcements`
224
+ - Utils: `applyDragResult`, `reorderArray`
225
+ - Types: `KanbanBoardState`, `DropResult`, `KanbanCard`, `KanbanColumn`, and more
226
+
227
+ ## Examples
228
+
229
+ ### Repository examples
230
+
231
+ - `examples/basic-example.tsx`
232
+ - `examples/advanced-features.tsx`
233
+ - `examples/material-ui-example.tsx`
234
+ - `examples/tailwind-example.tsx`
235
+ - `examples/kanban/basic-kanban.tsx`
236
+ - `examples/kanban/rich-cards-kanban.tsx`
237
+ - `examples/kanban/themed-kanban.tsx`
238
+ - `examples/kanban/accessible-kanban.tsx`
239
+
240
+ ### Demo examples
241
+
242
+ - List/grid examples under `apps/demo/src/examples/*`
243
+ - Kanban demos:
244
+ - `apps/demo/src/examples/BasicKanban/index.tsx`
245
+ - `apps/demo/src/examples/RichKanban/index.tsx`
246
+ - `apps/demo/src/examples/SwimlanesKanban/index.tsx`
247
+ - `apps/demo/src/examples/WipLimitsKanban/index.tsx`
248
+
249
+ ## Migration notes
284
250
 
285
251
  ### From react-beautiful-dnd
286
252
 
287
- See our comprehensive [Kanban migration guide](./docs/kanban.md#migration-from-react-beautiful-dnd) for step-by-step instructions on migrating from `react-beautiful-dnd`.
288
-
289
- Key differences:
290
- - Normalized state structure
291
- - No auto-generated IDs
292
- - Render props instead of children
293
- - Better TypeScript support
294
-
295
- ---
296
-
297
- ## 🤝 Contributing
253
+ High-level mapping for Kanban use cases:
298
254
 
299
- Contributions are welcome! Please feel free to submit a Pull Request.
255
+ - `DragDropContext` -> `KanbanBoard`
256
+ - `Droppable` -> `KanbanColumnView`
257
+ - `Draggable` -> `KanbanCardView`
300
258
 
301
- ---
259
+ Important differences:
302
260
 
303
- ## 📝 License
261
+ 1. State is normalized (`columns` + `cards`) instead of nested.
262
+ 2. IDs are explicit and owned by your app.
263
+ 3. Rendering is done via render functions (`renderColumn`, `renderCard`).
304
264
 
305
- MIT © [Yourstruggle11](https://github.com/Yourstruggle11)
265
+ ## Development notes
306
266
 
307
- ---
267
+ ### React Strict Mode
308
268
 
309
- ## 🙏 Credits
269
+ In development, React Strict Mode can mount effects twice.
270
+ Ensure listener setup and cleanup are idempotent when integrating custom logic.
310
271
 
311
- Built with:
312
- - [`@atlaskit/pragmatic-drag-and-drop`](https://atlassian.design/components/pragmatic-drag-and-drop) - Performance-focused drag and drop
313
- - Inspired by `react-beautiful-dnd` API design
314
- - Community feedback and contributions
272
+ ## Bundle size
315
273
 
316
- ---
274
+ Approximate minified sizes:
317
275
 
318
- ## 📖 Documentation
276
+ - `react-dragdrop-kit`: about 5KB
277
+ - `react-dragdrop-kit/kanban`: about 9KB
319
278
 
320
- - [Kanban Board Guide](./docs/kanban.md)
321
- - [CHANGELOG](./CHANGELOG.md)
322
- - [Examples](./examples/)
323
- - [Demo Application](https://react-dragdrop-kit.netlify.app/)
279
+ ## Documentation and links
324
280
 
325
- ---
281
+ - [Kanban guide](./docs/kanban.md)
282
+ - [Known issues](./KNOWN_ISSUES.md)
283
+ - [Changelog](./packages/react-dragdrop-kit/CHANGELOG.md)
284
+ - [Examples folder](./examples)
285
+ - [Demo app](https://react-dragdrop-kit.netlify.app/)
326
286
 
327
- ## 💬 Support
287
+ ## Support
328
288
 
329
- - 🐛 [Report Issues](https://github.com/Yourstruggle11/react-dragdrop-kit/issues)
330
- - 💡 [Request Features](https://github.com/Yourstruggle11/react-dragdrop-kit/issues/new)
331
- - ⭐ [Star on GitHub](https://github.com/Yourstruggle11/react-dragdrop-kit)
289
+ - [Report issues](https://github.com/Yourstruggle11/react-dragdrop-kit/issues)
290
+ - [Request features](https://github.com/Yourstruggle11/react-dragdrop-kit/issues/new)
332
291
 
333
- ---
292
+ ## License
334
293
 
335
- <div align="center">
336
- <strong>Made with ❤️ by <a href="https://github.com/Yourstruggle11" target="_blank" rel="noopener noreferrer">Yourstruggle11</a> for the React community</strong>
337
- </div>
294
+ MIT
package/dist/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import e,{useRef as t,useState as a,useCallback as r,useEffect as o}from"react";import{combine as i}from"@atlaskit/pragmatic-drag-and-drop/combine";import{draggable as n,dropTargetForElements as d,monitorForElements as s}from"@atlaskit/pragmatic-drag-and-drop/element/adapter";import{extractClosestEdge as l}from"@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";const c={container:{default:{minHeight:"200px",transition:"all 0.2s ease",borderRadius:"8px",border:"1px solid #e2e8f0",backgroundColor:"#ffffff",display:"flex",flexDirection:"column",gap:"16px",padding:"16px"},dragging:{backgroundColor:"rgba(66, 153, 225, 0.05)",borderColor:"#3182ce"},horizontal:{flexDirection:"row",flexWrap:"wrap"}},item:{default:{transition:"all 0.2s ease",cursor:"grab",userSelect:"none",position:"relative"},dragging:{opacity:.5,cursor:"grabbing"},hover:{transform:"scale(1.02)",boxShadow:"0 4px 6px rgba(0, 0, 0, 0.1)"},disabled:{cursor:"not-allowed",opacity:.6}},preview:{position:"fixed",pointerEvents:"none",zIndex:9999,opacity:.8,transform:"rotate(2deg)",boxShadow:"0 10px 20px rgba(0, 0, 0, 0.15)"},dropIndicator:{position:"absolute",height:"2px",backgroundColor:"#3182ce",left:0,right:0,zIndex:10,transition:"all 0.2s ease"}},g="draggable-item";function p({item:s,index:l,children:p,className:m="",style:u={},dragPreviewClassName:b="",dragPreviewStyle:v={},onDragStart:f,onDragEnd:x,disabled:y=!1,showDropIndicator:D=!1,dropIndicatorClassName:h="",dropIndicatorStyle:I={},dropIndicatorPosition:j="bottom"}){const O=t(null),[w,S]=a(!1),[N,C]=a(!1),[E,P]=a(null),k=r(e=>y?()=>{}:n({element:e,getInitialData:()=>({type:g,id:s.id,index:l}),onGenerateDragPreview:({nativeSetDragImage:t})=>{const a=e.cloneNode(!0),r=Object.assign(Object.assign({},c.preview),v);b&&(a.className=b),Object.assign(a.style,r,{width:`${(null==e?void 0:e.offsetWidth)||100}px`,height:`${(null==e?void 0:e.offsetHeight)||50}px`}),document.body.appendChild(a),t&&t(a,20,20),requestAnimationFrame(()=>a.remove())},onDragStart:()=>{S(!0),null==f||f(s,l)},onDrop:()=>{S(!1),null==x||x(s,l)}}),[s,l,b,v,f,x,y]),z=r(e=>y?()=>{}:d({element:e,getData:()=>({type:g,id:s.id,index:l}),canDrop:e=>{var t;return(null===(t=e.source.data)||void 0===t?void 0:t.type)===g},getIsSticky:()=>!0,onDragEnter:({source:e,self:t})=>{var a,r;(null===(a=e.data)||void 0===a?void 0:a.id)!==(null===(r=t.data)||void 0===r?void 0:r.id)&&(C(!0),D&&P(j))},onDragLeave:()=>{C(!1),P(null)},onDrop:()=>{C(!1),P(null)}}),[s.id,l,y,D,j]);return o(()=>{const e=O.current;if(e)return e.setAttribute("data-index",l.toString()),y?()=>{}:i(k(e),z(e))},[l,k,z,y]),e.createElement("div",{ref:O,className:m,style:(()=>{const e=Object.assign({},c.item.default);return y?Object.assign(e,c.item.disabled):w?Object.assign(e,c.item.dragging):N&&Object.assign(e,c.item.hover),Object.assign(Object.assign({},e),u)})()},D&&"top"===E&&e.createElement("div",{className:h,style:Object.assign(Object.assign(Object.assign({},c.dropIndicator),I),{top:-1})}),p,D&&"bottom"===E&&e.createElement("div",{className:h,style:Object.assign(Object.assign(Object.assign({},c.dropIndicator),I),{bottom:-1})}))}function m({items:e,onReorder:t,disabled:a=!1}){if(a)return()=>{};return s({onDrop:({location:a,source:r})=>{var o;const i=null===(o=r.data)||void 0===o?void 0:o.index;if(void 0===i)return;const{dropTargets:n}=a.current,d=n.find(e=>{var t,a,o;return"draggable-item"===(null===(t=e.data)||void 0===t?void 0:t.type)&&(null===(a=e.data)||void 0===a?void 0:a.id)!==(null===(o=r.data)||void 0===o?void 0:o.id)});if(!d)return;const s=l(d),c=Number(d.element.getAttribute("data-index")),g="bottom"===s?c+1:c;if(i!==g){const a=[...e],r=function(e,t,a){const r=Array.from(e),[o]=r.splice(t,1);return r.splice(a,0,o),r}(e,i,g),o=function(e,t){const a=t.filter((t,a)=>{var r;return t.id!==(null===(r=e[a])||void 0===r?void 0:r.id)}),r=a.slice().sort((e,t)=>e.position-t.position).map(e=>e.position);return a.map((e,t)=>({id:e.id,newPosition:r[t]}))}(a,r);t(r,o)}}})}function u({items:n,onReorder:s,renderItem:l,containerClassName:g="",containerStyle:u={},itemClassName:b="",itemStyle:v={},dragPreviewClassName:f="",dragPreviewStyle:x={},onDragStart:y,onDragEnd:D,disabled:h=!1,gap:I,direction:j="vertical",showDropIndicator:O=!1,dropIndicatorClassName:w="",dropIndicatorStyle:S={},dropIndicatorPosition:N="bottom"}){const C=t(null),E=m({items:n,onReorder:s,disabled:h}),[P,k]=a(!1),z=r(e=>h?()=>{}:d({element:e,getData:()=>({type:"container"}),onDragEnter:()=>k(!0),onDragLeave:()=>k(!1),onDrop:()=>k(!1)}),[h]);return o(()=>{const e=C.current;if(e&&!h)return i(z(e),E)},[z,E,h]),e.createElement("div",{ref:C,className:g,style:(()=>{const e=Object.assign({},c.container.default);return"horizontal"===j&&Object.assign(e,c.container.horizontal),P&&Object.assign(e,c.container.dragging),void 0!==I&&(e.gap="number"==typeof I?`${I}px`:I),Object.assign(Object.assign({},e),u)})()},n.map((t,a)=>e.createElement(p,{key:t.id,item:t,index:a,className:b,style:v,dragPreviewClassName:f,dragPreviewStyle:x,onDragStart:y,onDragEnd:D,disabled:h,showDropIndicator:O,dropIndicatorClassName:w,dropIndicatorStyle:S,dropIndicatorPosition:N},l(t,a))))}export{u as DragDropList,p as DraggableItemWrapper,c as defaultStyles,m as useDragDropMonitor};
1
+ import e,{useRef as t,useState as n,useCallback as r,useEffect as i}from"react";import{draggable as o,dropTargetForElements as a,monitorForElements as d}from"@atlaskit/pragmatic-drag-and-drop/element/adapter";import{combine as l}from"@atlaskit/pragmatic-drag-and-drop/combine";import{attachClosestEdge as s,extractClosestEdge as c}from"@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";const u={container:{default:{minHeight:"200px",transition:"all 0.2s ease",borderRadius:"8px",border:"1px solid #e2e8f0",backgroundColor:"#ffffff",display:"flex",flexDirection:"column",gap:"16px",padding:"16px"},dragging:{backgroundColor:"rgba(66, 153, 225, 0.05)",borderColor:"#3182ce"},horizontal:{flexDirection:"row",flexWrap:"wrap"}},item:{default:{transition:"all 0.2s ease",cursor:"grab",userSelect:"none",position:"relative"},dragging:{opacity:.5,cursor:"grabbing"},hover:{transform:"scale(1.02)",boxShadow:"0 4px 6px rgba(0, 0, 0, 0.1)"},disabled:{cursor:"not-allowed",opacity:.6}},preview:{position:"fixed",pointerEvents:"none",zIndex:9999,opacity:.8,transform:"rotate(2deg)",boxShadow:"0 10px 20px rgba(0, 0, 0, 0.15)"},dropIndicator:{position:"absolute",height:"2px",backgroundColor:"#3182ce",left:0,right:0,zIndex:10,transition:"all 0.2s ease"}},g="draggable-item";function m({item:d,index:c,children:m,className:p="",style:v={},dragPreviewClassName:f="",dragPreviewStyle:I={},onDragStart:b,onDragEnd:h,disabled:x=!1,showDropIndicator:w=!1,dropIndicatorClassName:D="",dropIndicatorStyle:y={},dropIndicatorPosition:S="bottom",direction:E="vertical",dragHandle:O}){const R=t(null),[j,C]=n(!1),[N,P]=n(!1),[M,z]=n(null),K=r(e=>{if(x)return()=>{};const t=O?e.querySelector(O):void 0;return o({element:e,dragHandle:null!=t?t:void 0,canDrag:()=>!O||Boolean(t),getInitialData:()=>({type:g,id:d.id,index:c}),onGenerateDragPreview:({nativeSetDragImage:t})=>{const n=e.cloneNode(!0),r=Object.assign(Object.assign({},u.preview),I);f&&(n.className=f),Object.assign(n.style,r,{width:`${(null==e?void 0:e.offsetWidth)||100}px`,height:`${(null==e?void 0:e.offsetHeight)||50}px`}),document.body.appendChild(n),t&&t(n,20,20),requestAnimationFrame(()=>n.remove())},onDragStart:()=>{C(!0),null==b||b(d,c)},onDrop:()=>{C(!1),null==h||h(d,c)}})},[d,c,O,f,I,b,h,x]),k=r(e=>x?()=>{}:a({element:e,getData:({input:e,element:t})=>{const n={type:g,id:d.id,index:c};return s(n,{input:e,element:t,allowedEdges:[..."horizontal"===E?["left","right"]:["top","bottom"]]})},canDrop:({source:e})=>{var t,n;return(null===(t=e.data)||void 0===t?void 0:t.type)===g&&(null===(n=e.data)||void 0===n?void 0:n.id)!==d.id},onDragEnter:({source:e,self:t})=>{var n,r;(null===(n=e.data)||void 0===n?void 0:n.id)!==(null===(r=t.data)||void 0===r?void 0:r.id)&&(P(!0),w&&z(S))},onDragLeave:()=>{P(!1),z(null)},onDrop:()=>{P(!1),z(null)}}),[d.id,c,E,x,w,S]);return i(()=>{const e=R.current;if(e)return x?()=>{}:l(K(e),k(e))},[c,K,k,x]),e.createElement("div",{ref:R,"data-index":c,"data-rdk-item-id":d.id,className:p,style:(()=>{const e=Object.assign({},u.item.default);return x?Object.assign(e,u.item.disabled):j?Object.assign(e,u.item.dragging):N&&Object.assign(e,u.item.hover),Object.assign(Object.assign({},e),v)})()},w&&"top"===M&&e.createElement("div",{className:D,style:Object.assign(Object.assign(Object.assign({},u.dropIndicator),y),{top:-1})}),m,w&&"bottom"===M&&e.createElement("div",{className:D,style:Object.assign(Object.assign(Object.assign({},u.dropIndicator),y),{bottom:-1})}))}function p(e,t){const n=new Map(e.map((e,t)=>[e.id,t]));return t.flatMap((e,t)=>{const r=n.get(e.id);return void 0===r||r===t?[]:[{id:e.id,newPosition:t,moved:!0}]})}function v(e,t){const n=e.element.getBoundingClientRect(),r=n.left+n.width/2,i=n.top+n.height/2;return Math.pow(t.clientX-r,2)+Math.pow(t.clientY-i,2)}function f(e,t){return e.length!==t.length||t.some((t,n)=>{var r;return t.id!==(null===(r=e[n])||void 0===r?void 0:r.id)})}function I(e){const{items:t,sourceId:n,selectedIds:r,multiDragEnabled:i}=e;if(!i)return new Set([n]);const o=new Set(t.map(e=>e.id)),a=new Set(r.filter(e=>o.has(e)));return a.has(n)?a:new Set([n])}function b(e){const{items:t,sourceId:n,sourceIndex:r,rawDestinationIndex:i,selectedIds:o,multiDragEnabled:a}=e;if(a){const e=new Set(o);e.has(n)||e.clear(),e.add(n);const r=t.map((t,n)=>e.has(t.id)?n:-1).filter(e=>-1!==e);if(r.length>1)return function(e,t,n){const r=Array.from(new Set(t.filter(t=>t>=0&&t<e.length))).sort((e,t)=>e-t);if(0===r.length)return e;const i=new Set(r),o=r.map(t=>e[t]),a=e.filter((e,t)=>!i.has(t)),d=Math.max(0,Math.min(n,e.length)),l=r.filter(e=>e<d).length,s=Math.max(0,Math.min(d-l,a.length)),c=[...a];return c.splice(s,0,...o),c}(t,r,i)}const d=function(e){const{itemCount:t,sourceIndex:n,rawDestinationIndex:r,isSameList:i}=e;if(t<=0)return 0;const o=t,a=Math.max(0,Math.min(r,o)),d=i&&n<a?a-1:a,l=i?t-1:t;return Math.max(0,Math.min(d,l))}({itemCount:t.length,sourceIndex:r,rawDestinationIndex:i,isSameList:!0});return function(e,t,n){const r=Array.from(e),[i]=r.splice(t,1);return r.splice(n,0,i),r}(t,r,d)}function h(e){var t,n;const{location:r,sourceId:i,sourceIndex:o,direction:a,items:d,excludedIds:l}=e,{dropTargets:s,input:u}=r,g=function(e){const{dropTargets:t,input:n,sourceId:r,excludedIds:i}=e,o=t.filter(e=>{var t,n,o;if("draggable-item"!==(null===(t=e.data)||void 0===t?void 0:t.type))return!1;const a=String(null!==(o=null===(n=e.data)||void 0===n?void 0:n.id)&&void 0!==o?o:"");return!(!a||a===r||i.has(a))});return 0===o.length?null:1===o.length?o[0]:o.reduce((e,t)=>v(t,n)<v(e,n)?t:e)}({dropTargets:s,input:u,sourceId:i,excludedIds:l}),m=s.find(e=>{var t;return"container"===(null===(t=e.data)||void 0===t?void 0:t.type)});if(!g&&!m)return null;if(!g){if(!m)return null;const e=function(e){const{containerElement:t,input:n,direction:r,items:i,excludedIds:o}=e,a=new Map(i.map((e,t)=>[e.id,t])),d=Array.from(t.querySelectorAll("[data-rdk-item-id]"));if(0===d.length)return 0;const l="horizontal"===r?n.clientX:n.clientY,s=d.map(e=>{const t=e.getAttribute("data-rdk-item-id");if(!t||o.has(t))return null;const i=a.get(t);if(void 0===i)return null;const d=e.getBoundingClientRect(),l=d.left+d.width/2,s=d.top+d.height/2;return{index:i,distance:Math.pow(n.clientX-l,2)+Math.pow(n.clientY-s,2),centerOnAxis:"horizontal"===r?l:s}}).filter(e=>null!==e);if(0===s.length)return null;const c=s.reduce((e,t)=>t.distance<e.distance?t:e);return l>=c.centerOnAxis?c.index+1:c.index}({containerElement:m.element,input:u,direction:a,items:d,excludedIds:l}),t=null!=e?e:d.length;return{rawDestinationIndex:t,previewKey:`container:${t}`}}const p=String(null!==(n=null===(t=g.data)||void 0===t?void 0:t.id)&&void 0!==n?n:"");if(!p)return null;const f=d.findIndex(e=>e.id===p);if(-1===f)return null;const I=c(g.data);let b;if(o<f)b=!0;else if(o>f)b=!1;else if(null===I){const e=g.element.getBoundingClientRect(),t="horizontal"===a?e.left+e.width/2:e.top+e.height/2;b=("horizontal"===a?u.clientX:u.clientY)>=t}else b="bottom"===I||"horizontal"===a&&"right"===I;return{rawDestinationIndex:b?f+1:f,previewKey:`item:${p}:${b?"after":"before"}`}}function x({items:e,onReorder:n,disabled:o=!1,direction:a="vertical",selectedIds:l=[],multiDragEnabled:s=!1,liveReorder:c=!1}){const u=t(e),g=t(n),m=t({direction:a,selectedIds:l,multiDragEnabled:s,liveReorder:c}),v=t({sourceId:null,movingIds:null,originalItems:null,didLiveReorder:!1,lastPreviewKey:null});i(()=>{u.current=e,g.current=n,m.current={direction:a,selectedIds:l,multiDragEnabled:s,liveReorder:c}},[e,n,a,l,s,c]);const x=r(()=>{v.current={sourceId:null,movingIds:null,originalItems:null,didLiveReorder:!1,lastPreviewKey:null}},[]),w=r((e,t)=>{const n=u.current.findIndex(t=>t.id===e);if(-1!==n)return n;const r=Number(t);return Number.isFinite(r)?r:-1},[]),D=r(e=>{const t=u.current,{selectedIds:n,multiDragEnabled:r}=m.current;return I({items:t,sourceId:e,selectedIds:n,multiDragEnabled:r})},[]),y=r(e=>{const t=v.current.movingIds;return t?new Set(t):D(e)},[D]),S=r(({sourceId:e,sourceIndex:t,rawDestinationIndex:n})=>{const r=[...u.current],{selectedIds:i,multiDragEnabled:o}=m.current,a=b({items:r,sourceId:e,sourceIndex:t,rawDestinationIndex:n,selectedIds:i,multiDragEnabled:o});if(!f(r,a))return!1;const d=p(r,a);return u.current=a,g.current(a,d),!0},[]),E=r(({source:e})=>{var t,n;if("draggable-item"!==(null===(t=e.data)||void 0===t?void 0:t.type))return;const r=null===(n=e.data)||void 0===n?void 0:n.id;if(!r)return;const i=u.current,{selectedIds:o,multiDragEnabled:a}=m.current,d=I({items:i,sourceId:r,selectedIds:o,multiDragEnabled:a});v.current={sourceId:r,movingIds:d,originalItems:m.current.liveReorder?[...i]:null,didLiveReorder:!1,lastPreviewKey:null}},[]),O=r(({location:e,source:t})=>{var n,r,i;if("draggable-item"!==(null===(n=t.data)||void 0===n?void 0:n.type))return;const{direction:o,liveReorder:a}=m.current;if(!a)return;const d=null===(r=t.data)||void 0===r?void 0:r.id;if(!d)return;const l=w(d,null===(i=t.data)||void 0===i?void 0:i.index);if(l<0)return;const s=u.current,c=y(d),g=h({location:e.current,sourceId:d,sourceIndex:l,direction:o,items:s,excludedIds:c});if(!g)return;if(v.current.lastPreviewKey===g.previewKey)return;const p=S({sourceId:d,sourceIndex:l,rawDestinationIndex:g.rawDestinationIndex});v.current.lastPreviewKey=g.previewKey,p&&(v.current.didLiveReorder=!0)},[w,y,S]),R=r(({location:e,source:t})=>{var n,r,i;if("draggable-item"!==(null===(n=t.data)||void 0===n?void 0:n.type))return void x();const o=null===(r=t.data)||void 0===r?void 0:r.id;if(!o)return void x();const{direction:a,liveReorder:d}=m.current,l=u.current,s=y(o),c=w(o,null===(i=t.data)||void 0===i?void 0:i.index);if(c<0)return void x();const I=h({location:e.current,sourceId:o,sourceIndex:c,direction:a,items:l,excludedIds:s});if(!I){const{originalItems:e,didLiveReorder:t}=v.current;if(d&&t&&e){const t=[...u.current];if(f(t,e)){const n=p(t,e);u.current=e,g.current(e,n)}}return void x()}const{didLiveReorder:b,lastPreviewKey:D}=v.current;d&&b&&D===I.previewKey||S({sourceId:o,sourceIndex:c,rawDestinationIndex:I.rawDestinationIndex}),x()},[y,w,S,x]);return i(()=>{if(!o)return d({onDragStart:E,onDropTargetChange:O,onDrop:R})},[o,E,O,R]),r(()=>{},[])}function w({items:o,onReorder:d,renderItem:l,containerClassName:s="",containerStyle:c={},itemClassName:g="",itemStyle:p={},dragPreviewClassName:v="",dragPreviewStyle:f={},onDragStart:I,onDragEnd:b,disabled:h=!1,gap:w,direction:D="vertical",showDropIndicator:y=!1,dropIndicatorClassName:S="",dropIndicatorStyle:E={},dropIndicatorPosition:O="bottom",dragHandle:R,selectedIds:j=[],multiDragEnabled:C=!1,liveReorder:N=!1}){const P=t(null);x({items:o,onReorder:d,disabled:h,direction:D,selectedIds:j,multiDragEnabled:C,liveReorder:N});const[M,z]=n(!1),K=r(e=>h?()=>{}:a({element:e,getData:()=>({type:"container"}),onDragEnter:()=>z(!0),onDragLeave:()=>z(!1),onDrop:()=>z(!1)}),[h]);return i(()=>{const e=P.current;if(e&&!h)return K(e)},[K,h]),e.createElement("div",{ref:P,className:s,style:(()=>{const e=Object.assign({},u.container.default);return"horizontal"===D&&Object.assign(e,u.container.horizontal),M&&Object.assign(e,u.container.dragging),void 0!==w&&(e.gap="number"==typeof w?`${w}px`:w),Object.assign(Object.assign({},e),c)})()},o.map((t,n)=>e.createElement(m,{key:t.id,item:t,index:n,className:g,style:p,dragPreviewClassName:v,dragPreviewStyle:f,onDragStart:I,onDragEnd:b,disabled:h,showDropIndicator:y,dropIndicatorClassName:S,dropIndicatorStyle:E,dropIndicatorPosition:O,direction:D,dragHandle:R},l(t,n))))}export{w as DragDropList,m as DraggableItemWrapper,u as defaultStyles,x as useDragDropMonitor};
2
2
  //# sourceMappingURL=index.esm.js.map