@hkdigital/lib-sveltekit 0.1.92 → 0.1.93
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 +77 -0
- package/dist/components/drag-drop/Draggable.svelte +221 -70
- package/dist/components/drag-drop/DropZone.svelte +76 -365
- package/dist/components/drag-drop/drag-state.svelte.d.ts +66 -1
- package/dist/components/drag-drop/drag-state.svelte.js +276 -3
- package/dist/components/drag-drop/util.js +7 -7
- package/dist/components/layout/grid-layers/GridLayers.svelte +5 -2
- package/dist/features/image-box/ImageBox.svelte +1 -1
- package/dist/features/image-box/ImageBox.svelte.d.ts +1 -1
- package/dist/themes/hkdev/components/drag-drop/drop-zone.css +2 -2
- package/dist/typedef/drag.d.ts +14 -0
- package/dist/typedef/drag.js +15 -0
- package/dist/typedef/drop.d.ts +1 -1
- package/dist/typedef/drop.js +1 -1
- package/dist/typedef/image.d.ts +1 -0
- package/dist/typedef/image.js +25 -0
- package/package.json +1 -1
@@ -15,10 +15,87 @@
|
|
15
15
|
// Create the state context at this level to ensure all children
|
16
16
|
// have access to the same state instance
|
17
17
|
const dragState = createDragState(contextKey);
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Handle drag enter at context level
|
21
|
+
* @param {DragEvent} event
|
22
|
+
*/
|
23
|
+
function onDragEnter(event) {
|
24
|
+
event.preventDefault();
|
25
|
+
dragState.updateActiveDropZone(event.clientX, event.clientY, event);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Handle drag over at context level
|
30
|
+
* @param {DragEvent} event
|
31
|
+
*/
|
32
|
+
function onDragOver(event) {
|
33
|
+
event.preventDefault();
|
34
|
+
dragState.updateActiveDropZone(event.clientX, event.clientY, event);
|
35
|
+
|
36
|
+
// Set appropriate drop effect
|
37
|
+
const activeZone = dragState.activeDropZone;
|
38
|
+
if (activeZone) {
|
39
|
+
const config = dragState.dropZones.get(activeZone);
|
40
|
+
if (config?.canDrop) {
|
41
|
+
event.dataTransfer.dropEffect = 'move';
|
42
|
+
} else {
|
43
|
+
event.dataTransfer.dropEffect = 'none';
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Handle drag leave at context level
|
50
|
+
* @param {DragEvent} event
|
51
|
+
*/
|
52
|
+
function onDragLeave(event) {
|
53
|
+
// Only handle if we're leaving the entire context
|
54
|
+
const rect =
|
55
|
+
/** @type {Element} */ (event.currentTarget).getBoundingClientRect();
|
56
|
+
|
57
|
+
|
58
|
+
const x = event.clientX;
|
59
|
+
const y = event.clientY;
|
60
|
+
|
61
|
+
// Check if we're truly leaving the context bounds
|
62
|
+
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
|
63
|
+
dragState.updateActiveDropZone(-1, -1, event);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
/**
|
68
|
+
* Handle drop at context level
|
69
|
+
* @param {DragEvent} event
|
70
|
+
*/
|
71
|
+
function onDrop(event) {
|
72
|
+
event.preventDefault();
|
73
|
+
dragState.handleDropAtPoint(event.clientX, event.clientY, event);
|
74
|
+
}
|
75
|
+
|
76
|
+
/**
|
77
|
+
* Handle drag end to clean up
|
78
|
+
* @param {DragEvent} event
|
79
|
+
*/
|
80
|
+
function onDragEnd(event) {
|
81
|
+
// This will trigger cleanup in drag state
|
82
|
+
dragState.updateActiveDropZone(-1, -1, event);
|
83
|
+
}
|
18
84
|
</script>
|
19
85
|
|
20
86
|
<div
|
21
87
|
data-component="drag-drop-context"
|
88
|
+
ondragenter={onDragEnter}
|
89
|
+
ondragover={onDragOver}
|
90
|
+
ondragleave={onDragLeave}
|
91
|
+
ondrop={onDrop}
|
92
|
+
ondragend={onDragEnd}
|
93
|
+
ontouchmove={(e) => {
|
94
|
+
// Prevent scrolling if we're dragging
|
95
|
+
if (dragState.isDragging()) {
|
96
|
+
e.preventDefault();
|
97
|
+
}
|
98
|
+
}}
|
22
99
|
class="{base} {classes}"
|
23
100
|
{...attrs}
|
24
101
|
>
|
@@ -12,6 +12,9 @@
|
|
12
12
|
DRAG_DISABLED
|
13
13
|
} from '../../constants/state-labels/drag-states.js';
|
14
14
|
|
15
|
+
|
16
|
+
/** @typedef {import('../../typedef').SimulatedDragEvent} SimulatedDragEvent */
|
17
|
+
|
15
18
|
/**
|
16
19
|
* @type {{
|
17
20
|
* item: any,
|
@@ -162,79 +165,83 @@
|
|
162
165
|
startDrag(event);
|
163
166
|
}
|
164
167
|
|
165
|
-
/**
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
function startDrag(event) {
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
168
|
+
/**
|
169
|
+
* Start the drag operation
|
170
|
+
* @param {DragEvent} event - The drag event
|
171
|
+
*/
|
172
|
+
function startDrag(event) {
|
173
|
+
// Get the element's bounding rectangle
|
174
|
+
const rect = draggableElement.getBoundingClientRect();
|
175
|
+
|
176
|
+
// Calculate grab offsets - this is where the user grabbed the element
|
177
|
+
dragOffsetX = event.clientX - rect.left;
|
178
|
+
dragOffsetY = event.clientY - rect.top;
|
179
|
+
|
180
|
+
// Create drag data with draggableId included
|
181
|
+
const dragData = {
|
182
|
+
draggableId,
|
183
|
+
offsetX: dragOffsetX,
|
184
|
+
offsetY: dragOffsetY,
|
185
|
+
item,
|
186
|
+
source,
|
187
|
+
group
|
188
|
+
};
|
189
|
+
|
190
|
+
// Set shared drag state
|
191
|
+
dragState.start(draggableId, dragData);
|
192
|
+
|
193
|
+
// Set minimal data transfer for browser drag and drop API
|
194
|
+
event.dataTransfer.effectAllowed = 'move';
|
195
|
+
event.dataTransfer.setData(
|
196
|
+
'application/json',
|
197
|
+
JSON.stringify({ draggableId })
|
198
|
+
);
|
199
|
+
|
200
|
+
// Create the preview controller
|
201
|
+
const previewController = new DragController(event);
|
202
|
+
|
203
|
+
// Function to get the preview controller
|
204
|
+
const getController = () => previewController;
|
205
|
+
|
206
|
+
// Call onDragStart with the getController function
|
207
|
+
onDragStart?.({ event, item, source, group, getController });
|
208
|
+
|
209
|
+
// Apply drag preview if available
|
210
|
+
if (draggingSnippet && !previewController.hasCustomPreview()) {
|
211
|
+
try {
|
212
|
+
// Store rectangle information for the snippet
|
213
|
+
elementRect = rect;
|
214
|
+
|
215
|
+
// These offsets represent where the user grabbed the element relative to its top-left corner
|
216
|
+
dragOffsetX = event.clientX - rect.left;
|
217
|
+
dragOffsetY = event.clientY - rect.top;
|
218
|
+
|
219
|
+
// Set initial position - this places the preview at the element's original position
|
220
|
+
previewX = rect.left;
|
221
|
+
previewY = rect.top;
|
222
|
+
|
223
|
+
// Set a transparent 1x1 pixel image to hide browser's default preview
|
224
|
+
const emptyImg = new Image();
|
225
|
+
emptyImg.src =
|
226
|
+
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|
227
|
+
event.dataTransfer.setDragImage(emptyImg, 0, 0);
|
228
|
+
|
229
|
+
// Add document level event listener to track mouse movement
|
230
|
+
document.addEventListener('dragover', handleDocumentDragOver);
|
231
|
+
|
232
|
+
// Show custom preview
|
233
|
+
showPreview = true;
|
234
|
+
customPreviewSet = true;
|
235
|
+
} catch (err) {
|
236
|
+
console.error('Error setting up custom preview:', err);
|
237
|
+
// Fallback to default preview
|
238
|
+
previewController.applyDefaultPreview();
|
239
|
+
}
|
240
|
+
} else if (!previewController.hasCustomPreview()) {
|
241
|
+
// Apply default preview if no custom preview was set
|
231
242
|
previewController.applyDefaultPreview();
|
232
243
|
}
|
233
|
-
} else if (!previewController.hasCustomPreview()) {
|
234
|
-
// Apply default preview if no custom preview was set
|
235
|
-
previewController.applyDefaultPreview();
|
236
244
|
}
|
237
|
-
}
|
238
245
|
|
239
246
|
/**
|
240
247
|
* Handle during drag
|
@@ -302,6 +309,140 @@ function startDrag(event) {
|
|
302
309
|
currentState = IDLE;
|
303
310
|
}
|
304
311
|
}
|
312
|
+
|
313
|
+
// Add these variables for touch handling
|
314
|
+
let touchDragging = $state(false);
|
315
|
+
let touchStartX = 0;
|
316
|
+
let touchStartY = 0;
|
317
|
+
let touchPreviewElement = null;
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Handle touch start
|
321
|
+
* @param {TouchEvent} event
|
322
|
+
*/
|
323
|
+
function handleTouchStart(event) {
|
324
|
+
if (disabled || !canDrag(item)) return;
|
325
|
+
|
326
|
+
const touch = event.touches[0];
|
327
|
+
touchStartX = touch.clientX;
|
328
|
+
touchStartY = touch.clientY;
|
329
|
+
|
330
|
+
// Start drag after a small delay to distinguish from scrolling
|
331
|
+
dragTimeout = setTimeout(() => {
|
332
|
+
touchDragging = true;
|
333
|
+
currentState = DRAGGING;
|
334
|
+
|
335
|
+
// Create drag data
|
336
|
+
const rect = draggableElement.getBoundingClientRect();
|
337
|
+
dragOffsetX = touch.clientX - rect.left;
|
338
|
+
dragOffsetY = touch.clientY - rect.top;
|
339
|
+
|
340
|
+
const dragData = {
|
341
|
+
draggableId,
|
342
|
+
offsetX: dragOffsetX,
|
343
|
+
offsetY: dragOffsetY,
|
344
|
+
item,
|
345
|
+
source,
|
346
|
+
group
|
347
|
+
};
|
348
|
+
|
349
|
+
dragState.start(draggableId, dragData);
|
350
|
+
|
351
|
+
// Show preview
|
352
|
+
if (draggingSnippet) {
|
353
|
+
elementRect = rect;
|
354
|
+
previewX = rect.left;
|
355
|
+
previewY = rect.top;
|
356
|
+
showPreview = true;
|
357
|
+
}
|
358
|
+
|
359
|
+
// Prevent scrolling while dragging
|
360
|
+
event.preventDefault();
|
361
|
+
|
362
|
+
// Add document-level touch handlers
|
363
|
+
document.addEventListener('touchmove', handleTouchMove, {
|
364
|
+
passive: false
|
365
|
+
});
|
366
|
+
document.addEventListener('touchend', handleTouchEnd);
|
367
|
+
}, 150); // 150ms delay to distinguish from scrolling
|
368
|
+
}
|
369
|
+
|
370
|
+
/**
|
371
|
+
* Handle touch move
|
372
|
+
* @param {TouchEvent} event
|
373
|
+
*/
|
374
|
+
function handleTouchMove(event) {
|
375
|
+
if (!touchDragging) return;
|
376
|
+
|
377
|
+
event.preventDefault();
|
378
|
+
const touch = event.touches[0];
|
379
|
+
|
380
|
+
// Update preview position
|
381
|
+
if (showPreview) {
|
382
|
+
previewX = touch.clientX - dragOffsetX;
|
383
|
+
previewY = touch.clientY - dragOffsetY;
|
384
|
+
}
|
385
|
+
|
386
|
+
/** @type {SimulatedDragEvent} */
|
387
|
+
const simulatedEvent = {
|
388
|
+
type: 'dragover',
|
389
|
+
clientX: touch.clientX,
|
390
|
+
clientY: touch.clientY,
|
391
|
+
dataTransfer: {
|
392
|
+
types: ['application/json'],
|
393
|
+
getData: () => JSON.stringify({ draggableId }),
|
394
|
+
dropEffect: 'move',
|
395
|
+
effectAllowed: 'move',
|
396
|
+
files: []
|
397
|
+
},
|
398
|
+
preventDefault: () => {},
|
399
|
+
stopPropagation: () => {}
|
400
|
+
};
|
401
|
+
|
402
|
+
// Update active dropzone in drag state
|
403
|
+
dragState.updateActiveDropZone(touch.clientX, touch.clientY, simulatedEvent);
|
404
|
+
}
|
405
|
+
|
406
|
+
/**
|
407
|
+
* Handle touch end
|
408
|
+
* @param {TouchEvent} event
|
409
|
+
*/
|
410
|
+
function handleTouchEnd(event) {
|
411
|
+
clearTimeout(dragTimeout);
|
412
|
+
|
413
|
+
if (!touchDragging) return;
|
414
|
+
|
415
|
+
const touch = event.changedTouches[0];
|
416
|
+
|
417
|
+
/** @type {SimulatedDragEvent} */
|
418
|
+
const simulatedEvent = {
|
419
|
+
type: 'drop',
|
420
|
+
clientX: touch.clientX,
|
421
|
+
clientY: touch.clientY,
|
422
|
+
dataTransfer: {
|
423
|
+
types: ['application/json'],
|
424
|
+
getData: () => JSON.stringify({ draggableId }),
|
425
|
+
dropEffect: 'move',
|
426
|
+
effectAllowed: 'move',
|
427
|
+
files: []
|
428
|
+
},
|
429
|
+
preventDefault: () => {}, // Add this!
|
430
|
+
stopPropagation: () => {} // And this!
|
431
|
+
};
|
432
|
+
|
433
|
+
// Trigger drop at final touch position
|
434
|
+
dragState.handleDropAtPoint(touch.clientX, touch.clientY, simulatedEvent);
|
435
|
+
|
436
|
+
// Clean up
|
437
|
+
touchDragging = false;
|
438
|
+
currentState = IDLE;
|
439
|
+
showPreview = false;
|
440
|
+
dragState.end(draggableId);
|
441
|
+
|
442
|
+
// Remove document handlers
|
443
|
+
document.removeEventListener('touchmove', handleTouchMove);
|
444
|
+
document.removeEventListener('touchend', handleTouchEnd);
|
445
|
+
}
|
305
446
|
</script>
|
306
447
|
|
307
448
|
<div
|
@@ -313,7 +454,9 @@ function startDrag(event) {
|
|
313
454
|
ondragend={handleDragEnd}
|
314
455
|
onmousedown={handleMouseDown}
|
315
456
|
onmouseup={handleMouseUp}
|
457
|
+
ontouchstart={handleTouchStart}
|
316
458
|
class="{base} {classes} {stateClasses}"
|
459
|
+
style="touch-action: none;"
|
317
460
|
{...attrs}
|
318
461
|
>
|
319
462
|
{@render children()}
|
@@ -329,3 +472,11 @@ function startDrag(event) {
|
|
329
472
|
{@render draggingSnippet({ element: draggableElement, rect: elementRect })}
|
330
473
|
</div>
|
331
474
|
{/if}
|
475
|
+
|
476
|
+
<style>
|
477
|
+
[data-component='draggable'] {
|
478
|
+
-webkit-touch-callout: none;
|
479
|
+
-webkit-user-select: none;
|
480
|
+
user-select: none;
|
481
|
+
}
|
482
|
+
</style>
|