react-dragdrop-kit 1.1.0 → 1.2.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 +223 -82
- package/dist/kanban.esm.js +2 -0
- package/dist/kanban.esm.js.map +1 -0
- package/dist/kanban.js +2 -0
- package/dist/kanban.js.map +1 -0
- package/dist/src/kanban/a11y/Announcer.d.ts +37 -0
- package/dist/src/kanban/a11y/useLiveRegion.d.ts +17 -0
- package/dist/src/kanban/components/KanbanBoard.d.ts +8 -0
- package/dist/src/kanban/components/KanbanCardView.d.ts +8 -0
- package/dist/src/kanban/components/KanbanColumnView.d.ts +8 -0
- package/dist/src/kanban/context.d.ts +13 -0
- package/dist/src/kanban/hooks/useAutoscroll.d.ts +10 -0
- package/dist/src/kanban/hooks/useKanbanDnd.d.ts +16 -0
- package/dist/src/kanban/index.d.ts +15 -0
- package/dist/src/kanban/types.d.ts +185 -0
- package/dist/src/kanban/utils/dndMath.d.ts +61 -0
- package/dist/src/kanban/utils/dom.d.ts +62 -0
- package/dist/src/kanban/utils/reorder.d.ts +32 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# react-dragdrop-kit
|
|
2
2
|
|
|
3
|
-
A flexible and lightweight **drag-and-drop toolkit for React**. Build **sortable lists, grids, and boards** with a simple, fully-controlled API and customizable previews. Powered by Atlassian
|
|
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.
|
|
4
4
|
|
|
5
5
|
<p>
|
|
6
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>
|
|
@@ -12,12 +12,25 @@ A flexible and lightweight **drag-and-drop toolkit for React**. Build **sortable
|
|
|
12
12
|
|
|
13
13
|
## ✨ Features
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### Drag & Drop Lists
|
|
16
|
+
- 🔁 **Sortable** lists (vertical / horizontal)
|
|
16
17
|
- 🎯 **Controlled**: you own the array, update on `onReorder`
|
|
17
18
|
- 🧩 **Custom render** per item (cards, compact, detailed… anything)
|
|
18
19
|
- 🧲 **Drop indicator** + optional **custom drag preview**
|
|
20
|
+
|
|
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
|
|
28
|
+
|
|
29
|
+
### General
|
|
19
30
|
- 🧪 **TypeScript** types included
|
|
20
|
-
- ⚡
|
|
31
|
+
- ⚡ **Tiny bundles** (~5KB main, ~9KB Kanban)
|
|
32
|
+
- 🎨 **Framework agnostic styling** - Works with Tailwind, MUI, Chakra, etc.
|
|
33
|
+
- 📚 **Comprehensive documentation**
|
|
21
34
|
|
|
22
35
|
---
|
|
23
36
|
|
|
@@ -29,54 +42,32 @@ npm i react-dragdrop-kit
|
|
|
29
42
|
yarn add react-dragdrop-kit
|
|
30
43
|
# or
|
|
31
44
|
pnpm add react-dragdrop-kit
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## 🚀 Quick Start (Basic Example)
|
|
36
|
-
|
|
37
|
-
```tsx
|
|
38
|
-
// examples/basic-example.tsx
|
|
45
|
+
```
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
import { DragDropList, type DraggableItem, type OrderUpdate } from "react-dragdrop-kit";
|
|
47
|
+
---
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
title: string;
|
|
45
|
-
completed: boolean;
|
|
46
|
-
}
|
|
49
|
+
## 🚀 Quick Start
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
{ id: "1", position: 0, title: "Learn React", completed: false },
|
|
50
|
-
{ id: "2", position: 1, title: "Build awesome app", completed: false },
|
|
51
|
-
{ id: "3", position: 2, title: "Deploy to production", completed: false },
|
|
52
|
-
];
|
|
51
|
+
### Sortable List
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
```tsx
|
|
54
|
+
import { useState } from "react";
|
|
55
|
+
import { DragDropList } from "react-dragdrop-kit";
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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" },
|
|
62
|
+
]);
|
|
63
63
|
|
|
64
64
|
return (
|
|
65
65
|
<DragDropList
|
|
66
|
-
items={
|
|
67
|
-
onReorder={
|
|
66
|
+
items={items}
|
|
67
|
+
onReorder={(next) => setItems(next.map((it, i) => ({ ...it, position: i })))}
|
|
68
68
|
renderItem={(item) => (
|
|
69
69
|
<div style={{ padding: 12, border: "1px solid #e5e7eb", borderRadius: 8 }}>
|
|
70
|
-
|
|
71
|
-
type="checkbox"
|
|
72
|
-
checked={item.completed}
|
|
73
|
-
onChange={() =>
|
|
74
|
-
setTodos((prev) =>
|
|
75
|
-
prev.map((t) => (t.id === item.id ? { ...t, completed: !t.completed } : t))
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
/>
|
|
79
|
-
<span style={{ marginLeft: 8 }}>{item.title}</span>
|
|
70
|
+
{item.title}
|
|
80
71
|
</div>
|
|
81
72
|
)}
|
|
82
73
|
showDropIndicator
|
|
@@ -86,22 +77,64 @@ export default function BasicExample() {
|
|
|
86
77
|
}
|
|
87
78
|
```
|
|
88
79
|
|
|
80
|
+
### Kanban Board
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { useState } from 'react';
|
|
84
|
+
import {
|
|
85
|
+
KanbanBoard,
|
|
86
|
+
applyDragResult,
|
|
87
|
+
AnnouncerProvider,
|
|
88
|
+
} from 'react-dragdrop-kit/kanban';
|
|
89
|
+
|
|
90
|
+
function App() {
|
|
91
|
+
const [state, setState] = useState({
|
|
92
|
+
columns: [
|
|
93
|
+
{ id: 'todo', title: 'To Do', cardIds: ['task-1', 'task-2'] },
|
|
94
|
+
{ id: 'done', title: 'Done', cardIds: [] },
|
|
95
|
+
],
|
|
96
|
+
cards: {
|
|
97
|
+
'task-1': { id: 'task-1', title: 'Design landing page' },
|
|
98
|
+
'task-2': { id: 'task-2', title: 'Implement auth' },
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
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>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
89
124
|
---
|
|
90
125
|
|
|
91
|
-
## 🎨 Styled
|
|
126
|
+
## 🎨 Styled Examples
|
|
92
127
|
|
|
93
|
-
|
|
94
|
-
// examples/tailwind-example.tsx
|
|
128
|
+
### With Tailwind CSS
|
|
95
129
|
|
|
96
|
-
|
|
130
|
+
```tsx
|
|
97
131
|
import { DragDropList } from "react-dragdrop-kit";
|
|
98
132
|
|
|
99
133
|
export default function TailwindExample() {
|
|
100
|
-
const [items, setItems] =
|
|
134
|
+
const [items, setItems] = useState([
|
|
101
135
|
{ id: "1", position: 0, name: "Dashboard", icon: "📊" },
|
|
102
136
|
{ id: "2", position: 1, name: "Projects", icon: "📁" },
|
|
103
137
|
{ id: "3", position: 2, name: "Team", icon: "👥" },
|
|
104
|
-
{ id: "4", position: 3, name: "Calendar", icon: "📅" },
|
|
105
138
|
]);
|
|
106
139
|
|
|
107
140
|
return (
|
|
@@ -110,18 +143,12 @@ export default function TailwindExample() {
|
|
|
110
143
|
onReorder={(next) => setItems(next.map((it, i) => ({ ...it, position: i })))}
|
|
111
144
|
containerClassName="bg-gray-50 rounded-xl p-6 space-y-2"
|
|
112
145
|
itemClassName="bg-white rounded-lg shadow-sm hover:shadow-lg transition-all duration-200 cursor-move"
|
|
113
|
-
dragPreviewClassName="bg-blue-100 rounded-lg shadow-2xl"
|
|
114
146
|
showDropIndicator
|
|
115
147
|
dropIndicatorClassName="bg-blue-500"
|
|
116
148
|
renderItem={(item) => (
|
|
117
149
|
<div className="flex items-center p-4 space-x-3">
|
|
118
150
|
<span className="text-2xl">{item.icon}</span>
|
|
119
151
|
<span className="font-medium text-gray-700">{item.name}</span>
|
|
120
|
-
<div className="ml-auto">
|
|
121
|
-
<svg className="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
|
122
|
-
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
|
|
123
|
-
</svg>
|
|
124
|
-
</div>
|
|
125
152
|
</div>
|
|
126
153
|
)}
|
|
127
154
|
/>
|
|
@@ -131,66 +158,180 @@ export default function TailwindExample() {
|
|
|
131
158
|
|
|
132
159
|
---
|
|
133
160
|
|
|
134
|
-
##
|
|
161
|
+
## 📚 API Reference
|
|
135
162
|
|
|
136
|
-
###
|
|
163
|
+
### DragDropList Component
|
|
137
164
|
|
|
138
165
|
| Prop | Type | Default | Description |
|
|
139
166
|
| ------------------------ | --------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------ |
|
|
140
|
-
| `items` | `Array<DraggableItem & T>` | — | Items to render. Must include `{ id: string; position: number }`.
|
|
167
|
+
| `items` | `Array<DraggableItem & T>` | — | Items to render. Must include `{ id: string; position: number }`. |
|
|
141
168
|
| `onReorder` | `(next: T[], updates: OrderUpdate[]) => void` | — | Called after drop. `next` is the new array; `updates` has id + newPosition. |
|
|
142
169
|
| `renderItem` | `(item: T) => React.ReactNode` | — | Custom renderer for each item. |
|
|
143
170
|
| `direction` | `"vertical" \| "horizontal"` | `"vertical"` | Drag axis + layout. |
|
|
144
171
|
| `gap` | `number` | `0` | Gap (px) between items. |
|
|
145
172
|
| `disabled` | `boolean` | `false` | Disable dragging. |
|
|
146
173
|
| `showDropIndicator` | `boolean` | `false` | Show a drop indicator while dragging. |
|
|
174
|
+
| `dropIndicatorPosition` | `"top" \| "bottom"` | `"bottom"` | Position of drop indicator relative to target item. |
|
|
147
175
|
| `dropIndicatorClassName` | `string` | — | CSS class applied to the drop indicator element. |
|
|
148
176
|
| `dragPreviewStyle` | `React.CSSProperties` | — | Inline styles for custom drag preview. |
|
|
149
177
|
| `containerClassName` | `string` | — | Class applied to the container. |
|
|
150
178
|
| `itemClassName` | `string` | — | Class applied to each item wrapper. |
|
|
151
|
-
| `containerStyle` | `React.CSSProperties` | — | Inline styles for the container. |
|
|
152
|
-
| `itemStyle` | `React.CSSProperties` | — | Inline styles for each item wrapper. |
|
|
153
|
-
| `onDragStart` | `(item: T, index: number) => void` | — | Optional callback when drag starts. |
|
|
154
|
-
| `onDragEnd` | `(item: T, index: number) => void` | — | Optional callback when drag ends. |
|
|
155
|
-
|
|
156
|
-
#### Types
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
export type DraggableItem = {
|
|
160
|
-
id: string;
|
|
161
|
-
position: number; // 0-based
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
export type OrderUpdate = {
|
|
165
|
-
id: string;
|
|
166
|
-
newPosition: number;
|
|
167
|
-
// Extend in your app if you need: { oldPosition, moved }
|
|
168
|
-
};
|
|
169
|
-
```
|
|
170
179
|
|
|
171
|
-
|
|
180
|
+
### Kanban Components
|
|
172
181
|
|
|
173
|
-
|
|
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
|
|
174
188
|
|
|
175
|
-
|
|
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
|
|
176
219
|
|
|
177
220
|
---
|
|
178
221
|
|
|
179
|
-
##
|
|
222
|
+
## 📂 Examples
|
|
180
223
|
|
|
181
|
-
This repo includes
|
|
224
|
+
This repo includes comprehensive examples:
|
|
182
225
|
|
|
226
|
+
### Drag & Drop Lists
|
|
183
227
|
* [Basic Example](./examples/basic-example.tsx)
|
|
184
228
|
* [Tailwind Example](./examples/tailwind-example.tsx)
|
|
185
229
|
* [Material UI Example](./examples/material-ui-example.tsx)
|
|
186
|
-
* [Horizontal Kanban](./examples/horizontal-kanban.tsx)
|
|
187
230
|
* [Advanced Features](./examples/advanced-features.tsx)
|
|
188
|
-
* [Styled Components
|
|
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)
|
|
189
238
|
|
|
190
239
|
👉 Explore the [`examples/`](./examples) folder for the complete code.
|
|
191
240
|
|
|
241
|
+
🎮 **Live Demo**: Check out our [interactive demo app](https://react-dragdrop-kit.netlify.app/) with full Kanban showcase!
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
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
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
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
|
|
284
|
+
|
|
285
|
+
### From react-beautiful-dnd
|
|
286
|
+
|
|
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
|
|
298
|
+
|
|
299
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
300
|
+
|
|
192
301
|
---
|
|
193
302
|
|
|
194
303
|
## 📝 License
|
|
195
304
|
|
|
196
305
|
MIT © [Yourstruggle11](https://github.com/Yourstruggle11)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 🙏 Credits
|
|
310
|
+
|
|
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
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 📖 Documentation
|
|
319
|
+
|
|
320
|
+
- [Kanban Board Guide](./docs/kanban.md)
|
|
321
|
+
- [CHANGELOG](./CHANGELOG.md)
|
|
322
|
+
- [Examples](./examples/)
|
|
323
|
+
- [Demo Application](https://react-dragdrop-kit.netlify.app/)
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 💬 Support
|
|
328
|
+
|
|
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)
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
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>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e,{createContext as n,useRef as t,useState as r,useCallback as a,useEffect as o,useMemo as i,useContext as d}from"react";import{draggable as c,dropTargetForElements as l,monitorForElements as s}from"@atlaskit/pragmatic-drag-and-drop/element/adapter";import{attachClosestEdge as u,extractClosestEdge as g}from"@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";import{combine as m}from"@atlaskit/pragmatic-drag-and-drop/combine";const p=n(null),f="kanban-column";function b({column:n,cardIds:i,children:d,isDragDisabled:s=!1,index:g}){const m=t(null),p=t(null),[b,x]=r(!1),[D,y]=r(!1),v=a(e=>s?()=>{}:c({element:e,getInitialData:()=>({type:f,id:n.id,index:g}),onDragStart:()=>x(!0),onDrop:()=>x(!1)}),[n.id,g,s]),I=a(e=>s?()=>{}:l({element:e,getData:({input:e,element:t})=>{const r={type:f,id:n.id,index:g};return u(r,{input:e,element:t,allowedEdges:["left","right"]})},canDrop:({source:e})=>e.data.type===f&&e.data.id!==n.id}),[n.id,g,s]),h=a(e=>l({element:e,getData:()=>({type:"kanban-column-drop-zone",columnId:n.id}),canDrop:({source:e})=>"kanban-card"===e.data.type,onDragEnter:()=>y(!0),onDragLeave:()=>y(!1),onDrop:()=>y(!1)}),[n.id]);o(()=>{const e=m.current,n=p.current;if(!e||!n)return;const t=[v(e),I(e),h(n)];return()=>{t.forEach(e=>e())}},[v,I,h]);const O={draggableProps:{"data-draggable-id":n.id,"data-draggable-type":"COLUMN",style:b?{opacity:.5}:void 0},dragHandleProps:{tabIndex:0,role:"button","aria-roledescription":"draggable column","aria-label":`${n.title}, press space to pick up`},innerRef:m},E={isDragging:b,isDraggingOver:D},w=Object.assign(Object.assign({},O),{dropZoneRef:p});return e.createElement(e.Fragment,null,d(w,E))}const x="kanban-card";function D({card:n,children:i,isDragDisabled:d=!1,index:s,columnId:g}){const p=t(null),[f,b]=r(!1),[D,y]=r(!1),v=a(e=>d?()=>{}:c({element:e,getInitialData:()=>({type:x,id:n.id,index:s,columnId:g}),onDragStart:()=>b(!0),onDrop:()=>b(!1)}),[n.id,s,g,d]),I=a(e=>d?()=>{}:l({element:e,getData:({input:e,element:t})=>{const r={type:x,id:n.id,index:s,columnId:g};return u(r,{input:e,element:t,allowedEdges:["top","bottom"]})},canDrop:({source:e})=>e.data.type===x&&e.data.id!==n.id,onDragEnter:()=>y(!0),onDragLeave:()=>y(!1),onDrop:()=>y(!1)}),[n.id,s,g,d]);o(()=>{const e=p.current;if(e&&!d)return m(v(e),I(e))},[v,I,d]);const h={draggableProps:{"data-draggable-id":n.id,"data-draggable-type":"CARD",style:f?{opacity:.5}:void 0},dragHandleProps:{tabIndex:0,role:"button","aria-roledescription":"draggable card","aria-label":`${n.title}, press space to pick up`},innerRef:p},O={isDragging:f,isDraggingOver:D};return e.createElement(e.Fragment,null,i(h,O))}const y="kanban-card",v="kanban-column";function I({onDragStart:e,onDragEnd:n,state:t,disabled:a}){const[i,d]=r({draggingId:null,draggingType:null,source:null,destination:null});return o(()=>{if(a)return;return s({onDragStart({source:n}){const t=n.data.type,r=n.data.id,a=n.data.columnId,o=n.data.index,i=t===y?"CARD":"COLUMN",c={columnId:"CARD"===i?a:void 0,index:o};d({draggingId:r,draggingType:i,source:c,destination:c}),null==e||e({id:r,type:i})},onDrop({source:e,location:r}){const a=e.data.type,o=e.data.id,i=e.data.index,c=e.data.columnId,l=r.current.dropTargets;let s;if(a===y){const e=l.find(e=>"kanban-column-drop-zone"===e.data.type);if(e){const n=e.data.columnId,t=l.find(e=>e.data.type===y&&e.data.id!==o);if(t){const e=t.data.index;s={columnId:n,index:"bottom"===g(t)?e+1:e}}else s={columnId:n,index:0}}}else if(a===v){const e=l.find(e=>e.data.type===v&&e.data.id!==o);if(e){const n=e.data.index,t=g(e);s={index:"bottom"===t||"right"===t?n+1:n}}}const u={type:a===y?"CARD":"COLUMN",source:{columnId:c,index:i},destination:s,draggableId:o};d({draggingId:null,draggingType:null,source:null,destination:null}),n(u,t)}})},[a,e,n,t]),i}function h({state:n,onDragEnd:t,onDragStart:r,renderColumn:a,renderCard:o,getCardKey:d,getColumnKey:c,isDragDisabled:l,className:s,style:u}){const g=I({state:n,onDragEnd:t,onDragStart:r,disabled:!1}),m=i(()=>({state:n,dragState:g,isDragDisabled:l}),[n,g,l]),f=d||(e=>e.id),x=c||(e=>e.id);return e.createElement(p.Provider,{value:m},e.createElement("div",{className:s,style:Object.assign({display:"flex",gap:"16px"},u)},n.columns.map((t,r)=>e.createElement(b,{key:x(t),column:t,cardIds:t.cardIds,index:r,isDragDisabled:null==l?void 0:l(t.id,"COLUMN")},(r,i)=>e.createElement("div",Object.assign({ref:r.innerRef},r.draggableProps,{style:Object.assign({minWidth:"250px",display:"flex",flexDirection:"column"},r.draggableProps.style)}),e.createElement("div",Object.assign({},r.dragHandleProps),a(t,r,i)),e.createElement("div",{ref:r.dropZoneRef,style:{flex:1,minHeight:"100px",display:"flex",flexDirection:"column",gap:"8px"}},t.cardIds.map((r,a)=>{const i=n.cards[r];return i?e.createElement(D,{key:f(i),card:i,index:a,columnId:t.id,isDragDisabled:null==l?void 0:l(i.id,"CARD")},(n,t)=>e.createElement("div",Object.assign({ref:n.innerRef},n.draggableProps,n.dragHandleProps),o(i,n,t))):null})))))))}const O={threshold:50,maxSpeed:20,enabled:!0};function E(e,n,r={}){const{threshold:i,maxSpeed:d,enabled:c}=Object.assign(Object.assign({},O),r),l=t(),s=t(null),u=a(e=>{s.current={x:e.clientX,y:e.clientY}},[]),g=a(()=>{if(!(c&&n&&e.current&&s.current))return;const t=function(e){if(!e)return null;let n=e.parentElement;for(;n;){const{overflow:e,overflowY:t}=window.getComputedStyle(n);if(/(auto|scroll)/.test(e+t))return n;n=n.parentElement}return document.documentElement}(e.current);if(!t)return;const r=t.getBoundingClientRect(),a=s.current,o=function(e){const{overflow:n,overflowY:t,overflowX:r}=window.getComputedStyle(e),a=/(auto|scroll)/.test(n+t),o=/(auto|scroll)/.test(n+r);return{vertical:a&&e.scrollHeight>e.clientHeight,horizontal:o&&e.scrollWidth>e.clientWidth}}(t),u=function(e,n,t){const r=function(e,n){return{top:e.y-n.top,right:n.right-e.x,bottom:n.bottom-e.y,left:e.x-n.left}}(e,n);for(const[e,n]of Object.entries(r))if(n<t)return{edge:e,distance:n};return{edge:null,distance:1/0}}(a,r,i);if(u.edge){const e=function(e,n,t){return e>=n?0:(n-e)/n*t}(u.distance,i,d),n={x:0,y:0};o.vertical&&("top"===u.edge?n.y=-e:"bottom"===u.edge&&(n.y=e)),o.horizontal&&("left"===u.edge?n.x=-e:"right"===u.edge&&(n.x=e)),0===n.x&&0===n.y||function(e,n){n.x&&(e.scrollLeft+=n.x),n.y&&(e.scrollTop+=n.y)}(t,n)}l.current=requestAnimationFrame(g)},[c,n,e,i,d]);o(()=>c&&n?(window.addEventListener("mousemove",u),l.current=requestAnimationFrame(g),()=>{window.removeEventListener("mousemove",u),l.current&&cancelAnimationFrame(l.current)}):(l.current&&(cancelAnimationFrame(l.current),l.current=void 0),void(s.current=null)),[c,n,u,g])}const w=n(null);function j(){const e=d(w);if(!e)throw new Error("useAnnouncer must be used within AnnouncerProvider");return e}function C({children:n,politeness:r="polite"}){const a=t(null),i=t(null),d=t();o(()=>()=>{d.current&&clearTimeout(d.current)},[]);const c={announce:(e,n=r)=>{const t="assertive"===n?i:a;t.current&&(d.current&&clearTimeout(d.current),t.current.textContent="",d.current=setTimeout(()=>{t.current&&(t.current.textContent=e)},100))}};return e.createElement(w.Provider,{value:c},n,e.createElement("div",{ref:a,role:"status","aria-live":"polite","aria-atomic":"true",style:{position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",border:"0"}}),e.createElement("div",{ref:i,role:"alert","aria-live":"assertive","aria-atomic":"true",style:{position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",border:"0"}}))}const $={onDragStart:(e,n,t)=>`Picked up ${e}. Position ${n+1} of ${t}.`,onDragMove:(e,n,t)=>`${e} moved to position ${n+1} of ${t}.`,onDragEnd:(e,n,t,r)=>n===t?`${e} dropped. Position ${r+1} in ${t}.`:`${e} moved from ${n} to ${t}. Position ${r+1}.`,onDragCancel:(e,n)=>`Drag cancelled. ${e} returned to ${n}.`,onColumnMove:(e,n,t)=>`${e} moved to position ${n+1} of ${t}.`};function P(e,n,t){const r=Array.from(e),[a]=r.splice(n,1);return r.splice(t,0,a),r}function k(e,n){if(!n.destination)return e;const{source:t,destination:r,type:a}=n;return"COLUMN"===a?t.index===r.index?e:function(e,n,t){return Object.assign(Object.assign({},e),{columns:P(e.columns,n,t)})}(e,t.index,r.index):"CARD"===a?t.columnId===r.columnId?t.index===r.index?e:function(e,n,t,r){const a=e.columns.find(e=>e.id===n);if(!a)return e;const o=P(a.cardIds,t,r);return Object.assign(Object.assign({},e),{columns:e.columns.map(e=>e.id===n?Object.assign(Object.assign({},e),{cardIds:o}):e)})}(e,t.columnId,t.index,r.index):function(e,n,t,r){const a=e.columns.find(e=>e.id===n.columnId),o=e.columns.find(e=>e.id===t.columnId);if(!a||!o)return e;const i=[...a.cardIds];i.splice(n.index,1);const d=[...o.cardIds];return d.splice(t.index,0,r),Object.assign(Object.assign({},e),{columns:e.columns.map(e=>e.id===n.columnId?Object.assign(Object.assign({},e),{cardIds:i}):e.id===t.columnId?Object.assign(Object.assign({},e),{cardIds:d}):e)})}(e,t,r,n.draggableId):e}export{C as AnnouncerProvider,h as KanbanBoard,D as KanbanCardView,b as KanbanColumnView,$ as announcements,k as applyDragResult,P as reorderArray,j as useAnnouncer,E as useAutoscroll,I as useKanbanDnd};
|
|
2
|
+
//# sourceMappingURL=kanban.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.esm.js","sources":["../src/kanban/context.ts","../src/kanban/components/KanbanColumnView.tsx","../src/kanban/components/KanbanCardView.tsx","../src/kanban/hooks/useKanbanDnd.ts","../src/kanban/components/KanbanBoard.tsx","../src/kanban/hooks/useAutoscroll.ts","../src/kanban/utils/dom.ts","../src/kanban/utils/dndMath.ts","../src/kanban/a11y/Announcer.tsx","../src/kanban/utils/reorder.ts"],"sourcesContent":["/**\n * Kanban Board Context\n *\n * Internal context for sharing board state between components.\n */\n\nimport { createContext, useContext } from 'react';\nimport type { KanbanBoardState, KanbanDragState } from './types';\n\nexport interface KanbanContextValue {\n state: KanbanBoardState;\n dragState: KanbanDragState;\n isDragDisabled?: (id: string, type: 'CARD' | 'COLUMN') => boolean;\n}\n\nexport const KanbanContext = createContext<KanbanContextValue | null>(null);\n\nexport function useKanbanContext(): KanbanContextValue {\n const context = useContext(KanbanContext);\n if (!context) {\n throw new Error('Kanban components must be used within KanbanBoard');\n }\n return context;\n}\n","/**\n * Kanban Column View (Headless)\n *\n * Headless component for rendering draggable columns with drop zones.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanColumnViewProps, DragProvided, DragSnapshot } from '../types';\n\nconst KANBAN_COLUMN = 'kanban-column';\nconst KANBAN_COLUMN_DROP_ZONE = 'kanban-column-drop-zone';\n\nexport function KanbanColumnView({\n column,\n cardIds,\n children,\n isDragDisabled = false,\n index,\n}: KanbanColumnViewProps) {\n const columnRef = useRef<HTMLDivElement>(null);\n const dropZoneRef = useRef<HTMLDivElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [isDraggingOver, setIsDraggingOver] = useState(false);\n\n // Make column header draggable\n const createDraggableColumn = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return draggable({\n element,\n getInitialData: () => ({\n type: KANBAN_COLUMN,\n id: column.id,\n index,\n }),\n onDragStart: () => setIsDragging(true),\n onDrop: () => setIsDragging(false),\n });\n },\n [column.id, index, isDragDisabled]\n );\n\n // Make column header a drop target for column reordering\n const createColumnDropTarget = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return dropTargetForElements({\n element,\n getData: ({ input, element }) => {\n const data = { type: KANBAN_COLUMN, id: column.id, index };\n return attachClosestEdge(data, { input, element, allowedEdges: ['left', 'right'] });\n },\n canDrop: ({ source }) => source.data.type === KANBAN_COLUMN && source.data.id !== column.id,\n });\n },\n [column.id, index, isDragDisabled]\n );\n\n // Make card list area a drop zone for cards\n const createCardDropZone = useCallback(\n (element: HTMLElement) => {\n return dropTargetForElements({\n element,\n getData: () => ({\n type: KANBAN_COLUMN_DROP_ZONE,\n columnId: column.id,\n }),\n canDrop: ({ source }) => source.data.type === 'kanban-card',\n onDragEnter: () => setIsDraggingOver(true),\n onDragLeave: () => setIsDraggingOver(false),\n onDrop: () => setIsDraggingOver(false),\n });\n },\n [column.id]\n );\n\n useEffect(() => {\n const columnElement = columnRef.current;\n const dropZoneElement = dropZoneRef.current;\n if (!columnElement || !dropZoneElement) return;\n\n const cleanups = [\n createDraggableColumn(columnElement),\n createColumnDropTarget(columnElement),\n createCardDropZone(dropZoneElement),\n ];\n\n return () => {\n cleanups.forEach((cleanup) => cleanup());\n };\n }, [createDraggableColumn, createColumnDropTarget, createCardDropZone]);\n\n const provided: DragProvided = {\n draggableProps: {\n 'data-draggable-id': column.id,\n 'data-draggable-type': 'COLUMN',\n style: isDragging ? { opacity: 0.5 } : undefined,\n },\n dragHandleProps: {\n tabIndex: 0,\n role: 'button',\n 'aria-roledescription': 'draggable column',\n 'aria-label': `${column.title}, press space to pick up`,\n },\n innerRef: columnRef as React.RefObject<HTMLElement>,\n };\n\n const snapshot: DragSnapshot = {\n isDragging,\n isDraggingOver,\n };\n\n // Pass both refs through provided\n const providedWithDropZone = {\n ...provided,\n dropZoneRef,\n };\n\n return <>{children(providedWithDropZone as any, snapshot)}</>;\n}\n","/**\n * Kanban Card View (Headless)\n *\n * Headless component for rendering draggable cards.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanCardViewProps, DragProvided, DragSnapshot } from '../types';\n\nconst KANBAN_CARD = 'kanban-card';\n\nexport function KanbanCardView({\n card,\n children,\n isDragDisabled = false,\n index,\n columnId,\n}: KanbanCardViewProps) {\n const cardRef = useRef<HTMLDivElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [isDraggingOver, setIsDraggingOver] = useState(false);\n\n const createDraggable = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return draggable({\n element,\n getInitialData: () => ({\n type: KANBAN_CARD,\n id: card.id,\n index,\n columnId,\n }),\n onDragStart: () => setIsDragging(true),\n onDrop: () => setIsDragging(false),\n });\n },\n [card.id, index, columnId, isDragDisabled]\n );\n\n const createDropTarget = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return dropTargetForElements({\n element,\n getData: ({ input, element }) => {\n const data = { type: KANBAN_CARD, id: card.id, index, columnId };\n return attachClosestEdge(data, { input, element, allowedEdges: ['top', 'bottom'] });\n },\n canDrop: ({ source }) => source.data.type === KANBAN_CARD && source.data.id !== card.id,\n onDragEnter: () => setIsDraggingOver(true),\n onDragLeave: () => setIsDraggingOver(false),\n onDrop: () => setIsDraggingOver(false),\n });\n },\n [card.id, index, columnId, isDragDisabled]\n );\n\n useEffect(() => {\n const element = cardRef.current;\n if (!element || isDragDisabled) return;\n\n return combine(createDraggable(element), createDropTarget(element));\n }, [createDraggable, createDropTarget, isDragDisabled]);\n\n const provided: DragProvided = {\n draggableProps: {\n 'data-draggable-id': card.id,\n 'data-draggable-type': 'CARD',\n style: isDragging ? { opacity: 0.5 } : undefined,\n },\n dragHandleProps: {\n tabIndex: 0,\n role: 'button',\n 'aria-roledescription': 'draggable card',\n 'aria-label': `${card.title}, press space to pick up`,\n },\n innerRef: cardRef as React.RefObject<HTMLElement>,\n };\n\n const snapshot: DragSnapshot = {\n isDragging,\n isDraggingOver,\n };\n\n return <>{children(provided, snapshot)}</>;\n}\n","/**\n * Core Kanban DnD Hook\n *\n * Main hook that manages drag-and-drop state for the Kanban board.\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanDragState, DropResult, DragLocation, KanbanBoardState } from '../types';\n\nconst KANBAN_CARD = 'kanban-card';\nconst KANBAN_COLUMN = 'kanban-column';\n\nexport interface UseKanbanDndOptions {\n onDragStart?: (draggable: { id: string; type: 'CARD' | 'COLUMN' }) => void;\n onDragEnd: (result: DropResult, stateBefore: KanbanBoardState) => void;\n state: KanbanBoardState;\n disabled?: boolean;\n}\n\nexport function useKanbanDnd({ onDragStart, onDragEnd, state, disabled }: UseKanbanDndOptions) {\n const [dragState, setDragState] = useState<KanbanDragState>({\n draggingId: null,\n draggingType: null,\n source: null,\n destination: null,\n });\n\n useEffect(() => {\n if (disabled) return;\n\n const cleanup = monitorForElements({\n onDragStart({ source }) {\n const typeRaw = source.data.type as string;\n const id = source.data.id as string;\n const columnId = source.data.columnId as string | undefined;\n const index = source.data.index as number;\n\n const type: 'CARD' | 'COLUMN' = typeRaw === KANBAN_CARD ? 'CARD' : 'COLUMN';\n\n const sourceLocation: DragLocation = {\n columnId: type === 'CARD' ? columnId : undefined,\n index,\n };\n\n setDragState({\n draggingId: id,\n draggingType: type,\n source: sourceLocation,\n destination: sourceLocation,\n });\n\n onDragStart?.({ id, type });\n },\n\n onDrop({ source, location }) {\n const sourceType = source.data.type as string;\n const sourceId = source.data.id as string;\n const sourceIndex = source.data.index as number;\n const sourceColumnId = source.data.columnId as string | undefined;\n\n // Find the drop target\n const dropTargets = location.current.dropTargets;\n\n let destination: DragLocation | undefined;\n\n if (sourceType === KANBAN_CARD) {\n // Card drop - find column drop target\n const columnTarget = dropTargets.find((target) =>\n target.data.type === 'kanban-column-drop-zone'\n );\n\n if (columnTarget) {\n const destColumnId = columnTarget.data.columnId as string;\n const cardTarget = dropTargets.find((target) =>\n target.data.type === KANBAN_CARD && target.data.id !== sourceId\n );\n\n if (cardTarget) {\n // Dropping on another card\n const destIndex = cardTarget.data.index as number;\n const edge = extractClosestEdge(cardTarget);\n\n destination = {\n columnId: destColumnId,\n index: edge === 'bottom' ? destIndex + 1 : destIndex,\n };\n } else {\n // Dropping in empty column\n destination = {\n columnId: destColumnId,\n index: 0,\n };\n }\n }\n } else if (sourceType === KANBAN_COLUMN) {\n // Column drop\n const columnTarget = dropTargets.find((target) =>\n target.data.type === KANBAN_COLUMN && target.data.id !== sourceId\n );\n\n if (columnTarget) {\n const destIndex = columnTarget.data.index as number;\n const edge = extractClosestEdge(columnTarget);\n\n destination = {\n index: edge === 'bottom' || edge === 'right' ? destIndex + 1 : destIndex,\n };\n }\n }\n\n const result: DropResult = {\n type: sourceType === KANBAN_CARD ? 'CARD' : 'COLUMN',\n source: {\n columnId: sourceColumnId,\n index: sourceIndex,\n },\n destination,\n draggableId: sourceId,\n };\n\n setDragState({\n draggingId: null,\n draggingType: null,\n source: null,\n destination: null,\n });\n\n onDragEnd(result, state);\n },\n });\n\n return cleanup;\n }, [disabled, onDragStart, onDragEnd, state]);\n\n return dragState;\n}\n","/**\n * Kanban Board Component\n *\n * High-level component that composes the Kanban board with all features.\n */\n\nimport React, { useMemo } from 'react';\nimport type { KanbanBoardProps } from '../types';\nimport { KanbanContext } from '../context';\nimport { KanbanColumnView } from './KanbanColumnView';\nimport { KanbanCardView } from './KanbanCardView';\nimport { useKanbanDnd } from '../hooks/useKanbanDnd';\n\nexport function KanbanBoard({\n state,\n onDragEnd,\n onDragStart,\n renderColumn,\n renderCard,\n getCardKey,\n getColumnKey,\n isDragDisabled,\n className,\n style,\n}: KanbanBoardProps) {\n const dragState = useKanbanDnd({\n state,\n onDragEnd,\n onDragStart,\n disabled: false,\n });\n\n const contextValue = useMemo(\n () => ({\n state,\n dragState,\n isDragDisabled,\n }),\n [state, dragState, isDragDisabled]\n );\n\n const defaultGetCardKey = (card: any) => card.id;\n const defaultGetColumnKey = (column: any) => column.id;\n\n const cardKeyExtractor = getCardKey || defaultGetCardKey;\n const columnKeyExtractor = getColumnKey || defaultGetColumnKey;\n\n return (\n <KanbanContext.Provider value={contextValue}>\n <div\n className={className}\n style={{\n display: 'flex',\n gap: '16px',\n ...style,\n }}\n >\n {state.columns.map((column, columnIndex) => (\n <KanbanColumnView\n key={columnKeyExtractor(column)}\n column={column}\n cardIds={column.cardIds}\n index={columnIndex}\n isDragDisabled={isDragDisabled?.(column.id, 'COLUMN')}\n >\n {(columnProvided, columnSnapshot) => (\n <div\n ref={columnProvided.innerRef as any}\n {...columnProvided.draggableProps}\n style={{\n minWidth: '250px',\n display: 'flex',\n flexDirection: 'column',\n ...columnProvided.draggableProps.style,\n }}\n >\n {/* Column header (draggable) */}\n <div {...columnProvided.dragHandleProps}>\n {renderColumn(column, columnProvided, columnSnapshot)}\n </div>\n\n {/* Card drop zone */}\n <div\n ref={(columnProvided as any).dropZoneRef}\n style={{\n flex: 1,\n minHeight: '100px',\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n }}\n >\n {column.cardIds.map((cardId, cardIndex) => {\n const card = state.cards[cardId];\n if (!card) return null;\n\n return (\n <KanbanCardView\n key={cardKeyExtractor(card)}\n card={card}\n index={cardIndex}\n columnId={column.id}\n isDragDisabled={isDragDisabled?.(card.id, 'CARD')}\n >\n {(cardProvided, cardSnapshot) => (\n <div\n ref={cardProvided.innerRef as any}\n {...cardProvided.draggableProps}\n {...cardProvided.dragHandleProps}\n >\n {renderCard(card, cardProvided, cardSnapshot)}\n </div>\n )}\n </KanbanCardView>\n );\n })}\n </div>\n </div>\n )}\n </KanbanColumnView>\n ))}\n </div>\n </KanbanContext.Provider>\n );\n}\n","/**\n * Autoscroll Hook\n *\n * Automatically scrolls containers when dragging near edges.\n */\n\nimport { useEffect, useRef, useCallback } from 'react';\nimport type { AutoscrollConfig } from '../types';\nimport { isNearEdge, calculateScrollSpeed } from '../utils/dndMath';\nimport { getScrollParent, scrollBy, isScrollable } from '../utils/dom';\n\nconst DEFAULT_CONFIG: AutoscrollConfig = {\n threshold: 50, // px from edge\n maxSpeed: 20, // px per frame\n enabled: true,\n};\n\n/**\n * Hook for autoscrolling containers during drag\n */\nexport function useAutoscroll(\n containerRef: React.RefObject<HTMLElement>,\n isDragging: boolean,\n config: Partial<AutoscrollConfig> = {}\n) {\n const { threshold, maxSpeed, enabled } = { ...DEFAULT_CONFIG, ...config };\n const rafRef = useRef<number>();\n const lastMousePosRef = useRef<{ x: number; y: number } | null>(null);\n\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n lastMousePosRef.current = { x: event.clientX, y: event.clientY };\n },\n []\n );\n\n const performScroll = useCallback(() => {\n if (!enabled || !isDragging || !containerRef.current || !lastMousePosRef.current) {\n return;\n }\n\n const container = containerRef.current;\n const scrollParent = getScrollParent(container);\n if (!scrollParent) return;\n\n const rect = scrollParent.getBoundingClientRect();\n const mousePos = lastMousePosRef.current;\n const scrollability = isScrollable(scrollParent);\n\n const nearEdge = isNearEdge(mousePos, rect, threshold);\n\n if (nearEdge.edge) {\n const speed = calculateScrollSpeed(nearEdge.distance, threshold, maxSpeed);\n\n const delta = { x: 0, y: 0 };\n\n if (scrollability.vertical) {\n if (nearEdge.edge === 'top') {\n delta.y = -speed;\n } else if (nearEdge.edge === 'bottom') {\n delta.y = speed;\n }\n }\n\n if (scrollability.horizontal) {\n if (nearEdge.edge === 'left') {\n delta.x = -speed;\n } else if (nearEdge.edge === 'right') {\n delta.x = speed;\n }\n }\n\n if (delta.x !== 0 || delta.y !== 0) {\n scrollBy(scrollParent, delta);\n }\n }\n\n rafRef.current = requestAnimationFrame(performScroll);\n }, [enabled, isDragging, containerRef, threshold, maxSpeed]);\n\n useEffect(() => {\n if (!enabled || !isDragging) {\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = undefined;\n }\n lastMousePosRef.current = null;\n return;\n }\n\n window.addEventListener('mousemove', handleMouseMove);\n rafRef.current = requestAnimationFrame(performScroll);\n\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n }\n };\n }, [enabled, isDragging, handleMouseMove, performScroll]);\n}\n","/**\n * DOM Utilities\n *\n * Helper functions for DOM measurements and manipulation.\n */\n\n/**\n * Safely get bounding client rect for an element\n */\nexport function getBoundingRect(element: HTMLElement | null): DOMRect | null {\n if (!element) return null;\n return element.getBoundingClientRect();\n}\n\n/**\n * Get scrollable parent of an element\n */\nexport function getScrollParent(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null;\n\n let parent = element.parentElement;\n\n while (parent) {\n const { overflow, overflowY } = window.getComputedStyle(parent);\n if (/(auto|scroll)/.test(overflow + overflowY)) {\n return parent;\n }\n parent = parent.parentElement;\n }\n\n return document.documentElement as HTMLElement;\n}\n\n/**\n * Scroll an element by a delta\n */\nexport function scrollBy(\n element: HTMLElement,\n delta: { x?: number; y?: number }\n): void {\n if (delta.x) {\n element.scrollLeft += delta.x;\n }\n if (delta.y) {\n element.scrollTop += delta.y;\n }\n}\n\n/**\n * Check if an element is scrollable\n */\nexport function isScrollable(element: HTMLElement): {\n vertical: boolean;\n horizontal: boolean;\n} {\n const { overflow, overflowY, overflowX } = window.getComputedStyle(element);\n\n const hasVerticalScroll = /(auto|scroll)/.test(overflow + overflowY);\n const hasHorizontalScroll = /(auto|scroll)/.test(overflow + overflowX);\n\n return {\n vertical: hasVerticalScroll && element.scrollHeight > element.clientHeight,\n horizontal: hasHorizontalScroll && element.scrollWidth > element.clientWidth,\n };\n}\n\n/**\n * Get element's scroll position\n */\nexport function getScrollPosition(element: HTMLElement): { x: number; y: number } {\n if (element === document.documentElement) {\n return {\n x: window.pageXOffset || document.documentElement.scrollLeft,\n y: window.pageYOffset || document.documentElement.scrollTop,\n };\n }\n\n return {\n x: element.scrollLeft,\n y: element.scrollTop,\n };\n}\n\n/**\n * Get maximum scroll for an element\n */\nexport function getMaxScroll(element: HTMLElement): { x: number; y: number } {\n return {\n x: element.scrollWidth - element.clientWidth,\n y: element.scrollHeight - element.clientHeight,\n };\n}\n\n/**\n * Clamp scroll position to valid range\n */\nexport function clampScroll(\n scroll: { x: number; y: number },\n maxScroll: { x: number; y: number }\n): { x: number; y: number } {\n return {\n x: Math.max(0, Math.min(scroll.x, maxScroll.x)),\n y: Math.max(0, Math.min(scroll.y, maxScroll.y)),\n };\n}\n\n/**\n * Get all data attributes from an element as an object\n */\nexport function getDataAttributes(element: HTMLElement): Record<string, string> {\n const data: Record<string, string> = {};\n for (const key in element.dataset) {\n data[key] = element.dataset[key] || '';\n }\n return data;\n}\n\n/**\n * Find closest ancestor with a data attribute\n */\nexport function findClosestWithData(\n element: HTMLElement | null,\n dataKey: string\n): HTMLElement | null {\n let current = element;\n while (current) {\n if (current.dataset[dataKey]) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n}\n","/**\n * DnD Math Utilities\n *\n * Helper functions for collision detection and positioning calculations.\n */\n\n/**\n * Calculate the distance from a point to a rectangle's edges\n */\nexport function distanceToEdge(\n point: { x: number; y: number },\n rect: DOMRect\n): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n return {\n top: point.y - rect.top,\n right: rect.right - point.x,\n bottom: rect.bottom - point.y,\n left: point.x - rect.left,\n };\n}\n\n/**\n * Get the closest edge of a rectangle to a point\n */\nexport function getClosestEdge(\n point: { x: number; y: number },\n rect: DOMRect\n): 'top' | 'right' | 'bottom' | 'left' {\n const distances = distanceToEdge(point, rect);\n const entries = Object.entries(distances) as Array<[keyof typeof distances, number]>;\n const sorted = entries.sort((a, b) => a[1] - b[1]);\n return sorted[0][0];\n}\n\n/**\n * Check if a point is within a threshold distance from an edge\n */\nexport function isNearEdge(\n point: { x: number; y: number },\n rect: DOMRect,\n threshold: number\n): { edge: 'top' | 'right' | 'bottom' | 'left' | null; distance: number } {\n const distances = distanceToEdge(point, rect);\n\n for (const [edge, distance] of Object.entries(distances)) {\n if (distance < threshold) {\n return {\n edge: edge as 'top' | 'right' | 'bottom' | 'left',\n distance,\n };\n }\n }\n\n return { edge: null, distance: Infinity };\n}\n\n/**\n * Calculate scroll speed based on distance from edge\n * Closer to edge = faster scroll\n */\nexport function calculateScrollSpeed(\n distance: number,\n threshold: number,\n maxSpeed: number\n): number {\n if (distance >= threshold) return 0;\n const ratio = (threshold - distance) / threshold;\n return ratio * maxSpeed;\n}\n\n/**\n * Check if a point is inside a rectangle\n */\nexport function isPointInRect(point: { x: number; y: number }, rect: DOMRect): boolean {\n return (\n point.x >= rect.left &&\n point.x <= rect.right &&\n point.y >= rect.top &&\n point.y <= rect.bottom\n );\n}\n\n/**\n * Calculate the center point of a rectangle\n */\nexport function getRectCenter(rect: DOMRect): { x: number; y: number } {\n return {\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n };\n}\n\n/**\n * Calculate vertical drop position based on mouse Y relative to element center\n */\nexport function getVerticalDropPosition(\n mouseY: number,\n elementRect: DOMRect\n): 'top' | 'bottom' {\n const center = getRectCenter(elementRect);\n return mouseY < center.y ? 'top' : 'bottom';\n}\n\n/**\n * Calculate horizontal drop position based on mouse X relative to element center\n */\nexport function getHorizontalDropPosition(\n mouseX: number,\n elementRect: DOMRect\n): 'left' | 'right' {\n const center = getRectCenter(elementRect);\n return mouseX < center.x ? 'left' : 'right';\n}\n","/**\n * Announcer Component\n *\n * Provides screen reader announcements for drag-and-drop operations.\n */\n\nimport React, { createContext, useContext, useEffect, useRef } from 'react';\n\ninterface AnnouncerContextValue {\n announce: (message: string, politeness?: 'polite' | 'assertive') => void;\n}\n\nconst AnnouncerContext = createContext<AnnouncerContextValue | null>(null);\n\n/**\n * Hook to access the announcer context\n */\nexport function useAnnouncer(): AnnouncerContextValue {\n const context = useContext(AnnouncerContext);\n if (!context) {\n throw new Error('useAnnouncer must be used within AnnouncerProvider');\n }\n return context;\n}\n\n/**\n * Props for AnnouncerProvider\n */\nexport interface AnnouncerProviderProps {\n /** Child components */\n children: React.ReactNode;\n /** Default politeness level */\n politeness?: 'polite' | 'assertive';\n}\n\n/**\n * Provider component that creates a live region for screen reader announcements\n */\nexport function AnnouncerProvider({\n children,\n politeness = 'polite',\n}: AnnouncerProviderProps) {\n const politeRef = useRef<HTMLDivElement>(null);\n const assertiveRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<NodeJS.Timeout>();\n\n const announce = (message: string, announcePoliteness: 'polite' | 'assertive' = politeness) => {\n const regionRef = announcePoliteness === 'assertive' ? assertiveRef : politeRef;\n\n if (!regionRef.current) return;\n\n // Clear existing timeout\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n // Clear the region first\n regionRef.current.textContent = '';\n\n // Set the new message after a brief delay to ensure screen readers pick it up\n timeoutRef.current = setTimeout(() => {\n if (regionRef.current) {\n regionRef.current.textContent = message;\n }\n }, 100);\n };\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n const value: AnnouncerContextValue = {\n announce,\n };\n\n return (\n <AnnouncerContext.Provider value={value}>\n {children}\n {/* Polite live region */}\n <div\n ref={politeRef}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n }}\n />\n {/* Assertive live region */}\n <div\n ref={assertiveRef}\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n }}\n />\n </AnnouncerContext.Provider>\n );\n}\n\n/**\n * Generate announcement messages for different drag events\n */\nexport const announcements = {\n onDragStart: (itemName: string, position: number, totalItems: number): string =>\n `Picked up ${itemName}. Position ${position + 1} of ${totalItems}.`,\n\n onDragMove: (itemName: string, newPosition: number, totalItems: number): string =>\n `${itemName} moved to position ${newPosition + 1} of ${totalItems}.`,\n\n onDragEnd: (itemName: string, from: string, to: string, position: number): string =>\n from === to\n ? `${itemName} dropped. Position ${position + 1} in ${to}.`\n : `${itemName} moved from ${from} to ${to}. Position ${position + 1}.`,\n\n onDragCancel: (itemName: string, column: string): string =>\n `Drag cancelled. ${itemName} returned to ${column}.`,\n\n onColumnMove: (columnName: string, position: number, totalColumns: number): string =>\n `${columnName} moved to position ${position + 1} of ${totalColumns}.`,\n};\n","/**\n * Kanban Reorder Utilities\n *\n * Functions for reordering cards and columns in a Kanban board.\n */\n\nimport type { KanbanBoardState, KanbanColumn, DragLocation } from '../types';\n\n/**\n * Reorder an array by moving an item from one index to another\n */\nexport function reorderArray<T>(list: T[], startIndex: number, endIndex: number): T[] {\n const result = Array.from(list);\n const [removed] = result.splice(startIndex, 1);\n result.splice(endIndex, 0, removed);\n return result;\n}\n\n/**\n * Move a card within the same column\n */\nexport function reorderCardInColumn(\n state: KanbanBoardState,\n columnId: string,\n startIndex: number,\n endIndex: number\n): KanbanBoardState {\n const column = state.columns.find((col) => col.id === columnId);\n if (!column) return state;\n\n const newCardIds = reorderArray(column.cardIds, startIndex, endIndex);\n\n return {\n ...state,\n columns: state.columns.map((col) =>\n col.id === columnId ? { ...col, cardIds: newCardIds } : col\n ),\n };\n}\n\n/**\n * Move a card from one column to another\n */\nexport function moveCardBetweenColumns(\n state: KanbanBoardState,\n source: DragLocation,\n destination: DragLocation,\n cardId: string\n): KanbanBoardState {\n const sourceColumn = state.columns.find((col) => col.id === source.columnId);\n const destColumn = state.columns.find((col) => col.id === destination.columnId);\n\n if (!sourceColumn || !destColumn) return state;\n\n // Remove from source\n const newSourceCardIds = [...sourceColumn.cardIds];\n newSourceCardIds.splice(source.index, 1);\n\n // Add to destination\n const newDestCardIds = [...destColumn.cardIds];\n newDestCardIds.splice(destination.index, 0, cardId);\n\n return {\n ...state,\n columns: state.columns.map((col) => {\n if (col.id === source.columnId) {\n return { ...col, cardIds: newSourceCardIds };\n }\n if (col.id === destination.columnId) {\n return { ...col, cardIds: newDestCardIds };\n }\n return col;\n }),\n };\n}\n\n/**\n * Reorder columns\n */\nexport function reorderColumns(\n state: KanbanBoardState,\n startIndex: number,\n endIndex: number\n): KanbanBoardState {\n return {\n ...state,\n columns: reorderArray(state.columns, startIndex, endIndex),\n };\n}\n\n/**\n * Apply a drag result to the board state\n * This is a convenience function that handles all reorder cases\n */\nexport function applyDragResult(\n state: KanbanBoardState,\n result: {\n type: 'CARD' | 'COLUMN';\n source: DragLocation;\n destination?: DragLocation;\n draggableId: string;\n }\n): KanbanBoardState {\n // No destination = drag canceled\n if (!result.destination) {\n return state;\n }\n\n const { source, destination, type } = result;\n\n // Column reorder\n if (type === 'COLUMN') {\n if (source.index === destination.index) {\n return state; // No change\n }\n return reorderColumns(state, source.index, destination.index);\n }\n\n // Card reorder\n if (type === 'CARD') {\n // Same column\n if (source.columnId === destination.columnId) {\n if (source.index === destination.index) {\n return state; // No change\n }\n return reorderCardInColumn(\n state,\n source.columnId!,\n source.index,\n destination.index\n );\n }\n\n // Different columns\n return moveCardBetweenColumns(state, source, destination, result.draggableId);\n }\n\n return state;\n}\n"],"names":["KanbanContext","createContext","KANBAN_COLUMN","KanbanColumnView","column","cardIds","children","isDragDisabled","index","columnRef","useRef","dropZoneRef","isDragging","setIsDragging","useState","isDraggingOver","setIsDraggingOver","createDraggableColumn","useCallback","element","draggable","getInitialData","type","id","onDragStart","onDrop","createColumnDropTarget","dropTargetForElements","getData","input","data","attachClosestEdge","allowedEdges","canDrop","source","createCardDropZone","columnId","onDragEnter","onDragLeave","useEffect","columnElement","current","dropZoneElement","cleanups","forEach","cleanup","provided","draggableProps","style","opacity","undefined","dragHandleProps","tabIndex","role","title","innerRef","snapshot","providedWithDropZone","Object","assign","React","createElement","Fragment","KANBAN_CARD","KanbanCardView","card","cardRef","createDraggable","createDropTarget","combine","useKanbanDnd","onDragEnd","state","disabled","dragState","setDragState","draggingId","draggingType","destination","monitorForElements","typeRaw","sourceLocation","location","sourceType","sourceId","sourceIndex","sourceColumnId","dropTargets","columnTarget","find","target","destColumnId","cardTarget","destIndex","extractClosestEdge","edge","result","draggableId","KanbanBoard","renderColumn","renderCard","getCardKey","getColumnKey","className","contextValue","useMemo","cardKeyExtractor","columnKeyExtractor","Provider","value","display","gap","columns","map","columnIndex","key","columnProvided","columnSnapshot","ref","minWidth","flexDirection","flex","minHeight","cardId","cardIndex","cards","cardProvided","cardSnapshot","DEFAULT_CONFIG","threshold","maxSpeed","enabled","useAutoscroll","containerRef","config","rafRef","lastMousePosRef","handleMouseMove","event","x","clientX","y","clientY","performScroll","scrollParent","parent","parentElement","overflow","overflowY","window","getComputedStyle","test","document","documentElement","getScrollParent","rect","getBoundingClientRect","mousePos","scrollability","overflowX","hasVerticalScroll","hasHorizontalScroll","vertical","scrollHeight","clientHeight","horizontal","scrollWidth","clientWidth","isScrollable","nearEdge","point","distances","top","right","bottom","left","distanceToEdge","distance","entries","Infinity","isNearEdge","speed","calculateScrollSpeed","delta","scrollLeft","scrollTop","scrollBy","requestAnimationFrame","addEventListener","removeEventListener","cancelAnimationFrame","AnnouncerContext","useAnnouncer","context","useContext","Error","AnnouncerProvider","politeness","politeRef","assertiveRef","timeoutRef","clearTimeout","announce","message","announcePoliteness","regionRef","textContent","setTimeout","position","width","height","padding","margin","clip","whiteSpace","border","announcements","itemName","totalItems","onDragMove","newPosition","from","to","onDragCancel","onColumnMove","columnName","totalColumns","reorderArray","list","startIndex","endIndex","Array","removed","splice","applyDragResult","reorderColumns","col","newCardIds","reorderCardInColumn","sourceColumn","destColumn","newSourceCardIds","newDestCardIds","moveCardBetweenColumns"],"mappings":"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"}
|
package/dist/kanban.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("react"),t=require("@atlaskit/pragmatic-drag-and-drop/element/adapter"),n=require("@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"),r=require("@atlaskit/pragmatic-drag-and-drop/combine");const a=e.createContext(null),o="kanban-column";function i({column:r,cardIds:a,children:i,isDragDisabled:d=!1,index:s}){const l=e.useRef(null),c=e.useRef(null),[u,g]=e.useState(!1),[p,m]=e.useState(!1),f=e.useCallback(e=>d?()=>{}:t.draggable({element:e,getInitialData:()=>({type:o,id:r.id,index:s}),onDragStart:()=>g(!0),onDrop:()=>g(!1)}),[r.id,s,d]),b=e.useCallback(e=>d?()=>{}:t.dropTargetForElements({element:e,getData:({input:e,element:t})=>{const a={type:o,id:r.id,index:s};return n.attachClosestEdge(a,{input:e,element:t,allowedEdges:["left","right"]})},canDrop:({source:e})=>e.data.type===o&&e.data.id!==r.id}),[r.id,s,d]),x=e.useCallback(e=>t.dropTargetForElements({element:e,getData:()=>({type:"kanban-column-drop-zone",columnId:r.id}),canDrop:({source:e})=>"kanban-card"===e.data.type,onDragEnter:()=>m(!0),onDragLeave:()=>m(!1),onDrop:()=>m(!1)}),[r.id]);e.useEffect(()=>{const e=l.current,t=c.current;if(!e||!t)return;const n=[f(e),b(e),x(t)];return()=>{n.forEach(e=>e())}},[f,b,x]);const D={draggableProps:{"data-draggable-id":r.id,"data-draggable-type":"COLUMN",style:u?{opacity:.5}:void 0},dragHandleProps:{tabIndex:0,role:"button","aria-roledescription":"draggable column","aria-label":`${r.title}, press space to pick up`},innerRef:l},y={isDragging:u,isDraggingOver:p},v=Object.assign(Object.assign({},D),{dropZoneRef:c});return e.createElement(e.Fragment,null,i(v,y))}const d="kanban-card";function s({card:a,children:o,isDragDisabled:i=!1,index:s,columnId:l}){const c=e.useRef(null),[u,g]=e.useState(!1),[p,m]=e.useState(!1),f=e.useCallback(e=>i?()=>{}:t.draggable({element:e,getInitialData:()=>({type:d,id:a.id,index:s,columnId:l}),onDragStart:()=>g(!0),onDrop:()=>g(!1)}),[a.id,s,l,i]),b=e.useCallback(e=>i?()=>{}:t.dropTargetForElements({element:e,getData:({input:e,element:t})=>{const r={type:d,id:a.id,index:s,columnId:l};return n.attachClosestEdge(r,{input:e,element:t,allowedEdges:["top","bottom"]})},canDrop:({source:e})=>e.data.type===d&&e.data.id!==a.id,onDragEnter:()=>m(!0),onDragLeave:()=>m(!1),onDrop:()=>m(!1)}),[a.id,s,l,i]);e.useEffect(()=>{const e=c.current;if(e&&!i)return r.combine(f(e),b(e))},[f,b,i]);const x={draggableProps:{"data-draggable-id":a.id,"data-draggable-type":"CARD",style:u?{opacity:.5}:void 0},dragHandleProps:{tabIndex:0,role:"button","aria-roledescription":"draggable card","aria-label":`${a.title}, press space to pick up`},innerRef:c},D={isDragging:u,isDraggingOver:p};return e.createElement(e.Fragment,null,o(x,D))}const l="kanban-card",c="kanban-column";function u({onDragStart:r,onDragEnd:a,state:o,disabled:i}){const[d,s]=e.useState({draggingId:null,draggingType:null,source:null,destination:null});return e.useEffect(()=>{if(i)return;return t.monitorForElements({onDragStart({source:e}){const t=e.data.type,n=e.data.id,a=e.data.columnId,o=e.data.index,i=t===l?"CARD":"COLUMN",d={columnId:"CARD"===i?a:void 0,index:o};s({draggingId:n,draggingType:i,source:d,destination:d}),null==r||r({id:n,type:i})},onDrop({source:e,location:t}){const r=e.data.type,i=e.data.id,d=e.data.index,u=e.data.columnId,g=t.current.dropTargets;let p;if(r===l){const e=g.find(e=>"kanban-column-drop-zone"===e.data.type);if(e){const t=e.data.columnId,r=g.find(e=>e.data.type===l&&e.data.id!==i);if(r){const e=r.data.index;p={columnId:t,index:"bottom"===n.extractClosestEdge(r)?e+1:e}}else p={columnId:t,index:0}}}else if(r===c){const e=g.find(e=>e.data.type===c&&e.data.id!==i);if(e){const t=e.data.index,r=n.extractClosestEdge(e);p={index:"bottom"===r||"right"===r?t+1:t}}}const m={type:r===l?"CARD":"COLUMN",source:{columnId:u,index:d},destination:p,draggableId:i};s({draggingId:null,draggingType:null,source:null,destination:null}),a(m,o)}})},[i,r,a,o]),d}const g={threshold:50,maxSpeed:20,enabled:!0};const p=e.createContext(null);function m(e,t,n){const r=Array.from(e),[a]=r.splice(t,1);return r.splice(n,0,a),r}exports.AnnouncerProvider=function({children:t,politeness:n="polite"}){const r=e.useRef(null),a=e.useRef(null),o=e.useRef();e.useEffect(()=>()=>{o.current&&clearTimeout(o.current)},[]);const i={announce:(e,t=n)=>{const i="assertive"===t?a:r;i.current&&(o.current&&clearTimeout(o.current),i.current.textContent="",o.current=setTimeout(()=>{i.current&&(i.current.textContent=e)},100))}};return e.createElement(p.Provider,{value:i},t,e.createElement("div",{ref:r,role:"status","aria-live":"polite","aria-atomic":"true",style:{position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",border:"0"}}),e.createElement("div",{ref:a,role:"alert","aria-live":"assertive","aria-atomic":"true",style:{position:"absolute",width:"1px",height:"1px",padding:"0",margin:"-1px",overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",border:"0"}}))},exports.KanbanBoard=function({state:t,onDragEnd:n,onDragStart:r,renderColumn:o,renderCard:d,getCardKey:l,getColumnKey:c,isDragDisabled:g,className:p,style:m}){const f=u({state:t,onDragEnd:n,onDragStart:r,disabled:!1}),b=e.useMemo(()=>({state:t,dragState:f,isDragDisabled:g}),[t,f,g]),x=l||(e=>e.id),D=c||(e=>e.id);return e.createElement(a.Provider,{value:b},e.createElement("div",{className:p,style:Object.assign({display:"flex",gap:"16px"},m)},t.columns.map((n,r)=>e.createElement(i,{key:D(n),column:n,cardIds:n.cardIds,index:r,isDragDisabled:null==g?void 0:g(n.id,"COLUMN")},(r,a)=>e.createElement("div",Object.assign({ref:r.innerRef},r.draggableProps,{style:Object.assign({minWidth:"250px",display:"flex",flexDirection:"column"},r.draggableProps.style)}),e.createElement("div",Object.assign({},r.dragHandleProps),o(n,r,a)),e.createElement("div",{ref:r.dropZoneRef,style:{flex:1,minHeight:"100px",display:"flex",flexDirection:"column",gap:"8px"}},n.cardIds.map((r,a)=>{const o=t.cards[r];return o?e.createElement(s,{key:x(o),card:o,index:a,columnId:n.id,isDragDisabled:null==g?void 0:g(o.id,"CARD")},(t,n)=>e.createElement("div",Object.assign({ref:t.innerRef},t.draggableProps,t.dragHandleProps),d(o,t,n))):null})))))))},exports.KanbanCardView=s,exports.KanbanColumnView=i,exports.announcements={onDragStart:(e,t,n)=>`Picked up ${e}. Position ${t+1} of ${n}.`,onDragMove:(e,t,n)=>`${e} moved to position ${t+1} of ${n}.`,onDragEnd:(e,t,n,r)=>t===n?`${e} dropped. Position ${r+1} in ${n}.`:`${e} moved from ${t} to ${n}. Position ${r+1}.`,onDragCancel:(e,t)=>`Drag cancelled. ${e} returned to ${t}.`,onColumnMove:(e,t,n)=>`${e} moved to position ${t+1} of ${n}.`},exports.applyDragResult=function(e,t){if(!t.destination)return e;const{source:n,destination:r,type:a}=t;return"COLUMN"===a?n.index===r.index?e:function(e,t,n){return Object.assign(Object.assign({},e),{columns:m(e.columns,t,n)})}(e,n.index,r.index):"CARD"===a?n.columnId===r.columnId?n.index===r.index?e:function(e,t,n,r){const a=e.columns.find(e=>e.id===t);if(!a)return e;const o=m(a.cardIds,n,r);return Object.assign(Object.assign({},e),{columns:e.columns.map(e=>e.id===t?Object.assign(Object.assign({},e),{cardIds:o}):e)})}(e,n.columnId,n.index,r.index):function(e,t,n,r){const a=e.columns.find(e=>e.id===t.columnId),o=e.columns.find(e=>e.id===n.columnId);if(!a||!o)return e;const i=[...a.cardIds];i.splice(t.index,1);const d=[...o.cardIds];return d.splice(n.index,0,r),Object.assign(Object.assign({},e),{columns:e.columns.map(e=>e.id===t.columnId?Object.assign(Object.assign({},e),{cardIds:i}):e.id===n.columnId?Object.assign(Object.assign({},e),{cardIds:d}):e)})}(e,n,r,t.draggableId):e},exports.reorderArray=m,exports.useAnnouncer=function(){const t=e.useContext(p);if(!t)throw new Error("useAnnouncer must be used within AnnouncerProvider");return t},exports.useAutoscroll=function(t,n,r={}){const{threshold:a,maxSpeed:o,enabled:i}=Object.assign(Object.assign({},g),r),d=e.useRef(),s=e.useRef(null),l=e.useCallback(e=>{s.current={x:e.clientX,y:e.clientY}},[]),c=e.useCallback(()=>{if(!(i&&n&&t.current&&s.current))return;const e=function(e){if(!e)return null;let t=e.parentElement;for(;t;){const{overflow:e,overflowY:n}=window.getComputedStyle(t);if(/(auto|scroll)/.test(e+n))return t;t=t.parentElement}return document.documentElement}(t.current);if(!e)return;const r=e.getBoundingClientRect(),l=s.current,u=function(e){const{overflow:t,overflowY:n,overflowX:r}=window.getComputedStyle(e),a=/(auto|scroll)/.test(t+n),o=/(auto|scroll)/.test(t+r);return{vertical:a&&e.scrollHeight>e.clientHeight,horizontal:o&&e.scrollWidth>e.clientWidth}}(e),g=function(e,t,n){const r=function(e,t){return{top:e.y-t.top,right:t.right-e.x,bottom:t.bottom-e.y,left:e.x-t.left}}(e,t);for(const[e,t]of Object.entries(r))if(t<n)return{edge:e,distance:t};return{edge:null,distance:1/0}}(l,r,a);if(g.edge){const t=function(e,t,n){return e>=t?0:(t-e)/t*n}(g.distance,a,o),n={x:0,y:0};u.vertical&&("top"===g.edge?n.y=-t:"bottom"===g.edge&&(n.y=t)),u.horizontal&&("left"===g.edge?n.x=-t:"right"===g.edge&&(n.x=t)),0===n.x&&0===n.y||function(e,t){t.x&&(e.scrollLeft+=t.x),t.y&&(e.scrollTop+=t.y)}(e,n)}d.current=requestAnimationFrame(c)},[i,n,t,a,o]);e.useEffect(()=>i&&n?(window.addEventListener("mousemove",l),d.current=requestAnimationFrame(c),()=>{window.removeEventListener("mousemove",l),d.current&&cancelAnimationFrame(d.current)}):(d.current&&(cancelAnimationFrame(d.current),d.current=void 0),void(s.current=null)),[i,n,l,c])},exports.useKanbanDnd=u;
|
|
2
|
+
//# sourceMappingURL=kanban.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban.js","sources":["../src/kanban/context.ts","../src/kanban/components/KanbanColumnView.tsx","../src/kanban/components/KanbanCardView.tsx","../src/kanban/hooks/useKanbanDnd.ts","../src/kanban/hooks/useAutoscroll.ts","../src/kanban/a11y/Announcer.tsx","../src/kanban/utils/reorder.ts","../src/kanban/components/KanbanBoard.tsx","../src/kanban/utils/dom.ts","../src/kanban/utils/dndMath.ts"],"sourcesContent":["/**\n * Kanban Board Context\n *\n * Internal context for sharing board state between components.\n */\n\nimport { createContext, useContext } from 'react';\nimport type { KanbanBoardState, KanbanDragState } from './types';\n\nexport interface KanbanContextValue {\n state: KanbanBoardState;\n dragState: KanbanDragState;\n isDragDisabled?: (id: string, type: 'CARD' | 'COLUMN') => boolean;\n}\n\nexport const KanbanContext = createContext<KanbanContextValue | null>(null);\n\nexport function useKanbanContext(): KanbanContextValue {\n const context = useContext(KanbanContext);\n if (!context) {\n throw new Error('Kanban components must be used within KanbanBoard');\n }\n return context;\n}\n","/**\n * Kanban Column View (Headless)\n *\n * Headless component for rendering draggable columns with drop zones.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanColumnViewProps, DragProvided, DragSnapshot } from '../types';\n\nconst KANBAN_COLUMN = 'kanban-column';\nconst KANBAN_COLUMN_DROP_ZONE = 'kanban-column-drop-zone';\n\nexport function KanbanColumnView({\n column,\n cardIds,\n children,\n isDragDisabled = false,\n index,\n}: KanbanColumnViewProps) {\n const columnRef = useRef<HTMLDivElement>(null);\n const dropZoneRef = useRef<HTMLDivElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [isDraggingOver, setIsDraggingOver] = useState(false);\n\n // Make column header draggable\n const createDraggableColumn = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return draggable({\n element,\n getInitialData: () => ({\n type: KANBAN_COLUMN,\n id: column.id,\n index,\n }),\n onDragStart: () => setIsDragging(true),\n onDrop: () => setIsDragging(false),\n });\n },\n [column.id, index, isDragDisabled]\n );\n\n // Make column header a drop target for column reordering\n const createColumnDropTarget = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return dropTargetForElements({\n element,\n getData: ({ input, element }) => {\n const data = { type: KANBAN_COLUMN, id: column.id, index };\n return attachClosestEdge(data, { input, element, allowedEdges: ['left', 'right'] });\n },\n canDrop: ({ source }) => source.data.type === KANBAN_COLUMN && source.data.id !== column.id,\n });\n },\n [column.id, index, isDragDisabled]\n );\n\n // Make card list area a drop zone for cards\n const createCardDropZone = useCallback(\n (element: HTMLElement) => {\n return dropTargetForElements({\n element,\n getData: () => ({\n type: KANBAN_COLUMN_DROP_ZONE,\n columnId: column.id,\n }),\n canDrop: ({ source }) => source.data.type === 'kanban-card',\n onDragEnter: () => setIsDraggingOver(true),\n onDragLeave: () => setIsDraggingOver(false),\n onDrop: () => setIsDraggingOver(false),\n });\n },\n [column.id]\n );\n\n useEffect(() => {\n const columnElement = columnRef.current;\n const dropZoneElement = dropZoneRef.current;\n if (!columnElement || !dropZoneElement) return;\n\n const cleanups = [\n createDraggableColumn(columnElement),\n createColumnDropTarget(columnElement),\n createCardDropZone(dropZoneElement),\n ];\n\n return () => {\n cleanups.forEach((cleanup) => cleanup());\n };\n }, [createDraggableColumn, createColumnDropTarget, createCardDropZone]);\n\n const provided: DragProvided = {\n draggableProps: {\n 'data-draggable-id': column.id,\n 'data-draggable-type': 'COLUMN',\n style: isDragging ? { opacity: 0.5 } : undefined,\n },\n dragHandleProps: {\n tabIndex: 0,\n role: 'button',\n 'aria-roledescription': 'draggable column',\n 'aria-label': `${column.title}, press space to pick up`,\n },\n innerRef: columnRef as React.RefObject<HTMLElement>,\n };\n\n const snapshot: DragSnapshot = {\n isDragging,\n isDraggingOver,\n };\n\n // Pass both refs through provided\n const providedWithDropZone = {\n ...provided,\n dropZoneRef,\n };\n\n return <>{children(providedWithDropZone as any, snapshot)}</>;\n}\n","/**\n * Kanban Card View (Headless)\n *\n * Headless component for rendering draggable cards.\n */\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';\nimport { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanCardViewProps, DragProvided, DragSnapshot } from '../types';\n\nconst KANBAN_CARD = 'kanban-card';\n\nexport function KanbanCardView({\n card,\n children,\n isDragDisabled = false,\n index,\n columnId,\n}: KanbanCardViewProps) {\n const cardRef = useRef<HTMLDivElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [isDraggingOver, setIsDraggingOver] = useState(false);\n\n const createDraggable = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return draggable({\n element,\n getInitialData: () => ({\n type: KANBAN_CARD,\n id: card.id,\n index,\n columnId,\n }),\n onDragStart: () => setIsDragging(true),\n onDrop: () => setIsDragging(false),\n });\n },\n [card.id, index, columnId, isDragDisabled]\n );\n\n const createDropTarget = useCallback(\n (element: HTMLElement) => {\n if (isDragDisabled) return () => {};\n\n return dropTargetForElements({\n element,\n getData: ({ input, element }) => {\n const data = { type: KANBAN_CARD, id: card.id, index, columnId };\n return attachClosestEdge(data, { input, element, allowedEdges: ['top', 'bottom'] });\n },\n canDrop: ({ source }) => source.data.type === KANBAN_CARD && source.data.id !== card.id,\n onDragEnter: () => setIsDraggingOver(true),\n onDragLeave: () => setIsDraggingOver(false),\n onDrop: () => setIsDraggingOver(false),\n });\n },\n [card.id, index, columnId, isDragDisabled]\n );\n\n useEffect(() => {\n const element = cardRef.current;\n if (!element || isDragDisabled) return;\n\n return combine(createDraggable(element), createDropTarget(element));\n }, [createDraggable, createDropTarget, isDragDisabled]);\n\n const provided: DragProvided = {\n draggableProps: {\n 'data-draggable-id': card.id,\n 'data-draggable-type': 'CARD',\n style: isDragging ? { opacity: 0.5 } : undefined,\n },\n dragHandleProps: {\n tabIndex: 0,\n role: 'button',\n 'aria-roledescription': 'draggable card',\n 'aria-label': `${card.title}, press space to pick up`,\n },\n innerRef: cardRef as React.RefObject<HTMLElement>,\n };\n\n const snapshot: DragSnapshot = {\n isDragging,\n isDraggingOver,\n };\n\n return <>{children(provided, snapshot)}</>;\n}\n","/**\n * Core Kanban DnD Hook\n *\n * Main hook that manages drag-and-drop state for the Kanban board.\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';\nimport { extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';\nimport type { KanbanDragState, DropResult, DragLocation, KanbanBoardState } from '../types';\n\nconst KANBAN_CARD = 'kanban-card';\nconst KANBAN_COLUMN = 'kanban-column';\n\nexport interface UseKanbanDndOptions {\n onDragStart?: (draggable: { id: string; type: 'CARD' | 'COLUMN' }) => void;\n onDragEnd: (result: DropResult, stateBefore: KanbanBoardState) => void;\n state: KanbanBoardState;\n disabled?: boolean;\n}\n\nexport function useKanbanDnd({ onDragStart, onDragEnd, state, disabled }: UseKanbanDndOptions) {\n const [dragState, setDragState] = useState<KanbanDragState>({\n draggingId: null,\n draggingType: null,\n source: null,\n destination: null,\n });\n\n useEffect(() => {\n if (disabled) return;\n\n const cleanup = monitorForElements({\n onDragStart({ source }) {\n const typeRaw = source.data.type as string;\n const id = source.data.id as string;\n const columnId = source.data.columnId as string | undefined;\n const index = source.data.index as number;\n\n const type: 'CARD' | 'COLUMN' = typeRaw === KANBAN_CARD ? 'CARD' : 'COLUMN';\n\n const sourceLocation: DragLocation = {\n columnId: type === 'CARD' ? columnId : undefined,\n index,\n };\n\n setDragState({\n draggingId: id,\n draggingType: type,\n source: sourceLocation,\n destination: sourceLocation,\n });\n\n onDragStart?.({ id, type });\n },\n\n onDrop({ source, location }) {\n const sourceType = source.data.type as string;\n const sourceId = source.data.id as string;\n const sourceIndex = source.data.index as number;\n const sourceColumnId = source.data.columnId as string | undefined;\n\n // Find the drop target\n const dropTargets = location.current.dropTargets;\n\n let destination: DragLocation | undefined;\n\n if (sourceType === KANBAN_CARD) {\n // Card drop - find column drop target\n const columnTarget = dropTargets.find((target) =>\n target.data.type === 'kanban-column-drop-zone'\n );\n\n if (columnTarget) {\n const destColumnId = columnTarget.data.columnId as string;\n const cardTarget = dropTargets.find((target) =>\n target.data.type === KANBAN_CARD && target.data.id !== sourceId\n );\n\n if (cardTarget) {\n // Dropping on another card\n const destIndex = cardTarget.data.index as number;\n const edge = extractClosestEdge(cardTarget);\n\n destination = {\n columnId: destColumnId,\n index: edge === 'bottom' ? destIndex + 1 : destIndex,\n };\n } else {\n // Dropping in empty column\n destination = {\n columnId: destColumnId,\n index: 0,\n };\n }\n }\n } else if (sourceType === KANBAN_COLUMN) {\n // Column drop\n const columnTarget = dropTargets.find((target) =>\n target.data.type === KANBAN_COLUMN && target.data.id !== sourceId\n );\n\n if (columnTarget) {\n const destIndex = columnTarget.data.index as number;\n const edge = extractClosestEdge(columnTarget);\n\n destination = {\n index: edge === 'bottom' || edge === 'right' ? destIndex + 1 : destIndex,\n };\n }\n }\n\n const result: DropResult = {\n type: sourceType === KANBAN_CARD ? 'CARD' : 'COLUMN',\n source: {\n columnId: sourceColumnId,\n index: sourceIndex,\n },\n destination,\n draggableId: sourceId,\n };\n\n setDragState({\n draggingId: null,\n draggingType: null,\n source: null,\n destination: null,\n });\n\n onDragEnd(result, state);\n },\n });\n\n return cleanup;\n }, [disabled, onDragStart, onDragEnd, state]);\n\n return dragState;\n}\n","/**\n * Autoscroll Hook\n *\n * Automatically scrolls containers when dragging near edges.\n */\n\nimport { useEffect, useRef, useCallback } from 'react';\nimport type { AutoscrollConfig } from '../types';\nimport { isNearEdge, calculateScrollSpeed } from '../utils/dndMath';\nimport { getScrollParent, scrollBy, isScrollable } from '../utils/dom';\n\nconst DEFAULT_CONFIG: AutoscrollConfig = {\n threshold: 50, // px from edge\n maxSpeed: 20, // px per frame\n enabled: true,\n};\n\n/**\n * Hook for autoscrolling containers during drag\n */\nexport function useAutoscroll(\n containerRef: React.RefObject<HTMLElement>,\n isDragging: boolean,\n config: Partial<AutoscrollConfig> = {}\n) {\n const { threshold, maxSpeed, enabled } = { ...DEFAULT_CONFIG, ...config };\n const rafRef = useRef<number>();\n const lastMousePosRef = useRef<{ x: number; y: number } | null>(null);\n\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n lastMousePosRef.current = { x: event.clientX, y: event.clientY };\n },\n []\n );\n\n const performScroll = useCallback(() => {\n if (!enabled || !isDragging || !containerRef.current || !lastMousePosRef.current) {\n return;\n }\n\n const container = containerRef.current;\n const scrollParent = getScrollParent(container);\n if (!scrollParent) return;\n\n const rect = scrollParent.getBoundingClientRect();\n const mousePos = lastMousePosRef.current;\n const scrollability = isScrollable(scrollParent);\n\n const nearEdge = isNearEdge(mousePos, rect, threshold);\n\n if (nearEdge.edge) {\n const speed = calculateScrollSpeed(nearEdge.distance, threshold, maxSpeed);\n\n const delta = { x: 0, y: 0 };\n\n if (scrollability.vertical) {\n if (nearEdge.edge === 'top') {\n delta.y = -speed;\n } else if (nearEdge.edge === 'bottom') {\n delta.y = speed;\n }\n }\n\n if (scrollability.horizontal) {\n if (nearEdge.edge === 'left') {\n delta.x = -speed;\n } else if (nearEdge.edge === 'right') {\n delta.x = speed;\n }\n }\n\n if (delta.x !== 0 || delta.y !== 0) {\n scrollBy(scrollParent, delta);\n }\n }\n\n rafRef.current = requestAnimationFrame(performScroll);\n }, [enabled, isDragging, containerRef, threshold, maxSpeed]);\n\n useEffect(() => {\n if (!enabled || !isDragging) {\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = undefined;\n }\n lastMousePosRef.current = null;\n return;\n }\n\n window.addEventListener('mousemove', handleMouseMove);\n rafRef.current = requestAnimationFrame(performScroll);\n\n return () => {\n window.removeEventListener('mousemove', handleMouseMove);\n if (rafRef.current) {\n cancelAnimationFrame(rafRef.current);\n }\n };\n }, [enabled, isDragging, handleMouseMove, performScroll]);\n}\n","/**\n * Announcer Component\n *\n * Provides screen reader announcements for drag-and-drop operations.\n */\n\nimport React, { createContext, useContext, useEffect, useRef } from 'react';\n\ninterface AnnouncerContextValue {\n announce: (message: string, politeness?: 'polite' | 'assertive') => void;\n}\n\nconst AnnouncerContext = createContext<AnnouncerContextValue | null>(null);\n\n/**\n * Hook to access the announcer context\n */\nexport function useAnnouncer(): AnnouncerContextValue {\n const context = useContext(AnnouncerContext);\n if (!context) {\n throw new Error('useAnnouncer must be used within AnnouncerProvider');\n }\n return context;\n}\n\n/**\n * Props for AnnouncerProvider\n */\nexport interface AnnouncerProviderProps {\n /** Child components */\n children: React.ReactNode;\n /** Default politeness level */\n politeness?: 'polite' | 'assertive';\n}\n\n/**\n * Provider component that creates a live region for screen reader announcements\n */\nexport function AnnouncerProvider({\n children,\n politeness = 'polite',\n}: AnnouncerProviderProps) {\n const politeRef = useRef<HTMLDivElement>(null);\n const assertiveRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<NodeJS.Timeout>();\n\n const announce = (message: string, announcePoliteness: 'polite' | 'assertive' = politeness) => {\n const regionRef = announcePoliteness === 'assertive' ? assertiveRef : politeRef;\n\n if (!regionRef.current) return;\n\n // Clear existing timeout\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n // Clear the region first\n regionRef.current.textContent = '';\n\n // Set the new message after a brief delay to ensure screen readers pick it up\n timeoutRef.current = setTimeout(() => {\n if (regionRef.current) {\n regionRef.current.textContent = message;\n }\n }, 100);\n };\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, []);\n\n const value: AnnouncerContextValue = {\n announce,\n };\n\n return (\n <AnnouncerContext.Provider value={value}>\n {children}\n {/* Polite live region */}\n <div\n ref={politeRef}\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n }}\n />\n {/* Assertive live region */}\n <div\n ref={assertiveRef}\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n style={{\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n }}\n />\n </AnnouncerContext.Provider>\n );\n}\n\n/**\n * Generate announcement messages for different drag events\n */\nexport const announcements = {\n onDragStart: (itemName: string, position: number, totalItems: number): string =>\n `Picked up ${itemName}. Position ${position + 1} of ${totalItems}.`,\n\n onDragMove: (itemName: string, newPosition: number, totalItems: number): string =>\n `${itemName} moved to position ${newPosition + 1} of ${totalItems}.`,\n\n onDragEnd: (itemName: string, from: string, to: string, position: number): string =>\n from === to\n ? `${itemName} dropped. Position ${position + 1} in ${to}.`\n : `${itemName} moved from ${from} to ${to}. Position ${position + 1}.`,\n\n onDragCancel: (itemName: string, column: string): string =>\n `Drag cancelled. ${itemName} returned to ${column}.`,\n\n onColumnMove: (columnName: string, position: number, totalColumns: number): string =>\n `${columnName} moved to position ${position + 1} of ${totalColumns}.`,\n};\n","/**\n * Kanban Reorder Utilities\n *\n * Functions for reordering cards and columns in a Kanban board.\n */\n\nimport type { KanbanBoardState, KanbanColumn, DragLocation } from '../types';\n\n/**\n * Reorder an array by moving an item from one index to another\n */\nexport function reorderArray<T>(list: T[], startIndex: number, endIndex: number): T[] {\n const result = Array.from(list);\n const [removed] = result.splice(startIndex, 1);\n result.splice(endIndex, 0, removed);\n return result;\n}\n\n/**\n * Move a card within the same column\n */\nexport function reorderCardInColumn(\n state: KanbanBoardState,\n columnId: string,\n startIndex: number,\n endIndex: number\n): KanbanBoardState {\n const column = state.columns.find((col) => col.id === columnId);\n if (!column) return state;\n\n const newCardIds = reorderArray(column.cardIds, startIndex, endIndex);\n\n return {\n ...state,\n columns: state.columns.map((col) =>\n col.id === columnId ? { ...col, cardIds: newCardIds } : col\n ),\n };\n}\n\n/**\n * Move a card from one column to another\n */\nexport function moveCardBetweenColumns(\n state: KanbanBoardState,\n source: DragLocation,\n destination: DragLocation,\n cardId: string\n): KanbanBoardState {\n const sourceColumn = state.columns.find((col) => col.id === source.columnId);\n const destColumn = state.columns.find((col) => col.id === destination.columnId);\n\n if (!sourceColumn || !destColumn) return state;\n\n // Remove from source\n const newSourceCardIds = [...sourceColumn.cardIds];\n newSourceCardIds.splice(source.index, 1);\n\n // Add to destination\n const newDestCardIds = [...destColumn.cardIds];\n newDestCardIds.splice(destination.index, 0, cardId);\n\n return {\n ...state,\n columns: state.columns.map((col) => {\n if (col.id === source.columnId) {\n return { ...col, cardIds: newSourceCardIds };\n }\n if (col.id === destination.columnId) {\n return { ...col, cardIds: newDestCardIds };\n }\n return col;\n }),\n };\n}\n\n/**\n * Reorder columns\n */\nexport function reorderColumns(\n state: KanbanBoardState,\n startIndex: number,\n endIndex: number\n): KanbanBoardState {\n return {\n ...state,\n columns: reorderArray(state.columns, startIndex, endIndex),\n };\n}\n\n/**\n * Apply a drag result to the board state\n * This is a convenience function that handles all reorder cases\n */\nexport function applyDragResult(\n state: KanbanBoardState,\n result: {\n type: 'CARD' | 'COLUMN';\n source: DragLocation;\n destination?: DragLocation;\n draggableId: string;\n }\n): KanbanBoardState {\n // No destination = drag canceled\n if (!result.destination) {\n return state;\n }\n\n const { source, destination, type } = result;\n\n // Column reorder\n if (type === 'COLUMN') {\n if (source.index === destination.index) {\n return state; // No change\n }\n return reorderColumns(state, source.index, destination.index);\n }\n\n // Card reorder\n if (type === 'CARD') {\n // Same column\n if (source.columnId === destination.columnId) {\n if (source.index === destination.index) {\n return state; // No change\n }\n return reorderCardInColumn(\n state,\n source.columnId!,\n source.index,\n destination.index\n );\n }\n\n // Different columns\n return moveCardBetweenColumns(state, source, destination, result.draggableId);\n }\n\n return state;\n}\n","/**\n * Kanban Board Component\n *\n * High-level component that composes the Kanban board with all features.\n */\n\nimport React, { useMemo } from 'react';\nimport type { KanbanBoardProps } from '../types';\nimport { KanbanContext } from '../context';\nimport { KanbanColumnView } from './KanbanColumnView';\nimport { KanbanCardView } from './KanbanCardView';\nimport { useKanbanDnd } from '../hooks/useKanbanDnd';\n\nexport function KanbanBoard({\n state,\n onDragEnd,\n onDragStart,\n renderColumn,\n renderCard,\n getCardKey,\n getColumnKey,\n isDragDisabled,\n className,\n style,\n}: KanbanBoardProps) {\n const dragState = useKanbanDnd({\n state,\n onDragEnd,\n onDragStart,\n disabled: false,\n });\n\n const contextValue = useMemo(\n () => ({\n state,\n dragState,\n isDragDisabled,\n }),\n [state, dragState, isDragDisabled]\n );\n\n const defaultGetCardKey = (card: any) => card.id;\n const defaultGetColumnKey = (column: any) => column.id;\n\n const cardKeyExtractor = getCardKey || defaultGetCardKey;\n const columnKeyExtractor = getColumnKey || defaultGetColumnKey;\n\n return (\n <KanbanContext.Provider value={contextValue}>\n <div\n className={className}\n style={{\n display: 'flex',\n gap: '16px',\n ...style,\n }}\n >\n {state.columns.map((column, columnIndex) => (\n <KanbanColumnView\n key={columnKeyExtractor(column)}\n column={column}\n cardIds={column.cardIds}\n index={columnIndex}\n isDragDisabled={isDragDisabled?.(column.id, 'COLUMN')}\n >\n {(columnProvided, columnSnapshot) => (\n <div\n ref={columnProvided.innerRef as any}\n {...columnProvided.draggableProps}\n style={{\n minWidth: '250px',\n display: 'flex',\n flexDirection: 'column',\n ...columnProvided.draggableProps.style,\n }}\n >\n {/* Column header (draggable) */}\n <div {...columnProvided.dragHandleProps}>\n {renderColumn(column, columnProvided, columnSnapshot)}\n </div>\n\n {/* Card drop zone */}\n <div\n ref={(columnProvided as any).dropZoneRef}\n style={{\n flex: 1,\n minHeight: '100px',\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n }}\n >\n {column.cardIds.map((cardId, cardIndex) => {\n const card = state.cards[cardId];\n if (!card) return null;\n\n return (\n <KanbanCardView\n key={cardKeyExtractor(card)}\n card={card}\n index={cardIndex}\n columnId={column.id}\n isDragDisabled={isDragDisabled?.(card.id, 'CARD')}\n >\n {(cardProvided, cardSnapshot) => (\n <div\n ref={cardProvided.innerRef as any}\n {...cardProvided.draggableProps}\n {...cardProvided.dragHandleProps}\n >\n {renderCard(card, cardProvided, cardSnapshot)}\n </div>\n )}\n </KanbanCardView>\n );\n })}\n </div>\n </div>\n )}\n </KanbanColumnView>\n ))}\n </div>\n </KanbanContext.Provider>\n );\n}\n","/**\n * DOM Utilities\n *\n * Helper functions for DOM measurements and manipulation.\n */\n\n/**\n * Safely get bounding client rect for an element\n */\nexport function getBoundingRect(element: HTMLElement | null): DOMRect | null {\n if (!element) return null;\n return element.getBoundingClientRect();\n}\n\n/**\n * Get scrollable parent of an element\n */\nexport function getScrollParent(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null;\n\n let parent = element.parentElement;\n\n while (parent) {\n const { overflow, overflowY } = window.getComputedStyle(parent);\n if (/(auto|scroll)/.test(overflow + overflowY)) {\n return parent;\n }\n parent = parent.parentElement;\n }\n\n return document.documentElement as HTMLElement;\n}\n\n/**\n * Scroll an element by a delta\n */\nexport function scrollBy(\n element: HTMLElement,\n delta: { x?: number; y?: number }\n): void {\n if (delta.x) {\n element.scrollLeft += delta.x;\n }\n if (delta.y) {\n element.scrollTop += delta.y;\n }\n}\n\n/**\n * Check if an element is scrollable\n */\nexport function isScrollable(element: HTMLElement): {\n vertical: boolean;\n horizontal: boolean;\n} {\n const { overflow, overflowY, overflowX } = window.getComputedStyle(element);\n\n const hasVerticalScroll = /(auto|scroll)/.test(overflow + overflowY);\n const hasHorizontalScroll = /(auto|scroll)/.test(overflow + overflowX);\n\n return {\n vertical: hasVerticalScroll && element.scrollHeight > element.clientHeight,\n horizontal: hasHorizontalScroll && element.scrollWidth > element.clientWidth,\n };\n}\n\n/**\n * Get element's scroll position\n */\nexport function getScrollPosition(element: HTMLElement): { x: number; y: number } {\n if (element === document.documentElement) {\n return {\n x: window.pageXOffset || document.documentElement.scrollLeft,\n y: window.pageYOffset || document.documentElement.scrollTop,\n };\n }\n\n return {\n x: element.scrollLeft,\n y: element.scrollTop,\n };\n}\n\n/**\n * Get maximum scroll for an element\n */\nexport function getMaxScroll(element: HTMLElement): { x: number; y: number } {\n return {\n x: element.scrollWidth - element.clientWidth,\n y: element.scrollHeight - element.clientHeight,\n };\n}\n\n/**\n * Clamp scroll position to valid range\n */\nexport function clampScroll(\n scroll: { x: number; y: number },\n maxScroll: { x: number; y: number }\n): { x: number; y: number } {\n return {\n x: Math.max(0, Math.min(scroll.x, maxScroll.x)),\n y: Math.max(0, Math.min(scroll.y, maxScroll.y)),\n };\n}\n\n/**\n * Get all data attributes from an element as an object\n */\nexport function getDataAttributes(element: HTMLElement): Record<string, string> {\n const data: Record<string, string> = {};\n for (const key in element.dataset) {\n data[key] = element.dataset[key] || '';\n }\n return data;\n}\n\n/**\n * Find closest ancestor with a data attribute\n */\nexport function findClosestWithData(\n element: HTMLElement | null,\n dataKey: string\n): HTMLElement | null {\n let current = element;\n while (current) {\n if (current.dataset[dataKey]) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n}\n","/**\n * DnD Math Utilities\n *\n * Helper functions for collision detection and positioning calculations.\n */\n\n/**\n * Calculate the distance from a point to a rectangle's edges\n */\nexport function distanceToEdge(\n point: { x: number; y: number },\n rect: DOMRect\n): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n return {\n top: point.y - rect.top,\n right: rect.right - point.x,\n bottom: rect.bottom - point.y,\n left: point.x - rect.left,\n };\n}\n\n/**\n * Get the closest edge of a rectangle to a point\n */\nexport function getClosestEdge(\n point: { x: number; y: number },\n rect: DOMRect\n): 'top' | 'right' | 'bottom' | 'left' {\n const distances = distanceToEdge(point, rect);\n const entries = Object.entries(distances) as Array<[keyof typeof distances, number]>;\n const sorted = entries.sort((a, b) => a[1] - b[1]);\n return sorted[0][0];\n}\n\n/**\n * Check if a point is within a threshold distance from an edge\n */\nexport function isNearEdge(\n point: { x: number; y: number },\n rect: DOMRect,\n threshold: number\n): { edge: 'top' | 'right' | 'bottom' | 'left' | null; distance: number } {\n const distances = distanceToEdge(point, rect);\n\n for (const [edge, distance] of Object.entries(distances)) {\n if (distance < threshold) {\n return {\n edge: edge as 'top' | 'right' | 'bottom' | 'left',\n distance,\n };\n }\n }\n\n return { edge: null, distance: Infinity };\n}\n\n/**\n * Calculate scroll speed based on distance from edge\n * Closer to edge = faster scroll\n */\nexport function calculateScrollSpeed(\n distance: number,\n threshold: number,\n maxSpeed: number\n): number {\n if (distance >= threshold) return 0;\n const ratio = (threshold - distance) / threshold;\n return ratio * maxSpeed;\n}\n\n/**\n * Check if a point is inside a rectangle\n */\nexport function isPointInRect(point: { x: number; y: number }, rect: DOMRect): boolean {\n return (\n point.x >= rect.left &&\n point.x <= rect.right &&\n point.y >= rect.top &&\n point.y <= rect.bottom\n );\n}\n\n/**\n * Calculate the center point of a rectangle\n */\nexport function getRectCenter(rect: DOMRect): { x: number; y: number } {\n return {\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n };\n}\n\n/**\n * Calculate vertical drop position based on mouse Y relative to element center\n */\nexport function getVerticalDropPosition(\n mouseY: number,\n elementRect: DOMRect\n): 'top' | 'bottom' {\n const center = getRectCenter(elementRect);\n return mouseY < center.y ? 'top' : 'bottom';\n}\n\n/**\n * Calculate horizontal drop position based on mouse X relative to element center\n */\nexport function getHorizontalDropPosition(\n mouseX: number,\n elementRect: DOMRect\n): 'left' | 'right' {\n const center = getRectCenter(elementRect);\n return mouseX < center.x ? 'left' : 'right';\n}\n"],"names":["KanbanContext","createContext","KANBAN_COLUMN","KanbanColumnView","column","cardIds","children","isDragDisabled","index","columnRef","useRef","dropZoneRef","isDragging","setIsDragging","useState","isDraggingOver","setIsDraggingOver","createDraggableColumn","useCallback","element","draggable","getInitialData","type","id","onDragStart","onDrop","createColumnDropTarget","dropTargetForElements","getData","input","data","attachClosestEdge","allowedEdges","canDrop","source","createCardDropZone","columnId","onDragEnter","onDragLeave","useEffect","columnElement","current","dropZoneElement","cleanups","forEach","cleanup","provided","draggableProps","style","opacity","undefined","dragHandleProps","tabIndex","role","title","innerRef","snapshot","providedWithDropZone","Object","assign","React","createElement","Fragment","KANBAN_CARD","KanbanCardView","card","cardRef","createDraggable","createDropTarget","combine","useKanbanDnd","onDragEnd","state","disabled","dragState","setDragState","draggingId","draggingType","destination","monitorForElements","typeRaw","sourceLocation","location","sourceType","sourceId","sourceIndex","sourceColumnId","dropTargets","columnTarget","find","target","destColumnId","cardTarget","destIndex","extractClosestEdge","edge","result","draggableId","DEFAULT_CONFIG","threshold","maxSpeed","enabled","AnnouncerContext","reorderArray","list","startIndex","endIndex","Array","from","removed","splice","politeness","politeRef","assertiveRef","timeoutRef","clearTimeout","value","announce","message","announcePoliteness","regionRef","textContent","setTimeout","Provider","ref","position","width","height","padding","margin","overflow","clip","whiteSpace","border","renderColumn","renderCard","getCardKey","getColumnKey","className","contextValue","useMemo","cardKeyExtractor","columnKeyExtractor","display","gap","columns","map","columnIndex","key","columnProvided","columnSnapshot","minWidth","flexDirection","flex","minHeight","cardId","cardIndex","cards","cardProvided","cardSnapshot","itemName","totalItems","onDragMove","newPosition","to","onDragCancel","onColumnMove","columnName","totalColumns","reorderColumns","col","newCardIds","reorderCardInColumn","sourceColumn","destColumn","newSourceCardIds","newDestCardIds","moveCardBetweenColumns","context","useContext","Error","containerRef","config","rafRef","lastMousePosRef","handleMouseMove","event","x","clientX","y","clientY","performScroll","scrollParent","parent","parentElement","overflowY","window","getComputedStyle","test","document","documentElement","getScrollParent","rect","getBoundingClientRect","mousePos","scrollability","overflowX","hasVerticalScroll","hasHorizontalScroll","vertical","scrollHeight","clientHeight","horizontal","scrollWidth","clientWidth","isScrollable","nearEdge","point","distances","top","right","bottom","left","distanceToEdge","distance","entries","Infinity","isNearEdge","speed","calculateScrollSpeed","delta","scrollLeft","scrollTop","scrollBy","requestAnimationFrame","addEventListener","removeEventListener","cancelAnimationFrame"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Announcer Component
|
|
3
|
+
*
|
|
4
|
+
* Provides screen reader announcements for drag-and-drop operations.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
interface AnnouncerContextValue {
|
|
8
|
+
announce: (message: string, politeness?: 'polite' | 'assertive') => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the announcer context
|
|
12
|
+
*/
|
|
13
|
+
export declare function useAnnouncer(): AnnouncerContextValue;
|
|
14
|
+
/**
|
|
15
|
+
* Props for AnnouncerProvider
|
|
16
|
+
*/
|
|
17
|
+
export interface AnnouncerProviderProps {
|
|
18
|
+
/** Child components */
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
/** Default politeness level */
|
|
21
|
+
politeness?: 'polite' | 'assertive';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Provider component that creates a live region for screen reader announcements
|
|
25
|
+
*/
|
|
26
|
+
export declare function AnnouncerProvider({ children, politeness, }: AnnouncerProviderProps): React.JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* Generate announcement messages for different drag events
|
|
29
|
+
*/
|
|
30
|
+
export declare const announcements: {
|
|
31
|
+
onDragStart: (itemName: string, position: number, totalItems: number) => string;
|
|
32
|
+
onDragMove: (itemName: string, newPosition: number, totalItems: number) => string;
|
|
33
|
+
onDragEnd: (itemName: string, from: string, to: string, position: number) => string;
|
|
34
|
+
onDragCancel: (itemName: string, column: string) => string;
|
|
35
|
+
onColumnMove: (columnName: string, position: number, totalColumns: number) => string;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Region Hook
|
|
3
|
+
*
|
|
4
|
+
* Hook for announcing messages to screen readers.
|
|
5
|
+
*/
|
|
6
|
+
export interface LiveRegionOptions {
|
|
7
|
+
/** Politeness level for announcements */
|
|
8
|
+
politeness?: 'polite' | 'assertive';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Hook to announce messages to screen readers via live region
|
|
12
|
+
*/
|
|
13
|
+
export declare function useLiveRegion(options?: LiveRegionOptions): {
|
|
14
|
+
announce: (message: string, announcePoliteness?: "polite" | "assertive") => void;
|
|
15
|
+
clear: () => void;
|
|
16
|
+
cleanup: () => void;
|
|
17
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Board Component
|
|
3
|
+
*
|
|
4
|
+
* High-level component that composes the Kanban board with all features.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { KanbanBoardProps } from '../types';
|
|
8
|
+
export declare function KanbanBoard({ state, onDragEnd, onDragStart, renderColumn, renderCard, getCardKey, getColumnKey, isDragDisabled, className, style, }: KanbanBoardProps): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Card View (Headless)
|
|
3
|
+
*
|
|
4
|
+
* Headless component for rendering draggable cards.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { KanbanCardViewProps } from '../types';
|
|
8
|
+
export declare function KanbanCardView({ card, children, isDragDisabled, index, columnId, }: KanbanCardViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Column View (Headless)
|
|
3
|
+
*
|
|
4
|
+
* Headless component for rendering draggable columns with drop zones.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { KanbanColumnViewProps } from '../types';
|
|
8
|
+
export declare function KanbanColumnView({ column, cardIds, children, isDragDisabled, index, }: KanbanColumnViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Board Context
|
|
3
|
+
*
|
|
4
|
+
* Internal context for sharing board state between components.
|
|
5
|
+
*/
|
|
6
|
+
import type { KanbanBoardState, KanbanDragState } from './types';
|
|
7
|
+
export interface KanbanContextValue {
|
|
8
|
+
state: KanbanBoardState;
|
|
9
|
+
dragState: KanbanDragState;
|
|
10
|
+
isDragDisabled?: (id: string, type: 'CARD' | 'COLUMN') => boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const KanbanContext: import("react").Context<KanbanContextValue | null>;
|
|
13
|
+
export declare function useKanbanContext(): KanbanContextValue;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autoscroll Hook
|
|
3
|
+
*
|
|
4
|
+
* Automatically scrolls containers when dragging near edges.
|
|
5
|
+
*/
|
|
6
|
+
import type { AutoscrollConfig } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Hook for autoscrolling containers during drag
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAutoscroll(containerRef: React.RefObject<HTMLElement>, isDragging: boolean, config?: Partial<AutoscrollConfig>): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Kanban DnD Hook
|
|
3
|
+
*
|
|
4
|
+
* Main hook that manages drag-and-drop state for the Kanban board.
|
|
5
|
+
*/
|
|
6
|
+
import type { KanbanDragState, DropResult, KanbanBoardState } from '../types';
|
|
7
|
+
export interface UseKanbanDndOptions {
|
|
8
|
+
onDragStart?: (draggable: {
|
|
9
|
+
id: string;
|
|
10
|
+
type: 'CARD' | 'COLUMN';
|
|
11
|
+
}) => void;
|
|
12
|
+
onDragEnd: (result: DropResult, stateBefore: KanbanBoardState) => void;
|
|
13
|
+
state: KanbanBoardState;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function useKanbanDnd({ onDragStart, onDragEnd, state, disabled }: UseKanbanDndOptions): KanbanDragState;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Board Module
|
|
3
|
+
*
|
|
4
|
+
* A headless, accessible Kanban board implementation for React.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export type { Id, KanbanCard, KanbanColumn, KanbanBoardState, DragLocation, DropResult, DragProvided, DragSnapshot, KanbanBoardProps, KanbanColumnViewProps, KanbanCardViewProps, AutoscrollConfig, LiveAnnouncement, } from './types';
|
|
9
|
+
export { KanbanBoard } from './components/KanbanBoard';
|
|
10
|
+
export { KanbanColumnView } from './components/KanbanColumnView';
|
|
11
|
+
export { KanbanCardView } from './components/KanbanCardView';
|
|
12
|
+
export { useKanbanDnd } from './hooks/useKanbanDnd';
|
|
13
|
+
export { useAutoscroll } from './hooks/useAutoscroll';
|
|
14
|
+
export { AnnouncerProvider, useAnnouncer, announcements } from './a11y/Announcer';
|
|
15
|
+
export { applyDragResult, reorderArray } from './utils/reorder';
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Board Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the Kanban board feature.
|
|
5
|
+
* Follows a normalized state pattern for efficient cross-column operations.
|
|
6
|
+
*/
|
|
7
|
+
import type React from 'react';
|
|
8
|
+
export type Id = string;
|
|
9
|
+
/**
|
|
10
|
+
* Represents a single card in the Kanban board
|
|
11
|
+
*/
|
|
12
|
+
export interface KanbanCard {
|
|
13
|
+
/** Unique identifier for the card */
|
|
14
|
+
id: Id;
|
|
15
|
+
/** Card title */
|
|
16
|
+
title: string;
|
|
17
|
+
/** Additional custom fields */
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Represents a column in the Kanban board
|
|
22
|
+
*/
|
|
23
|
+
export interface KanbanColumn {
|
|
24
|
+
/** Unique identifier for the column */
|
|
25
|
+
id: Id;
|
|
26
|
+
/** Column title */
|
|
27
|
+
title: string;
|
|
28
|
+
/** Ordered list of card IDs in this column */
|
|
29
|
+
cardIds: Id[];
|
|
30
|
+
/** Additional custom fields */
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Normalized state structure for the Kanban board
|
|
35
|
+
* Separates column ordering from card data for efficient updates
|
|
36
|
+
*/
|
|
37
|
+
export interface KanbanBoardState {
|
|
38
|
+
/** Ordered list of columns */
|
|
39
|
+
columns: KanbanColumn[];
|
|
40
|
+
/** Flat lookup object for all cards (by ID) */
|
|
41
|
+
cards: Record<Id, KanbanCard>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Location within the Kanban board
|
|
45
|
+
*/
|
|
46
|
+
export interface DragLocation {
|
|
47
|
+
/** Column ID (undefined for column reorder operations) */
|
|
48
|
+
columnId?: Id;
|
|
49
|
+
/** Index within the column or board */
|
|
50
|
+
index: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Result of a drag-and-drop operation
|
|
54
|
+
* Similar to react-beautiful-dnd's DropResult for migration compatibility
|
|
55
|
+
*/
|
|
56
|
+
export interface DropResult {
|
|
57
|
+
/** Type of draggable that was moved */
|
|
58
|
+
type: 'CARD' | 'COLUMN';
|
|
59
|
+
/** Source location before drag */
|
|
60
|
+
source: DragLocation;
|
|
61
|
+
/** Destination location after drop (undefined if canceled) */
|
|
62
|
+
destination?: DragLocation;
|
|
63
|
+
/** ID of the dragged item */
|
|
64
|
+
draggableId: Id;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Props provided to render functions with drag-and-drop state and handlers
|
|
68
|
+
*/
|
|
69
|
+
export interface DragProvided {
|
|
70
|
+
/** Props to spread on the draggable element */
|
|
71
|
+
draggableProps: React.HTMLAttributes<HTMLElement> & {
|
|
72
|
+
'data-draggable-id': Id;
|
|
73
|
+
'data-draggable-type': 'CARD' | 'COLUMN';
|
|
74
|
+
};
|
|
75
|
+
/** Props to spread on the drag handle element (can be same as draggable) */
|
|
76
|
+
dragHandleProps: React.HTMLAttributes<HTMLElement> & {
|
|
77
|
+
tabIndex: number;
|
|
78
|
+
role: string;
|
|
79
|
+
'aria-roledescription': string;
|
|
80
|
+
};
|
|
81
|
+
/** Ref to attach to the draggable element */
|
|
82
|
+
innerRef: React.RefObject<HTMLElement>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Snapshot of current drag state
|
|
86
|
+
*/
|
|
87
|
+
export interface DragSnapshot {
|
|
88
|
+
/** Whether this element is currently being dragged */
|
|
89
|
+
isDragging: boolean;
|
|
90
|
+
/** Whether a drag is happening over this drop zone */
|
|
91
|
+
isDraggingOver?: boolean;
|
|
92
|
+
/** ID of the item being dragged (if any) */
|
|
93
|
+
draggingId?: Id;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Props for KanbanBoard component
|
|
97
|
+
*/
|
|
98
|
+
export interface KanbanBoardProps {
|
|
99
|
+
/** Current board state (controlled) */
|
|
100
|
+
state: KanbanBoardState;
|
|
101
|
+
/** Callback fired when drag ends */
|
|
102
|
+
onDragEnd: (result: DropResult, stateBefore: KanbanBoardState) => void;
|
|
103
|
+
/** Callback fired when drag starts (optional) */
|
|
104
|
+
onDragStart?: (draggable: {
|
|
105
|
+
id: Id;
|
|
106
|
+
type: 'CARD' | 'COLUMN';
|
|
107
|
+
}) => void;
|
|
108
|
+
/** Render function for a column */
|
|
109
|
+
renderColumn: (column: KanbanColumn, provided: DragProvided, snapshot: DragSnapshot) => React.ReactNode;
|
|
110
|
+
/** Render function for a card */
|
|
111
|
+
renderCard: (card: KanbanCard, provided: DragProvided, snapshot: DragSnapshot) => React.ReactNode;
|
|
112
|
+
/** Custom key extractor for cards (default: card.id) */
|
|
113
|
+
getCardKey?: (card: KanbanCard) => string;
|
|
114
|
+
/** Custom key extractor for columns (default: column.id) */
|
|
115
|
+
getColumnKey?: (column: KanbanColumn) => string;
|
|
116
|
+
/** Callback to determine if a drag should be disabled */
|
|
117
|
+
isDragDisabled?: (id: Id, type: 'CARD' | 'COLUMN') => boolean;
|
|
118
|
+
/** Class name for the board container */
|
|
119
|
+
className?: string;
|
|
120
|
+
/** Inline styles for the board container */
|
|
121
|
+
style?: React.CSSProperties;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Props for KanbanColumnView (headless)
|
|
125
|
+
*/
|
|
126
|
+
export interface KanbanColumnViewProps {
|
|
127
|
+
/** Column data */
|
|
128
|
+
column: KanbanColumn;
|
|
129
|
+
/** Card IDs in this column */
|
|
130
|
+
cardIds: Id[];
|
|
131
|
+
/** Render prop with provided props and snapshot */
|
|
132
|
+
children: (provided: DragProvided, snapshot: DragSnapshot) => React.ReactNode;
|
|
133
|
+
/** Whether column dragging is disabled */
|
|
134
|
+
isDragDisabled?: boolean;
|
|
135
|
+
/** Column index in the board */
|
|
136
|
+
index: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Props for KanbanCardView (headless)
|
|
140
|
+
*/
|
|
141
|
+
export interface KanbanCardViewProps {
|
|
142
|
+
/** Card data */
|
|
143
|
+
card: KanbanCard;
|
|
144
|
+
/** Render prop with provided props and snapshot */
|
|
145
|
+
children: (provided: DragProvided, snapshot: DragSnapshot) => React.ReactNode;
|
|
146
|
+
/** Whether card dragging is disabled */
|
|
147
|
+
isDragDisabled?: boolean;
|
|
148
|
+
/** Card index within its column */
|
|
149
|
+
index: number;
|
|
150
|
+
/** Parent column ID */
|
|
151
|
+
columnId: Id;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Internal drag state managed by useKanbanDnd
|
|
155
|
+
*/
|
|
156
|
+
export interface KanbanDragState {
|
|
157
|
+
/** Currently dragging item ID */
|
|
158
|
+
draggingId: Id | null;
|
|
159
|
+
/** Type of item being dragged */
|
|
160
|
+
draggingType: 'CARD' | 'COLUMN' | null;
|
|
161
|
+
/** Source location */
|
|
162
|
+
source: DragLocation | null;
|
|
163
|
+
/** Current destination (updates during drag) */
|
|
164
|
+
destination: DragLocation | null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Autoscroll configuration
|
|
168
|
+
*/
|
|
169
|
+
export interface AutoscrollConfig {
|
|
170
|
+
/** Distance from edge to trigger autoscroll (px) */
|
|
171
|
+
threshold: number;
|
|
172
|
+
/** Maximum scroll speed (px per frame) */
|
|
173
|
+
maxSpeed: number;
|
|
174
|
+
/** Whether autoscroll is enabled */
|
|
175
|
+
enabled: boolean;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Live region announcement
|
|
179
|
+
*/
|
|
180
|
+
export interface LiveAnnouncement {
|
|
181
|
+
/** Message to announce */
|
|
182
|
+
message: string;
|
|
183
|
+
/** Politeness level */
|
|
184
|
+
politeness: 'polite' | 'assertive';
|
|
185
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DnD Math Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for collision detection and positioning calculations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Calculate the distance from a point to a rectangle's edges
|
|
8
|
+
*/
|
|
9
|
+
export declare function distanceToEdge(point: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}, rect: DOMRect): {
|
|
13
|
+
top: number;
|
|
14
|
+
right: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
left: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Get the closest edge of a rectangle to a point
|
|
20
|
+
*/
|
|
21
|
+
export declare function getClosestEdge(point: {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
}, rect: DOMRect): 'top' | 'right' | 'bottom' | 'left';
|
|
25
|
+
/**
|
|
26
|
+
* Check if a point is within a threshold distance from an edge
|
|
27
|
+
*/
|
|
28
|
+
export declare function isNearEdge(point: {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
}, rect: DOMRect, threshold: number): {
|
|
32
|
+
edge: 'top' | 'right' | 'bottom' | 'left' | null;
|
|
33
|
+
distance: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Calculate scroll speed based on distance from edge
|
|
37
|
+
* Closer to edge = faster scroll
|
|
38
|
+
*/
|
|
39
|
+
export declare function calculateScrollSpeed(distance: number, threshold: number, maxSpeed: number): number;
|
|
40
|
+
/**
|
|
41
|
+
* Check if a point is inside a rectangle
|
|
42
|
+
*/
|
|
43
|
+
export declare function isPointInRect(point: {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
}, rect: DOMRect): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Calculate the center point of a rectangle
|
|
49
|
+
*/
|
|
50
|
+
export declare function getRectCenter(rect: DOMRect): {
|
|
51
|
+
x: number;
|
|
52
|
+
y: number;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Calculate vertical drop position based on mouse Y relative to element center
|
|
56
|
+
*/
|
|
57
|
+
export declare function getVerticalDropPosition(mouseY: number, elementRect: DOMRect): 'top' | 'bottom';
|
|
58
|
+
/**
|
|
59
|
+
* Calculate horizontal drop position based on mouse X relative to element center
|
|
60
|
+
*/
|
|
61
|
+
export declare function getHorizontalDropPosition(mouseX: number, elementRect: DOMRect): 'left' | 'right';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for DOM measurements and manipulation.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Safely get bounding client rect for an element
|
|
8
|
+
*/
|
|
9
|
+
export declare function getBoundingRect(element: HTMLElement | null): DOMRect | null;
|
|
10
|
+
/**
|
|
11
|
+
* Get scrollable parent of an element
|
|
12
|
+
*/
|
|
13
|
+
export declare function getScrollParent(element: HTMLElement | null): HTMLElement | null;
|
|
14
|
+
/**
|
|
15
|
+
* Scroll an element by a delta
|
|
16
|
+
*/
|
|
17
|
+
export declare function scrollBy(element: HTMLElement, delta: {
|
|
18
|
+
x?: number;
|
|
19
|
+
y?: number;
|
|
20
|
+
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* Check if an element is scrollable
|
|
23
|
+
*/
|
|
24
|
+
export declare function isScrollable(element: HTMLElement): {
|
|
25
|
+
vertical: boolean;
|
|
26
|
+
horizontal: boolean;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Get element's scroll position
|
|
30
|
+
*/
|
|
31
|
+
export declare function getScrollPosition(element: HTMLElement): {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Get maximum scroll for an element
|
|
37
|
+
*/
|
|
38
|
+
export declare function getMaxScroll(element: HTMLElement): {
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Clamp scroll position to valid range
|
|
44
|
+
*/
|
|
45
|
+
export declare function clampScroll(scroll: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
}, maxScroll: {
|
|
49
|
+
x: number;
|
|
50
|
+
y: number;
|
|
51
|
+
}): {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Get all data attributes from an element as an object
|
|
57
|
+
*/
|
|
58
|
+
export declare function getDataAttributes(element: HTMLElement): Record<string, string>;
|
|
59
|
+
/**
|
|
60
|
+
* Find closest ancestor with a data attribute
|
|
61
|
+
*/
|
|
62
|
+
export declare function findClosestWithData(element: HTMLElement | null, dataKey: string): HTMLElement | null;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kanban Reorder Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions for reordering cards and columns in a Kanban board.
|
|
5
|
+
*/
|
|
6
|
+
import type { KanbanBoardState, DragLocation } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Reorder an array by moving an item from one index to another
|
|
9
|
+
*/
|
|
10
|
+
export declare function reorderArray<T>(list: T[], startIndex: number, endIndex: number): T[];
|
|
11
|
+
/**
|
|
12
|
+
* Move a card within the same column
|
|
13
|
+
*/
|
|
14
|
+
export declare function reorderCardInColumn(state: KanbanBoardState, columnId: string, startIndex: number, endIndex: number): KanbanBoardState;
|
|
15
|
+
/**
|
|
16
|
+
* Move a card from one column to another
|
|
17
|
+
*/
|
|
18
|
+
export declare function moveCardBetweenColumns(state: KanbanBoardState, source: DragLocation, destination: DragLocation, cardId: string): KanbanBoardState;
|
|
19
|
+
/**
|
|
20
|
+
* Reorder columns
|
|
21
|
+
*/
|
|
22
|
+
export declare function reorderColumns(state: KanbanBoardState, startIndex: number, endIndex: number): KanbanBoardState;
|
|
23
|
+
/**
|
|
24
|
+
* Apply a drag result to the board state
|
|
25
|
+
* This is a convenience function that handles all reorder cases
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyDragResult(state: KanbanBoardState, result: {
|
|
28
|
+
type: 'CARD' | 'COLUMN';
|
|
29
|
+
source: DragLocation;
|
|
30
|
+
destination?: DragLocation;
|
|
31
|
+
draggableId: string;
|
|
32
|
+
}): KanbanBoardState;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-dragdrop-kit",
|
|
3
3
|
"author": "Souvik Sen <https://github.com/Yourstruggle11>",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "A flexible, lightweight React drag-and-drop kit for building sortable lists, grids, and boards with ease.",
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"description": "A flexible, lightweight React drag-and-drop kit for building sortable lists, grids, and kanban boards with ease.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
|
|
8
8
|
"main": "dist/index.js",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"types": "./dist/src/index.d.ts",
|
|
14
14
|
"import": "./dist/index.esm.js",
|
|
15
15
|
"require": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./kanban": {
|
|
18
|
+
"types": "./dist/src/kanban/index.d.ts",
|
|
19
|
+
"import": "./dist/kanban.esm.js",
|
|
20
|
+
"require": "./dist/kanban.js"
|
|
16
21
|
}
|
|
17
22
|
},
|
|
18
23
|
"files": ["dist", "README.md", "LICENSE"],
|