purgetss 7.4.0 → 7.5.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.
@@ -1,17 +1,29 @@
1
- // PurgeTSS v7.3.1
1
+ // PurgeTSS v7.5.1
2
2
  // Created by César Estrada
3
3
  // https://purgetss.com
4
4
 
5
5
  function Animation(args = {}) {
6
6
  const params = {
7
+ // Global
7
8
  id: args.id,
9
+ debug: args.debug ?? false,
10
+ isIOS: Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad',
11
+
12
+ // Play / Toggle / Sequence
8
13
  open: false,
9
- draggables: [],
10
14
  playing: false,
11
15
  delay: args.delay ?? 0,
12
- debug: args.debug ?? false,
13
- isIOS: Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad',
14
- hasTransformation: (args.scale !== undefined || args.rotate !== undefined)
16
+ hasTransformation: (args.scale !== undefined || args.rotate !== undefined),
17
+
18
+ // Draggable
19
+ draggables: [],
20
+
21
+ // Collision Detection
22
+ collisionViews: [],
23
+ dragCB: null,
24
+ dropCB: null,
25
+ lastHoverTarget: null,
26
+ lastKnownTarget: null
15
27
  }
16
28
 
17
29
  logger('Create Animation View: ' + params.id)
@@ -42,6 +54,242 @@ function Animation(args = {}) {
42
54
  makeViewsDraggable(_views)
43
55
  }
44
56
 
57
+ animationView.detectCollisions = (views, _dragCB, _dropCB) => {
58
+ if (params.debug) { console.log('') }
59
+ logger('`detectCollisions` method called on: ' + params.id)
60
+ if (_dragCB) params.dragCB = _dragCB
61
+ if (_dropCB) params.dropCB = _dropCB
62
+ const arr = Array.isArray(views) ? views : [views]
63
+ arr.forEach(view => {
64
+ view._collisionEnabled = true
65
+ if (!params.collisionViews.includes(view)) {
66
+ params.collisionViews.push(view)
67
+ }
68
+ })
69
+ }
70
+
71
+ function resolvePosition(view) {
72
+ if (view._originTop !== undefined) return { top: view._originTop, left: view._originLeft }
73
+ if (view.top !== undefined && view.top !== null && view.left !== undefined && view.left !== null) return { top: view.top, left: view.left }
74
+ const r = view.rect
75
+ return { top: r.y, left: r.x }
76
+ }
77
+
78
+ function normalizePosition(view) {
79
+ const pos = resolvePosition(view)
80
+ view.applyProperties({ top: pos.top, left: pos.left, right: null, bottom: null })
81
+ view._originTop = pos.top
82
+ view._originLeft = pos.left
83
+ }
84
+
85
+ animationView.swap = (view1, view2) => {
86
+ if (params.debug) { console.log('') }
87
+ logger('`swap` method called on: ' + params.id)
88
+
89
+ // Cancel any pending bounce-back animations
90
+ ;[view1, view2].forEach(v => {
91
+ if (v._bouncingBack) {
92
+ logger(` -> cancelling bounce-back for ${v.id} before swap`)
93
+ v.applyProperties({ top: v._originTop, left: v._originLeft, transform: Ti.UI.createMatrix2D() })
94
+ v._bouncingBack = false
95
+ }
96
+ })
97
+
98
+ // Read REAL positions — rect gives actual rendered position, no stale variables
99
+ const targetRect = view2.rect
100
+ const sourceHome = { top: view1._originTop ?? view1.top, left: view1._originLeft ?? view1.left }
101
+
102
+ // Source goes to target's real position, target goes to source's home
103
+ const dest1Top = targetRect.y
104
+ const dest1Left = targetRect.x
105
+ const dest2Top = sourceHome.top
106
+ const dest2Left = sourceHome.left
107
+
108
+ logger(` -> ${view1.id}: home(${sourceHome.top}, ${sourceHome.left}) → dest(${dest1Top}, ${dest1Left})`)
109
+ logger(` -> ${view2.id}: rect(${targetRect.y}, ${targetRect.x}) → dest(${dest2Top}, ${dest2Left})`)
110
+
111
+ const maxZ = params.draggables.length
112
+ view1.zIndex = maxZ + 1
113
+ view2.zIndex = maxZ
114
+
115
+ view1.animate({
116
+ ...args, left: dest1Left, top: dest1Top, transform: Ti.UI.createMatrix2D()
117
+ }, () => {
118
+ view1.applyProperties({ left: dest1Left, top: dest1Top, transform: Ti.UI.createMatrix2D() })
119
+ params.draggables.forEach((d, k) => { d.zIndex = k })
120
+ })
121
+
122
+ view2.animate({
123
+ ...args, left: dest2Left, top: dest2Top
124
+ }, () => {
125
+ view2.applyProperties({ left: dest2Left, top: dest2Top })
126
+ })
127
+
128
+ view1._originTop = dest1Top
129
+ view1._originLeft = dest1Left
130
+ view2._originTop = dest2Top
131
+ view2._originLeft = dest2Left
132
+ }
133
+
134
+ animationView.sequence = (_views, _cb) => {
135
+ if (params.debug) { console.log('') }
136
+ logger('`sequence` method called on: ' + params.id)
137
+ const views = Array.isArray(_views) ? _views : [_views]
138
+ const total = views.length
139
+
140
+ params.open = !params.open
141
+ chooseAnimationBasedOnState('play')
142
+
143
+ function animateNext(index) {
144
+ if (index >= total) { return }
145
+ const isLast = index === total - 1
146
+ playView(views[index], (e) => {
147
+ if (isLast && typeof _cb === 'function') {
148
+ _cb(e)
149
+ } else {
150
+ animateNext(index + 1)
151
+ }
152
+ }, 'play', index, total)
153
+ }
154
+
155
+ animateNext(0)
156
+ }
157
+
158
+ animationView.shake = (view, intensity = 10) => {
159
+ if (params.debug) { console.log('') }
160
+ logger('`shake` method called on: ' + params.id)
161
+ if (!view) { return notFound() }
162
+
163
+ view.applyProperties({ transform: Ti.UI.createMatrix2D().translate(-intensity, 0) })
164
+
165
+ view.animate({
166
+ ...args,
167
+ transform: Ti.UI.createMatrix2D().translate(intensity, 0),
168
+ duration: Math.round((args.duration ?? 400) / 6),
169
+ autoreverse: true,
170
+ repeat: 3,
171
+ curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
172
+ }, () => {
173
+ view.applyProperties({ transform: Ti.UI.createMatrix2D() })
174
+ })
175
+ }
176
+
177
+ animationView.pulse = (view, count = 1) => {
178
+ if (params.debug) { console.log('') }
179
+ logger('`pulse` method called on: ' + params.id)
180
+ if (!view) { return notFound() }
181
+
182
+ view.animate({
183
+ ...args,
184
+ transform: args.transform ?? Ti.UI.createMatrix2D().scale(1.2),
185
+ autoreverse: true,
186
+ repeat: count,
187
+ curve: Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
188
+ }, () => {
189
+ view.applyProperties({ transform: Ti.UI.createMatrix2D() })
190
+ })
191
+ }
192
+
193
+ animationView.snapTo = (view, targets) => {
194
+ if (params.debug) { console.log('') }
195
+ logger('`snapTo` method called on: ' + params.id)
196
+ if (!view) { return notFound() }
197
+
198
+ const arr = Array.isArray(targets) ? targets : [targets]
199
+ const vr = view.rect
200
+ const vcx = (view._visualLeft ?? view.left) + vr.width / 2
201
+ const vcy = (view._visualTop ?? view.top) + vr.height / 2
202
+
203
+ let closest = null
204
+ let minDist = Infinity
205
+
206
+ arr.forEach(target => {
207
+ if (target === view) { return }
208
+ const tr = target.rect
209
+ const tcx = tr.x + tr.width / 2
210
+ const tcy = tr.y + tr.height / 2
211
+ const dist = Math.sqrt((vcx - tcx) ** 2 + (vcy - tcy) ** 2)
212
+ if (dist < minDist) {
213
+ minDist = dist
214
+ closest = target
215
+ }
216
+ })
217
+
218
+ if (!closest) { return null }
219
+
220
+ const cr = closest.rect
221
+ normalizePosition(closest)
222
+ const destLeft = closest._originLeft + (cr.width - vr.width) / 2
223
+ const destTop = closest._originTop + (cr.height - vr.height) / 2
224
+
225
+ view.animate({
226
+ ...args, left: destLeft, top: destTop, transform: Ti.UI.createMatrix2D()
227
+ }, () => {
228
+ view.applyProperties({ left: destLeft, top: destTop, transform: Ti.UI.createMatrix2D() })
229
+ })
230
+
231
+ view._originTop = destTop
232
+ view._originLeft = destLeft
233
+
234
+ return closest
235
+ }
236
+
237
+ animationView.reorder = (views, newOrder) => {
238
+ if (params.debug) { console.log('') }
239
+ logger('`reorder` method called on: ' + params.id)
240
+ const arr = Array.isArray(views) ? views : [views]
241
+ if (newOrder.length !== arr.length) { return }
242
+
243
+ arr.forEach(v => normalizePosition(v))
244
+
245
+ const positions = arr.map(v => ({
246
+ left: v._originLeft,
247
+ top: v._originTop
248
+ }))
249
+
250
+ arr.forEach((view, i) => {
251
+ const dest = positions[newOrder[i]]
252
+
253
+ view.animate({
254
+ ...args, left: dest.left, top: dest.top, transform: Ti.UI.createMatrix2D()
255
+ }, () => {
256
+ view.applyProperties({ left: dest.left, top: dest.top, transform: Ti.UI.createMatrix2D() })
257
+ })
258
+
259
+ view._originTop = dest.top
260
+ view._originLeft = dest.left
261
+ })
262
+ }
263
+
264
+ animationView.undraggable = (_views) => {
265
+ if (params.debug) { console.log('') }
266
+ logger('`undraggable` method called on: ' + params.id)
267
+ const arr = Array.isArray(_views) ? _views : [_views]
268
+
269
+ arr.forEach(view => {
270
+ if (view._dragListeners) {
271
+ view.removeEventListener('touchstart', view._dragListeners.onTouchStart)
272
+ view.removeEventListener('touchend', view._dragListeners.onTouchEnd)
273
+ view.removeEventListener('touchmove', view._dragListeners.onTouchMove)
274
+ Ti.Gesture.removeEventListener('orientationchange', view._dragListeners.onOrientationChange)
275
+ delete view._dragListeners
276
+ }
277
+
278
+ const idx = params.draggables.indexOf(view)
279
+ if (idx !== -1) { params.draggables.splice(idx, 1) }
280
+
281
+ const cIdx = params.collisionViews.indexOf(view)
282
+ if (cIdx !== -1) { params.collisionViews.splice(cIdx, 1) }
283
+
284
+ delete view._originTop
285
+ delete view._originLeft
286
+ delete view._visualTop
287
+ delete view._visualLeft
288
+ delete view._collisionEnabled
289
+ delete view._bouncingBack
290
+ })
291
+ }
292
+
45
293
  animationView.open = (_views, _cb) => {
46
294
  if (params.debug) { console.log('') }
47
295
  logger('`open` method called on: ' + params.id)
@@ -54,6 +302,54 @@ function Animation(args = {}) {
54
302
  mainPlayApplyFn(_views, _cb, 'play', 'close')
55
303
  }
56
304
 
305
+ // Mac Catalyst note: Parent containers of transitioned views should use fixed
306
+ // dimensions (not Ti.UI.FILL). Resizable containers trigger a UIKit re-layout
307
+ // that distorts views with rotated Matrix2D transforms.
308
+ animationView.transition = (views, layouts) => {
309
+ if (params.debug) { console.log('') }
310
+ logger('`transition` method called on: ' + params.id)
311
+
312
+ const arr = Array.isArray(views) ? views : [views]
313
+
314
+ arr.forEach((view, i) => {
315
+ const layout = layouts[i]
316
+
317
+ if (!layout) {
318
+ view.animate(Ti.UI.createAnimation({ ...args, zIndex: 0, opacity: 0 }), () => {
319
+ view.applyProperties({ zIndex: 0, opacity: 0, transform: Ti.UI.createMatrix2D(), translation: { x: 0, y: 0 }, rotate: 0, scale: 1 })
320
+ })
321
+ return
322
+ }
323
+
324
+ const tx = layout.translation?.x ?? 0
325
+ const ty = layout.translation?.y ?? 0
326
+ const rotate = layout.rotate ?? 0
327
+ const scale = layout.scale ?? 1
328
+
329
+ const transform = Ti.UI.createMatrix2D().translate(tx, ty).rotate(rotate).scale(scale)
330
+
331
+ const needsFadeIn = view.opacity === 0
332
+
333
+ if (layout.zIndex !== undefined) view.zIndex = layout.zIndex
334
+
335
+ const animation = Ti.UI.createAnimation({
336
+ ...args,
337
+ transform
338
+ })
339
+
340
+ if (needsFadeIn) animation.opacity = 1
341
+ if (layout.width !== undefined) animation.width = layout.width
342
+ if (layout.height !== undefined) animation.height = layout.height
343
+ if (layout.opacity !== undefined) animation.opacity = layout.opacity
344
+
345
+ view.animate(animation, () => {
346
+ const props = { transform, translation: { x: tx, y: ty }, rotate, scale, zIndex: layout.zIndex }
347
+ if (needsFadeIn) props.opacity = 1
348
+ view.applyProperties(props)
349
+ })
350
+ })
351
+ }
352
+
57
353
  function handleTransformations(args) {
58
354
  if ('anchorPoint' in args || 'rotate' in args || 'scale' in args) {
59
355
  logger(' -> Creating transform')
@@ -125,19 +421,113 @@ function Animation(args = {}) {
125
421
  let offsetX, offsetY
126
422
  setBounds(draggableView, args.bounds)
127
423
 
128
- draggableView.addEventListener('touchstart', (event) => {
424
+ const onTouchStart = (event) => {
129
425
  offsetX = event.x
130
426
  offsetY = event.y
427
+ draggableView._wasDragged = false
428
+
429
+ // If a bounce-back animation is still running, complete it immediately
430
+ if (draggableView._bouncingBack) {
431
+ logger(` -> completing pending bounce-back for ${draggableView.id}`)
432
+ draggableView.applyProperties({
433
+ top: draggableView._originTop,
434
+ left: draggableView._originLeft,
435
+ transform: Ti.UI.createMatrix2D()
436
+ })
437
+ draggableView._bouncingBack = false
438
+ }
131
439
 
132
440
  updateDraggableZIndex(event)
133
441
  checkDraggable(draggableView, 'drag')
134
- })
135
442
 
136
- draggableView.addEventListener('touchend', () => checkDraggable(draggableView, 'drop'))
443
+ if (draggableView._collisionEnabled) {
444
+ if (draggableView._originTop === undefined) {
445
+ draggableView._originTop = draggableView.top
446
+ draggableView._originLeft = draggableView.left
447
+ logger(` -> touchStart origin capture (first): ${draggableView.id} top=${draggableView.top} left=${draggableView.left}`)
448
+ } else {
449
+ logger(` -> touchStart origin kept: ${draggableView.id} origin=(${draggableView._originTop}, ${draggableView._originLeft}) vs top/left=(${draggableView.top}, ${draggableView.left})`)
450
+ }
451
+ }
452
+ }
137
453
 
138
- draggableView.addEventListener('touchmove', (event) => handleTouchMove(event, draggableView, offsetX, offsetY))
454
+ const onTouchEnd = () => {
455
+ checkDraggable(draggableView, 'drop')
139
456
 
140
- Ti.Gesture.addEventListener('orientationchange', () => checkBoundaries(draggableView))
457
+ // Android: consolidate transform after drag so transition reads correct state
458
+ if (!params.isIOS && draggableView.translation) {
459
+ const { translation, rotate, scale } = draggableView
460
+ draggableView.applyProperties({
461
+ translation, rotate, scale,
462
+ transform: Ti.UI.createMatrix2D().translate(translation.x, translation.y).rotate(rotate ?? 0).scale(scale ?? 1)
463
+ })
464
+ }
465
+
466
+ if (draggableView._collisionEnabled) {
467
+ const directTarget = checkCollision(draggableView)
468
+ const target = directTarget ?? params.lastKnownTarget
469
+ logger(` -> collision check: ${draggableView.id} | direct: ${directTarget?.id ?? 'null'} | lastKnown: ${params.lastKnownTarget?.id ?? 'null'} | final: ${target?.id ?? 'null'}`)
470
+ if (target) {
471
+ // On Android, consolidate drag position before snap to avoid animation conflict
472
+ if (!params.isIOS) {
473
+ draggableView.applyProperties({
474
+ top: draggableView._visualTop ?? draggableView.top,
475
+ left: draggableView._visualLeft ?? draggableView.left
476
+ })
477
+ }
478
+ if (args.animationProperties?.snap?.center) {
479
+ logger(` -> snap-center: ${draggableView.id} to ${target.id}`)
480
+ animationView.snapTo(draggableView, [target])
481
+ }
482
+ if (params.dropCB) {
483
+ logger(` -> dropCB: ${draggableView.id} on ${target.id}`)
484
+ params.dropCB(draggableView, target)
485
+ }
486
+ } else if (!target && args.animationProperties?.snap?.back) {
487
+ logger(` -> bounce-back: ${draggableView.id} to (${draggableView._originTop}, ${draggableView._originLeft})`)
488
+
489
+ // On Android, consolidate drag position before bounce-back to avoid animation conflict
490
+ if (!params.isIOS) {
491
+ draggableView.applyProperties({
492
+ top: draggableView._visualTop ?? draggableView.top,
493
+ left: draggableView._visualLeft ?? draggableView.left
494
+ })
495
+ }
496
+
497
+ draggableView._bouncingBack = true
498
+
499
+ draggableView.animate({
500
+ ...args,
501
+ top: draggableView._originTop,
502
+ left: draggableView._originLeft,
503
+ transform: Ti.UI.createMatrix2D()
504
+ }, () => {
505
+ draggableView._bouncingBack = false
506
+ draggableView.applyProperties({
507
+ top: draggableView._originTop,
508
+ left: draggableView._originLeft,
509
+ transform: Ti.UI.createMatrix2D()
510
+ })
511
+ logger(` -> bounce-back complete: ${draggableView.id}`)
512
+ })
513
+ }
514
+ params.lastHoverTarget = null
515
+ params.lastKnownTarget = null
516
+ if (params.dragCB) {
517
+ params.dragCB(draggableView, null)
518
+ }
519
+ }
520
+ }
521
+
522
+ const onTouchMove = (event) => handleTouchMove(event, draggableView, offsetX, offsetY)
523
+ const onOrientationChange = () => checkBoundaries(draggableView)
524
+
525
+ draggableView._dragListeners = { onTouchStart, onTouchEnd, onTouchMove, onOrientationChange }
526
+
527
+ draggableView.addEventListener('touchend', onTouchEnd)
528
+ draggableView.addEventListener('touchmove', onTouchMove)
529
+ draggableView.addEventListener('touchstart', onTouchStart)
530
+ Ti.Gesture.addEventListener('orientationchange', onOrientationChange)
141
531
 
142
532
  params.draggables.push(draggableView)
143
533
  } else {
@@ -146,6 +536,7 @@ function Animation(args = {}) {
146
536
  }
147
537
 
148
538
  function handleTouchMove(event, draggableView, offsetX, offsetY) {
539
+ draggableView._wasDragged = true
149
540
  const convertedPoint = draggableView.convertPointToView({ x: event.x, y: event.y }, draggableView.parent)
150
541
  let top = Math.round(convertedPoint.y - offsetY)
151
542
  let left = Math.round(convertedPoint.x - offsetX)
@@ -167,11 +558,63 @@ function Animation(args = {}) {
167
558
  }
168
559
  }
169
560
 
561
+ draggableView._visualTop = top
562
+ draggableView._visualLeft = left
563
+
170
564
  if (params.isIOS) {
171
- const { x, y } = calculateTranslation(draggableView, draggableView.parent.rect, left, top)
172
- draggableView.applyProperties({ duration: 0, transform: Ti.UI.createMatrix2D().translate(x, y) })
565
+ const r = draggableView.rotate ?? 0
566
+ const s = draggableView.scale ?? 1
567
+
568
+ if (r !== 0 || s !== 1) {
569
+ // Delta-based drag for transformed views (preserves rotate/scale)
570
+ const translation = draggableView.translation ?? { x: 0, y: 0 }
571
+ const deltaX = Math.round((event.x - offsetX) * s)
572
+ const deltaY = Math.round((event.y - offsetY) * s)
573
+ translation.x += deltaX
574
+ translation.y += deltaY
575
+ draggableView.applyProperties({
576
+ translation, rotate: r, scale: s,
577
+ transform: Ti.UI.createMatrix2D().translate(translation.x, translation.y).rotate(r).scale(s)
578
+ })
579
+ } else {
580
+ // Standard drag for non-transformed views
581
+ const { x, y } = calculateTranslation(draggableView, draggableView.parent.rect, left, top)
582
+ draggableView.applyProperties({ duration: 0, transform: Ti.UI.createMatrix2D().translate(x, y) })
583
+ }
173
584
  } else {
174
- draggableView.animate(Ti.UI.createAnimation({ top, left, duration: 0 }))
585
+ const r = draggableView.rotate ?? 0
586
+ const s = draggableView.scale ?? 1
587
+
588
+ if (r !== 0 || s !== 1) {
589
+ // Delta-based drag for transformed views on Android
590
+ const translation = draggableView.translation ?? { x: 0, y: 0 }
591
+ const deltaX = Math.round((event.x - offsetX) * s)
592
+ const deltaY = Math.round((event.y - offsetY) * s)
593
+ translation.x += deltaX
594
+ translation.y += deltaY
595
+ draggableView.animate(Ti.UI.createAnimation({
596
+ transform: Ti.UI.createMatrix2D().translate(translation.x, translation.y).rotate(r).scale(s),
597
+ duration: 0
598
+ }))
599
+ draggableView.translation = translation
600
+ draggableView.rotate = r
601
+ draggableView.scale = s
602
+ } else {
603
+ draggableView.animate(Ti.UI.createAnimation({ top, left, duration: 0 }))
604
+ }
605
+ }
606
+
607
+ if (draggableView._collisionEnabled && params.dragCB) {
608
+ try {
609
+ const target = checkCollision(draggableView)
610
+ if (target) { params.lastKnownTarget = target }
611
+ if (target !== params.lastHoverTarget) {
612
+ params.lastHoverTarget = target
613
+ params.dragCB(draggableView, target)
614
+ }
615
+ } catch (e) {
616
+ logger(` !! collision error in touchmove: ${e.message}`)
617
+ }
175
618
  }
176
619
  }
177
620
 
@@ -179,15 +622,15 @@ function Animation(args = {}) {
179
622
  let x = left - parentViewRect.width / 2 + draggableView.rect.width / 2
180
623
  let y = top - parentViewRect.height / 2 + draggableView.rect.height / 2
181
624
 
182
- if (draggableView.left) {
625
+ if (draggableView.left != null) {
183
626
  x = left - draggableView.left
184
- } else if (draggableView.right) {
627
+ } else if (draggableView.right != null) {
185
628
  x = left - (parentViewRect.width - draggableView.right - draggableView.rect.width)
186
629
  }
187
630
 
188
- if (draggableView.top) {
631
+ if (draggableView.top != null) {
189
632
  y = top - draggableView.top
190
- } else if (draggableView.bottom) {
633
+ } else if (draggableView.bottom != null) {
191
634
  y = top - (parentViewRect.height - draggableView.bottom - draggableView.rect.height)
192
635
  }
193
636
 
@@ -207,8 +650,14 @@ function Animation(args = {}) {
207
650
  }
208
651
 
209
652
  function updateDraggableZIndex(event) {
210
- params.draggables.push(params.draggables.splice(realSourceView(event.source).zIndex, 1)[0])
211
- params.draggables.forEach((draggable, key) => { draggable.zIndex = key })
653
+ if (args.animationProperties?.keepZIndex) return
654
+
655
+ const source = realSourceView(event.source)
656
+ const idx = params.draggables.indexOf(source)
657
+ if (idx !== -1) {
658
+ params.draggables.push(params.draggables.splice(idx, 1)[0])
659
+ params.draggables.forEach((draggable, key) => { draggable.zIndex = key })
660
+ }
212
661
  }
213
662
 
214
663
  function realSourceView(_source) {
@@ -270,6 +719,26 @@ function Animation(args = {}) {
270
719
  })
271
720
  }
272
721
 
722
+ function checkCollision(draggableView) {
723
+ const top = draggableView._visualTop
724
+ const left = draggableView._visualLeft
725
+ if (left === null || left === undefined || top === null || top === undefined) {
726
+ return null
727
+ }
728
+
729
+ const cy = top + draggableView.rect.height / 2
730
+ const cx = left + draggableView.rect.width / 2
731
+
732
+ for (const other of params.collisionViews) {
733
+ if (other === draggableView) { continue }
734
+ const r = other.rect
735
+ if (cx > r.x && cx < r.x + r.width && cy > r.y && cy < r.y + r.height) {
736
+ return other
737
+ }
738
+ }
739
+ return null
740
+ }
741
+
273
742
  function checkDraggable(_view, _action) {
274
743
  logger('Check Draggable')
275
744
  logger(' -> `' + _action + '`')
@@ -300,18 +769,18 @@ function Animation(args = {}) {
300
769
  params.playing = false
301
770
  if (typeof _cb === 'function') {
302
771
  const enrichedEvent = {
303
- // Solo propiedades seguras del event original
772
+ // Safe properties from the original event
304
773
  type: event.type,
305
774
  bubbles: event.bubbles,
306
775
  cancelBubble: event.cancelBubble,
307
- // Nuestras propiedades añadidas (solo primitivos)
776
+ // Added properties (primitives only)
308
777
  action, // 'play'
309
778
  state: params.open ? 'open' : 'close',
310
779
  id: params.id,
311
- targetId: view.id || 'unknown', // Solo el ID del view, no el objeto
780
+ targetId: view.id || 'unknown', // View ID only, not the object
312
781
  index,
313
782
  total,
314
- // Método helper para obtener el view
783
+ // Helper method to get the view
315
784
  getTarget: () => view
316
785
  }
317
786
  _cb(enrichedEvent)
@@ -341,14 +810,14 @@ function Animation(args = {}) {
341
810
  type: 'applied',
342
811
  bubbles: false,
343
812
  cancelBubble: false,
344
- // Nuestras propiedades (solo primitivos)
813
+ // Added properties (primitives only)
345
814
  action, // 'apply'
346
815
  state: params.open ? 'open' : 'close',
347
816
  id: params.id,
348
- targetId: view.id || 'unknown', // Solo el ID del view
817
+ targetId: view.id || 'unknown', // View ID only, not the object
349
818
  index,
350
819
  total,
351
- // Método helper para obtener el view
820
+ // Helper method to get the view
352
821
  getTarget: () => view
353
822
  }
354
823
  _cb(enrichedEvent)