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
|
@@ -4204,6 +4204,7 @@ class TinyDragger {
|
|
|
4204
4204
|
#offsetY = 0;
|
|
4205
4205
|
#offsetX = 0;
|
|
4206
4206
|
|
|
4207
|
+
#multiCollision = false;
|
|
4207
4208
|
#lockInsideJail = false;
|
|
4208
4209
|
#revertOnDrop = false;
|
|
4209
4210
|
#dragging = false;
|
|
@@ -4248,6 +4249,7 @@ class TinyDragger {
|
|
|
4248
4249
|
* @param {string} [options.classDragCollision='dragging-collision'] - CSS class applied to collision element.
|
|
4249
4250
|
* @param {boolean} [options.lockInsideJail=false] - Restrict movement within the jail container.
|
|
4250
4251
|
* @param {boolean} [options.dropInJailOnly=false] - Restrict drop within the jail container.
|
|
4252
|
+
* @param {boolean} [options.multiCollision=false] - Enables returning multiple collided elements.
|
|
4251
4253
|
* @param {VibrationPatterns|false} [options.vibration=false] - Vibration feedback configuration.
|
|
4252
4254
|
* @param {boolean} [options.revertOnDrop=false] - Whether to return to original position on drop.
|
|
4253
4255
|
* @param {string} [options.classHidden='drag-hidden'] - CSS class to hide original element during dragging.
|
|
@@ -4294,6 +4296,7 @@ class TinyDragger {
|
|
|
4294
4296
|
validateBoolean(options.lockInsideJail, 'lockInsideJail');
|
|
4295
4297
|
validateBoolean(options.dropInJailOnly, 'dropInJailOnly');
|
|
4296
4298
|
validateBoolean(options.revertOnDrop, 'revertOnDrop');
|
|
4299
|
+
validateBoolean(options.multiCollision, 'multiCollision');
|
|
4297
4300
|
|
|
4298
4301
|
validateString(options.classDragging, 'classDragging');
|
|
4299
4302
|
validateString(options.classBodyDragging, 'classBodyDragging');
|
|
@@ -4327,6 +4330,7 @@ class TinyDragger {
|
|
|
4327
4330
|
if (typeof options.revertOnDrop === 'boolean') this.#revertOnDrop = options.revertOnDrop;
|
|
4328
4331
|
if (typeof options.lockInsideJail === 'boolean') this.#lockInsideJail = options.lockInsideJail;
|
|
4329
4332
|
if (typeof options.dropInJailOnly === 'boolean') this.#dropInJailOnly = options.dropInJailOnly;
|
|
4333
|
+
if (typeof options.multiCollision === 'boolean') this.#multiCollision = options.multiCollision;
|
|
4330
4334
|
|
|
4331
4335
|
/** @private */
|
|
4332
4336
|
this._onMouseDown = this.#startDrag.bind(this);
|
|
@@ -4518,22 +4522,67 @@ class TinyDragger {
|
|
|
4518
4522
|
this.#dispatchEvent('drag');
|
|
4519
4523
|
}
|
|
4520
4524
|
|
|
4525
|
+
/** @type {HTMLElement[]} */
|
|
4526
|
+
#collisionsMarked = [];
|
|
4527
|
+
|
|
4528
|
+
/**
|
|
4529
|
+
* Marks an element as currently collided by adding the collision CSS class.
|
|
4530
|
+
* The element is stored in an internal list for easy removal later.
|
|
4531
|
+
*
|
|
4532
|
+
* @param {HTMLElement|null} el - The element to mark as collided.
|
|
4533
|
+
*/
|
|
4534
|
+
#addCollision(el) {
|
|
4535
|
+
if (!el) return;
|
|
4536
|
+
el.classList.add(this.#classDragCollision);
|
|
4537
|
+
this.#collisionsMarked.push(el);
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4540
|
+
/**
|
|
4541
|
+
* Removes the collision CSS class from all previously marked elements.
|
|
4542
|
+
* Also clears the last single collision element, if set.
|
|
4543
|
+
*
|
|
4544
|
+
*/
|
|
4545
|
+
#removeCollision() {
|
|
4546
|
+
while (this.#collisionsMarked.length > 0) {
|
|
4547
|
+
const el = this.#collisionsMarked.shift();
|
|
4548
|
+
if (el) el.classList.remove(this.#classDragCollision);
|
|
4549
|
+
}
|
|
4550
|
+
if (!this.#lastCollision) return;
|
|
4551
|
+
this.#lastCollision.classList.remove(this.#classDragCollision);
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4521
4554
|
/**
|
|
4522
4555
|
* Handles dragging collision.
|
|
4523
4556
|
* @param {MouseEvent|Touch} event - The drag event.
|
|
4524
4557
|
*/
|
|
4525
4558
|
checkDragCollision(event) {
|
|
4526
|
-
const {
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4559
|
+
const { collidedElements } = this.execCollision(event);
|
|
4560
|
+
const first = collidedElements[0] || null;
|
|
4561
|
+
|
|
4562
|
+
// Removes old marking if necessary
|
|
4563
|
+
if (this.#lastCollision && !collidedElements.includes(this.#lastCollision)) {
|
|
4564
|
+
this.#removeCollision();
|
|
4565
|
+
}
|
|
4566
|
+
|
|
4567
|
+
// Adds Marking for All Colluded
|
|
4568
|
+
collidedElements.forEach((el) => this.#addCollision(el));
|
|
4569
|
+
|
|
4570
|
+
// Removes markings from who no longer collided
|
|
4571
|
+
this.#collidables.forEach((el) => {
|
|
4572
|
+
if (!collidedElements.includes(el)) {
|
|
4573
|
+
el.classList.remove(this.#classDragCollision);
|
|
4530
4574
|
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
this.#
|
|
4575
|
+
});
|
|
4576
|
+
|
|
4577
|
+
if (
|
|
4578
|
+
navigator.vibrate &&
|
|
4579
|
+
Array.isArray(this.#vibration.collide) &&
|
|
4580
|
+
collidedElements.length > 0
|
|
4581
|
+
) {
|
|
4582
|
+
navigator.vibrate(this.#vibration.collide);
|
|
4536
4583
|
}
|
|
4584
|
+
|
|
4585
|
+
this.#lastCollision = first;
|
|
4537
4586
|
}
|
|
4538
4587
|
|
|
4539
4588
|
/**
|
|
@@ -4580,37 +4629,48 @@ class TinyDragger {
|
|
|
4580
4629
|
/**
|
|
4581
4630
|
* Handles the collision of a drag.
|
|
4582
4631
|
* @param {MouseEvent|Touch} event - The release event.
|
|
4583
|
-
* @returns {{ inJail: boolean;
|
|
4632
|
+
* @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
|
|
4584
4633
|
*/
|
|
4585
4634
|
execCollision(event) {
|
|
4586
|
-
if (this.#destroyed || !this.#dragProxy) return { inJail: false,
|
|
4635
|
+
if (this.#destroyed || !this.#dragProxy) return { inJail: false, collidedElements: [] };
|
|
4587
4636
|
|
|
4588
|
-
let
|
|
4637
|
+
let collidedElements = [];
|
|
4589
4638
|
let inJail = true;
|
|
4590
4639
|
const jailRect = this.#jail?.getBoundingClientRect();
|
|
4640
|
+
|
|
4591
4641
|
if (this.#collisionByMouse) {
|
|
4592
4642
|
const x = event.clientX;
|
|
4593
4643
|
const y = event.clientY;
|
|
4644
|
+
|
|
4594
4645
|
if (this.#dropInJailOnly && this.#jail && jailRect) {
|
|
4595
4646
|
inJail =
|
|
4596
4647
|
x >= jailRect.left && x <= jailRect.right && y >= jailRect.top && y <= jailRect.bottom;
|
|
4597
4648
|
}
|
|
4598
4649
|
|
|
4599
|
-
|
|
4650
|
+
collidedElements = inJail
|
|
4651
|
+
? this.#multiCollision
|
|
4652
|
+
? this.getAllCollidedElements(x, y)
|
|
4653
|
+
: [this.getCollidedElement(x, y)].filter(Boolean)
|
|
4654
|
+
: [];
|
|
4600
4655
|
} else {
|
|
4601
|
-
const
|
|
4656
|
+
const rect = this.#dragProxy.getBoundingClientRect();
|
|
4657
|
+
|
|
4602
4658
|
if (this.#dropInJailOnly && this.#jail && jailRect) {
|
|
4603
4659
|
inJail =
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4660
|
+
rect.left >= jailRect.left &&
|
|
4661
|
+
rect.right <= jailRect.right &&
|
|
4662
|
+
rect.top >= jailRect.top &&
|
|
4663
|
+
rect.bottom <= jailRect.bottom;
|
|
4608
4664
|
}
|
|
4609
4665
|
|
|
4610
|
-
|
|
4666
|
+
collidedElements = inJail
|
|
4667
|
+
? this.#multiCollision
|
|
4668
|
+
? this.getAllCollidedElementsByRect(rect)
|
|
4669
|
+
: [this.getCollidedElementByRect(rect)].filter(Boolean)
|
|
4670
|
+
: [];
|
|
4611
4671
|
}
|
|
4612
4672
|
|
|
4613
|
-
return { inJail,
|
|
4673
|
+
return { inJail, collidedElements };
|
|
4614
4674
|
}
|
|
4615
4675
|
|
|
4616
4676
|
/**
|
|
@@ -4633,7 +4693,7 @@ class TinyDragger {
|
|
|
4633
4693
|
|
|
4634
4694
|
document.removeEventListener('touchmove', this._onTouchMove);
|
|
4635
4695
|
document.removeEventListener('touchend', this._onTouchEnd);
|
|
4636
|
-
const {
|
|
4696
|
+
const { collidedElements } = this.execCollision(event);
|
|
4637
4697
|
|
|
4638
4698
|
if (navigator.vibrate && Array.isArray(this.#vibration.end)) {
|
|
4639
4699
|
navigator.vibrate(this.#vibration.end);
|
|
@@ -4646,7 +4706,7 @@ class TinyDragger {
|
|
|
4646
4706
|
this.#dragProxy = null;
|
|
4647
4707
|
}
|
|
4648
4708
|
|
|
4649
|
-
if (this.#lastCollision) this.#
|
|
4709
|
+
if (this.#lastCollision) this.#removeCollision();
|
|
4650
4710
|
this.#lastCollision = null;
|
|
4651
4711
|
|
|
4652
4712
|
this.#target.classList.remove(this.#dragHiddenClass);
|
|
@@ -4657,13 +4717,50 @@ class TinyDragger {
|
|
|
4657
4717
|
|
|
4658
4718
|
const dropEvent = new CustomEvent('drop', {
|
|
4659
4719
|
detail: {
|
|
4660
|
-
|
|
4720
|
+
targets: collidedElements,
|
|
4721
|
+
first: collidedElements[0] || null,
|
|
4661
4722
|
},
|
|
4662
4723
|
});
|
|
4663
|
-
|
|
4664
4724
|
this.#target.dispatchEvent(dropEvent);
|
|
4665
4725
|
}
|
|
4666
4726
|
|
|
4727
|
+
/**
|
|
4728
|
+
* Checks if the provided element intersects with the given bounding rectangle.
|
|
4729
|
+
*
|
|
4730
|
+
* @param {HTMLElement} el - The element to test for collision.
|
|
4731
|
+
* @param {DOMRect} rect - The bounding rectangle to check against.
|
|
4732
|
+
* @returns {boolean} True if the element intersects with the rectangle.
|
|
4733
|
+
*/
|
|
4734
|
+
#getCollidedElementByRect(el, rect) {
|
|
4735
|
+
const elRect = el.getBoundingClientRect();
|
|
4736
|
+
return !(
|
|
4737
|
+
rect.right < elRect.left ||
|
|
4738
|
+
rect.left > elRect.right ||
|
|
4739
|
+
rect.bottom < elRect.top ||
|
|
4740
|
+
rect.top > elRect.bottom
|
|
4741
|
+
);
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
/**
|
|
4745
|
+
* Returns all elements currently colliding with the given rectangle.
|
|
4746
|
+
*
|
|
4747
|
+
* @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
|
|
4748
|
+
* @returns {HTMLElement[]} A list of all collided elements.
|
|
4749
|
+
* @throws {Error} If the input is not a valid DOMRect with numeric bounds.
|
|
4750
|
+
*/
|
|
4751
|
+
getAllCollidedElementsByRect(rect) {
|
|
4752
|
+
this.#checkDestroy();
|
|
4753
|
+
if (
|
|
4754
|
+
!(rect instanceof DOMRect) ||
|
|
4755
|
+
typeof rect.left !== 'number' ||
|
|
4756
|
+
typeof rect.right !== 'number' ||
|
|
4757
|
+
typeof rect.top !== 'number' ||
|
|
4758
|
+
typeof rect.bottom !== 'number'
|
|
4759
|
+
)
|
|
4760
|
+
throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
|
|
4761
|
+
return this.#collidables.filter((el) => this.#getCollidedElementByRect(el, rect));
|
|
4762
|
+
}
|
|
4763
|
+
|
|
4667
4764
|
/**
|
|
4668
4765
|
* Detects collision based on rectangle intersection.
|
|
4669
4766
|
* @param {DOMRect} rect - Bounding rectangle of the dragged proxy.
|
|
@@ -4680,18 +4777,32 @@ class TinyDragger {
|
|
|
4680
4777
|
typeof rect.bottom !== 'number'
|
|
4681
4778
|
)
|
|
4682
4779
|
throw new Error('getCollidedElementByRect expects a valid DOMRect object.');
|
|
4780
|
+
return this.#collidables.find((el) => this.#getCollidedElementByRect(el, rect)) || null;
|
|
4781
|
+
}
|
|
4683
4782
|
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4783
|
+
/**
|
|
4784
|
+
* Checks whether a given (x, y) coordinate is inside the bounding rectangle of an element.
|
|
4785
|
+
*
|
|
4786
|
+
* @param {HTMLElement} el - The element to test for collision.
|
|
4787
|
+
* @param {number} x - Horizontal screen coordinate.
|
|
4788
|
+
* @param {number} y - Vertical screen coordinate.
|
|
4789
|
+
* @returns {boolean} True if the point is within the element's bounds.
|
|
4790
|
+
*/
|
|
4791
|
+
#getCollidedElement(el, x, y) {
|
|
4792
|
+
const rect = el.getBoundingClientRect();
|
|
4793
|
+
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
4794
|
+
}
|
|
4795
|
+
|
|
4796
|
+
/**
|
|
4797
|
+
* @param {number} x - Horizontal screen coordinate.
|
|
4798
|
+
* @param {number} y - Vertical screen coordinate.
|
|
4799
|
+
* @returns {HTMLElement[]} The collided element or null.
|
|
4800
|
+
*/
|
|
4801
|
+
getAllCollidedElements(x, y) {
|
|
4802
|
+
this.#checkDestroy();
|
|
4803
|
+
if (typeof x !== 'number' || typeof y !== 'number')
|
|
4804
|
+
throw new Error('getCollidedElement expects numeric x and y coordinates.');
|
|
4805
|
+
return this.#collidables.filter((el) => this.#getCollidedElement(el, x, y));
|
|
4695
4806
|
}
|
|
4696
4807
|
|
|
4697
4808
|
/**
|
|
@@ -4704,13 +4815,7 @@ class TinyDragger {
|
|
|
4704
4815
|
this.#checkDestroy();
|
|
4705
4816
|
if (typeof x !== 'number' || typeof y !== 'number')
|
|
4706
4817
|
throw new Error('getCollidedElement expects numeric x and y coordinates.');
|
|
4707
|
-
|
|
4708
|
-
return (
|
|
4709
|
-
this.#collidables.find((el) => {
|
|
4710
|
-
const rect = el.getBoundingClientRect();
|
|
4711
|
-
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
4712
|
-
}) || null
|
|
4713
|
-
);
|
|
4818
|
+
return this.#collidables.find((el) => this.#getCollidedElement(el, x, y)) || null;
|
|
4714
4819
|
}
|
|
4715
4820
|
|
|
4716
4821
|
/**
|
|
@@ -4960,7 +5065,7 @@ class TinyDragger {
|
|
|
4960
5065
|
document.removeEventListener('touchmove', this._onTouchMove);
|
|
4961
5066
|
document.removeEventListener('touchend', this._onTouchEnd);
|
|
4962
5067
|
|
|
4963
|
-
if (this.#lastCollision) this.#
|
|
5068
|
+
if (this.#lastCollision) this.#removeCollision();
|
|
4964
5069
|
if (this.#dragProxy) {
|
|
4965
5070
|
this.#dragProxy.remove();
|
|
4966
5071
|
this.#dragProxy = null;
|
|
@@ -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 {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
this.#
|
|
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;
|
|
451
|
+
* @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
|
|
403
452
|
*/
|
|
404
453
|
execCollision(event) {
|
|
405
|
-
if (this.#destroyed || !this.#dragProxy) return { inJail: false,
|
|
454
|
+
if (this.#destroyed || !this.#dragProxy) return { inJail: false, collidedElements: [] };
|
|
406
455
|
|
|
407
|
-
let
|
|
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
|
-
|
|
469
|
+
collidedElements = inJail
|
|
470
|
+
? this.#multiCollision
|
|
471
|
+
? this.getAllCollidedElements(x, y)
|
|
472
|
+
: [this.getCollidedElement(x, y)].filter(Boolean)
|
|
473
|
+
: [];
|
|
419
474
|
} else {
|
|
420
|
-
const
|
|
475
|
+
const rect = this.#dragProxy.getBoundingClientRect();
|
|
476
|
+
|
|
421
477
|
if (this.#dropInJailOnly && this.#jail && jailRect) {
|
|
422
478
|
inJail =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
485
|
+
collidedElements = inJail
|
|
486
|
+
? this.#multiCollision
|
|
487
|
+
? this.getAllCollidedElementsByRect(rect)
|
|
488
|
+
: [this.getCollidedElementByRect(rect)].filter(Boolean)
|
|
489
|
+
: [];
|
|
430
490
|
}
|
|
431
491
|
|
|
432
|
-
return { inJail,
|
|
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 {
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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.#
|
|
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;
|
|
145
|
+
* @returns {{ inJail: boolean; collidedElements: (HTMLElement | null)[] }}
|
|
144
146
|
*/
|
|
145
147
|
execCollision(event: MouseEvent | Touch): {
|
|
146
148
|
inJail: boolean;
|
|
147
|
-
|
|
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.
|