@hkdigital/lib-sveltekit 0.2.5 → 0.2.7

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.
@@ -34,7 +34,7 @@
34
34
 
35
35
  import { EventEmitter } from '../events';
36
36
 
37
- import { DEBUG, INFO, WARN, ERROR, FATAL, NONE, LEVELS } from './constants.js';
37
+ import { DEBUG, INFO, WARN, ERROR, FATAL, LEVELS } from './constants.js';
38
38
 
39
39
  /**
40
40
  * Logger class for consistent logging across services
@@ -12,26 +12,6 @@ export class DragController {
12
12
  targetElement: HTMLElement;
13
13
  offsetX: number;
14
14
  offsetY: number;
15
- _previewSet: boolean;
16
- /**
17
- * Create a preview image from the current draggable element or a specific child element
18
- * @param {string} [selector] - Optional CSS selector to target a specific child element
19
- * @returns {HTMLElement} - The created preview element
20
- */
21
- grabPreviewImage(selector?: string): HTMLElement;
22
- /**
23
- * Set a custom element as the drag preview image
24
- * @param {HTMLElement} element - Element to use as drag preview
25
- * @param {number} [offsetX] - Horizontal offset (uses natural offset if omitted)
26
- * @param {number} [offsetY] - Vertical offset (uses natural offset if omitted)
27
- * @returns {boolean} - Whether setting the preview was successful
28
- */
29
- setPreviewImage(element: HTMLElement, offsetX?: number, offsetY?: number): boolean;
30
- /**
31
- * Check if a custom preview has been set
32
- * @returns {boolean}
33
- */
34
- hasCustomPreview(): boolean;
35
15
  /**
36
16
  * Apply the default preview (uses the draggable element itself)
37
17
  * @returns {boolean}
@@ -15,74 +15,6 @@ export class DragController {
15
15
  const rect = this.targetElement.getBoundingClientRect();
16
16
  this.offsetX = event.clientX - rect.left;
17
17
  this.offsetY = event.clientY - rect.top;
18
-
19
- this._previewSet = false;
20
- }
21
-
22
- /**
23
- * Create a preview image from the current draggable element or a specific child element
24
- * @param {string} [selector] - Optional CSS selector to target a specific child element
25
- * @returns {HTMLElement} - The created preview element
26
- */
27
- grabPreviewImage(selector = null) {
28
- // Find the source element (either the whole draggable or a specific child)
29
- const sourceElement = selector
30
- ? this.targetElement.querySelector(selector)
31
- : this.targetElement;
32
-
33
- if (!sourceElement) {
34
- console.error(`Element with selector "${selector}" not found`);
35
- return this.grabPreviewImage(); // Fallback to the main element
36
- }
37
-
38
- // Clone the element to create the preview
39
- const previewElement = /** @type {HTMLElement} */ (
40
- sourceElement.cloneNode(true)
41
- );
42
-
43
- // Position off-screen (needed for setDragImage to work properly)
44
- previewElement.style.position = 'absolute';
45
- previewElement.style.top = '-9999px';
46
- previewElement.style.left = '-9999px';
47
-
48
- // Add to the document temporarily
49
- document.body.appendChild(previewElement);
50
-
51
- return previewElement;
52
- }
53
-
54
- /**
55
- * Set a custom element as the drag preview image
56
- * @param {HTMLElement} element - Element to use as drag preview
57
- * @param {number} [offsetX] - Horizontal offset (uses natural offset if omitted)
58
- * @param {number} [offsetY] - Vertical offset (uses natural offset if omitted)
59
- * @returns {boolean} - Whether setting the preview was successful
60
- */
61
- setPreviewImage(element, offsetX, offsetY) {
62
- if (!this.dataTransfer || !this.dataTransfer.setDragImage) {
63
- return false;
64
- }
65
-
66
- // Use provided offsets or fall back to natural offsets
67
- const finalOffsetX = offsetX !== undefined ? offsetX : this.offsetX;
68
- const finalOffsetY = offsetY !== undefined ? offsetY : this.offsetY;
69
-
70
- try {
71
- this.dataTransfer.setDragImage(element, finalOffsetX, finalOffsetY);
72
- this._previewSet = true;
73
- return true;
74
- } catch (err) {
75
- console.error('Failed to set drag preview image:', err);
76
- return false;
77
- }
78
- }
79
-
80
- /**
81
- * Check if a custom preview has been set
82
- * @returns {boolean}
83
- */
84
- hasCustomPreview() {
85
- return this._previewSet;
86
18
  }
87
19
 
