tiny-essentials 1.12.0 → 1.12.2

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.
Files changed (32) hide show
  1. package/dist/legacy/firebase/index.mjs +5 -5
  2. package/dist/v1/TinyBasicsEs.js +1 -1257
  3. package/dist/v1/TinyBasicsEs.min.js +1 -1
  4. package/dist/v1/TinyDragger.js +148 -43
  5. package/dist/v1/TinyDragger.min.js +1 -1
  6. package/dist/v1/TinyEssentials.js +484 -189
  7. package/dist/v1/TinyEssentials.min.js +1 -1
  8. package/dist/v1/TinyUploadClicker.js +478 -190
  9. package/dist/v1/TinyUploadClicker.min.js +1 -1
  10. package/dist/v1/basics/index.cjs +0 -23
  11. package/dist/v1/basics/index.d.mts +1 -23
  12. package/dist/v1/basics/index.mjs +1 -2
  13. package/dist/v1/fileManager/asyncFuncs.cjs +272 -0
  14. package/dist/v1/fileManager/asyncFuncs.d.mts +93 -0
  15. package/dist/v1/fileManager/asyncFuncs.mjs +236 -0
  16. package/dist/v1/fileManager/index.cjs +35 -0
  17. package/dist/v1/fileManager/index.d.mts +30 -0
  18. package/dist/v1/fileManager/index.mjs +3 -0
  19. package/dist/v1/{basics/fileManager.cjs → fileManager/normalFuncs.cjs} +10 -105
  20. package/dist/v1/{basics/fileManager.d.mts → fileManager/normalFuncs.d.mts} +3 -56
  21. package/dist/v1/{basics/fileManager.mjs → fileManager/normalFuncs.mjs} +48 -131
  22. package/dist/v1/index.cjs +30 -23
  23. package/dist/v1/index.d.mts +29 -23
  24. package/dist/v1/index.mjs +3 -2
  25. package/dist/v1/libs/TinyDragger.cjs +148 -43
  26. package/dist/v1/libs/TinyDragger.d.mts +18 -2
  27. package/dist/v1/libs/TinyDragger.mjs +132 -38
  28. package/dist/v1/libs/TinyUploadClicker.cjs +1 -0
  29. package/docs/v1/README.md +3 -1
  30. package/docs/v1/{basics/fileManager.md → fileManager/main.md} +12 -0
  31. package/docs/v1/libs/TinyDragger.md +15 -0
  32. package/package.json +5 -1
