tiny-essentials 1.12.0 → 1.12.1
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/v1/TinyDragger.js +148 -43
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyEssentials.js +148 -43
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyUploadClicker.js +148 -43
- package/dist/v1/libs/TinyDragger.cjs +148 -43
- package/dist/v1/libs/TinyDragger.d.mts +18 -2
- package/dist/v1/libs/TinyDragger.mjs +132 -38
- package/docs/v1/libs/TinyDragger.md +15 -0
- package/package.json +1 -1
|
@@ -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 {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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;
|
|
381
|
+
* @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
|
|
342
382
|
*/
|
|
343
383
|
execCollision(event) {
|
|
344
384
|
if (this.#destroyed || !this.#dragProxy)
|
|
345
|
-
return { inJail: false,
|
|
346
|
-
let
|
|
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
|
-
|
|
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
|
|
403
|
+
const rect = this.#dragProxy.getBoundingClientRect();
|
|
360
404
|
if (this.#dropInJailOnly && this.#jail && jailRect) {
|
|
361
405
|
inJail =
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
406
|
+
rect.left >= jailRect.left &&
|
|
407
|
+
rect.right <= jailRect.right &&
|
|
408
|
+
rect.top >= jailRect.top &&
|
|
409
|
+
rect.bottom <= jailRect.bottom;
|
|
366
410
|
}
|
|
367
|
-
|
|
411
|
+
collidedElements = inJail
|
|
412
|
+
? this.#multiCollision
|
|
413
|
+
? this.getAllCollidedElementsByRect(rect)
|
|
414
|
+
: [this.getCollidedElementByRect(rect)].filter(Boolean)
|
|
415
|
+
: [];
|
|
368
416
|
}
|
|
369
|
-
return { inJail,
|
|
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 {
|
|
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.#
|
|
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
|
-
|
|
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
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
|
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.#
|
|
768
|
+
this.#removeCollision();
|
|
675
769
|
if (this.#dragProxy) {
|
|
676
770
|
this.#dragProxy.remove();
|
|
677
771
|
this.#dragProxy = null;
|
|
@@ -54,6 +54,7 @@ new TinyDragger(targetElement, options?)
|
|
|
54
54
|
| `classHidden` | `string` | `'drag-hidden'` | Class used to hide the original element while dragging |
|
|
55
55
|
| `lockInsideJail` | `boolean` | `false` | Prevent drag from exceeding jail bounds |
|
|
56
56
|
| `dropInJailOnly` | `boolean` | `false` | Prevent drop outside the jail area |
|
|
57
|
+
| `multiCollision` | `boolean` | `false` | Enables returning multiple collided elements |
|
|
57
58
|
| `vibration` | `VibrationPatterns` or `false` | `false` | Vibration feedback configuration |
|
|
58
59
|
| `revertOnDrop` | `boolean` | `false` | Return to original position after dropping |
|
|
59
60
|
|
|
@@ -133,6 +134,13 @@ Each pattern must be either `false` or an array of numbers.
|
|
|
133
134
|
|
|
134
135
|
---
|
|
135
136
|
|
|
137
|
+
### `getAllCollidedElementsByRect(rect: DOMRect): HTMLElement[]`
|
|
138
|
+
|
|
139
|
+
📌 Returns **all elements** currently intersecting the given rectangle. Useful for detecting multiple overlaps when dragging.
|
|
140
|
+
🛑 Throws if `rect` is not a valid `DOMRect` with numeric `left`, `right`, `top`, and `bottom` properties.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
136
144
|
### `getCollidedElement(x: number, y: number): HTMLElement | null`
|
|
137
145
|
|
|
138
146
|
📌 Detects if the given screen coordinates collide with any tracked element.
|
|
@@ -140,6 +148,13 @@ Each pattern must be either `false` or an array of numbers.
|
|
|
140
148
|
|
|
141
149
|
---
|
|
142
150
|
|
|
151
|
+
### `getAllCollidedElements(x: number, y: number): HTMLElement[]`
|
|
152
|
+
|
|
153
|
+
📌 Detects **all elements** currently under the given screen coordinates. Works well when `collisionByMouse` is enabled.
|
|
154
|
+
🛑 Throws if `x` or `y` is not a number.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
143
158
|
### `getDragging(): boolean`
|
|
144
159
|
|
|
145
160
|
🔄 Returns whether dragging is currently active.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-essentials",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.1",
|
|
4
4
|
"description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "npm run test:mjs && npm run test:cjs && npm run test:js",
|