@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.
@@ -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
- * Start the drag operation
167
- * @param {DragEvent} event - The drag event
168
- */
169
- function startDrag(event) {
170
- // Get the element's bounding rectangle
171
- const rect = draggableElement.getBoundingClientRect();
172
-
173
- // Calculate grab offsets - this is where the user grabbed the element
174
- dragOffsetX = event.clientX - rect.left;
175
- dragOffsetY = event.clientY - rect.top;
176
-
177
- // Create drag data with draggableId included
178
- const dragData = {
179
- draggableId,
180
- offsetX: dragOffsetX,
181
- offsetY: dragOffsetY,
182
- item,
183
- source,
184
- group
185
- };
186
-
187
- // Set shared drag state
188
- dragState.start(draggableId, dragData);
189
-
190
- // Set minimal data transfer for browser drag and drop API
191
- event.dataTransfer.effectAllowed = 'move';
192
- event.dataTransfer.setData('application/json', JSON.stringify({ draggableId }));
193
-
194
- // Create the preview controller
195
- const previewController = new DragController(event);
196
-
197
- // Function to get the preview controller
198
- const getController = () => previewController;
199
-
200
- // Call onDragStart with the getController function
201
- onDragStart?.({ event, item, source, group, getController });
202
-
203
- // Apply drag preview if available
204
- if (draggingSnippet && !previewController.hasCustomPreview()) {
205
- try {
206
- // Store rectangle information for the snippet
207
- elementRect = rect;
208
-
209
- // These offsets represent where the user grabbed the element relative to its top-left corner
210
- dragOffsetX = event.clientX - rect.left;
211
- dragOffsetY = event.clientY - rect.top;
212
-
213
- // Set initial position - this places the preview at the element's original position
214
- previewX = rect.left;
215
- previewY = rect.top;
216
-
217
- // Set a transparent 1x1 pixel image to hide browser's default preview
218
- const emptyImg = new Image();
219
- emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
220
- event.dataTransfer.setDragImage(emptyImg, 0, 0);
221
-
222
- // Add document level event listener to track mouse movement
223
- document.addEventListener('dragover', handleDocumentDragOver);
224
-
225
- // Show custom preview
226
- showPreview = true;
227
- customPreviewSet = true;
228
- } catch (err) {
229
- console.error('Error setting up custom preview:', err);
230
- // Fallback to default preview
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>