@hkdigital/lib-sveltekit 0.2.9 → 0.2.10
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/drag-drop/DragDropContext.svelte +2 -3
- package/dist/components/drag-drop/Draggable.svelte +15 -11
- package/dist/components/drag-drop/actions.d.ts +4 -1
- package/dist/components/drag-drop/actions.js +12 -4
- package/dist/components/drag-drop/drag-state.svelte.d.ts +3 -1
- package/dist/components/drag-drop/drag-state.svelte.js +27 -24
- package/dist/components/drag-drop/drag-state.svelte.js__ +319 -0
- package/dist/components/drag-drop/util.d.ts +0 -32
- package/dist/components/drag-drop/util.js +74 -74
- package/package.json +1 -1
@@ -1,7 +1,7 @@
|
|
1
1
|
<script>
|
2
2
|
import { createDragState } from './drag-state.svelte.js';
|
3
3
|
|
4
|
-
import { activeDragOver,
|
4
|
+
import { activeTouchMove, activeDragOver, activeDrop } from './actions.js';
|
5
5
|
|
6
6
|
/**
|
7
7
|
* @type {{
|
@@ -97,10 +97,9 @@
|
|
97
97
|
ondragenter={onDragEnter}
|
98
98
|
use:activeDragOver={onDragOver}
|
99
99
|
ondragleave={onDragLeave}
|
100
|
-
|
100
|
+
use:activeDrop={onDrop}
|
101
101
|
ondragend={onDragEnd}
|
102
102
|
use:activeTouchMove={(e) => {
|
103
|
-
// Prevent scrolling if we're dragging
|
104
103
|
if (dragState.isDragging()) {
|
105
104
|
e.preventDefault();
|
106
105
|
}
|
@@ -4,7 +4,6 @@
|
|
4
4
|
import { toStateClasses } from '../../util/design-system/index.js';
|
5
5
|
import { createOrGetDragState } from './drag-state.svelte.js';
|
6
6
|
import { DragController } from './DragController.js';
|
7
|
-
import { generateLocalId } from '../../util/unique';
|
8
7
|
import { onDestroy } from 'svelte';
|
9
8
|
import {
|
10
9
|
IDLE,
|
@@ -86,7 +85,7 @@
|
|
86
85
|
|
87
86
|
const dragState = createOrGetDragState(contextKey);
|
88
87
|
|
89
|
-
const draggableId =
|
88
|
+
const draggableId = dragState.newDraggableId();
|
90
89
|
|
91
90
|
// svelte-ignore non_reactive_update
|
92
91
|
let draggableElement;
|
@@ -213,17 +212,20 @@ let stateObject = $derived({
|
|
213
212
|
group
|
214
213
|
};
|
215
214
|
|
215
|
+
// console.debug('handleDragStart:', draggableId, dragData);
|
216
|
+
|
216
217
|
// Set shared drag state
|
217
218
|
dragState.start(draggableId, dragData);
|
218
219
|
|
219
220
|
// Set minimal data transfer for browser drag and drop API
|
220
221
|
event.dataTransfer.effectAllowed = 'move';
|
221
|
-
event.dataTransfer.setData(
|
222
|
-
'application/json',
|
223
|
-
JSON.stringify({ draggableId })
|
224
|
-
);
|
225
222
|
|
226
|
-
//
|
223
|
+
// Set draggableId as custom mime type, since that value is availabe
|
224
|
+
// during all event types (dragstart, dragenter, dragover, dragleave,
|
225
|
+
// drop and dragend)
|
226
|
+
event.dataTransfer.setData(`application/x-draggable-${draggableId}`, '1');
|
227
|
+
|
228
|
+
// Also keep text/plain for browser compatibility
|
227
229
|
event.dataTransfer.setData('text/plain', draggableId);
|
228
230
|
|
229
231
|
// Create the preview controller
|
@@ -374,6 +376,7 @@ let stateObject = $derived({
|
|
374
376
|
document.addEventListener('touchmove', handleTouchMove, {
|
375
377
|
passive: false
|
376
378
|
});
|
379
|
+
|
377
380
|
document.addEventListener('touchend', handleTouchEnd);
|
378
381
|
}, 150); // 150ms delay to distinguish from scrolling
|
379
382
|
}
|
@@ -400,12 +403,13 @@ function handleTouchMove(event) {
|
|
400
403
|
clientX: touch.clientX,
|
401
404
|
clientY: touch.clientY,
|
402
405
|
dataTransfer: {
|
403
|
-
types: [
|
404
|
-
getData: () =>
|
406
|
+
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
407
|
+
getData: () => 1,
|
405
408
|
dropEffect: 'move',
|
406
409
|
effectAllowed: 'move',
|
407
410
|
files: []
|
408
411
|
},
|
412
|
+
|
409
413
|
preventDefault: () => {},
|
410
414
|
stopPropagation: () => {}
|
411
415
|
};
|
@@ -431,8 +435,8 @@ function handleTouchMove(event) {
|
|
431
435
|
clientX: touch.clientX,
|
432
436
|
clientY: touch.clientY,
|
433
437
|
dataTransfer: {
|
434
|
-
types: [
|
435
|
-
getData: () =>
|
438
|
+
types: [`application/x-draggable-${draggableId}`, 'text/plain'],
|
439
|
+
getData: () => 1,
|
436
440
|
dropEffect: 'move',
|
437
441
|
effectAllowed: 'move',
|
438
442
|
files: []
|
@@ -1,6 +1,9 @@
|
|
1
|
+
export function activeTouchMove(node: any, handler: any): {
|
2
|
+
destroy(): void;
|
3
|
+
};
|
1
4
|
export function activeDragOver(node: any, handler: any): {
|
2
5
|
destroy(): void;
|
3
6
|
};
|
4
|
-
export function
|
7
|
+
export function activeDrop(node: any, handler: any): {
|
5
8
|
destroy(): void;
|
6
9
|
};
|
@@ -1,6 +1,14 @@
|
|
1
|
+
export function activeTouchMove(node, handler) {
|
2
|
+
node.addEventListener('touchmove', handler, { passive: false });
|
3
|
+
return {
|
4
|
+
destroy() {
|
5
|
+
node.removeEventListener('touchmove', handler, { passive: false });
|
6
|
+
}
|
7
|
+
};
|
8
|
+
}
|
9
|
+
|
1
10
|
export function activeDragOver(node, handler) {
|
2
11
|
node.addEventListener('dragover', handler, { passive: false });
|
3
|
-
|
4
12
|
return {
|
5
13
|
destroy() {
|
6
14
|
node.removeEventListener('dragover', handler, { passive: false });
|
@@ -8,11 +16,11 @@ export function activeDragOver(node, handler) {
|
|
8
16
|
};
|
9
17
|
}
|
10
18
|
|
11
|
-
export function
|
12
|
-
node.addEventListener('
|
19
|
+
export function activeDrop(node, handler) {
|
20
|
+
node.addEventListener('drop', handler, { passive: false });
|
13
21
|
return {
|
14
22
|
destroy() {
|
15
|
-
node.removeEventListener('
|
23
|
+
node.removeEventListener('drop', handler, { passive: false });
|
16
24
|
}
|
17
25
|
};
|
18
26
|
}
|
@@ -5,6 +5,7 @@ export type SimulatedDragEvent = import("../../typedef").SimulatedDragEvent;
|
|
5
5
|
/** @typedef {import('../../typedef').SimulatedDragEvent} SimulatedDragEvent */
|
6
6
|
declare class DragState {
|
7
7
|
draggables: Map<any, any>;
|
8
|
+
draggableIdCount: number;
|
8
9
|
dropZones: Map<any, any>;
|
9
10
|
activeDropZone: any;
|
10
11
|
lastActiveDropZone: any;
|
@@ -67,6 +68,7 @@ declare class DragState {
|
|
67
68
|
* @param {string} draggableId
|
68
69
|
*/
|
69
70
|
end(draggableId: string): void;
|
71
|
+
newDraggableId(): string;
|
70
72
|
/**
|
71
73
|
* Get a drag data by draggable id
|
72
74
|
*
|
@@ -79,7 +81,7 @@ declare class DragState {
|
|
79
81
|
*
|
80
82
|
* @param {DragEvent|SimulatedDragEvent} event
|
81
83
|
*
|
82
|
-
* @returns {Object|null} The drag data, or null for file drops
|
84
|
+
* @returns {Object|null} The drag data, or null for file drops or not found
|
83
85
|
*/
|
84
86
|
getDraggable(event: DragEvent | SimulatedDragEvent): any | null;
|
85
87
|
/**
|
@@ -7,6 +7,9 @@ class DragState {
|
|
7
7
|
// Existing draggables map
|
8
8
|
draggables = $state(new Map());
|
9
9
|
|
10
|
+
// Draggable id counter
|
11
|
+
draggableIdCount = 1;
|
12
|
+
|
10
13
|
// New: Registry for dropzones
|
11
14
|
dropZones = $state(new Map());
|
12
15
|
|
@@ -162,6 +165,9 @@ class DragState {
|
|
162
165
|
if (dropZone && dropZone.config.canDrop) {
|
163
166
|
const dragData = this.getDraggable(event);
|
164
167
|
|
168
|
+
console.debug('handleDropAtPoint', event, dragData);
|
169
|
+
|
170
|
+
|
165
171
|
if (dragData && dropZone.config.element) {
|
166
172
|
// Calculate drop position relative to dropzone
|
167
173
|
const rect = dropZone.config.element.getBoundingClientRect();
|
@@ -213,6 +219,7 @@ class DragState {
|
|
213
219
|
* @param {import('../../typedef/drag.js').DragData} dragData
|
214
220
|
*/
|
215
221
|
start(draggableId, dragData) {
|
222
|
+
// console.debug('DragState.start called:', draggableId, dragData);
|
216
223
|
this.draggables.set(draggableId, dragData);
|
217
224
|
}
|
218
225
|
|
@@ -220,6 +227,7 @@ class DragState {
|
|
220
227
|
* @param {string} draggableId
|
221
228
|
*/
|
222
229
|
end(draggableId) {
|
230
|
+
// console.debug('DragState.end called:', draggableId);
|
223
231
|
this.draggables.delete(draggableId);
|
224
232
|
|
225
233
|
// Check both current AND last active dropzone
|
@@ -241,6 +249,10 @@ class DragState {
|
|
241
249
|
this.lastActiveDropZone = null;
|
242
250
|
}
|
243
251
|
|
252
|
+
newDraggableId() {
|
253
|
+
return `${this.draggableIdCount++}`;
|
254
|
+
}
|
255
|
+
|
244
256
|
/**
|
245
257
|
* Get a drag data by draggable id
|
246
258
|
*
|
@@ -256,7 +268,7 @@ class DragState {
|
|
256
268
|
*
|
257
269
|
* @param {DragEvent|SimulatedDragEvent} event
|
258
270
|
*
|
259
|
-
* @returns {Object|null} The drag data, or null for file drops
|
271
|
+
* @returns {Object|null} The drag data, or null for file drops or not found
|
260
272
|
*/
|
261
273
|
getDraggable(event) {
|
262
274
|
// Check if this is a file drop first
|
@@ -268,34 +280,25 @@ class DragState {
|
|
268
280
|
}
|
269
281
|
}
|
270
282
|
|
271
|
-
|
272
|
-
|
273
|
-
if (event.type === 'dragover'|| event.type === 'dragenter') {
|
274
|
-
if (this.draggables.size > 0) {
|
275
|
-
// Return the most recent drag operation
|
276
|
-
return this.current;
|
277
|
-
}
|
278
|
-
}
|
283
|
+
if (event.dataTransfer?.types) {
|
284
|
+
const types = Array.from(event.dataTransfer.types);
|
279
285
|
|
280
|
-
|
281
|
-
if (event.type === 'drop' && event.dataTransfer) {
|
282
|
-
try {
|
283
|
-
const jsonData = event.dataTransfer.getData('application/json');
|
284
|
-
if (jsonData) {
|
285
|
-
const transferData = JSON.parse(jsonData);
|
286
|
-
const draggableId = transferData.draggableId;
|
286
|
+
const PREFIX = 'application/x-draggable-';
|
287
287
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
288
|
+
const dragType = types.find((t) => t.startsWith(PREFIX));
|
289
|
+
|
290
|
+
if (dragType) {
|
291
|
+
const draggableId = dragType.slice(PREFIX.length);
|
292
|
+
|
293
|
+
const entry = this.getDraggableById(draggableId);
|
294
|
+
|
295
|
+
console.debug('getDraggable', draggableId, entry);
|
296
|
+
|
297
|
+
return entry;
|
294
298
|
}
|
295
299
|
}
|
296
300
|
|
297
|
-
|
298
|
-
return this.current;
|
301
|
+
return null;
|
299
302
|
}
|
300
303
|
|
301
304
|
/**
|
@@ -0,0 +1,319 @@
|
|
1
|
+
// drag-state.svelte.js
|
2
|
+
import { defineStateContext } from '$lib/util/svelte/state-context/index.js';
|
3
|
+
|
4
|
+
/** @typedef {import('$lib/typedef').SimulatedDragEvent} SimulatedDragEvent */
|
5
|
+
|
6
|
+
class DragState {
|
7
|
+
// Existing draggables map
|
8
|
+
draggables = $state(new Map());
|
9
|
+
|
10
|
+
// New: Registry for dropzones
|
11
|
+
dropZones = $state(new Map());
|
12
|
+
|
13
|
+
// Track which dropzone is currently active
|
14
|
+
activeDropZone = $state(null);
|
15
|
+
|
16
|
+
// Track the last active drop zone
|
17
|
+
// - activeDropZone gets cleared by dragLeavr
|
18
|
+
// - but we need it in 'end'
|
19
|
+
lastActiveDropZone = null;
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Register a dropzone
|
23
|
+
* @param {string} zoneId
|
24
|
+
* @param {Object} config
|
25
|
+
* @param {string} config.zone
|
26
|
+
* @param {string} config.group
|
27
|
+
* @param {Function} config.accepts
|
28
|
+
* @param {Function} config.onDragEnter
|
29
|
+
* @param {Function} config.onDragOver
|
30
|
+
* @param {Function} config.onDragLeave
|
31
|
+
* @param {(DropData) => void} config.onDrop
|
32
|
+
* @param {HTMLElement} config.element
|
33
|
+
*/
|
34
|
+
registerDropZone(zoneId, config) {
|
35
|
+
if (this.dropZones.has(zoneId)) {
|
36
|
+
throw new Error(`Zone [${zoneId}] is already registered`);
|
37
|
+
}
|
38
|
+
|
39
|
+
this.dropZones.set(zoneId, {
|
40
|
+
...config,
|
41
|
+
isOver: false,
|
42
|
+
canDrop: false
|
43
|
+
});
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Unregister a dropzone
|
48
|
+
* @param {string} zoneId
|
49
|
+
*/
|
50
|
+
unregisterDropZone(zoneId) {
|
51
|
+
if (this.activeDropZone === zoneId) {
|
52
|
+
this.activeDropZone = null;
|
53
|
+
}
|
54
|
+
this.dropZones.delete(zoneId);
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Get dropzone at coordinates
|
59
|
+
* @param {number} x
|
60
|
+
* @param {number} y
|
61
|
+
* @returns {Object|null}
|
62
|
+
*/
|
63
|
+
getDropZoneAtPoint(x, y) {
|
64
|
+
// Check all registered dropzones
|
65
|
+
for (const [zoneId, config] of this.dropZones) {
|
66
|
+
const rect = config.element.getBoundingClientRect();
|
67
|
+
|
68
|
+
if (
|
69
|
+
x >= rect.left &&
|
70
|
+
x <= rect.right &&
|
71
|
+
y >= rect.top &&
|
72
|
+
y <= rect.bottom
|
73
|
+
) {
|
74
|
+
// Found a dropzone at this point
|
75
|
+
// Check if it's the deepest one (for nested zones)
|
76
|
+
let deepestZone = { zoneId, config, depth: 0 };
|
77
|
+
|
78
|
+
// Check for nested dropzones
|
79
|
+
for (const [otherId, otherConfig] of this.dropZones) {
|
80
|
+
if (otherId === zoneId) continue;
|
81
|
+
|
82
|
+
const otherRect = otherConfig.element.getBoundingClientRect();
|
83
|
+
if (
|
84
|
+
x >= otherRect.left &&
|
85
|
+
x <= otherRect.right &&
|
86
|
+
y >= otherRect.top &&
|
87
|
+
y <= otherRect.bottom
|
88
|
+
) {
|
89
|
+
// Check if this zone is nested inside our current zone
|
90
|
+
if (config.element.contains(otherConfig.element)) {
|
91
|
+
deepestZone = {
|
92
|
+
zoneId: otherId,
|
93
|
+
config: otherConfig,
|
94
|
+
depth: deepestZone.depth + 1
|
95
|
+
};
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
return { zoneId: deepestZone.zoneId, config: deepestZone.config };
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
return null;
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Update active dropzone based on coordinates
|
109
|
+
*
|
110
|
+
* @param {number} x
|
111
|
+
* @param {number} y
|
112
|
+
* @param {DragEvent|SimulatedDragEvent} event
|
113
|
+
*/
|
114
|
+
updateActiveDropZone(x, y, event) {
|
115
|
+
const dropZone = this.getDropZoneAtPoint(x, y);
|
116
|
+
const newActiveId = dropZone?.zoneId || null;
|
117
|
+
|
118
|
+
// Handle leave/enter transitions
|
119
|
+
if (this.activeDropZone !== newActiveId) {
|
120
|
+
// Leave previous zone
|
121
|
+
if (this.activeDropZone) {
|
122
|
+
this.lastActiveDropZone = this.activeDropZone;
|
123
|
+
|
124
|
+
const prevConfig = this.dropZones.get(this.activeDropZone);
|
125
|
+
if (prevConfig) {
|
126
|
+
prevConfig.isOver = false;
|
127
|
+
prevConfig.canDrop = false;
|
128
|
+
prevConfig.onDragLeave?.({ event, zone: prevConfig.zone });
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
// Enter new zone
|
133
|
+
if (newActiveId && dropZone) {
|
134
|
+
const dragData = this.getDraggable(event);
|
135
|
+
const canDrop = dragData && dropZone.config.accepts(dragData);
|
136
|
+
|
137
|
+
dropZone.config.isOver = true;
|
138
|
+
dropZone.config.canDrop = canDrop;
|
139
|
+
dropZone.config.onDragEnter?.({
|
140
|
+
event,
|
141
|
+
zone: dropZone.config.zone,
|
142
|
+
canDrop
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
this.activeDropZone = newActiveId;
|
147
|
+
} else if (newActiveId) {
|
148
|
+
// Still in the same zone, just send dragOver
|
149
|
+
dropZone.config.onDragOver?.({ event, zone: dropZone.config.zone });
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Handle drop at coordinates
|
155
|
+
* @param {number} x
|
156
|
+
* @param {number} y
|
157
|
+
* @param {DragEvent|SimulatedDragEvent} event
|
158
|
+
*/
|
159
|
+
handleDropAtPoint(x, y, event) {
|
160
|
+
const dropZone = this.getDropZoneAtPoint(x, y);
|
161
|
+
|
162
|
+
if (dropZone && dropZone.config.canDrop) {
|
163
|
+
const dragData = this.getDraggable(event);
|
164
|
+
|
165
|
+
if (dragData && dropZone.config.element) {
|
166
|
+
// Calculate drop position relative to dropzone
|
167
|
+
const rect = dropZone.config.element.getBoundingClientRect();
|
168
|
+
|
169
|
+
const style = window.getComputedStyle(dropZone.config.element);
|
170
|
+
|
171
|
+
const borderLeftWidth = parseInt(style.borderLeftWidth, 10) || 0;
|
172
|
+
const borderTopWidth = parseInt(style.borderTopWidth, 10) || 0;
|
173
|
+
|
174
|
+
const dropOffsetX = x - rect.left - borderLeftWidth;
|
175
|
+
const dropOffsetY = y - rect.top - borderTopWidth;
|
176
|
+
|
177
|
+
const dropX = dropOffsetX - (dragData.offsetX ?? 0);
|
178
|
+
const dropY = dropOffsetY - (dragData.offsetY ?? 0);
|
179
|
+
|
180
|
+
// Call the dropzone's drop handler
|
181
|
+
dropZone.config.onDrop?.({
|
182
|
+
zone: dropZone.config.zone,
|
183
|
+
source: dragData.source,
|
184
|
+
item: dragData.item,
|
185
|
+
x: dropX,
|
186
|
+
y: dropY,
|
187
|
+
drag: dragData,
|
188
|
+
drop: {
|
189
|
+
offsetX: dropOffsetX,
|
190
|
+
offsetY: dropOffsetY,
|
191
|
+
target: dropZone.config.element
|
192
|
+
}
|
193
|
+
});
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
// Ensure we notify the active dropzone that drag ended
|
198
|
+
if (this.activeDropZone) {
|
199
|
+
const config = this.dropZones.get(this.activeDropZone);
|
200
|
+
if (config) {
|
201
|
+
config.isOver = false;
|
202
|
+
config.canDrop = false;
|
203
|
+
config.onDragLeave?.({ event, zone: config.zone });
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
// Reset active dropzone
|
208
|
+
this.activeDropZone = null;
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* @param {string} draggableId
|
213
|
+
* @param {import('$lib/typedef/drag.js').DragData} dragData
|
214
|
+
*/
|
215
|
+
start(draggableId, dragData) {
|
216
|
+
this.draggables.set(draggableId, dragData);
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* @param {string} draggableId
|
221
|
+
*/
|
222
|
+
end(draggableId) {
|
223
|
+
this.draggables.delete(draggableId);
|
224
|
+
|
225
|
+
// Check both current AND last active dropzone
|
226
|
+
const zoneToNotify = this.activeDropZone || this.lastActiveDropZone;
|
227
|
+
|
228
|
+
if (zoneToNotify) {
|
229
|
+
const config = this.dropZones.get(zoneToNotify);
|
230
|
+
if (config && (config.isOver || config.canDrop)) {
|
231
|
+
config.isOver = false;
|
232
|
+
config.canDrop = false;
|
233
|
+
config.onDragLeave?.({
|
234
|
+
event: new DragEvent('dragend'),
|
235
|
+
zone: config.zone
|
236
|
+
});
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
this.activeDropZone = null;
|
241
|
+
this.lastActiveDropZone = null;
|
242
|
+
}
|
243
|
+
|
244
|
+
/**
|
245
|
+
* Get a drag data by draggable id
|
246
|
+
*
|
247
|
+
* @param {string} draggableId
|
248
|
+
* @returns {import('$lib/typedef/drag.js').DragData|undefined}
|
249
|
+
*/
|
250
|
+
getDraggableById(draggableId) {
|
251
|
+
return this.draggables.get(draggableId);
|
252
|
+
}
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Get a drag data. Extracts draggable id from the supplied DragEvent
|
256
|
+
*
|
257
|
+
* @param {DragEvent|SimulatedDragEvent} event
|
258
|
+
*
|
259
|
+
* @returns {Object|null} The drag data, or null for file drops
|
260
|
+
*/
|
261
|
+
getDraggable(event) {
|
262
|
+
// Check if this is a file drop first
|
263
|
+
if (event.dataTransfer && event.dataTransfer.types) {
|
264
|
+
// Check if types is an array or DOMStringList
|
265
|
+
const types = Array.from(event.dataTransfer.types);
|
266
|
+
if (types.includes('Files')) {
|
267
|
+
return null; // This is a file drop, not an internal drag
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
// For dragover events, we can't read dataTransfer.getData in Chrome
|
272
|
+
// Instead, check if we have an active drag operation
|
273
|
+
if (event.type === 'dragover'|| event.type === 'dragenter') {
|
274
|
+
if (this.draggables.size > 0) {
|
275
|
+
// Return the most recent drag operation
|
276
|
+
return this.current;
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
// For drop events, we can read the data
|
281
|
+
if (event.type === 'drop' && event.dataTransfer) {
|
282
|
+
try {
|
283
|
+
const jsonData = event.dataTransfer.getData('application/json');
|
284
|
+
if (jsonData) {
|
285
|
+
const transferData = JSON.parse(jsonData);
|
286
|
+
const draggableId = transferData.draggableId;
|
287
|
+
|
288
|
+
if (draggableId) {
|
289
|
+
return this.getDraggableById(draggableId);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
} catch (error) {
|
293
|
+
console.error('Error getting drag data from drop:', error);
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
// Fallback to checking active drags
|
298
|
+
return this.current;
|
299
|
+
}
|
300
|
+
|
301
|
+
/**
|
302
|
+
* Get the most recently started drag operation (convenience method)
|
303
|
+
* @returns {import('$lib/typedef/drag.js').DragData|undefined}
|
304
|
+
*/
|
305
|
+
get current() {
|
306
|
+
const entries = Array.from(this.draggables.entries());
|
307
|
+
return entries.length > 0 ? entries[entries.length - 1][1] : undefined;
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* @returns {boolean}
|
312
|
+
*/
|
313
|
+
isDragging() {
|
314
|
+
return this.draggables.size > 0;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
|
318
|
+
export const [createOrGetDragState, createDragState, getDragState] =
|
319
|
+
defineStateContext(DragState);
|
@@ -1,32 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Find the source draggable element from an event
|
3
|
-
*
|
4
|
-
* @param {DragEvent} event
|
5
|
-
* @returns {HTMLElement|null}
|
6
|
-
*/
|
7
|
-
export function findDraggableSource(event: DragEvent): HTMLElement | null;
|
8
|
-
/**
|
9
|
-
* Get draggable ID from an event, if available
|
10
|
-
* @param {DragEvent} event
|
11
|
-
* @returns {string|null}
|
12
|
-
*/
|
13
|
-
export function getDraggableIdFromEvent(event: DragEvent): string | null;
|
14
|
-
/**
|
15
|
-
* Process a drop event with the provided data and handlers
|
16
|
-
* @param {DragEvent} event
|
17
|
-
* @param {any} data The drag data
|
18
|
-
* @param {Object} options
|
19
|
-
* @param {Function} options.onDropStart Optional drop start handler
|
20
|
-
* @param {Function} options.onDrop Main drop handler
|
21
|
-
* @param {Function} options.onDropEnd Optional drop end handler
|
22
|
-
* @param {string} options.zone The drop zone identifier
|
23
|
-
* @param {Function} options.setState Function to update component state
|
24
|
-
* @returns {Promise<boolean>} Success status
|
25
|
-
*/
|
26
|
-
export function processDropWithData(event: DragEvent, data: any, { onDropStart, onDrop, onDropEnd, zone, setState }: {
|
27
|
-
onDropStart: Function;
|
28
|
-
onDrop: Function;
|
29
|
-
onDropEnd: Function;
|
30
|
-
zone: string;
|
31
|
-
setState: Function;
|
32
|
-
}): Promise<boolean>;
|
@@ -1,85 +1,85 @@
|
|
1
|
-
import { createOrGetDragState } from './drag-state.svelte.js';
|
1
|
+
// import { createOrGetDragState } from './drag-state.svelte.js';
|
2
2
|
|
3
|
-
/**
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
export function findDraggableSource(event) {
|
10
|
-
|
3
|
+
// /**
|
4
|
+
// * Find the source draggable element from an event
|
5
|
+
// *
|
6
|
+
// * @param {DragEvent} event
|
7
|
+
// * @returns {HTMLElement|null}
|
8
|
+
// */
|
9
|
+
// export function findDraggableSource(event) {
|
10
|
+
// const target = /** @type {Element|EventTarget|null} */ (event.target);
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
// if (!(target instanceof Element)) {
|
13
|
+
// return null;
|
14
|
+
// }
|
15
15
|
|
16
|
-
|
16
|
+
// let element = /** @type {Element|null} */ (target);
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
// // Walk up the DOM tree
|
19
|
+
// while (element !== null && element !== document.body) {
|
20
|
+
// if (element.hasAttribute('data-id')) {
|
21
|
+
// // Return as HTMLElement if needed
|
22
|
+
// return /** @type {HTMLElement} */ (element);
|
23
|
+
// }
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
// element = element.parentElement;
|
26
|
+
// }
|
27
27
|
|
28
|
-
|
29
|
-
}
|
28
|
+
// return null;
|
29
|
+
// }
|
30
30
|
|
31
|
-
/**
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
export function getDraggableIdFromEvent(event) {
|
37
|
-
|
38
|
-
|
39
|
-
}
|
31
|
+
// /**
|
32
|
+
// * Get draggable ID from an event, if available
|
33
|
+
// * @param {DragEvent} event
|
34
|
+
// * @returns {string|null}
|
35
|
+
// */
|
36
|
+
// export function getDraggableIdFromEvent(event) {
|
37
|
+
// const element = findDraggableSource(event);
|
38
|
+
// return element ? element.getAttribute('data-id') : null;
|
39
|
+
// }
|
40
40
|
|
41
|
-
/**
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
export async function processDropWithData(
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
) {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
41
|
+
// /**
|
42
|
+
// * Process a drop event with the provided data and handlers
|
43
|
+
// * @param {DragEvent} event
|
44
|
+
// * @param {any} data The drag data
|
45
|
+
// * @param {Object} options
|
46
|
+
// * @param {Function} options.onDropStart Optional drop start handler
|
47
|
+
// * @param {Function} options.onDrop Main drop handler
|
48
|
+
// * @param {Function} options.onDropEnd Optional drop end handler
|
49
|
+
// * @param {string} options.zone The drop zone identifier
|
50
|
+
// * @param {Function} options.setState Function to update component state
|
51
|
+
// * @returns {Promise<boolean>} Success status
|
52
|
+
// */
|
53
|
+
// export async function processDropWithData(
|
54
|
+
// event,
|
55
|
+
// data,
|
56
|
+
// { onDropStart, onDrop, onDropEnd, zone, setState }
|
57
|
+
// ) {
|
58
|
+
// try {
|
59
|
+
// // Update state and notify listeners
|
60
|
+
// setState('ACTIVE_DROP');
|
61
|
+
// onDropStart?.({ event, zone, data });
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
// // Call the onDrop handler
|
64
|
+
// const dropResult = onDrop?.({
|
65
|
+
// event,
|
66
|
+
// zone,
|
67
|
+
// item: data.item,
|
68
|
+
// source: data.source,
|
69
|
+
// metadata: data.metadata
|
70
|
+
// });
|
71
71
|
|
72
|
-
|
73
|
-
|
72
|
+
// // Handle async or sync results
|
73
|
+
// await Promise.resolve(dropResult);
|
74
74
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
}
|
75
|
+
// // Success path
|
76
|
+
// setState('READY');
|
77
|
+
// onDropEnd?.({ event, zone, data, success: true });
|
78
|
+
// return true;
|
79
|
+
// } catch (error) {
|
80
|
+
// // Error path
|
81
|
+
// setState('READY');
|
82
|
+
// onDropEnd?.({ event, zone, data, success: false, error });
|
83
|
+
// return false;
|
84
|
+
// }
|
85
|
+
// }
|