@@ -23,6 +23,7 @@ class TinyDragger {
23
23
  #offsetY = 0;
24
24
  #offsetX = 0;
25
25
 
26
+ #multiCollision = false;
26
27
  #lockInsideJail = false;
27
28
  #revertOnDrop = false;
28
29
  #dragging = false;
@@ -67,6 +68,7 @@ class TinyDragger {
67
68
  * @param {string} [options.classDragCollision='dragging-collision'] - CSS class applied to collision element.
68
69
  * @param {boolean} [options.lockInsideJail=false] - Restrict movement within the jail container.
69
70
  * @param {boolean} [options.dropInJailOnly=false] - Restrict drop within the jail container.
71
+ * @param {boolean} [options.multiCollision=false] - Enables returning multiple collided elements.
70
72
  * @param {VibrationPatterns|false} [options.vibration=false] - Vibration feedback configuration.
71
73
  * @param {boolean} [options.revertOnDrop=false] - Whether to return to original position on drop.
72
74
  * @param {string} [options.classHidden='drag-hidden'] - CSS class to hide original element during dragging.
@@ -113,6 +115,7 @@ class TinyDragger {
113
115
  validateBoolean(options.lockInsideJail, 'lockInsideJail');
114
116
  validateBoolean(options.dropInJailOnly, 'dropInJailOnly');
115
117
  validateBoolean(options.revertOnDrop, 'revertOnDrop');
118
+ validateBoolean(options.multiCollision, 'multiCollision');
116
119
 
117
120
  validateString(options.classDragging, 'classDragging');
118
121
  validateString(options.classBodyDragging, 'classBodyDragging');
@@ -146,6 +149,7 @@ class TinyDragger {
146
149
  if (typeof options.revertOnDrop === 'boolean') this.#revertOnDrop = options.revertOnDrop;
147
150
  if (typeof options.lockInsideJail === 'boolean') this.#lockInsideJail = options.lockInsideJail;
148
151
  if (typeof options.dropInJailOnly === 'boolean') this.#dropInJailOnly = options.dropInJailOnly;
152
+ if (typeof options.multiCollision === 'boolean') this.#multiCollision = options.multiCollision;
149
153
 
150
154
  /** @private */
151
155
  this._onMouseDown = this.#startDrag.bind(this);
@@ -337,22 +341,67 @@ class TinyDragger {
337
341
  this.#dispatchEvent('drag');
338
342
  }
339
343
 
344
+ /** @type {HTMLElement[]} */
345
+ #collisionsMarked = [];
346
+
347
+ /**
348
+ * Marks an element as currently collided by adding the collision CSS class.
349
+ * The element is stored in an internal list for easy removal later.
350
+ *
351
+ * @param {HTMLElement|null} el - The element to mark as collided.
352
+ */
353
+ #addCollision(el) {
354
+ if (!el) return;
355
+ el.classList.add(this.#classDragCollision);
356
+ this.#collisionsMarked.push(el);
357
+ }
358
+
359
+ /**
360
+ * Removes the collision CSS class from all previously marked elements.
361
+ * Also clears the last single collision element, if set.
362
+ *
363
+ */
364
+ #removeCollision() {
365
+ while (this.#collisionsMarked.length > 0) {
366
+ const el = this.#collisionsMarked.shift();
367
+ if (el) el.classList.remove(this.#classDragCollision);
368
+ }
369
+ if (!this.#lastCollision) return;
370
+ this.#lastCollision.classList.remove(this.#classDragCollision);
371
+ }
372
+
340
373
  /**
341
374
  * Handles dragging collision.
342
375
  * @param {MouseEvent|Touch} event - The drag event.
343
376
  */
344
377
  checkDragCollision(event) {
345
- const { collidedElement: collided } = this.execCollision(event);
346
- if (collided && collided !== this.#lastCollision) {
347
- if (navigator.vibrate && Array.isArray(this.#vibration.collide)) {
348
- navigator.vibrate(this.#vibration.collide);
378
+ const { collidedElements } = this.execCollision(event);
379
+ const first = collidedElements[0] || null;
380
+
381
+ // Removes old marking if necessary
382
+ if (this.#lastCollision && !collidedElements.includes(this.#lastCollision)) {
383
+ this.#removeCollision();
384
+ }
385
+
386
+ // Adds Marking for All Colluded
387
+ collidedElements.forEach((el) => this.#addCollision(el));
388
+
389
+ // Removes markings from who no longer collided
390
+ this.#collidables.forEach((el) => {
391
+ if (!collidedElements.includes(el)) {
392
+ el.classList.remove(this.#classDragCollision);
349
393
  }
350
- this.#lastCollision = collided;
351
- if (this.#lastCollision) this.#lastCollision.classList.add(this.#classDragCollision);
352
- } else if (!collided) {
353
- if (this.#lastCollision) this.#lastCollision.classList.remove(this.#classDragCollision);
354
- this.#lastCollision = null;
394
+ });
395
+
396
+ if (
397
+ navigator.vibrate &&
398
+ Array.isArray(this.#vibration.collide) &&
399
+ collidedElements.length > 0
400
+ ) {
401
+ navigator.vibrate(this.#vibration.collide);
355
402
  }
403
+
404
+ this.#lastCollision = first;
356
405
  }
357
406
 
358
407
  /**
@@ -399,37 +448,48 @@ class TinyDragger {
399
448
  /**
400
449
  * Handles the collision of a drag.
401
450
  * @param {MouseEvent|Touch} event - The release event.
402
- * @returns {{ inJail: boolean; collidedElement: HTMLElement|null }}
451
+ * @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
403
452
  */
404
453
  execCollision(event) {
405
- if (this.#destroyed || !this.#dragProxy) return { inJail: false, collidedElement: null };
454
+ if (this.#destroyed || !this.#dragProxy) return { inJail: false, collidedElements: [] };
406
455
 
407
- let collidedElement;
456
+ let collidedElements = [];
408
457
  let inJail = true;
409
458
  const jailRect = this.#jail?.getBoundingClientRect();
459
+
410
460
  if (this.#collisionByMouse) {
411
461
  const x = event.clientX;
412
462
  const y = event.clientY;
463
+
413
464
  if (this.#dropInJailOnly && this.#jail && jailRect) {
414
465
  inJail =
415
466
  x >= jailRect.left && x <= jailRect.right && y >= jailRect.top && y <= jailRect.bottom;
416
467
  }
417
468
 
418
- collidedElement = inJail ? this.getCollidedElement(x, y) : null;
469
+ collidedElements = inJail
470
+ ? this.#multiCollision
471
+ ? this.getAllCollidedElements(x, y)
472
+ : [this.getCollidedElement(x, y)].filter(Boolean)
473
+ : [];
419
474
  } else {
420
- const targetRect = this.#dragProxy.getBoundingClientRect();
475
+ const rect = this.#dragProxy.getBoundingClientRect();
476
+
421
477
  if (this.#dropInJailOnly && this.#jail && jailRect) {
422
478
  inJail =
423
- targetRect.left >= jailRect.left &&
424
- targetRect.right <= jailRect.right &&
425
- targetRect.top >= jailRect.top &&
426
- targetRect.bottom <= jailRect.bottom;
479
+ rect.left >= jailRect.left &&
480
+ rect.right <= jailRect.right &&
481
+ rect.top >= jailRect.top &&
482
+ rect.bottom <= jailRect.bottom;
427
483
  }
428
484
 
429
- collidedElement = inJail ? this.getCollidedElementByRect(targetRect) : null;
485
+ collidedElements = inJail
486
+ ? this.#multiCollision
487
+ ? this.getAllCollidedElementsByRect(rect)
488
+ : [this.getCollidedElementByRect(rect)].filter(Boolean)
489
+ : [];
430
490
  }
431
491
 
432
- return { inJail, collidedElement };
492
+ return { inJail, collidedElements };
433
493
  }
434
494
 
435
495
  /**
@@ -452,7 +512,7 @@ class TinyDragger {
452
512
 
453
513
  document.removeEventListener('touchmove', this._onTouchMove);
454
514
  document.removeEventListener('touchend', this._onTouchEnd);
455
- const { collidedElement } = this.execCollision(event);
515
+ const { collidedElements } = this.execCollision(event);
456
516
 
457
517
  if (navigator.vibrate && Array.isArray(this.#vibration.end)) {
458
518
  navigator.vibrate(this.#vibration.end);
@@ -465,7 +525,7 @@ class TinyDragger {
465
525
  this.#dragProxy = null;
466
526
  }
467
527
 
468
- if (this.#lastCollision) this.#lastCollision.classList.remove(this.#classDragCollision);
528
+ if (this.#lastCollision) this.#removeCollision();
469
529
  this.#lastCollision = null;
470
530
 
471
531
  this.#target.classList.remove(this.#dragHiddenClass);
@@ -476,13 +536,50 @@ class TinyDragger {
476
536
 
477
537
  const dropEvent = new CustomEvent('drop', {
478
538
  detail: {
479
- target: collidedElement,
539
+ targets: collidedElements,
540
+ first: collidedElements[0] || null,
480
541
  },
481
542
  });
482
-
483
543
  this.#target.dispatchEvent(dropEvent);
484
544
  }
485
545
 
546
+ /**
547
+ * Checks if the provided element intersects with the given bounding rectangle.
548
+ *
549
+ * @param {HTMLElement} el - The element to test for collision.
550
+ * @param {DOMRect} rect - The bounding rectangle to check against.
551
+ * @returns {boolean} True if the element intersects with the rectangle.
552
+ */
553
+ #getCollidedElementByRect(el, rect) {
554
+ const elRect = el.getBoundingClientRect();
555
+ return !(
556
+ rect.right < elRect.left ||
557
+ rect.left > elRect.right ||
558
+ rect.bottom < elRect.top ||
559
+ rect.top > elRect.bottom
560
+ );
561
+ }
562
+
563
+ /**
564
+ * Returns all elements currently colliding with the given rectangle.
565
+ *
566
+ * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
567
+ * @returns {HTMLElement[]} A list of all collided elements.
568
+ * @throws {Error} If the input is not a valid DOMRect with numeric bounds.
569
+ */
570
+ getAllCollidedElementsByRect(rect) {
571
+ this.#checkDestroy();
572
+ if (
573
+ !(rect instanceof DOMRect) ||
574
+ typeof rect.left !== 'number' ||
575
+ typeof rect.right !== 'number' ||
576
+ typeof rect.top !== 'number' ||
577
+ typeof rect.bottom !== 'number'
578
+ )
579
+ throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
580
+ return this.#collidables.filter((el) => this.#getCollidedElementByRect(el, rect));
581
+ }
582
+
486
583
  /**
487
584
  * Detects collision based on rectangle intersection.
488
585
  * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
@@ -499,18 +596,32 @@ class TinyDragger {
499
596
  typeof rect.bottom !== 'number'
500
597
  )
501
598
  throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
599
+ return this.#collidables.find((el) => this.#getCollidedElementByRect(el, rect)) || null;
600
+ }
502
601
 
503
- return (
504
- this.#collidables.find((el) => {
505
- const elRect = el.getBoundingClientRect();
506
- return !(
507
- rect.right < elRect.left ||
508
- rect.left > elRect.right ||
509
- rect.bottom < elRect.top ||
510
- rect.top > elRect.bottom
511
- );
512
- }) || null
513
- );
602
+ /**
603
+ * Checks whether a given (x, y) coordinate is inside the bounding rectangle of an element.
604
+ *
605
+ * @param {HTMLElement} el - The element to test for collision.
606
+ * @param {number} x - Horizontal screen coordinate.
607
+ * @param {number} y - Vertical screen coordinate.
608
+ * @returns {boolean} True if the point is within the element's bounds.
609
+ */
610
+ #getCollidedElement(el, x, y) {
611
+ const rect = el.getBoundingClientRect();
612
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
613
+ }
614
+
615
+ /**
616
+ * @param {number} x - Horizontal screen coordinate.
617
+ * @param {number} y - Vertical screen coordinate.
618
+ * @returns {HTMLElement[]} The collided element or null.
619
+ */
620
+ getAllCollidedElements(x, y) {
621
+ this.#checkDestroy();
622
+ if (typeof x !== 'number' || typeof y !== 'number')
623
+ throw new Error('getCollidedElement expects numeric x and y coordinates.');
624
+ return this.#collidables.filter((el) => this.#getCollidedElement(el, x, y));
514
625
  }
515
626
 
516
627
  /**
@@ -523,13 +634,7 @@ class TinyDragger {
523
634
  this.#checkDestroy();
524
635
  if (typeof x !== 'number' || typeof y !== 'number')
525
636
  throw new Error('getCollidedElement expects numeric x and y coordinates.');
526
-
527
- return (
528
- this.#collidables.find((el) => {
529
- const rect = el.getBoundingClientRect();
530
- return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
531
- }) || null
532
- );
637
+ return this.#collidables.find((el) => this.#getCollidedElement(el, x, y)) || null;
533
638
  }
534
639
 
535
640
  /**
@@ -779,7 +884,7 @@ class TinyDragger {
779
884
  document.removeEventListener('touchmove', this._onTouchMove);
780
885
  document.removeEventListener('touchend', this._onTouchEnd);
781
886
 
782
- if (this.#lastCollision) this.#lastCollision.classList.remove(this.#classDragCollision);
887
+ if (this.#lastCollision) this.#removeCollision();
783
888
  if (this.#dragProxy) {
784
889
  this.#dragProxy.remove();
785
890
  this.#dragProxy = null;
@@ -43,6 +43,7 @@ declare class TinyDragger {
43
43
  * @param {string} [options.classDragCollision='dragging-collision'] - CSS class applied to collision element.
44
44
  * @param {boolean} [options.lockInsideJail=false] - Restrict movement within the jail container.
45
45
  * @param {boolean} [options.dropInJailOnly=false] - Restrict drop within the jail container.
46
+ * @param {boolean} [options.multiCollision=false] - Enables returning multiple collided elements.
46
47
  * @param {VibrationPatterns|false} [options.vibration=false] - Vibration feedback configuration.
47
48
  * @param {boolean} [options.revertOnDrop=false] - Whether to return to original position on drop.
48
49
  * @param {string} [options.classHidden='drag-hidden'] - CSS class to hide original element during dragging.
@@ -58,6 +59,7 @@ declare class TinyDragger {
58
59
  classDragCollision?: string | undefined;
59
60
  lockInsideJail?: boolean | undefined;
60
61
  dropInJailOnly?: boolean | undefined;
62
+ multiCollision?: boolean | undefined;
61
63
  vibration?: false | VibrationPatterns | undefined;
62
64
  revertOnDrop?: boolean | undefined;
63
65
  classHidden?: string | undefined;
@@ -140,12 +142,20 @@ declare class TinyDragger {
140
142
  /**
141
143
  * Handles the collision of a drag.
142
144
  * @param {MouseEvent|Touch} event - The release event.
143
- * @returns {{ inJail: boolean; collidedElement: HTMLElement|null }}
145
+ * @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
144
146
  */
145
147
  execCollision(event: MouseEvent | Touch): {
146
148
  inJail: boolean;
147
- collidedElement: HTMLElement | null;
149
+ collidedElements: (HTMLElement | null)[];
148
150
  };
151
+ /**
152
+ * Returns all elements currently colliding with the given rectangle.
153
+ *
154
+ * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
155
+ * @returns {HTMLElement[]} A list of all collided elements.
156
+ * @throws {Error} If the input is not a valid DOMRect with numeric bounds.
157
+ */
158
+ getAllCollidedElementsByRect(rect: DOMRect): HTMLElement[];
149
159
  /**
150
160
  * Detects collision based on rectangle intersection.
151
161
  * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
@@ -153,6 +163,12 @@ declare class TinyDragger {
153
163
  * @throws {Error} If rect is not a DOMRect with valid numeric properties.
154
164
  */
155
165
  getCollidedElementByRect(rect: DOMRect): HTMLElement | null;
166
+ /**
167
+ * @param {number} x - Horizontal screen coordinate.
168
+ * @param {number} y - Vertical screen coordinate.
169
+ * @returns {HTMLElement[]} The collided element or null.
170
+ */
171
+ getAllCollidedElements(x: number, y: number): HTMLElement[];
156
172
  /**
157
173
  * Detects collision with a point using element bounding rectangles.
158
174
  * @param {number} x - Horizontal screen coordinate.
@@ -17,6 +17,7 @@ class TinyDragger {
17
17
  #destroyed = false;
18
18
  #offsetY = 0;
19
19
  #offsetX = 0;
20
+ #multiCollision = false;
20
21
  #lockInsideJail = false;
21
22
  #revertOnDrop = false;
22
23
  #dragging = false;
@@ -52,6 +53,7 @@ class TinyDragger {
52
53
  * @param {string} [options.classDragCollision='dragging-collision'] - CSS class applied to collision element.
53
54
  * @param {boolean} [options.lockInsideJail=false] - Restrict movement within the jail container.
54
55
  * @param {boolean} [options.dropInJailOnly=false] - Restrict drop within the jail container.
56
+ * @param {boolean} [options.multiCollision=false] - Enables returning multiple collided elements.
55
57
  * @param {VibrationPatterns|false} [options.vibration=false] - Vibration feedback configuration.
56
58
  * @param {boolean} [options.revertOnDrop=false] - Whether to return to original position on drop.
57
59
  * @param {string} [options.classHidden='drag-hidden'] - CSS class to hide original element during dragging.
@@ -90,6 +92,7 @@ class TinyDragger {
90
92
  validateBoolean(options.lockInsideJail, 'lockInsideJail');
91
93
  validateBoolean(options.dropInJailOnly, 'dropInJailOnly');
92
94
  validateBoolean(options.revertOnDrop, 'revertOnDrop');
95
+ validateBoolean(options.multiCollision, 'multiCollision');
93
96
  validateString(options.classDragging, 'classDragging');
94
97
  validateString(options.classBodyDragging, 'classBodyDragging');
95
98
  validateString(options.classJailDragging, 'classJailDragging');
@@ -121,6 +124,8 @@ class TinyDragger {
121
124
  this.#lockInsideJail = options.lockInsideJail;
122
125
  if (typeof options.dropInJailOnly === 'boolean')
123
126
  this.#dropInJailOnly = options.dropInJailOnly;
127
+ if (typeof options.multiCollision === 'boolean')
128
+ this.#multiCollision = options.multiCollision;
124
129
  /** @private */
125
130
  this._onMouseDown = this.#startDrag.bind(this);
126
131
  /** @private */
@@ -282,25 +287,60 @@ class TinyDragger {
282
287
  this.checkDragCollision(event);
283
288
  this.#dispatchEvent('drag');
284
289
  }
290
+ /** @type {HTMLElement[]} */
291
+ #collisionsMarked = [];
292
+ /**
293
+ * Marks an element as currently collided by adding the collision CSS class.
294
+ * The element is stored in an internal list for easy removal later.
295
+ *
296
+ * @param {HTMLElement|null} el - The element to mark as collided.
297
+ */
298
+ #addCollision(el) {
299
+ if (!el)
300
+ return;
301
+ el.classList.add(this.#classDragCollision);
302
+ this.#collisionsMarked.push(el);
303
+ }
304
+ /**
305
+ * Removes the collision CSS class from all previously marked elements.
306
+ * Also clears the last single collision element, if set.
307
+ *
308
+ */
309
+ #removeCollision() {
310
+ while (this.#collisionsMarked.length > 0) {
311
+ const el = this.#collisionsMarked.shift();
312
+ if (el)
313
+ el.classList.remove(this.#classDragCollision);
314
+ }
315
+ if (!this.#lastCollision)
316
+ return;
317
+ this.#lastCollision.classList.remove(this.#classDragCollision);
318
+ }
285
319
  /**
286
320
  * Handles dragging collision.
287
321
  * @param {MouseEvent|Touch} event - The drag event.
288
322
  */
289
323
  checkDragCollision(event) {
290
- const { collidedElement: collided } = this.execCollision(event);
291
- if (collided && collided !== this.#lastCollision) {
292
- if (navigator.vibrate && Array.isArray(this.#vibration.collide)) {
293
- navigator.vibrate(this.#vibration.collide);
294
- }
295
- this.#lastCollision = collided;
296
- if (this.#lastCollision)
297
- this.#lastCollision.classList.add(this.#classDragCollision);
324
+ const { collidedElements } = this.execCollision(event);
325
+ const first = collidedElements[0] || null;
326
+ // Removes old marking if necessary
327
+ if (this.#lastCollision && !collidedElements.includes(this.#lastCollision)) {
328
+ this.#removeCollision();
298
329
  }
299
- else if (!collided) {
300
- if (this.#lastCollision)
301
- this.#lastCollision.classList.remove(this.#classDragCollision);
302
- this.#lastCollision = null;
330
+ // Adds Marking for All Colluded
331
+ collidedElements.forEach((el) => this.#addCollision(el));
332
+ // Removes markings from who no longer collided
333
+ this.#collidables.forEach((el) => {
334
+ if (!collidedElements.includes(el)) {
335
+ el.classList.remove(this.#classDragCollision);
336
+ }
337
+ });
338
+ if (navigator.vibrate &&
339
+ Array.isArray(this.#vibration.collide) &&
340
+ collidedElements.length > 0) {
341
+ navigator.vibrate(this.#vibration.collide);
303
342
  }
343
+ this.#lastCollision = first;
304
344
  }
305
345
  /**
306
346
  * Handles dragging movement.
@@ -338,12 +378,12 @@ class TinyDragger {
338
378
  /**
339
379
  * Handles the collision of a drag.
340
380
  * @param {MouseEvent|Touch} event - The release event.
341
- * @returns {{ inJail: boolean; collidedElement: HTMLElement|null }}
381
+ * @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
342
382
  */
343
383
  execCollision(event) {
344
384
  if (this.#destroyed || !this.#dragProxy)
345
- return { inJail: false, collidedElement: null };
346
- let collidedElement;
385
+ return { inJail: false, collidedElements: [] };
386
+ let collidedElements = [];
347
387
  let inJail = true;
348
388
  const jailRect = this.#jail?.getBoundingClientRect();
349
389
  if (this.#collisionByMouse) {
@@ -353,20 +393,28 @@ class TinyDragger {
353
393
  inJail =
354
394
  x >= jailRect.left && x <= jailRect.right && y >= jailRect.top && y <= jailRect.bottom;
355
395
  }
356
- collidedElement = inJail ? this.getCollidedElement(x, y) : null;
396
+ collidedElements = inJail
397
+ ? this.#multiCollision
398
+ ? this.getAllCollidedElements(x, y)
399
+ : [this.getCollidedElement(x, y)].filter(Boolean)
400
+ : [];
357
401
  }
358
402
  else {
359
- const targetRect = this.#dragProxy.getBoundingClientRect();
403
+ const rect = this.#dragProxy.getBoundingClientRect();
360
404
  if (this.#dropInJailOnly && this.#jail && jailRect) {
361
405
  inJail =
362
- targetRect.left >= jailRect.left &&
363
- targetRect.right <= jailRect.right &&
364
- targetRect.top >= jailRect.top &&
365
- targetRect.bottom <= jailRect.bottom;
406
+ rect.left >= jailRect.left &&
407
+ rect.right <= jailRect.right &&
408
+ rect.top >= jailRect.top &&
409
+ rect.bottom <= jailRect.bottom;
366
410
  }
367
- collidedElement = inJail ? this.getCollidedElementByRect(targetRect) : null;
411
+ collidedElements = inJail
412
+ ? this.#multiCollision
413
+ ? this.getAllCollidedElementsByRect(rect)
414
+ : [this.getCollidedElementByRect(rect)].filter(Boolean)
415
+ : [];
368
416
  }
369
- return { inJail, collidedElement };
417
+ return { inJail, collidedElements };
370
418
  }
371
419
  /**
372
420
  * Handles the end of a drag.
@@ -388,7 +436,7 @@ class TinyDragger {
388
436
  document.removeEventListener('mouseup', this._onMouseUp);
389
437
  document.removeEventListener('touchmove', this._onTouchMove);
390
438
  document.removeEventListener('touchend', this._onTouchEnd);
391
- const { collidedElement } = this.execCollision(event);
439
+ const { collidedElements } = this.execCollision(event);
392
440
  if (navigator.vibrate && Array.isArray(this.#vibration.end)) {
393
441
  navigator.vibrate(this.#vibration.end);
394
442
  }
@@ -399,7 +447,7 @@ class TinyDragger {
399
447
  this.#dragProxy = null;
400
448
  }
401
449
  if (this.#lastCollision)
402
- this.#lastCollision.classList.remove(this.#classDragCollision);
450
+ this.#removeCollision();
403
451
  this.#lastCollision = null;
404
452
  this.#target.classList.remove(this.#dragHiddenClass);
405
453
  if (!this.#revertOnDrop) {
@@ -408,11 +456,43 @@ class TinyDragger {
408
456
  }
409
457
  const dropEvent = new CustomEvent('drop', {
410
458
  detail: {
411
- target: collidedElement,
459
+ targets: collidedElements,
460
+ first: collidedElements[0] || null,
412
461
  },
413
462
  });
414
463
  this.#target.dispatchEvent(dropEvent);
415
464
  }
465
+ /**
466
+ * Checks if the provided element intersects with the given bounding rectangle.
467
+ *
468
+ * @param {HTMLElement} el - The element to test for collision.
469
+ * @param {DOMRect} rect - The bounding rectangle to check against.
470
+ * @returns {boolean} True if the element intersects with the rectangle.
471
+ */
472
+ #getCollidedElementByRect(el, rect) {
473
+ const elRect = el.getBoundingClientRect();
474
+ return !(rect.right < elRect.left ||
475
+ rect.left > elRect.right ||
476
+ rect.bottom < elRect.top ||
477
+ rect.top > elRect.bottom);
478
+ }
479
+ /**
480
+ * Returns all elements currently colliding with the given rectangle.
481
+ *
482
+ * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
483
+ * @returns {HTMLElement[]} A list of all collided elements.
484
+ * @throws {Error} If the input is not a valid DOMRect with numeric bounds.
485
+ */
486
+ getAllCollidedElementsByRect(rect) {
487
+ this.#checkDestroy();
488
+ if (!(rect instanceof DOMRect) ||
489
+ typeof rect.left !== 'number' ||
490
+ typeof rect.right !== 'number' ||
491
+ typeof rect.top !== 'number' ||
492
+ typeof rect.bottom !== 'number')
493
+ throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
494
+ return this.#collidables.filter((el) => this.#getCollidedElementByRect(el, rect));
495
+ }
416
496
  /**
417
497
  * Detects collision based on rectangle intersection.
418
498
  * @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
@@ -427,13 +507,30 @@ class TinyDragger {
427
507
  typeof rect.top !== 'number' ||
428
508
  typeof rect.bottom !== 'number')
429
509
  throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
430
- return (this.#collidables.find((el) => {
431
- const elRect = el.getBoundingClientRect();
432
- return !(rect.right < elRect.left ||
433
- rect.left > elRect.right ||
434
- rect.bottom < elRect.top ||
435
- rect.top > elRect.bottom);
436
- }) || null);
510
+ return this.#collidables.find((el) => this.#getCollidedElementByRect(el, rect)) || null;
511
+ }
512
+ /**
513
+ * Checks whether a given (x, y) coordinate is inside the bounding rectangle of an element.
514
+ *
515
+ * @param {HTMLElement} el - The element to test for collision.
516
+ * @param {number} x - Horizontal screen coordinate.
517
+ * @param {number} y - Vertical screen coordinate.
518
+ * @returns {boolean} True if the point is within the element's bounds.
519
+ */
520
+ #getCollidedElement(el, x, y) {
521
+ const rect = el.getBoundingClientRect();
522
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
523
+ }
524
+ /**
525
+ * @param {number} x - Horizontal screen coordinate.
526
+ * @param {number} y - Vertical screen coordinate.
527
+ * @returns {HTMLElement[]} The collided element or null.
528
+ */
529
+ getAllCollidedElements(x, y) {
530
+ this.#checkDestroy();
531
+ if (typeof x !== 'number' || typeof y !== 'number')
532
+ throw new Error('getCollidedElement expects numeric x and y coordinates.');
533
+ return this.#collidables.filter((el) => this.#getCollidedElement(el, x, y));
437
534
  }
438
535
  /**
439
536
  * Detects collision with a point using element bounding rectangles.
@@ -445,10 +542,7 @@ class TinyDragger {
445
542
  this.#checkDestroy();
446
543
  if (typeof x !== 'number' || typeof y !== 'number')
447
544
  throw new Error('getCollidedElement expects numeric x and y coordinates.');
448
- return (this.#collidables.find((el) => {
449
- const rect = el.getBoundingClientRect();
450
- return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
451
- }) || null);
545
+ return this.#collidables.find((el) => this.#getCollidedElement(el, x, y)) || null;
452
546
  }
453
547
  /**
454
548
  * Dispatches a custom event from the target element.
@@ -671,7 +765,7 @@ class TinyDragger {
671
765
  document.removeEventListener('touchmove', this._onTouchMove);
672
766
  document.removeEventListener('touchend', this._onTouchEnd);
673
767
  if (this.#lastCollision)
674
- this.#lastCollision.classList.remove(this.#classDragCollision);
768
+ this.#removeCollision();
675
769
  if (this.#dragProxy) {
676
770
  this.#dragProxy.remove();
677
771
  this.#dragProxy = null;
@@ -3,6 +3,7 @@
3
3
  var objFilter = require('../basics/objFilter.cjs');
4
4
  require('fs');
5
5
  require('path');
6
+ require('fs/promises');
6
7
 
7
8
  /**
8
9
  * @typedef {Object} UploaderConfig
package/docs/v1/README.md CHANGED
@@ -20,7 +20,6 @@ This folder contains the core scripts we have worked on so far. Each file is a m
20
20
  - 🔄 **[asyncReplace](./basics/asyncReplace.md)** — Asynchronously replaces matches in a string using a regex and an async function.
21
21
  - 🖼️ **[html](./basics/html.md)** — Utilities for handling DOM element interactions like collision detection and basic element manipulation.
22
22
  - 📺 **[fullScreen](./basics/fullScreen.md)** — A complete fullscreen API manager with detection, event handling, and cross-browser compatibility.
23
- * 📁 **[fileManager](./basics/fileManager.mjs.md)** — A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
24
23
 
25
24
  ### 2. **`libs/`**
26
25
  - 🗂️ **[TinyPromiseQueue](./libs/TinyPromiseQueue.md)** — A class that allows sequential execution of asynchronous tasks, supporting task delays, cancellation, and queue management.
@@ -33,6 +32,9 @@ This folder contains the core scripts we have worked on so far. Each file is a m
33
32
  * 📂 **[TinyUploadClicker](./libs/TinyUploadClicker.md)** — A minimal utility to bind any clickable element to a hidden file input, offering full control over styling, behavior, and upload event hooks.
34
33
  * 🧲 **[TinyDragger](./libs/TinyDragger.md)** — A flexible drag-and-drop manager with collision detection, jail constraints, vibration feedback, visual proxies, revert-on-drop, and full custom event support.
35
34
 
35
+ ### 3. **`fileManager/`**
36
+ * 📁 **[main](./fileManager/main.md)** — A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
37
+
36
38
  ---
37
39
 
38
40
  ## 🚀 Usage