88
20
  /**
@@ -92,8 +24,7 @@ export class DragController {
92
24
  applyDefaultPreview() {
93
25
  if (
94
26
  !this.dataTransfer ||
95
- !this.dataTransfer.setDragImage ||
96
- this._previewSet
27
+ !this.dataTransfer.setDragImage
97
28
  ) {
98
29
  return false;
99
30
  }
@@ -25,7 +25,7 @@
25
25
  dragState.updateActiveDropZone(event.clientX, event.clientY, event);
26
26
  }
27
27
 
28
- /**
28
+ /**
29
29
  * Handle drag over at context level
30
30
  * @param {DragEvent} event
31
31
  */
@@ -33,15 +33,22 @@
33
33
  event.preventDefault();
34
34
  dragState.updateActiveDropZone(event.clientX, event.clientY, event);
35
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';
36
+ // Set appropriate drop effect based on current drag operation
37
+ if (dragState.isDragging()) {
38
+ const activeZone = dragState.activeDropZone;
39
+ if (activeZone) {
40
+ const config = dragState.dropZones.get(activeZone);
41
+ if (config?.canDrop) {
42
+ event.dataTransfer.dropEffect = 'move';
43
+ } else {
44
+ event.dataTransfer.dropEffect = 'none';
45
+ }
42
46
  } else {
43
47
  event.dataTransfer.dropEffect = 'none';
44
48
  }
49
+ } else {
50
+ // No internal drag operation, might be file drag
51
+ event.dataTransfer.dropEffect = 'copy';
45
52
  }
46
53
  }
47
54
 
@@ -23,7 +23,10 @@
23
23
  * dragDelay?: number,
24
24
  * base?: string,
25
25
  * classes?: string,
