@rebasepro/ui 0.12.1-canary.gf5f1d39 → 0.13.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/dist/components/Button.d.ts +2 -1
- package/dist/components/Menubar.d.ts +9 -2
- package/dist/components/SearchBar.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTable.d.ts +12 -6
- package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/icons/GitHubIcon.d.ts +1 -1
- package/dist/icons/HandleIcon.d.ts +3 -1
- package/dist/index.css +29 -61
- package/dist/index.d.ts +2 -1
- package/dist/index.es.js +484 -298
- package/dist/index.es.js.map +1 -1
- package/dist/src/index.css +29 -61
- package/dist/src/theme.css +109 -0
- package/dist/styles.d.ts +28 -0
- package/dist/theme.css +109 -0
- package/dist/util/chip_colors.d.ts +29 -3
- package/dist/views/CollectionView/index.d.ts +0 -6
- package/dist/views/Kanban/BoardColumn.d.ts +1 -0
- package/dist/views/Kanban/BoardSortableList.d.ts +2 -1
- package/dist/views/Kanban/board_types.d.ts +10 -0
- package/dist/views/Kanban/placement.d.ts +26 -0
- package/dist/views/index.d.ts +0 -1
- package/package.json +31 -29
- package/src/components/BooleanSwitch.tsx +44 -17
- package/src/components/BooleanSwitchWithLabel.tsx +6 -6
- package/src/components/Button.tsx +9 -13
- package/src/components/Checkbox.tsx +16 -6
- package/src/components/Chip.tsx +5 -2
- package/src/components/ColorPicker.tsx +56 -43
- package/src/components/DateTimeField.tsx +18 -6
- package/src/components/DialogActions.tsx +3 -1
- package/src/components/IconButton.tsx +35 -6
- package/src/components/Menubar.tsx +19 -3
- package/src/components/MultiSelect.tsx +9 -5
- package/src/components/SearchBar.tsx +6 -4
- package/src/components/Select.tsx +6 -6
- package/src/components/TextField.tsx +17 -7
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +35 -341
- package/src/components/VirtualTable/VirtualTable.tsx +19 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +6 -1
- package/src/components/index.tsx +2 -1
- package/src/icons/GitHubIcon.tsx +11 -5
- package/src/icons/HandleIcon.tsx +15 -2
- package/src/index.css +29 -61
- package/src/index.ts +2 -1
- package/src/styles.ts +47 -0
- package/src/theme.css +109 -0
- package/src/util/chip_colors.ts +109 -67
- package/src/views/CollectionView/index.ts +6 -11
- package/src/views/Kanban/Board.tsx +80 -117
- package/src/views/Kanban/BoardColumn.tsx +3 -0
- package/src/views/Kanban/BoardSortableList.tsx +16 -4
- package/src/views/Kanban/board_types.ts +10 -0
- package/src/views/Kanban/placement.ts +62 -0
- package/src/views/index.ts +1 -1
- package/dist/views/TableView.d.ts +0 -6
- package/src/views/TableView.tsx +0 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import {
|
|
4
|
+
closestCorners,
|
|
4
5
|
DndContext,
|
|
5
6
|
DragEndEvent,
|
|
6
7
|
DragOverEvent,
|
|
@@ -12,7 +13,8 @@ import {
|
|
|
12
13
|
} from "@dnd-kit/core";
|
|
13
14
|
import { arrayMove, SortableContext } from "@dnd-kit/sortable";
|
|
14
15
|
import { BoardColumn } from "./BoardColumn";
|
|
15
|
-
import {
|
|
16
|
+
import { placeDroppedCard } from "./placement";
|
|
17
|
+
import { BoardItem, BoardItemMap, BoardProps } from "./board_types";
|
|
16
18
|
import { cls } from "../../util";
|
|
17
19
|
|
|
18
20
|
export function Board<T, COLUMN extends string>({
|
|
@@ -33,26 +35,21 @@ export function Board<T, COLUMN extends string>({
|
|
|
33
35
|
}: BoardProps<T, COLUMN>) {
|
|
34
36
|
|
|
35
37
|
const [activeItem, setActiveItem] = useState<BoardItem<T> | null>(null);
|
|
36
|
-
const [activeColumn, setActiveColumn] = useState<COLUMN | null>(null);
|
|
37
38
|
const [isDragging, setIsDragging] = useState(false);
|
|
38
39
|
const [dragOverColumnId, setDragOverColumnId] = useState<string | null>(null);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
window.removeEventListener("mousemove", handleMouseMove);
|
|
53
|
-
window.removeEventListener("pointermove", handleMouseMove);
|
|
54
|
-
};
|
|
55
|
-
}, [handleMouseMove]);
|
|
41
|
+
/**
|
|
42
|
+
* Which column the card was picked up from.
|
|
43
|
+
*
|
|
44
|
+
* `handleDragOver` moves the card between columns in `itemMapState` while
|
|
45
|
+
* the pointer is still down — that is what makes the gap open up under the
|
|
46
|
+
* cursor. So by the time the drop is handled, looking the card up by id
|
|
47
|
+
* finds it in its *destination*, and the board was reporting the move as
|
|
48
|
+
* source === target: a cross-column drop looked like a plain reorder and
|
|
49
|
+
* the column property was never written. The card snapped back on the next
|
|
50
|
+
* fetch.
|
|
51
|
+
*/
|
|
52
|
+
const dragSourceColumnRef = useRef<COLUMN | null>(null);
|
|
56
53
|
|
|
57
54
|
const [itemMapState, setItemMapState] = useState<BoardItemMap<T>>(() => {
|
|
58
55
|
const dataColumnMap: Record<string, COLUMN> = data.reduce((prev, item: BoardItem<T>) => ({
|
|
@@ -146,31 +143,9 @@ export function Board<T, COLUMN extends string>({
|
|
|
146
143
|
setDragOverColumnId(null);
|
|
147
144
|
const { active } = event;
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
if (activatorEvt) {
|
|
151
|
-
const target = activatorEvt.target as HTMLElement;
|
|
152
|
-
const draggableEl = target.closest<HTMLElement>("[role='button']") ?? target;
|
|
153
|
-
const rect = draggableEl.getBoundingClientRect();
|
|
154
|
-
grabOffsetRef.current = {
|
|
155
|
-
x: activatorEvt.clientX - rect.left,
|
|
156
|
-
y: activatorEvt.clientY - rect.top
|
|
157
|
-
};
|
|
158
|
-
setOverlayPos({
|
|
159
|
-
x: rect.left,
|
|
160
|
-
y: rect.top
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
window.addEventListener("mousemove", handleMouseMove);
|
|
164
|
-
window.addEventListener("pointermove", handleMouseMove);
|
|
165
|
-
|
|
166
|
-
if (active.data.current?.type === "COLUMN") {
|
|
167
|
-
const columnId = active.id as string;
|
|
168
|
-
const column = columnsProp.find(col => String(col) === columnId);
|
|
169
|
-
if (column) {
|
|
170
|
-
setActiveColumn(column);
|
|
171
|
-
}
|
|
172
|
-
} else if (active.data.current?.type === "ITEM") {
|
|
146
|
+
if (active.data.current?.type === "ITEM") {
|
|
173
147
|
const columnId = findColumnByItemId(active.id as string);
|
|
148
|
+
dragSourceColumnRef.current = (columnId as COLUMN | undefined) ?? null;
|
|
174
149
|
if (columnId) {
|
|
175
150
|
const item = itemMapState[columnId]?.find(i => i.id === active.id);
|
|
176
151
|
setActiveItem(item || null);
|
|
@@ -208,7 +183,7 @@ export function Board<T, COLUMN extends string>({
|
|
|
208
183
|
}
|
|
209
184
|
|
|
210
185
|
const activeId = active.id as string;
|
|
211
|
-
const
|
|
186
|
+
const activeItemColumn = findColumnByItemId(activeId);
|
|
212
187
|
let overColumnForMove = findColumnByItemId(overId);
|
|
213
188
|
|
|
214
189
|
if (!overColumnForMove && overDataType === "ITEM-LIST") {
|
|
@@ -218,15 +193,15 @@ export function Board<T, COLUMN extends string>({
|
|
|
218
193
|
overColumnForMove = overId;
|
|
219
194
|
}
|
|
220
195
|
|
|
221
|
-
if (!
|
|
222
|
-
if (
|
|
196
|
+
if (!activeItemColumn || !overColumnForMove) return;
|
|
197
|
+
if (activeItemColumn === overColumnForMove) return;
|
|
223
198
|
|
|
224
199
|
if (itemMapState[overColumnForMove]?.some(i => i.id === activeId)) {
|
|
225
200
|
return;
|
|
226
201
|
}
|
|
227
202
|
|
|
228
203
|
setItemMapState(currentMap => {
|
|
229
|
-
const activeItems = [...(currentMap[
|
|
204
|
+
const activeItems = [...(currentMap[activeItemColumn] || [])];
|
|
230
205
|
const overItems = [...(currentMap[overColumnForMove!] || [])];
|
|
231
206
|
const activeIndex = activeItems.findIndex(i => i.id === activeId);
|
|
232
207
|
|
|
@@ -258,26 +233,34 @@ export function Board<T, COLUMN extends string>({
|
|
|
258
233
|
const newItemMap = { ...currentMap };
|
|
259
234
|
const [moved] = activeItems.splice(activeIndex, 1);
|
|
260
235
|
overItems.splice(overIndex, 0, moved);
|
|
261
|
-
newItemMap[
|
|
236
|
+
newItemMap[activeItemColumn] = activeItems;
|
|
262
237
|
newItemMap[overColumnForMove!] = overItems;
|
|
263
238
|
return newItemMap;
|
|
264
239
|
});
|
|
265
240
|
};
|
|
266
241
|
|
|
242
|
+
// Escape, or a drop outside the board. `handleDragOver` may already have
|
|
243
|
+
// moved the card between columns, so the next `data` effect has to be
|
|
244
|
+
// allowed to put it back.
|
|
245
|
+
const handleDragCancel = () => {
|
|
246
|
+
setIsDragging(false);
|
|
247
|
+
setActiveItem(null);
|
|
248
|
+
setDragOverColumnId(null);
|
|
249
|
+
dragSourceColumnRef.current = null;
|
|
250
|
+
};
|
|
251
|
+
|
|
267
252
|
const handleDragEnd = (event: DragEndEvent) => {
|
|
268
253
|
const {
|
|
269
254
|
active,
|
|
270
255
|
over
|
|
271
256
|
} = event;
|
|
272
257
|
|
|
273
|
-
|
|
274
|
-
window.removeEventListener("pointermove", handleMouseMove);
|
|
275
|
-
setOverlayPos(null);
|
|
258
|
+
const sourceColumn = dragSourceColumnRef.current;
|
|
276
259
|
|
|
277
260
|
setIsDragging(false);
|
|
278
261
|
setActiveItem(null);
|
|
279
|
-
setActiveColumn(null);
|
|
280
262
|
setDragOverColumnId(null);
|
|
263
|
+
dragSourceColumnRef.current = null;
|
|
281
264
|
|
|
282
265
|
if (!over) return;
|
|
283
266
|
|
|
@@ -296,76 +279,56 @@ export function Board<T, COLUMN extends string>({
|
|
|
296
279
|
return;
|
|
297
280
|
}
|
|
298
281
|
|
|
299
|
-
|
|
300
|
-
|
|
282
|
+
// Where the card sits right now — `handleDragOver` has already put it in
|
|
283
|
+
// the column being hovered, so this is the destination, not the origin.
|
|
284
|
+
const currentCol = findColumnByItemId(activeId) as COLUMN | undefined;
|
|
285
|
+
let targetCol = findColumnByItemId(overId) as COLUMN | undefined;
|
|
301
286
|
|
|
302
|
-
if (!
|
|
287
|
+
if (!targetCol) {
|
|
303
288
|
const overDataType = over.data.current?.type;
|
|
304
289
|
if (overDataType === "ITEM-LIST" || columnsProp.includes(overId as COLUMN)) {
|
|
305
|
-
|
|
290
|
+
targetCol = overId as COLUMN;
|
|
306
291
|
}
|
|
307
292
|
}
|
|
293
|
+
targetCol = targetCol ?? currentCol;
|
|
308
294
|
|
|
309
|
-
if (!
|
|
295
|
+
if (!currentCol || !targetCol) return;
|
|
310
296
|
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
297
|
+
const targetItems = itemMapState[targetCol] || [];
|
|
298
|
+
const movedItem = (itemMapState[currentCol] || []).find(i => i.id === activeId);
|
|
299
|
+
if (!movedItem) return;
|
|
314
300
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
} else {
|
|
321
|
-
overIndex = overItems.findIndex(i => i.id === overId);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (activeIndex === -1 || overIndex === -1) return;
|
|
325
|
-
|
|
326
|
-
let finalItems: BoardItem<T>[] = [];
|
|
327
|
-
|
|
328
|
-
if (isSameColumn) {
|
|
329
|
-
finalItems = arrayMove(activeItems, activeIndex, overIndex);
|
|
330
|
-
} else {
|
|
331
|
-
const newActiveItems = [...activeItems];
|
|
332
|
-
const newOverItems = [...overItems];
|
|
333
|
-
const [moved] = newActiveItems.splice(activeIndex, 1);
|
|
334
|
-
newOverItems.splice(overIndex, 0, moved);
|
|
335
|
-
finalItems = [...newActiveItems, ...newOverItems];
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const fullFlattenedList: BoardItem<T>[] = [];
|
|
339
|
-
columnsProp.forEach(col => {
|
|
340
|
-
if (col === activeCol) {
|
|
341
|
-
if (isSameColumn) {
|
|
342
|
-
fullFlattenedList.push(...finalItems);
|
|
343
|
-
} else {
|
|
344
|
-
fullFlattenedList.push(...activeItems.filter(i => i.id !== activeId));
|
|
345
|
-
}
|
|
346
|
-
} else if (col === overCol) {
|
|
347
|
-
const newOverItems = [...overItems];
|
|
348
|
-
const [moved] = [...activeItems].splice(activeIndex, 1);
|
|
349
|
-
newOverItems.splice(overIndex, 0, moved);
|
|
350
|
-
fullFlattenedList.push(...newOverItems);
|
|
351
|
-
} else {
|
|
352
|
-
fullFlattenedList.push(...(itemMapState[col] || []));
|
|
353
|
-
}
|
|
301
|
+
const finalTargetItems = placeDroppedCard({
|
|
302
|
+
targetItems,
|
|
303
|
+
movedItem,
|
|
304
|
+
overId,
|
|
305
|
+
changedColumn: (sourceColumn ?? currentCol) !== targetCol
|
|
354
306
|
});
|
|
355
307
|
|
|
356
|
-
onItemsReorder?.(
|
|
308
|
+
onItemsReorder?.(finalTargetItems, {
|
|
357
309
|
itemId: activeId,
|
|
358
|
-
|
|
359
|
-
|
|
310
|
+
// The column the pointer went down in. `currentCol` would report a
|
|
311
|
+
// cross-column move as a same-column reorder.
|
|
312
|
+
sourceColumn: (sourceColumn ?? currentCol),
|
|
313
|
+
targetColumn: targetCol
|
|
360
314
|
});
|
|
361
315
|
};
|
|
362
316
|
|
|
363
317
|
return (
|
|
364
318
|
<DndContext
|
|
365
319
|
sensors={sensors}
|
|
320
|
+
// `rectIntersection`, the default, only reports a target while the
|
|
321
|
+
// dragged rect physically overlaps it, and prefers whichever
|
|
322
|
+
// overlap is largest — on a board of tall columns that means a card
|
|
323
|
+
// held over a gap reports nothing, and one held near a column edge
|
|
324
|
+
// reports the neighbour. `closestCorners` is the sortable-list
|
|
325
|
+
// recommendation and is what makes the drop land where it looks
|
|
326
|
+
// like it will.
|
|
327
|
+
collisionDetection={closestCorners}
|
|
366
328
|
onDragStart={handleDragStart}
|
|
367
329
|
onDragOver={handleDragOver}
|
|
368
330
|
onDragEnd={handleDragEnd}
|
|
331
|
+
onDragCancel={handleDragCancel}
|
|
369
332
|
>
|
|
370
333
|
<div className={cls("flex flex-row h-full w-full overflow-x-auto p-4 select-none items-start", className)}>
|
|
371
334
|
<SortableContext items={columnsProp}>
|
|
@@ -385,6 +348,7 @@ export function Board<T, COLUMN extends string>({
|
|
|
385
348
|
allowReorder={allowColumnReorder}
|
|
386
349
|
loading={loadingState?.loading}
|
|
387
350
|
hasMore={loadingState?.hasMore}
|
|
351
|
+
error={loadingState?.error}
|
|
388
352
|
totalCount={loadingState?.totalCount}
|
|
389
353
|
color={columnColors?.[col]}
|
|
390
354
|
onLoadMore={onLoadMoreColumn ? () => onLoadMoreColumn(col) : undefined}
|
|
@@ -396,22 +360,21 @@ export function Board<T, COLUMN extends string>({
|
|
|
396
360
|
{AddColumnComponent}
|
|
397
361
|
</div>
|
|
398
362
|
|
|
363
|
+
{/* The overlay used to be positioned by hand, from `mousemove`
|
|
364
|
+
listeners on `window` and a grab offset measured off whatever
|
|
365
|
+
`[role='button']` the pointer went down on. dnd-kit already
|
|
366
|
+
tracks the pointer and already knows the grab offset — the
|
|
367
|
+
hand-rolled copy drifted from the position collision detection
|
|
368
|
+
was actually using, so the card you saw and the slot you got
|
|
369
|
+
were two different things. */}
|
|
399
370
|
{typeof document !== "undefined" && createPortal(
|
|
400
|
-
<DragOverlay dropAnimation={null}>
|
|
401
|
-
{
|
|
402
|
-
<
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
zIndex: 9999
|
|
408
|
-
}}>
|
|
409
|
-
<ItemComponent
|
|
410
|
-
item={activeItem}
|
|
411
|
-
isDragging={true}
|
|
412
|
-
isClone={true}
|
|
413
|
-
/>
|
|
414
|
-
</div>
|
|
371
|
+
<DragOverlay dropAnimation={null} zIndex={9999}>
|
|
372
|
+
{activeItem && (
|
|
373
|
+
<ItemComponent
|
|
374
|
+
item={activeItem}
|
|
375
|
+
isDragging={true}
|
|
376
|
+
isClone={true}
|
|
377
|
+
/>
|
|
415
378
|
)}
|
|
416
379
|
</DragOverlay>,
|
|
417
380
|
document.body
|
|
@@ -21,6 +21,7 @@ export interface BoardColumnProps<T> {
|
|
|
21
21
|
allowReorder?: boolean;
|
|
22
22
|
loading?: boolean;
|
|
23
23
|
hasMore?: boolean;
|
|
24
|
+
error?: Error;
|
|
24
25
|
onLoadMore?: () => void;
|
|
25
26
|
onAddItem?: () => void;
|
|
26
27
|
totalCount?: number;
|
|
@@ -38,6 +39,7 @@ export const BoardColumn = memo(function BoardColumn<T>({
|
|
|
38
39
|
allowReorder = false,
|
|
39
40
|
loading = false,
|
|
40
41
|
hasMore = false,
|
|
42
|
+
error,
|
|
41
43
|
onLoadMore,
|
|
42
44
|
onAddItem,
|
|
43
45
|
totalCount,
|
|
@@ -127,6 +129,7 @@ export const BoardColumn = memo(function BoardColumn<T>({
|
|
|
127
129
|
isDragOverColumn={isDragOverColumn}
|
|
128
130
|
loading={loading}
|
|
129
131
|
hasMore={hasMore}
|
|
132
|
+
error={error}
|
|
130
133
|
onLoadMore={onLoadMore}
|
|
131
134
|
/>
|
|
132
135
|
</SortableContext>
|
|
@@ -14,6 +14,7 @@ interface BoardSortableListProps<T> {
|
|
|
14
14
|
isDragOverColumn: boolean;
|
|
15
15
|
loading?: boolean;
|
|
16
16
|
hasMore?: boolean;
|
|
17
|
+
error?: Error;
|
|
17
18
|
onLoadMore?: () => void;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -25,6 +26,7 @@ export function BoardSortableList<T>({
|
|
|
25
26
|
isDragOverColumn,
|
|
26
27
|
loading = false,
|
|
27
28
|
hasMore = false,
|
|
29
|
+
error,
|
|
28
30
|
onLoadMore
|
|
29
31
|
}: BoardSortableListProps<T>) {
|
|
30
32
|
const {
|
|
@@ -76,7 +78,10 @@ export function BoardSortableList<T>({
|
|
|
76
78
|
}, [hasMore, onLoadMore]);
|
|
77
79
|
|
|
78
80
|
const containerClassName = useMemo(() => cls(
|
|
79
|
-
|
|
81
|
+
// The scrollbar stays: this is the only thing on the page that scrolls
|
|
82
|
+
// vertically, and hiding it left a column of cards with no sign that
|
|
83
|
+
// there were more below the fold.
|
|
84
|
+
"flex flex-col p-2 transition-opacity duration-100 transition-bg ease-linear w-full overflow-y-auto flex-1 min-h-0 rounded-md",
|
|
80
85
|
isDragging && isDragOverColumn
|
|
81
86
|
? "bg-surface-accent-200 dark:bg-surface-900"
|
|
82
87
|
: isDragging
|
|
@@ -95,9 +100,16 @@ export function BoardSortableList<T>({
|
|
|
95
100
|
strategy={verticalListSortingStrategy}
|
|
96
101
|
>
|
|
97
102
|
{items.length === 0 && !loading ? (
|
|
98
|
-
<div className="flex-1 flex items-center justify-center">
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
<div className="flex-1 flex items-center justify-center px-3 text-center">
|
|
104
|
+
{/* "No items" over a header counting eleven of them is
|
|
105
|
+
the wrong thing to say when the read failed. */}
|
|
106
|
+
<span className={cls(
|
|
107
|
+
"text-xs",
|
|
108
|
+
error
|
|
109
|
+
? "text-red-600 dark:text-red-400"
|
|
110
|
+
: "text-surface-400 dark:text-surface-500"
|
|
111
|
+
)}>
|
|
112
|
+
{error ? "Could not load this column" : "No items"}
|
|
101
113
|
</span>
|
|
102
114
|
</div>
|
|
103
115
|
) : (
|
|
@@ -39,6 +39,8 @@ export interface ColumnLoadingState {
|
|
|
39
39
|
hasMore: boolean;
|
|
40
40
|
itemCount: number;
|
|
41
41
|
totalCount?: number;
|
|
42
|
+
/** Set when the column could not be read at all. */
|
|
43
|
+
error?: Error;
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -54,6 +56,14 @@ export interface BoardProps<T, COLUMN extends string> {
|
|
|
54
56
|
assignColumn: (item: BoardItem<T>) => COLUMN;
|
|
55
57
|
allowColumnReorder?: boolean;
|
|
56
58
|
onColumnReorder?: (columns: COLUMN[]) => void;
|
|
59
|
+
/**
|
|
60
|
+
* A card was dropped.
|
|
61
|
+
*
|
|
62
|
+
* `items` is the target column's contents in their new order — the moved
|
|
63
|
+
* card included — so the neighbours of the moved card are the cards it
|
|
64
|
+
* actually landed between. `sourceColumn` is where the card was picked up,
|
|
65
|
+
* which is not where it now sits whenever the two differ.
|
|
66
|
+
*/
|
|
57
67
|
onItemsReorder?: (
|
|
58
68
|
items: BoardItem<T>[],
|
|
59
69
|
moveInfo?: {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { arrayMove } from "@dnd-kit/sortable";
|
|
2
|
+
import { BoardItem } from "./board_types";
|
|
3
|
+
|
|
4
|
+
export interface PlaceDroppedCardParams<T> {
|
|
5
|
+
/**
|
|
6
|
+
* The destination column as it stands at the moment of the drop. When the
|
|
7
|
+
* card came from another column it is normally already in here:
|
|
8
|
+
* `handleDragOver` moves it while the pointer is still down, which is what
|
|
9
|
+
* opens the gap under the cursor.
|
|
10
|
+
*/
|
|
11
|
+
targetItems: BoardItem<T>[];
|
|
12
|
+
/** The card being dropped. */
|
|
13
|
+
movedItem: BoardItem<T>;
|
|
14
|
+
/** What the pointer was over — a card id, or the column's own droppable id. */
|
|
15
|
+
overId: string;
|
|
16
|
+
/** Whether the card was picked up in a different column than it landed in. */
|
|
17
|
+
changedColumn: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Where a dropped card ends up in its destination column.
|
|
22
|
+
*
|
|
23
|
+
* Pulled out of the drop handler because it is the part that was wrong twice:
|
|
24
|
+
* once appending a card that had been dropped into the middle of a column, and
|
|
25
|
+
* once refusing to place a card at all when it was released over an empty
|
|
26
|
+
* column rather than over another card. It is pure, so it can be checked
|
|
27
|
+
* without driving a pointer.
|
|
28
|
+
*/
|
|
29
|
+
export function placeDroppedCard<T>({
|
|
30
|
+
targetItems,
|
|
31
|
+
movedItem,
|
|
32
|
+
overId,
|
|
33
|
+
changedColumn
|
|
34
|
+
}: PlaceDroppedCardParams<T>): BoardItem<T>[] {
|
|
35
|
+
|
|
36
|
+
const indexInTarget = targetItems.findIndex(i => i.id === movedItem.id);
|
|
37
|
+
const overIndex = targetItems.findIndex(i => i.id === overId);
|
|
38
|
+
|
|
39
|
+
// Not placed during the drag — drop straight into the slot. `-1` means the
|
|
40
|
+
// pointer was over the column itself (an empty one, or the space below the
|
|
41
|
+
// last card), which appends.
|
|
42
|
+
if (indexInTarget === -1) {
|
|
43
|
+
const placed = [...targetItems];
|
|
44
|
+
placed.splice(overIndex === -1 ? targetItems.length : overIndex, 0, movedItem);
|
|
45
|
+
return placed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Released over another card: that card's slot is the one the user chose.
|
|
49
|
+
if (overIndex !== -1) {
|
|
50
|
+
return arrayMove(targetItems, indexInTarget, overIndex);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Released over the column rather than a card. The card is already sitting
|
|
54
|
+
// in the gap the drag opened, so its position is the one on screen —
|
|
55
|
+
// appending here is what used to send it to the bottom.
|
|
56
|
+
if (changedColumn) {
|
|
57
|
+
return targetItems;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Same column, released below the last card.
|
|
61
|
+
return arrayMove(targetItems, indexInTarget, targetItems.length - 1);
|
|
62
|
+
}
|
package/src/views/index.ts
CHANGED
package/src/views/TableView.tsx
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { VirtualTable as TableView } from "../components/VirtualTable/VirtualTable";
|
|
2
|
-
export type { VirtualTableProps as TableViewProps } from "../components/VirtualTable/VirtualTableProps";
|
|
3
|
-
|
|
4
|
-
export type OnColumnResizeParams = {
|
|
5
|
-
width: number;
|
|
6
|
-
key: string;
|
|
7
|
-
};
|