@joewinke/jatui 0.1.29 → 0.1.30
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/dnd/DropIndicator.svelte +119 -0
- package/dist/dnd/DropIndicator.svelte.d.ts +16 -0
- package/dist/dnd/announce.d.ts +1 -0
- package/dist/dnd/announce.js +1 -0
- package/dist/dnd/autoScroll.d.ts +7 -0
- package/dist/dnd/autoScroll.js +25 -0
- package/dist/dnd/createDndBoard.svelte.d.ts +43 -0
- package/dist/dnd/createDndBoard.svelte.js +183 -0
- package/dist/dnd/createDndBoard.test.d.ts +1 -0
- package/dist/dnd/createDndBoard.test.js +185 -0
- package/dist/dnd/draggable.d.ts +13 -0
- package/dist/dnd/draggable.js +42 -0
- package/dist/dnd/dropTarget.d.ts +14 -0
- package/dist/dnd/dropTarget.js +31 -0
- package/dist/dnd/index.d.ts +13 -0
- package/dist/dnd/index.js +9 -0
- package/dist/dnd/types.d.ts +41 -0
- package/dist/dnd/types.js +14 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/package.json +22 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Drop-edge indicator — Svelte port of pdnd's react-drop-indicator Line.
|
|
4
|
+
* Render conditionally: {#if closestEdge}<DropIndicator edge={closestEdge} gap="0.5rem" />{/if}
|
|
5
|
+
* Host item wrapper MUST be `position: relative`.
|
|
6
|
+
*/
|
|
7
|
+
import type { Edge } from './types';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
edge,
|
|
11
|
+
gap = '0px',
|
|
12
|
+
indent = '0px',
|
|
13
|
+
type = 'terminal',
|
|
14
|
+
appearance = 'default',
|
|
15
|
+
}: {
|
|
16
|
+
edge: Edge;
|
|
17
|
+
gap?: string;
|
|
18
|
+
indent?: string;
|
|
19
|
+
type?: 'terminal' | 'no-terminal' | 'terminal-no-bleed';
|
|
20
|
+
appearance?: 'default' | 'warning';
|
|
21
|
+
} = $props();
|
|
22
|
+
|
|
23
|
+
const orientation = $derived(edge === 'top' || edge === 'bottom' ? 'horizontal' : 'vertical');
|
|
24
|
+
|
|
25
|
+
const lineMainAxisStart = $derived(
|
|
26
|
+
type === 'terminal'
|
|
27
|
+
? `calc(var(--terminal-radius) + ${indent})`
|
|
28
|
+
: type === 'terminal-no-bleed'
|
|
29
|
+
? `calc(var(--terminal-diameter) + ${indent})`
|
|
30
|
+
: indent,
|
|
31
|
+
);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
class="drop-indicator {orientation} edge-{edge}"
|
|
36
|
+
style:--stroke-color={appearance === 'warning' ? 'var(--color-warning)' : 'var(--color-primary)'}
|
|
37
|
+
style:--main-axis-offset="calc(-0.5 * ({gap} + var(--stroke-width)))"
|
|
38
|
+
style:--line-main-axis-start={lineMainAxisStart}
|
|
39
|
+
style:--terminal-display={type === 'no-terminal' ? 'none' : 'block'}
|
|
40
|
+
></div>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.drop-indicator {
|
|
44
|
+
--stroke-width: 2px;
|
|
45
|
+
--terminal-diameter: calc(var(--stroke-width) * 4);
|
|
46
|
+
--terminal-radius: calc(var(--terminal-diameter) / 2);
|
|
47
|
+
--terminal-main-axis-start: calc(-1 * var(--terminal-diameter));
|
|
48
|
+
--terminal-cross-axis-offset: calc((var(--stroke-width) - var(--terminal-diameter)) / 2);
|
|
49
|
+
|
|
50
|
+
display: block;
|
|
51
|
+
position: absolute;
|
|
52
|
+
z-index: 1;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
background: var(--stroke-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.drop-indicator::before {
|
|
58
|
+
display: var(--terminal-display);
|
|
59
|
+
content: '';
|
|
60
|
+
position: absolute;
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
width: var(--terminal-diameter);
|
|
63
|
+
height: var(--terminal-diameter);
|
|
64
|
+
border: var(--stroke-width) solid var(--stroke-color);
|
|
65
|
+
border-radius: 50%;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.horizontal {
|
|
69
|
+
height: var(--stroke-width);
|
|
70
|
+
inset-inline-start: var(--line-main-axis-start);
|
|
71
|
+
inset-inline-end: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.horizontal::before {
|
|
75
|
+
inset-inline-start: var(--terminal-main-axis-start);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.vertical {
|
|
79
|
+
width: var(--stroke-width);
|
|
80
|
+
top: var(--line-main-axis-start);
|
|
81
|
+
bottom: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.vertical::before {
|
|
85
|
+
top: var(--terminal-main-axis-start);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.edge-top {
|
|
89
|
+
top: var(--main-axis-offset);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.edge-top::before {
|
|
93
|
+
top: var(--terminal-cross-axis-offset);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.edge-bottom {
|
|
97
|
+
bottom: var(--main-axis-offset);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.edge-bottom::before {
|
|
101
|
+
bottom: var(--terminal-cross-axis-offset);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.edge-left {
|
|
105
|
+
left: var(--main-axis-offset);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.edge-left::before {
|
|
109
|
+
left: var(--terminal-cross-axis-offset);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.edge-right {
|
|
113
|
+
right: var(--main-axis-offset);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.edge-right::before {
|
|
117
|
+
right: var(--terminal-cross-axis-offset);
|
|
118
|
+
}
|
|
119
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drop-edge indicator — Svelte port of pdnd's react-drop-indicator Line.
|
|
3
|
+
* Render conditionally: {#if closestEdge}<DropIndicator edge={closestEdge} gap="0.5rem" />{/if}
|
|
4
|
+
* Host item wrapper MUST be `position: relative`.
|
|
5
|
+
*/
|
|
6
|
+
import type { Edge } from './types';
|
|
7
|
+
type $$ComponentProps = {
|
|
8
|
+
edge: Edge;
|
|
9
|
+
gap?: string;
|
|
10
|
+
indent?: string;
|
|
11
|
+
type?: 'terminal' | 'no-terminal' | 'terminal-no-bleed';
|
|
12
|
+
appearance?: 'default' | 'warning';
|
|
13
|
+
};
|
|
14
|
+
declare const DropIndicator: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type DropIndicator = ReturnType<typeof DropIndicator>;
|
|
16
|
+
export default DropIndicator;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { announce, cleanup as cleanupAnnouncements } from '@atlaskit/pragmatic-drag-and-drop-live-region';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { announce, cleanup as cleanupAnnouncements } from '@atlaskit/pragmatic-drag-and-drop-live-region';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element';
|
|
2
|
+
export const autoScroll = (node, params) => {
|
|
3
|
+
let current = params ?? {};
|
|
4
|
+
function register() {
|
|
5
|
+
return autoScrollForElements({
|
|
6
|
+
element: node,
|
|
7
|
+
canScroll: ({ source }) => {
|
|
8
|
+
if (!current.types?.length)
|
|
9
|
+
return true;
|
|
10
|
+
return current.types.includes(source.data.type);
|
|
11
|
+
},
|
|
12
|
+
getAllowedAxis: () => current.axis ?? 'all',
|
|
13
|
+
getConfiguration: () => ({ maxScrollSpeed: current.speed ?? 'standard' }),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
let cleanup = register();
|
|
17
|
+
return {
|
|
18
|
+
update(next) {
|
|
19
|
+
current = next ?? {};
|
|
20
|
+
cleanup();
|
|
21
|
+
cleanup = register();
|
|
22
|
+
},
|
|
23
|
+
destroy: () => cleanup(),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
2
|
+
import { type ItemDragData, type ContainerDropData } from './types';
|
|
3
|
+
import type { DraggableParams } from './draggable';
|
|
4
|
+
import type { DropTargetParams } from './dropTarget';
|
|
5
|
+
export interface BoardMove<TItem> {
|
|
6
|
+
item: TItem;
|
|
7
|
+
from: {
|
|
8
|
+
container: string;
|
|
9
|
+
index: number;
|
|
10
|
+
};
|
|
11
|
+
to: {
|
|
12
|
+
container: string;
|
|
13
|
+
index: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface DndBoardOptions<TItem> {
|
|
17
|
+
type: string;
|
|
18
|
+
items: () => ReadonlyMap<string, readonly TItem[]> | Record<string, readonly TItem[]>;
|
|
19
|
+
key: (item: TItem) => string;
|
|
20
|
+
commit: (move: BoardMove<TItem>) => Promise<void>;
|
|
21
|
+
onError?: (error: unknown, move: BoardMove<TItem>) => void;
|
|
22
|
+
announce?: (move: BoardMove<TItem>) => string;
|
|
23
|
+
hover?: 'indicator' | 'shadow';
|
|
24
|
+
edges?: Edge[];
|
|
25
|
+
}
|
|
26
|
+
export declare function createDndBoard<TItem>(opts: DndBoardOptions<TItem>): {
|
|
27
|
+
get: (container: string) => readonly TItem[];
|
|
28
|
+
containers: () => string[];
|
|
29
|
+
readonly dragging: {
|
|
30
|
+
key: string;
|
|
31
|
+
item: TItem;
|
|
32
|
+
from: BoardMove<TItem>["from"];
|
|
33
|
+
} | null;
|
|
34
|
+
readonly proposal: {
|
|
35
|
+
container: string;
|
|
36
|
+
index: number;
|
|
37
|
+
} | null;
|
|
38
|
+
isSource: (item: TItem) => boolean;
|
|
39
|
+
isShadow: (item: TItem) => boolean;
|
|
40
|
+
item: (item: TItem, container: string, index: number) => DraggableParams<ItemDragData<TItem>>;
|
|
41
|
+
itemTarget: (container: string, index: number) => DropTargetParams<ContainerDropData>;
|
|
42
|
+
containerTarget: (container: string) => DropTargetParams<ContainerDropData>;
|
|
43
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
2
|
+
import { extractClosestEdge, attachClosestEdge, } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
3
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
4
|
+
import { announce } from './announce';
|
|
5
|
+
import { makeItemData, isItemData, } from './types';
|
|
6
|
+
const SHADOW_KEY = '__dnd-shadow__';
|
|
7
|
+
export function createDndBoard(opts) {
|
|
8
|
+
const edges = opts.edges ?? ['top', 'bottom'];
|
|
9
|
+
const hover = opts.hover ?? 'indicator';
|
|
10
|
+
const mirror = new SvelteMap();
|
|
11
|
+
let dragging = $state(null);
|
|
12
|
+
let proposal = $state(null);
|
|
13
|
+
const seq = new Map();
|
|
14
|
+
let inFlight = 0;
|
|
15
|
+
let seedQueued = false;
|
|
16
|
+
function seed() {
|
|
17
|
+
mirror.clear();
|
|
18
|
+
const src = opts.items();
|
|
19
|
+
const entries = src instanceof Map
|
|
20
|
+
? src
|
|
21
|
+
: new Map(Object.entries(src));
|
|
22
|
+
for (const [id, list] of entries)
|
|
23
|
+
mirror.set(id, [...list]);
|
|
24
|
+
}
|
|
25
|
+
$effect(() => {
|
|
26
|
+
opts.items();
|
|
27
|
+
if (dragging || inFlight > 0) {
|
|
28
|
+
seedQueued = true;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
seed();
|
|
32
|
+
});
|
|
33
|
+
function placementFrom(dropTargets) {
|
|
34
|
+
const inner = dropTargets[0];
|
|
35
|
+
if (!inner)
|
|
36
|
+
return null;
|
|
37
|
+
const d = inner.data;
|
|
38
|
+
if (typeof d.container !== 'string')
|
|
39
|
+
return null;
|
|
40
|
+
const list = mirror.get(d.container) ?? [];
|
|
41
|
+
if (typeof d.index !== 'number')
|
|
42
|
+
return { container: d.container, index: list.length };
|
|
43
|
+
const edge = extractClosestEdge(d);
|
|
44
|
+
const after = edge === 'bottom' || edge === 'right';
|
|
45
|
+
return { container: d.container, index: d.index + (after ? 1 : 0) };
|
|
46
|
+
}
|
|
47
|
+
function syncShadow(next) {
|
|
48
|
+
for (const [cid, list] of mirror) {
|
|
49
|
+
const idx = list.findIndex((item) => item[SHADOW_KEY]);
|
|
50
|
+
if (idx !== -1) {
|
|
51
|
+
list.splice(idx, 1);
|
|
52
|
+
mirror.set(cid, list);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!next || !dragging)
|
|
57
|
+
return;
|
|
58
|
+
const list = [...(mirror.get(next.container) ?? [])];
|
|
59
|
+
const shadowItem = { [SHADOW_KEY]: true, key: `${SHADOW_KEY}-${dragging.key}` };
|
|
60
|
+
list.splice(Math.min(next.index, list.length), 0, shadowItem);
|
|
61
|
+
mirror.set(next.container, list);
|
|
62
|
+
}
|
|
63
|
+
function consider(next) {
|
|
64
|
+
if (next?.container === proposal?.container && next?.index === proposal?.index)
|
|
65
|
+
return;
|
|
66
|
+
proposal = next;
|
|
67
|
+
if (hover === 'shadow')
|
|
68
|
+
syncShadow(next);
|
|
69
|
+
}
|
|
70
|
+
function applyToMirror(move) {
|
|
71
|
+
const fromList = [...(mirror.get(move.from.container) ?? [])];
|
|
72
|
+
const toList = move.from.container === move.to.container
|
|
73
|
+
? fromList
|
|
74
|
+
: [...(mirror.get(move.to.container) ?? [])];
|
|
75
|
+
const srcIdx = fromList.findIndex((it) => opts.key(it) === opts.key(move.item));
|
|
76
|
+
if (srcIdx === -1)
|
|
77
|
+
return;
|
|
78
|
+
fromList.splice(srcIdx, 1);
|
|
79
|
+
let destIdx = move.to.index;
|
|
80
|
+
if (move.from.container === move.to.container && move.to.index > move.from.index) {
|
|
81
|
+
destIdx = Math.max(0, destIdx - 1);
|
|
82
|
+
}
|
|
83
|
+
toList.splice(Math.min(destIdx, toList.length), 0, move.item);
|
|
84
|
+
mirror.set(move.from.container, fromList);
|
|
85
|
+
if (move.from.container !== move.to.container) {
|
|
86
|
+
mirror.set(move.to.container, toList);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function rollback(move) {
|
|
90
|
+
for (const [cid, list] of mirror) {
|
|
91
|
+
const idx = list.findIndex((it) => opts.key(it) === opts.key(move.item));
|
|
92
|
+
if (idx !== -1) {
|
|
93
|
+
list.splice(idx, 1);
|
|
94
|
+
mirror.set(cid, list);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const fromList = [...(mirror.get(move.from.container) ?? [])];
|
|
99
|
+
fromList.splice(Math.min(move.from.index, fromList.length), 0, move.item);
|
|
100
|
+
mirror.set(move.from.container, fromList);
|
|
101
|
+
}
|
|
102
|
+
function finalize(dropped) {
|
|
103
|
+
const drag = dragging;
|
|
104
|
+
const to = dropped ? proposal : null;
|
|
105
|
+
dragging = null;
|
|
106
|
+
consider(null);
|
|
107
|
+
if (!drag)
|
|
108
|
+
return;
|
|
109
|
+
if (!to ||
|
|
110
|
+
(to.container === drag.from.container && to.index === drag.from.index)) {
|
|
111
|
+
if (seedQueued && inFlight === 0) {
|
|
112
|
+
seedQueued = false;
|
|
113
|
+
seed();
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const move = { item: drag.item, from: drag.from, to };
|
|
118
|
+
applyToMirror(move);
|
|
119
|
+
if (opts.announce)
|
|
120
|
+
announce(opts.announce(move));
|
|
121
|
+
const token = (seq.get(drag.key) ?? 0) + 1;
|
|
122
|
+
seq.set(drag.key, token);
|
|
123
|
+
inFlight++;
|
|
124
|
+
opts
|
|
125
|
+
.commit(move)
|
|
126
|
+
.catch((error) => {
|
|
127
|
+
if (seq.get(drag.key) === token)
|
|
128
|
+
rollback(move);
|
|
129
|
+
opts.onError?.(error, move);
|
|
130
|
+
})
|
|
131
|
+
.finally(() => {
|
|
132
|
+
inFlight--;
|
|
133
|
+
if (inFlight === 0 && seedQueued && !dragging) {
|
|
134
|
+
seedQueued = false;
|
|
135
|
+
seed();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
$effect(() => monitorForElements({
|
|
140
|
+
canMonitor: ({ source }) => isItemData(source.data, opts.type),
|
|
141
|
+
onDragStart({ source }) {
|
|
142
|
+
const d = source.data;
|
|
143
|
+
dragging = {
|
|
144
|
+
key: d.key,
|
|
145
|
+
item: d.item,
|
|
146
|
+
from: { container: d.container, index: d.index },
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
onDropTargetChange({ location }) {
|
|
150
|
+
consider(placementFrom(location.current.dropTargets));
|
|
151
|
+
},
|
|
152
|
+
onDrag({ location }) {
|
|
153
|
+
consider(placementFrom(location.current.dropTargets));
|
|
154
|
+
},
|
|
155
|
+
onDrop({ location }) {
|
|
156
|
+
finalize(location.current.dropTargets.length > 0);
|
|
157
|
+
},
|
|
158
|
+
}));
|
|
159
|
+
return {
|
|
160
|
+
get: (container) => mirror.get(container) ?? [],
|
|
161
|
+
containers: () => [...mirror.keys()],
|
|
162
|
+
get dragging() {
|
|
163
|
+
return dragging;
|
|
164
|
+
},
|
|
165
|
+
get proposal() {
|
|
166
|
+
return proposal;
|
|
167
|
+
},
|
|
168
|
+
isSource: (item) => dragging?.key === opts.key(item),
|
|
169
|
+
isShadow: (item) => !!item[SHADOW_KEY],
|
|
170
|
+
item: (item, container, index) => ({
|
|
171
|
+
data: () => makeItemData({ type: opts.type, key: opts.key(item), item, container, index }),
|
|
172
|
+
}),
|
|
173
|
+
itemTarget: (container, index) => ({
|
|
174
|
+
data: (feedback) => attachClosestEdge({ type: `${opts.type}-target`, container, index }, { input: feedback.input, element: feedback.element, allowedEdges: edges }),
|
|
175
|
+
accepts: (d) => isItemData(d, opts.type),
|
|
176
|
+
sticky: true,
|
|
177
|
+
}),
|
|
178
|
+
containerTarget: (container) => ({
|
|
179
|
+
data: () => ({ type: `${opts.type}-target`, container }),
|
|
180
|
+
accepts: (d) => isItemData(d, opts.type),
|
|
181
|
+
}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for createDndBoard internals.
|
|
4
|
+
*
|
|
5
|
+
* We can't exercise $effect/$state directly in vitest (no Svelte compiler context),
|
|
6
|
+
* so these tests validate the pure-logic layer: sequence-token rollback ordering
|
|
7
|
+
* and reseed suppression. They work by extracting the board logic functions
|
|
8
|
+
* that don't depend on runes and driving them directly.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
11
|
+
import { makeItemData, isItemData } from './types';
|
|
12
|
+
// ---- makeItemData / isItemData -------------------------------------------
|
|
13
|
+
describe('makeItemData / isItemData', () => {
|
|
14
|
+
it('round-trips through the Symbol brand', () => {
|
|
15
|
+
const data = makeItemData({ type: 'card', key: 'a', item: { id: 'a' }, container: 'col1', index: 0 });
|
|
16
|
+
expect(isItemData(data)).toBe(true);
|
|
17
|
+
expect(isItemData(data, 'card')).toBe(true);
|
|
18
|
+
expect(isItemData(data, 'other')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it('rejects plain objects without the brand', () => {
|
|
21
|
+
expect(isItemData({ type: 'card', key: 'a' })).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it('preserves all fields', () => {
|
|
24
|
+
const item = { id: 'x', name: 'Item X' };
|
|
25
|
+
const data = makeItemData({ type: 'board', key: 'x', item, container: 'c1', index: 2 });
|
|
26
|
+
expect(data.key).toBe('x');
|
|
27
|
+
expect(data.item).toBe(item);
|
|
28
|
+
expect(data.container).toBe('c1');
|
|
29
|
+
expect(data.index).toBe(2);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
function makeMirror(initial) {
|
|
33
|
+
const m = new Map();
|
|
34
|
+
for (const [k, v] of Object.entries(initial))
|
|
35
|
+
m.set(k, [...v]);
|
|
36
|
+
return m;
|
|
37
|
+
}
|
|
38
|
+
function applyToMirror(mirror, move, key) {
|
|
39
|
+
const fromList = [...(mirror.get(move.from.container) ?? [])];
|
|
40
|
+
const toList = move.from.container === move.to.container
|
|
41
|
+
? fromList
|
|
42
|
+
: [...(mirror.get(move.to.container) ?? [])];
|
|
43
|
+
const srcIdx = fromList.findIndex((it) => key(it) === key(move.item));
|
|
44
|
+
if (srcIdx === -1)
|
|
45
|
+
return;
|
|
46
|
+
fromList.splice(srcIdx, 1);
|
|
47
|
+
let destIdx = move.to.index;
|
|
48
|
+
if (move.from.container === move.to.container && move.to.index > move.from.index) {
|
|
49
|
+
destIdx = Math.max(0, destIdx - 1);
|
|
50
|
+
}
|
|
51
|
+
toList.splice(Math.min(destIdx, toList.length), 0, move.item);
|
|
52
|
+
mirror.set(move.from.container, fromList);
|
|
53
|
+
if (move.from.container !== move.to.container) {
|
|
54
|
+
mirror.set(move.to.container, toList);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function rollback(mirror, move, key) {
|
|
58
|
+
for (const [cid, list] of mirror) {
|
|
59
|
+
const idx = list.findIndex((it) => key(it) === key(move.item));
|
|
60
|
+
if (idx !== -1) {
|
|
61
|
+
list.splice(idx, 1);
|
|
62
|
+
mirror.set(cid, list);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const fromList = [...(mirror.get(move.from.container) ?? [])];
|
|
67
|
+
fromList.splice(Math.min(move.from.index, fromList.length), 0, move.item);
|
|
68
|
+
mirror.set(move.from.container, fromList);
|
|
69
|
+
}
|
|
70
|
+
const key = (i) => i.id;
|
|
71
|
+
const a = { id: 'a', name: 'A' };
|
|
72
|
+
const b = { id: 'b', name: 'B' };
|
|
73
|
+
const c = { id: 'c', name: 'C' };
|
|
74
|
+
describe('applyToMirror', () => {
|
|
75
|
+
it('moves item within same container (downward)', () => {
|
|
76
|
+
const mirror = makeMirror({ col: [a, b, c] });
|
|
77
|
+
applyToMirror(mirror, { item: a, from: { container: 'col', index: 0 }, to: { container: 'col', index: 2 } }, key);
|
|
78
|
+
expect(mirror.get('col')?.map(key)).toEqual(['b', 'a', 'c']);
|
|
79
|
+
});
|
|
80
|
+
it('moves item within same container (upward)', () => {
|
|
81
|
+
const mirror = makeMirror({ col: [a, b, c] });
|
|
82
|
+
applyToMirror(mirror, { item: c, from: { container: 'col', index: 2 }, to: { container: 'col', index: 0 } }, key);
|
|
83
|
+
expect(mirror.get('col')?.map(key)).toEqual(['c', 'a', 'b']);
|
|
84
|
+
});
|
|
85
|
+
it('moves item across containers', () => {
|
|
86
|
+
const mirror = makeMirror({ col1: [a, b], col2: [c] });
|
|
87
|
+
applyToMirror(mirror, { item: b, from: { container: 'col1', index: 1 }, to: { container: 'col2', index: 0 } }, key);
|
|
88
|
+
expect(mirror.get('col1')?.map(key)).toEqual(['a']);
|
|
89
|
+
expect(mirror.get('col2')?.map(key)).toEqual(['b', 'c']);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('rollback', () => {
|
|
93
|
+
it('restores item to its original position', () => {
|
|
94
|
+
const mirror = makeMirror({ col: [b, c, a] });
|
|
95
|
+
rollback(mirror, { item: a, from: { container: 'col', index: 0 } }, key);
|
|
96
|
+
expect(mirror.get('col')?.map(key)).toEqual(['a', 'b', 'c']);
|
|
97
|
+
});
|
|
98
|
+
it('restores item from a different container', () => {
|
|
99
|
+
const mirror = makeMirror({ col1: [b], col2: [a, c] });
|
|
100
|
+
rollback(mirror, { item: a, from: { container: 'col1', index: 0 } }, key);
|
|
101
|
+
expect(mirror.get('col1')?.map(key)).toEqual(['a', 'b']);
|
|
102
|
+
expect(mirror.get('col2')?.map(key)).toEqual(['c']);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('sequence-token rollback ordering', () => {
|
|
106
|
+
it('does not rollback when a newer commit has already completed', async () => {
|
|
107
|
+
const seq = new Map();
|
|
108
|
+
const mirror = makeMirror({ col1: [a, b], col2: [c] });
|
|
109
|
+
const move1 = { item: a, from: { container: 'col1', index: 0 }, to: { container: 'col2', index: 0 } };
|
|
110
|
+
const move2 = { item: a, from: { container: 'col2', index: 0 }, to: { container: 'col1', index: 0 } };
|
|
111
|
+
const token1 = (seq.get('a') ?? 0) + 1;
|
|
112
|
+
seq.set('a', token1);
|
|
113
|
+
applyToMirror(mirror, move1, key);
|
|
114
|
+
const token2 = (seq.get('a') ?? 0) + 1;
|
|
115
|
+
seq.set('a', token2);
|
|
116
|
+
applyToMirror(mirror, { ...move2, from: { container: 'col2', index: 0 } }, key);
|
|
117
|
+
const shouldRollbackMove1 = seq.get('a') === token1;
|
|
118
|
+
expect(shouldRollbackMove1).toBe(false);
|
|
119
|
+
const shouldRollbackMove2 = seq.get('a') === token2;
|
|
120
|
+
expect(shouldRollbackMove2).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
it('does rollback when this is the newest commit of the item', async () => {
|
|
123
|
+
const seq = new Map();
|
|
124
|
+
const mirror = makeMirror({ col1: [a, b], col2: [c] });
|
|
125
|
+
const move1 = { item: a, from: { container: 'col1', index: 0 }, to: { container: 'col2', index: 0 } };
|
|
126
|
+
const token1 = (seq.get('a') ?? 0) + 1;
|
|
127
|
+
seq.set('a', token1);
|
|
128
|
+
applyToMirror(mirror, move1, key);
|
|
129
|
+
const shouldRollback = seq.get('a') === token1;
|
|
130
|
+
expect(shouldRollback).toBe(true);
|
|
131
|
+
if (shouldRollback) {
|
|
132
|
+
rollback(mirror, move1, key);
|
|
133
|
+
}
|
|
134
|
+
expect(mirror.get('col1')?.map(key)).toContain('a');
|
|
135
|
+
expect(mirror.get('col2')?.map(key)).not.toContain('a');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe('reseed suppression', () => {
|
|
139
|
+
it('suppresses reseed while dragging and applies it after', () => {
|
|
140
|
+
let seedCalled = 0;
|
|
141
|
+
let dragging = true;
|
|
142
|
+
let inFlight = 0;
|
|
143
|
+
let seedQueued = false;
|
|
144
|
+
function maybeSeed() {
|
|
145
|
+
if (dragging || inFlight > 0) {
|
|
146
|
+
seedQueued = true;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
seedCalled++;
|
|
150
|
+
seedQueued = false;
|
|
151
|
+
}
|
|
152
|
+
maybeSeed();
|
|
153
|
+
expect(seedCalled).toBe(0);
|
|
154
|
+
expect(seedQueued).toBe(true);
|
|
155
|
+
dragging = false;
|
|
156
|
+
if (inFlight === 0 && seedQueued && !dragging) {
|
|
157
|
+
seedCalled++;
|
|
158
|
+
seedQueued = false;
|
|
159
|
+
}
|
|
160
|
+
expect(seedCalled).toBe(1);
|
|
161
|
+
expect(seedQueued).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
it('suppresses reseed while commits in-flight and applies after all settle', () => {
|
|
164
|
+
let seedCalled = 0;
|
|
165
|
+
let dragging = false;
|
|
166
|
+
let inFlight = 2;
|
|
167
|
+
let seedQueued = false;
|
|
168
|
+
function tryFlush() {
|
|
169
|
+
if (inFlight === 0 && seedQueued && !dragging) {
|
|
170
|
+
seedCalled++;
|
|
171
|
+
seedQueued = false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (dragging || inFlight > 0) {
|
|
175
|
+
seedQueued = true;
|
|
176
|
+
}
|
|
177
|
+
expect(seedQueued).toBe(true);
|
|
178
|
+
inFlight--;
|
|
179
|
+
tryFlush();
|
|
180
|
+
expect(seedCalled).toBe(0);
|
|
181
|
+
inFlight--;
|
|
182
|
+
tryFlush();
|
|
183
|
+
expect(seedCalled).toBe(1);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DragData } from './types';
|
|
2
|
+
export interface DraggableParams<TData extends DragData = DragData> {
|
|
3
|
+
data: () => TData;
|
|
4
|
+
disabled?: () => boolean;
|
|
5
|
+
dragHandle?: Element;
|
|
6
|
+
preview?: (container: HTMLElement, data: TData) => () => void;
|
|
7
|
+
onDragStart?: (data: TData) => void;
|
|
8
|
+
onDrop?: (data: TData) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const draggable: <TData extends DragData>(node: HTMLElement, params: DraggableParams<TData>) => {
|
|
11
|
+
update(next: DraggableParams<TData>): void;
|
|
12
|
+
destroy: () => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { draggable as registerDraggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
2
|
+
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
|
3
|
+
export const draggable = ((node, params) => {
|
|
4
|
+
let current = params;
|
|
5
|
+
let cleanup;
|
|
6
|
+
function register() {
|
|
7
|
+
cleanup = registerDraggable({
|
|
8
|
+
element: node,
|
|
9
|
+
dragHandle: current.dragHandle,
|
|
10
|
+
canDrag: () => !(current.disabled?.() ?? false),
|
|
11
|
+
getInitialData: () => current.data(),
|
|
12
|
+
onGenerateDragPreview({ source, nativeSetDragImage }) {
|
|
13
|
+
if (!current.preview)
|
|
14
|
+
return;
|
|
15
|
+
setCustomNativeDragPreview({
|
|
16
|
+
render: ({ container }) => current.preview(container, source.data),
|
|
17
|
+
nativeSetDragImage,
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
onDragStart({ source }) {
|
|
21
|
+
node.dataset.dragging = 'true';
|
|
22
|
+
current.onDragStart?.(source.data);
|
|
23
|
+
},
|
|
24
|
+
onDrop({ source }) {
|
|
25
|
+
delete node.dataset.dragging;
|
|
26
|
+
current.onDrop?.(source.data);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
register();
|
|
31
|
+
return {
|
|
32
|
+
update(next) {
|
|
33
|
+
const handleChanged = next.dragHandle !== current.dragHandle;
|
|
34
|
+
current = next;
|
|
35
|
+
if (handleChanged) {
|
|
36
|
+
cleanup();
|
|
37
|
+
register();
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
destroy: () => cleanup(),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DragData, TargetFeedback } from './types';
|
|
2
|
+
export interface DropTargetParams<TSelf extends DragData = DragData> {
|
|
3
|
+
data: (feedback: TargetFeedback) => TSelf;
|
|
4
|
+
accepts?: (source: Record<string | symbol, unknown>) => boolean;
|
|
5
|
+
sticky?: boolean;
|
|
6
|
+
onEnter?: (source: Record<string | symbol, unknown>) => void;
|
|
7
|
+
onLeave?: () => void;
|
|
8
|
+
onMove?: (self: TSelf, source: Record<string | symbol, unknown>) => void;
|
|
9
|
+
onDropped?: (self: TSelf, source: Record<string | symbol, unknown>) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const dropTarget: <TSelf extends DragData>(node: HTMLElement, params: DropTargetParams<TSelf>) => {
|
|
12
|
+
update(next: DropTargetParams<TSelf>): void;
|
|
13
|
+
destroy: import("@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types").CleanupFn;
|
|
14
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
2
|
+
export const dropTarget = ((node, params) => {
|
|
3
|
+
let current = params;
|
|
4
|
+
const cleanup = dropTargetForElements({
|
|
5
|
+
element: node,
|
|
6
|
+
getData: ({ input, element, source }) => current.data({ input, element, source }),
|
|
7
|
+
canDrop: ({ source }) => current.accepts?.(source.data) ?? true,
|
|
8
|
+
getIsSticky: () => current.sticky ?? false,
|
|
9
|
+
onDragEnter({ source }) {
|
|
10
|
+
node.dataset.dropOver = 'true';
|
|
11
|
+
current.onEnter?.(source.data);
|
|
12
|
+
},
|
|
13
|
+
onDragLeave() {
|
|
14
|
+
delete node.dataset.dropOver;
|
|
15
|
+
current.onLeave?.();
|
|
16
|
+
},
|
|
17
|
+
onDrag({ self, source }) {
|
|
18
|
+
current.onMove?.(self.data, source.data);
|
|
19
|
+
},
|
|
20
|
+
onDrop({ self, source }) {
|
|
21
|
+
delete node.dataset.dropOver;
|
|
22
|
+
current.onDropped?.(self.data, source.data);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
update(next) {
|
|
27
|
+
current = next;
|
|
28
|
+
},
|
|
29
|
+
destroy: cleanup,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { draggable } from './draggable';
|
|
2
|
+
export type { DraggableParams } from './draggable';
|
|
3
|
+
export { dropTarget } from './dropTarget';
|
|
4
|
+
export type { DropTargetParams } from './dropTarget';
|
|
5
|
+
export { createDndBoard } from './createDndBoard.svelte';
|
|
6
|
+
export type { BoardMove, DndBoardOptions } from './createDndBoard.svelte';
|
|
7
|
+
export { default as DropIndicator } from './DropIndicator.svelte';
|
|
8
|
+
export { autoScroll } from './autoScroll';
|
|
9
|
+
export type { AutoScrollParams } from './autoScroll';
|
|
10
|
+
export { announce, cleanupAnnouncements } from './announce';
|
|
11
|
+
export { makeItemData, isItemData } from './types';
|
|
12
|
+
export type { DragData, ItemDragData, ContainerDropData, TargetFeedback, Edge } from './types';
|
|
13
|
+
export { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { draggable } from './draggable';
|
|
2
|
+
export { dropTarget } from './dropTarget';
|
|
3
|
+
export { createDndBoard } from './createDndBoard.svelte';
|
|
4
|
+
export { default as DropIndicator } from './DropIndicator.svelte';
|
|
5
|
+
export { autoScroll } from './autoScroll';
|
|
6
|
+
export { announce, cleanupAnnouncements } from './announce';
|
|
7
|
+
export { makeItemData, isItemData } from './types';
|
|
8
|
+
// Low-level pdnd primitives re-exported to let host code avoid direct @atlaskit imports
|
|
9
|
+
export { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Input } from '@atlaskit/pragmatic-drag-and-drop/types';
|
|
2
|
+
export interface DragData extends Record<string | symbol, unknown> {
|
|
3
|
+
readonly type: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Module-scoped Symbol brand for board-managed items.
|
|
7
|
+
* ⚠ Symbol identity = module identity. Two copies of this module on one page makes
|
|
8
|
+
* isItemData() return false across the boundary — dnd/ must exist EXACTLY ONCE.
|
|
9
|
+
* Declare all @atlaskit/pragmatic-drag-and-drop* packages as peerDependencies in
|
|
10
|
+
* the host app (host owns the single instance).
|
|
11
|
+
*/
|
|
12
|
+
declare const ITEM: unique symbol;
|
|
13
|
+
export interface ItemDragData<TItem = unknown> extends DragData {
|
|
14
|
+
readonly [ITEM]: true;
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly item: TItem;
|
|
17
|
+
readonly container: string;
|
|
18
|
+
readonly index: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ItemDragDataInput<TItem = unknown> {
|
|
21
|
+
type: string;
|
|
22
|
+
key: string;
|
|
23
|
+
item: TItem;
|
|
24
|
+
container: string;
|
|
25
|
+
index: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function makeItemData<TItem>(args: ItemDragDataInput<TItem>): ItemDragData<TItem>;
|
|
28
|
+
export declare function isItemData<TItem = unknown>(data: Record<string | symbol, unknown>, type?: string): data is ItemDragData<TItem>;
|
|
29
|
+
export interface ContainerDropData extends DragData {
|
|
30
|
+
readonly container: string;
|
|
31
|
+
readonly index?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface TargetFeedback {
|
|
34
|
+
input: Input;
|
|
35
|
+
element: Element;
|
|
36
|
+
source: {
|
|
37
|
+
element: HTMLElement;
|
|
38
|
+
data: Record<string | symbol, unknown>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-scoped Symbol brand for board-managed items.
|
|
3
|
+
* ⚠ Symbol identity = module identity. Two copies of this module on one page makes
|
|
4
|
+
* isItemData() return false across the boundary — dnd/ must exist EXACTLY ONCE.
|
|
5
|
+
* Declare all @atlaskit/pragmatic-drag-and-drop* packages as peerDependencies in
|
|
6
|
+
* the host app (host owns the single instance).
|
|
7
|
+
*/
|
|
8
|
+
const ITEM = Symbol('jatui-dnd-item');
|
|
9
|
+
export function makeItemData(args) {
|
|
10
|
+
return { ...args, [ITEM]: true };
|
|
11
|
+
}
|
|
12
|
+
export function isItemData(data, type) {
|
|
13
|
+
return data[ITEM] === true && (type === undefined || data.type === type);
|
|
14
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -142,3 +142,8 @@ export { default as LocationMap } from './components/LocationMap.svelte';
|
|
|
142
142
|
export { default as GPSTracker } from './components/GPSTracker.svelte';
|
|
143
143
|
export type { MapJob, MapAgent } from './types/maps';
|
|
144
144
|
export { loadGoogleMapsAPI, isGoogleMapsLoaded, isPlacesLoaded, isGeometryLoaded, } from './utils/googleMapsLoader';
|
|
145
|
+
export { draggable, dropTarget, autoScroll, announce, cleanupAnnouncements, monitorForElements, } from './dnd/index';
|
|
146
|
+
export { createDndBoard } from './dnd/createDndBoard.svelte';
|
|
147
|
+
export { default as DropIndicator } from './dnd/DropIndicator.svelte';
|
|
148
|
+
export { makeItemData, isItemData } from './dnd/types';
|
|
149
|
+
export type { DragData, ItemDragData, ContainerDropData, TargetFeedback, Edge, DraggableParams, DropTargetParams, AutoScrollParams, BoardMove, DndBoardOptions, } from './dnd/index';
|
package/dist/index.js
CHANGED
|
@@ -141,3 +141,8 @@ export { default as LocationMap } from './components/LocationMap.svelte';
|
|
|
141
141
|
export { default as GPSTracker } from './components/GPSTracker.svelte';
|
|
142
142
|
// Utilities — Google Maps loader
|
|
143
143
|
export { loadGoogleMapsAPI, isGoogleMapsLoaded, isPlacesLoaded, isGeometryLoaded, } from './utils/googleMapsLoader';
|
|
144
|
+
// DnD — pragmatic-drag-and-drop wrappers (peerDep: @atlaskit/pragmatic-drag-and-drop*)
|
|
145
|
+
export { draggable, dropTarget, autoScroll, announce, cleanupAnnouncements, monitorForElements, } from './dnd/index';
|
|
146
|
+
export { createDndBoard } from './dnd/createDndBoard.svelte';
|
|
147
|
+
export { default as DropIndicator } from './dnd/DropIndicator.svelte';
|
|
148
|
+
export { makeItemData, isItemData } from './dnd/types';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joewinke/jatui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared Svelte 5 component library for JAT projects",
|
|
6
6
|
"type": "module",
|
|
@@ -31,12 +31,28 @@
|
|
|
31
31
|
"prepublishOnly": "npm run package"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
+
"@atlaskit/pragmatic-drag-and-drop": "~2.0.0",
|
|
35
|
+
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "~3.0.0",
|
|
36
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": "~2.0.0",
|
|
37
|
+
"@atlaskit/pragmatic-drag-and-drop-live-region": "~2.0.0",
|
|
34
38
|
"d3": "^7.0.0",
|
|
35
39
|
"daisyui": "^5.0.0",
|
|
36
40
|
"svelte": "^5.0.0",
|
|
37
41
|
"tailwindcss": "^4.0.0"
|
|
38
42
|
},
|
|
39
43
|
"peerDependenciesMeta": {
|
|
44
|
+
"@atlaskit/pragmatic-drag-and-drop": {
|
|
45
|
+
"optional": true
|
|
46
|
+
},
|
|
47
|
+
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": {
|
|
51
|
+
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"@atlaskit/pragmatic-drag-and-drop-live-region": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
40
56
|
"d3": {
|
|
41
57
|
"optional": true
|
|
42
58
|
},
|
|
@@ -48,6 +64,10 @@
|
|
|
48
64
|
}
|
|
49
65
|
},
|
|
50
66
|
"devDependencies": {
|
|
67
|
+
"@atlaskit/pragmatic-drag-and-drop": "~2.0.1",
|
|
68
|
+
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "~3.0.0",
|
|
69
|
+
"@atlaskit/pragmatic-drag-and-drop-hitbox": "~2.0.0",
|
|
70
|
+
"@atlaskit/pragmatic-drag-and-drop-live-region": "~2.0.0",
|
|
51
71
|
"@sveltejs/adapter-auto": "^6.0.0",
|
|
52
72
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
53
73
|
"@sveltejs/kit": "^2.21.0",
|
|
@@ -69,4 +89,4 @@
|
|
|
69
89
|
"svelte-dnd-action": "^0.9.69"
|
|
70
90
|
},
|
|
71
91
|
"license": "MIT"
|
|
72
|
-
}
|
|
92
|
+
}
|