26
- * children: import('svelte').Snippet,
26
+ * children: import('svelte').Snippet<[{
27
+ * element: HTMLElement,
28
+ * rect: DOMRect
29
+ * }]>,
27
30
  * draggingSnippet?: import('svelte').Snippet<[{
28
31
  * element: HTMLElement,
29
32
  * rect: DOMRect
@@ -206,6 +209,9 @@ let stateObject = $derived({
206
209
  JSON.stringify({ draggableId })
207
210
  );
208
211
 
212
+ // Chrome also likes to have text/plain
213
+ event.dataTransfer.setData('text/plain', draggableId);
214
+
209
215
  // Create the preview controller
210
216
  const previewController = new DragController(event);
211
217
 
@@ -216,8 +222,8 @@ let stateObject = $derived({
216
222
  onDragStart?.({ event, item, source, group, getController });
217
223
 
218
224
  // Apply drag preview if available
219
- if (draggingSnippet && !previewController.hasCustomPreview()) {
220
- try {
225
+ // if (draggingSnippet) {
226
+ // try {
221
227
  // Store rectangle information for the snippet
222
228
  elementRect = rect;
223
229
 
@@ -229,10 +235,20 @@ let stateObject = $derived({
229
235
  previewX = rect.left;
230
236
  previewY = rect.top;
231
237
 
232
- // Set a transparent 1x1 pixel image to hide browser's default preview
238
+ // Set a transparent 1x1 pixel image to hide browser's
239
+ // default preview
233
240
  const emptyImg = new Image();
234
241
  emptyImg.src =
235
- 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
242
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
243
+
244
+ // Chrome needs the image to be loaded before setting it
245
+ emptyImg.onload = () => {
246
+ if (event.dataTransfer) {
247
+ event.dataTransfer.setDragImage(emptyImg, 0, 0);
248
+ }
249
+ };
250
+
251
+ // Fallback: try to set it immediately too
236
252
  event.dataTransfer.setDragImage(emptyImg, 0, 0);
237
253
 
238
254
  // Add document level event listener to track mouse movement
@@ -241,15 +257,15 @@ let stateObject = $derived({
241
257
  // Show custom preview
242
258
  showPreview = true;
243
259
  customPreviewSet = true;
244
- } catch (err) {
245
- console.error('Error setting up custom preview:', err);
246
- // Fallback to default preview
247
- previewController.applyDefaultPreview();
248
- }
249
- } else if (!previewController.hasCustomPreview()) {
250
- // Apply default preview if no custom preview was set
251
- previewController.applyDefaultPreview();
252
- }
260
+ // } catch (err) {
261
+ // console.error('Error setting up custom preview:', err);
262
+ // // Fallback to default preview
263
+ // previewController.applyDefaultPreview();
264
+ // }
265
+ // } else {
266
+ // // Apply default preview if no custom preview was set
267
+ // previewController.applyDefaultPreview();
268
+ // }
253
269
  }
254
270
 
255
271
  /**
@@ -358,12 +374,12 @@ let stateObject = $derived({
358
374
  dragState.start(draggableId, dragData);
359
375
 
360
376
  // Show preview
361
- if (draggingSnippet) {
377
+ // if (draggingSnippet) {
362
378
  elementRect = rect;
363
379
  previewX = rect.left;
364
380
  previewY = rect.top;
365
381
  showPreview = true;
366
- }
382
+ // }
367
383
 
368
384
  // Prevent scrolling while dragging
369
385
  event.preventDefault();
@@ -398,7 +414,7 @@ function handleTouchMove(event) {
398
414
  clientX: touch.clientX,
399
415
  clientY: touch.clientY,
400
416
  dataTransfer: {
401
- types: ['application/json'],
417
+ types: ['application/json', 'text/plain'],
402
418
  getData: () => JSON.stringify({ draggableId }),
403
419
  dropEffect: 'move',
404
420
  effectAllowed: 'move',
@@ -429,7 +445,7 @@ function handleTouchMove(event) {
429
445
  clientX: touch.clientX,
430
446
  clientY: touch.clientY,
431
447
  dataTransfer: {
432
- types: ['application/json'],
448
+ types: ['application/json', 'text'],
433
449
  getData: () => JSON.stringify({ draggableId }),
434
450
  dropEffect: 'move',
435
451
  effectAllowed: 'move',
@@ -468,10 +484,10 @@ function handleTouchMove(event) {
468
484
  style="touch-action: none;"
469
485
  {...attrs}
470
486
  >
471
- {@render children()}
487
+ {@render children({ element: draggableElement, rect: elementRect })}
472
488
  </div>
473
489
 
474
- {#if draggingSnippet && showPreview && elementRect}
490
+ {#if showPreview && elementRect}
475
491
  <div
476
492
  data-companion="drag-preview-follower"
477
493
  class={stateClasses}
@@ -479,7 +495,11 @@ function handleTouchMove(event) {
479
495
  style:left="{previewX}px"
480
496
  style:top="{previewY}px"
481
497
  >
498
+ {#if draggingSnippet}
482
499
  {@render draggingSnippet({ element: draggableElement, rect: elementRect })}
500
+ {:else}
501
+ {@render children({ element: draggableElement, rect: elementRect })}
502
+ {/if}
483
503
  </div>
484
504
  {/if}
485
505
 
@@ -10,7 +10,10 @@ type Draggable = {
10
10
  dragDelay?: number;
11
11
  base?: string;
12
12
  classes?: string;
13
- children: Snippet<[]>;
13
+ children: Snippet<[{
14
+ element: HTMLElement;
15
+ rect: DOMRect;
16
+ }]>;
14
17
  draggingSnippet?: Snippet<[{
15
18
  element: HTMLElement;
16
19
  rect: DOMRect;
@@ -52,7 +55,10 @@ declare const Draggable: import("svelte").Component<{
52
55
  dragDelay?: number;
53
56
  base?: string;
54
57
  classes?: string;
55
- children: import("svelte").Snippet;
58
+ children: import("svelte").Snippet<[{
59
+ element: HTMLElement;
60
+ rect: DOMRect;
61
+ }]>;
56
62
  draggingSnippet?: import("svelte").Snippet<[{
57
63
  element: HTMLElement;
58
64
  rect: DOMRect;
@@ -259,8 +259,26 @@ class DragState {
259
259
  * @returns {Object|null} The drag data, or null for file drops
260
260
  */
261
261
  getDraggable(event) {
262
- // Check if this is a touch-simulated event
263
- if (event.dataTransfer && !event.dataTransfer.files) {
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') {
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) {
264
282
  try {
265
283
  const jsonData = event.dataTransfer.getData('application/json');
266
284
  if (jsonData) {
@@ -272,34 +290,12 @@ class DragState {
272
290
  }
273
291
  }
274
292
  } catch (error) {
275
- console.error('Error getting drag data:', error);
276
- }
277
- }
278
-
279
- // Check if this is a file drop
280
- if (event.dataTransfer.types.includes('Files')) {
281
- return null;
282
- }
283
-
284
- // Handle internal drag operations
285
- try {
286
- const jsonData = event.dataTransfer.getData('application/json');
287
- if (jsonData) {
288
- const transferData = JSON.parse(jsonData);
289
- const draggableId = transferData.draggableId;
290
-
291
- if (draggableId) {
292
- const dragData = this.getDraggableById(draggableId);
293
- if (dragData) {
294
- return dragData;
295
- }
296
- }
293
+ console.error('Error getting drag data from drop:', error);
297
294
  }
298
- } catch (error) {
299
- console.error('Error getting drag data:', error);
300
295
  }
301
296
 
302
- return null;
297
+ // Fallback to checking active drags
298
+ return this.current;
303
299
  }
304
300
 
305
301
  /**