@brandocms/jupiter 4.0.0-beta.1 → 5.0.0-beta.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.
Files changed (50) hide show
  1. package/README.md +191 -2
  2. package/package.json +20 -18
  3. package/src/index.js +10 -10
  4. package/src/modules/Application/index.js +203 -157
  5. package/src/modules/Cookies/index.js +34 -55
  6. package/src/modules/CoverOverlay/index.js +20 -13
  7. package/src/modules/Dataloader/index.js +71 -24
  8. package/src/modules/Dataloader/url-sync.js +238 -0
  9. package/src/modules/Dom/index.js +18 -0
  10. package/src/modules/DoubleHeader/index.js +571 -0
  11. package/src/modules/Dropdown/index.js +101 -75
  12. package/src/modules/EqualHeightElements/index.js +5 -7
  13. package/src/modules/EqualHeightImages/index.js +7 -2
  14. package/src/modules/FixedHeader/index.js +60 -30
  15. package/src/modules/FooterReveal/index.js +3 -3
  16. package/src/modules/HeroSlider/index.js +207 -91
  17. package/src/modules/HeroVideo/index.js +15 -27
  18. package/src/modules/Lazyload/index.js +101 -80
  19. package/src/modules/Lightbox/index.js +17 -55
  20. package/src/modules/Links/index.js +54 -49
  21. package/src/modules/Looper/index.js +1737 -0
  22. package/src/modules/Marquee/index.js +106 -37
  23. package/src/modules/MobileMenu/index.js +70 -124
  24. package/src/modules/Moonwalk/index.js +349 -150
  25. package/src/modules/Popover/index.js +186 -28
  26. package/src/modules/Popup/index.js +27 -34
  27. package/src/modules/StackedBoxes/index.js +3 -3
  28. package/src/modules/StickyHeader/index.js +364 -155
  29. package/src/modules/Toggler/index.js +184 -27
  30. package/src/utils/motion-helpers.js +330 -0
  31. package/types/index.d.ts +1 -30
  32. package/types/modules/Application/index.d.ts +6 -6
  33. package/types/modules/Breakpoints/index.d.ts +2 -0
  34. package/types/modules/Dataloader/index.d.ts +5 -2
  35. package/types/modules/Dataloader/url-sync.d.ts +36 -0
  36. package/types/modules/Dom/index.d.ts +7 -0
  37. package/types/modules/DoubleHeader/index.d.ts +63 -0
  38. package/types/modules/Dropdown/index.d.ts +7 -30
  39. package/types/modules/EqualHeightImages/index.d.ts +1 -1
  40. package/types/modules/FixedHeader/index.d.ts +1 -1
  41. package/types/modules/Lazyload/index.d.ts +9 -9
  42. package/types/modules/Lightbox/index.d.ts +0 -5
  43. package/types/modules/Looper/index.d.ts +127 -0
  44. package/types/modules/Moonwalk/index.d.ts +6 -15
  45. package/types/modules/Parallax/index.d.ts +10 -32
  46. package/types/modules/Popover/index.d.ts +12 -0
  47. package/types/modules/Popup/index.d.ts +6 -19
  48. package/types/modules/ScrollSpy/index.d.ts +1 -1
  49. package/types/modules/StickyHeader/index.d.ts +171 -14
  50. package/types/modules/Toggler/index.d.ts +24 -2
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Vendor imports
3
3
  */
4
- import { gsap, CSSPlugin } from 'gsap/all'
4
+ import { animate, stagger } from 'motion'
5
5
  import _defaultsDeep from 'lodash.defaultsdeep'
6
6
 
7
7
  /**
@@ -11,9 +11,28 @@ import * as Events from '../../events'
11
11
  import prefersReducedMotion from '../../utils/prefersReducedMotion'
12
12
  import imageIsLoaded from '../../utils/imageIsLoaded'
13
13
  import imagesAreLoaded from '../../utils/imagesAreLoaded'
14
+ import { set, animateAutoAlpha, delayedCall, convertEasing } from '../../utils/motion-helpers'
14
15
  import Dom from '../Dom'
15
16
 
16
- gsap.registerPlugin(CSSPlugin)
17
+ /**
18
+ * Debug logging
19
+ */
20
+ const DEBUG = false
21
+
22
+ function logMoonwalk(category, message, data = {}) {
23
+ if (DEBUG) {
24
+ console.log(`[Moonwalk:${category}]`, message, data)
25
+ }
26
+ }
27
+
28
+ function logComputedStyle(element, props = ['opacity', 'transform']) {
29
+ if (DEBUG && element) {
30
+ const computed = window.getComputedStyle(element)
31
+ const values = {}
32
+ props.forEach((prop) => (values[prop] = computed[prop]))
33
+ console.log('[Moonwalk:ComputedStyle]', element, values)
34
+ }
35
+ }
17
36
 
18
37
  /**
19
38
  * @typedef {Object} MoonwalkTransition
@@ -39,6 +58,7 @@ gsap.registerPlugin(CSSPlugin)
39
58
  * @property {boolean} [repeated=false] - Whether the run should repeat
40
59
  * @property {string} [rootMargin] - IntersectionObserver rootMargin
41
60
  * @property {Function} [initialize] - Function called during initialization
61
+ * @property {Function} [onReady] - Function called when APPLICATION_REVEALED fires, before viewport observers start
42
62
  */
43
63
 
44
64
  /**
@@ -344,6 +364,7 @@ export default class Moonwalk {
344
364
  el: run,
345
365
  threshold: foundRun.threshold || 0,
346
366
  initialize: foundRun.initialize,
367
+ onReady: foundRun.onReady,
347
368
  callback: foundRun.callback,
348
369
  onExit: foundRun.onExit,
349
370
  repeated: foundRun.repeated,
@@ -386,16 +407,15 @@ export default class Moonwalk {
386
407
  this.addIndexes(section)
387
408
  }
388
409
 
389
- const timeline = gsap.timeline({
390
- autoRemoveChildren: false,
391
- smoothChildTiming: false,
392
- })
393
-
394
410
  return {
395
411
  id: Math.random().toString(36).substring(7),
396
412
  el: section,
397
413
  name: section.getAttribute('data-moonwalk-section') || null,
398
- timeline,
414
+ animation: {
415
+ lastDelay: 0,
416
+ lastDuration: 0,
417
+ lastStartTime: null,
418
+ },
399
419
  observer: null,
400
420
  stage: {
401
421
  name: section.getAttribute('data-moonwalk-stage') || null,
@@ -481,14 +501,32 @@ export default class Moonwalk {
481
501
  section.children = this.orderChildren(section.el.children)
482
502
  }
483
503
 
484
- const fromTransition = sectionWalk.alphaTween
485
- ? {
486
- ...sectionWalk.transition.from,
487
- opacity: 0,
488
- }
489
- : sectionWalk.transition.from
490
-
491
- gsap.set(section.children, fromTransition)
504
+ // Only set initial states for JS animations (transition !== null)
505
+ if (sectionWalk.transition) {
506
+ const fromTransition = sectionWalk.alphaTween
507
+ ? {
508
+ ...sectionWalk.transition.from,
509
+ opacity: 0,
510
+ }
511
+ : sectionWalk.transition.from
512
+
513
+ logMoonwalk('InitialState', 'Setting initial state for named section', {
514
+ section: section.name,
515
+ childCount: section.children.length,
516
+ fromTransition,
517
+ })
518
+ set(section.children, fromTransition)
519
+
520
+ // Check if styles were actually applied
521
+ if (section.children.length > 0) {
522
+ logComputedStyle(section.children[0])
523
+ }
524
+ } else {
525
+ logMoonwalk('InitialState', 'Skipping initial state for CSS-only section', {
526
+ section: section.name,
527
+ childCount: section.children.length,
528
+ })
529
+ }
492
530
  }
493
531
 
494
532
  if (section.stage.name) {
@@ -500,7 +538,12 @@ export default class Moonwalk {
500
538
  section.stage.name
501
539
  )
502
540
  } else {
503
- gsap.set(section.el, stageTween.transition.from)
541
+ logMoonwalk('InitialState', 'Setting stage initial state', {
542
+ stage: section.stage.name,
543
+ from: stageTween.transition.from,
544
+ })
545
+ set(section.el, stageTween.transition.from)
546
+ logComputedStyle(section.el)
504
547
  }
505
548
  }
506
549
 
@@ -530,12 +573,9 @@ export default class Moonwalk {
530
573
  // run stage tween
531
574
  const stageTween = walks[section.stage.name]
532
575
 
533
- const to = {
534
- ...stageTween.transition.to,
576
+ animate(entry.target, stageTween.transition.to, {
535
577
  duration: stageTween.duration,
536
- }
537
-
538
- section.timeline.to(entry.target, to, 0)
578
+ })
539
579
  section.stage.firstTween = true
540
580
  }
541
581
  }
@@ -550,44 +590,75 @@ export default class Moonwalk {
550
590
  )
551
591
  }
552
592
 
553
- if (typeof tween.alphaTween === 'object') {
554
- tween.alphaTween.duration = tween.alphaTween.duration
555
- ? tween.alphaTween.duration
556
- : tween.duration
557
- } else if (tween.alphaTween === true) {
558
- tween.alphaTween = {
559
- duration: tween.duration,
560
- ease: 'sine.in',
593
+ logMoonwalk('SectionObserver', 'Named section triggered', {
594
+ sectionName: section.name,
595
+ childCount: section.children.length,
596
+ interval: tween.interval,
597
+ duration: tween.duration,
598
+ hasAlphaTween: !!tween.alphaTween,
599
+ isCssOnly: !tween.transition,
600
+ })
601
+
602
+ // Check if this is CSS-only animation (transition: null)
603
+ if (!tween.transition) {
604
+ // CSS-only mode - stagger adding the data-moonwalked attribute
605
+ logMoonwalk('SectionObserver', 'Using CSS-only mode', {
606
+ sectionName: section.name,
607
+ })
608
+
609
+ section.children.forEach((child, index) => {
610
+ const delay = (tween.startDelay || 0) + index * tween.interval
611
+ delayedCall(delay, () => {
612
+ child.setAttribute('data-moonwalked', '')
613
+ })
614
+ })
615
+ } else {
616
+ // JS animation mode
617
+ if (typeof tween.alphaTween === 'object') {
618
+ tween.alphaTween.duration = tween.alphaTween.duration
619
+ ? tween.alphaTween.duration
620
+ : tween.duration
621
+ } else if (tween.alphaTween === true) {
622
+ tween.alphaTween = {
623
+ duration: tween.duration,
624
+ ease: 'easeIn',
625
+ }
561
626
  }
562
- }
563
627
 
564
- if (tween.startDelay) {
565
- tween.transition.to = {
566
- ...tween.transition.to,
567
- delay: tween.startDelay,
568
- }
569
- }
628
+ // Extract ease from to values and convert for Motion.js
629
+ const { ease: tweenEase, ...toValues } = tween.transition.to
630
+ const convertedEase = convertEasing(tweenEase || 'easeOut')
570
631
 
571
- section.timeline.staggerTo(
572
- section.children,
573
- tween.duration,
574
- tween.transition.to,
575
- tween.interval,
576
- 0
577
- )
632
+ const animationOptions = {
633
+ duration: tween.duration,
634
+ ease: convertedEase,
635
+ delay: stagger(tween.interval, {
636
+ startDelay: tween.startDelay || 0,
637
+ }),
638
+ }
578
639
 
579
- if (tween.alphaTween) {
580
- section.timeline.staggerTo(
581
- section.children,
582
- tween.alphaTween.duration,
583
- {
584
- opacity: 1,
585
- ease: tween.alphaTween.ease,
586
- delay: tween.startDelay || 0,
587
- },
588
- tween.interval,
589
- 0
590
- )
640
+ logMoonwalk('SectionObserver', 'Starting stagger animation', {
641
+ sectionName: section.name,
642
+ to: toValues,
643
+ ease: convertedEase,
644
+ options: animationOptions,
645
+ })
646
+
647
+ animate(section.children, toValues, animationOptions)
648
+
649
+ if (tween.alphaTween) {
650
+ animate(
651
+ section.children,
652
+ { opacity: 1 },
653
+ {
654
+ duration: tween.alphaTween.duration,
655
+ ease: convertEasing(tween.alphaTween.ease || 'easeIn'),
656
+ delay: stagger(tween.interval, {
657
+ startDelay: tween.startDelay || 0,
658
+ }),
659
+ }
660
+ )
661
+ }
591
662
  }
592
663
  }
593
664
 
@@ -629,6 +700,64 @@ export default class Moonwalk {
629
700
  })
630
701
  }
631
702
 
703
+ /**
704
+ * Calculate the delay for the next animation in the section.
705
+ * This replaces GSAP's timeline.recent() logic.
706
+ *
707
+ * @param {*} section - The section object
708
+ * @param {*} duration - Duration of the animation to add
709
+ * @param {*} overlap - How much the animations should overlap
710
+ * @returns {number} The delay in seconds
711
+ */
712
+ calculateDelay(section, duration, overlap) {
713
+ if (!section.animation.lastStartTime) {
714
+ // First animation in section
715
+ logMoonwalk('DelayCalc', 'First animation in section', { delay: 0 })
716
+ return 0
717
+ }
718
+
719
+ const now = performance.now()
720
+ const elapsed = (now - section.animation.lastStartTime) / 1000
721
+ // overlap is negative when animations should stagger (start before previous ends)
722
+ // So we ADD overlap (which is negative) to get the correct next start time
723
+ const idealNextStart =
724
+ section.animation.lastDelay + section.animation.lastDuration + overlap
725
+ const actualDelay = Math.max(0, idealNextStart - elapsed)
726
+
727
+ logMoonwalk('DelayCalc', 'Calculating delay', {
728
+ elapsed: elapsed.toFixed(3),
729
+ lastDelay: section.animation.lastDelay,
730
+ lastDuration: section.animation.lastDuration,
731
+ overlap,
732
+ idealNextStart,
733
+ actualDelay: actualDelay.toFixed(3),
734
+ })
735
+
736
+ return actualDelay
737
+ }
738
+
739
+ /**
740
+ * Update the animation state after adding an animation.
741
+ *
742
+ * @param {*} section - The section object
743
+ * @param {*} delay - The delay that was used
744
+ * @param {*} duration - The duration of the animation
745
+ */
746
+ updateAnimationState(section, delay, duration) {
747
+ const previousState = { ...section.animation }
748
+
749
+ section.animation.lastDelay = delay
750
+ section.animation.lastDuration = duration
751
+ section.animation.lastStartTime = performance.now()
752
+
753
+ logMoonwalk('StateUpdate', 'Updating animation state', {
754
+ delay,
755
+ duration,
756
+ previousState,
757
+ newState: { ...section.animation },
758
+ })
759
+ }
760
+
632
761
  onReady() {
633
762
  if (this.opts.initialDelay) {
634
763
  setTimeout(() => {
@@ -646,6 +775,14 @@ export default class Moonwalk {
646
775
  ready() {
647
776
  const { opts } = this
648
777
 
778
+ // Execute onReady callbacks for all runs
779
+ for (let idx = 0; idx < this.runs.length; idx += 1) {
780
+ const run = this.runs[idx]
781
+ if (run && run.onReady) {
782
+ run.onReady(run.el)
783
+ }
784
+ }
785
+
649
786
  for (let idx = 0; idx < this.runs.length; idx += 1) {
650
787
  const run = this.runs[idx]
651
788
 
@@ -688,7 +825,29 @@ export default class Moonwalk {
688
825
  }
689
826
 
690
827
  section.elements = section.el.querySelectorAll('[data-moonwalk]')
691
- section.elements.forEach((box) => section.observer.observe(box))
828
+
829
+ // Only set initial states and observe individual elements for unnamed sections
830
+ // Named sections are observed at the section level via sectionObserver
831
+ if (!section.name) {
832
+ // Set initial states for tweenJS elements BEFORE observing
833
+ section.elements.forEach((element) => {
834
+ const walkName = element.getAttribute('data-moonwalk')
835
+ const cfg = !walkName.length
836
+ ? opts.walks.default
837
+ : opts.walks[walkName]
838
+
839
+ // Only set initial state if this uses tweenJS (has transition property)
840
+ if (cfg && cfg.transition) {
841
+ logMoonwalk('InitialState', 'Setting initial state for individual element', {
842
+ walkName: walkName || 'default',
843
+ from: cfg.transition.from,
844
+ })
845
+ set(element, cfg.transition.from)
846
+ }
847
+ })
848
+
849
+ section.elements.forEach((box) => section.observer.observe(box))
850
+ }
692
851
  }
693
852
  }
694
853
 
@@ -830,16 +989,25 @@ export default class Moonwalk {
830
989
  if (entry.isIntersecting || entry.intersectionRatio > 0) {
831
990
  section.running = true
832
991
 
833
- if (entry.target.dataset.moonwalkId) {
834
- console.debug('-- intersecting', entry.target.dataset.moonwalkId)
835
- }
836
-
837
992
  const walkName = entry.target.getAttribute('data-moonwalk')
993
+ const targetId =
994
+ entry.target.getAttribute('data-testid') ||
995
+ walkName ||
996
+ entry.target.className
997
+
998
+ logMoonwalk('Observer', 'Element entered viewport', {
999
+ target: targetId,
1000
+ walkName,
1001
+ isIntersecting: entry.isIntersecting,
1002
+ intersectionRatio: entry.intersectionRatio,
1003
+ })
838
1004
  const cfg = !walkName.length
839
1005
  ? opts.walks.default
840
1006
  : opts.walks[walkName]
841
1007
 
842
- const { duration, transition, interval, startDelay } = cfg
1008
+ const { duration, transition, startDelay } = cfg
1009
+ // Default interval to 0.15 if not specified (same as default walk)
1010
+ const interval = cfg.interval !== undefined ? cfg.interval : 0.15
843
1011
 
844
1012
  let { alphaTween } = cfg
845
1013
  let overlap = (duration - interval) * -1 // flip it
@@ -856,27 +1024,36 @@ export default class Moonwalk {
856
1024
  } else if (alphaTween === true) {
857
1025
  alphaTween = {
858
1026
  duration,
859
- ease: 'sine.in',
1027
+ ease: 'easeIn',
860
1028
  }
861
1029
  }
862
1030
 
863
- const tween = transition ? this.tweenJS : this.tweenCSS
864
-
865
1031
  const tweenFn = () => {
866
- tween(
867
- section,
868
- entry.target,
869
- duration,
870
- interval,
871
- transition,
872
- overlap,
873
- alphaTween
874
- )
1032
+ if (transition) {
1033
+ this.tweenJS(
1034
+ section,
1035
+ entry.target,
1036
+ duration,
1037
+ interval,
1038
+ transition,
1039
+ overlap,
1040
+ alphaTween
1041
+ )
1042
+ } else {
1043
+ this.tweenCSS(
1044
+ section,
1045
+ entry.target,
1046
+ duration,
1047
+ interval,
1048
+ transition,
1049
+ overlap
1050
+ )
1051
+ }
875
1052
  }
876
1053
 
877
1054
  const wrappedTweenFn = () => {
878
1055
  if (startDelay) {
879
- gsap.delayedCall(startDelay, tweenFn)
1056
+ delayedCall(startDelay, tweenFn)
880
1057
  } else {
881
1058
  tweenFn()
882
1059
  }
@@ -940,56 +1117,90 @@ export default class Moonwalk {
940
1117
  tweenOverlap,
941
1118
  alphaTween
942
1119
  ) {
943
- let tweenPosition
944
- const startingPoint = tweenDuration - tweenOverlap
1120
+ const targetId =
1121
+ target.getAttribute('data-testid') ||
1122
+ target.getAttribute('data-moonwalk') ||
1123
+ target.className
1124
+
1125
+ logMoonwalk('TweenJS', 'Starting tweenJS', {
1126
+ target: targetId,
1127
+ duration: tweenDuration,
1128
+ overlap: tweenOverlap,
1129
+ hasAlphaTween: !!alphaTween,
1130
+ })
945
1131
 
946
1132
  if (Dom.hasAttribute(target, 'data-moonwalked')) {
1133
+ logMoonwalk('TweenJS', 'Already moonwalked, skipping', { target: targetId })
947
1134
  return
948
1135
  }
949
1136
 
950
- if (section.timeline.isActive() && section.timeline.recent()) {
951
- const currentTime = section.timeline.time()
952
- const lastTweenTime = section.timeline.recent().time()
953
- const lastTweenEndTime = section.timeline.recent().endTime()
954
- if (lastTweenTime > startingPoint) {
955
- /* We're late for this tween if it was supposed to be sequential,
956
- so insert at current time in timeline instead */
957
- tweenPosition = () => section.timeline.time()
958
- } else {
959
- if (currentTime + tweenOverlap * -1 < lastTweenEndTime) {
960
- /* Still time, add as normal overlap at the end */
961
- tweenPosition = () => `>${tweenOverlap}`
962
- } else {
963
- /* Won't make it */
964
- tweenPosition = () => section.timeline.time()
965
- }
966
- }
967
- } else {
968
- tweenPosition = () => '>'
969
- }
1137
+ // Calculate delay using our new helper method
1138
+ const delay = this.calculateDelay(section, tweenDuration, tweenOverlap)
1139
+
1140
+ // Initial state should already be set during ready()
1141
+ // Only log for debugging
1142
+ logMoonwalk('TweenJS', 'Element should already have initial state', {
1143
+ target: targetId,
1144
+ expectedFrom: tweenTransition.from,
1145
+ })
1146
+ logComputedStyle(target, ['opacity', 'transform', 'x', 'y'])
970
1147
 
971
- gsap.set(target, tweenTransition.from)
1148
+ // Extract ease from to values (GSAP format) and convert to Motion easing option
1149
+ const { ease, ...toValues } = tweenTransition.to
1150
+ const easingOption = convertEasing(ease || 'easeOut')
972
1151
 
973
- const toTransition = {
974
- ...tweenTransition.to,
1152
+ logMoonwalk('TweenJS', 'Starting animation', {
1153
+ target: targetId,
1154
+ toValues,
975
1155
  duration: tweenDuration,
976
- onComplete: () => target.setAttribute('data-moonwalked', ''),
977
- }
1156
+ delay: delay.toFixed(3),
1157
+ ease: easingOption,
1158
+ })
1159
+
1160
+ // Animate to final state
1161
+ const animation = animate(target, toValues, {
1162
+ duration: tweenDuration,
1163
+ delay,
1164
+ ease: easingOption,
1165
+ })
978
1166
 
979
- section.timeline.to(target, toTransition, tweenPosition())
1167
+ // Use .finished promise for completion callback
1168
+ if (animation && animation.finished) {
1169
+ animation.finished
1170
+ .then(() => {
1171
+ logMoonwalk('TweenJS', 'Animation completed', { target: targetId })
1172
+ target.setAttribute('data-moonwalked', '')
1173
+ })
1174
+ .catch((err) => {
1175
+ // Animation cancelled or failed, still mark as walked
1176
+ logMoonwalk('TweenJS', 'Animation failed/cancelled', {
1177
+ target: targetId,
1178
+ error: err,
1179
+ })
1180
+ target.setAttribute('data-moonwalked', '')
1181
+ })
1182
+ } else {
1183
+ // No animation object returned, mark immediately
1184
+ logMoonwalk('TweenJS', 'No animation object returned', { target: targetId })
1185
+ target.setAttribute('data-moonwalked', '')
1186
+ }
980
1187
 
1188
+ // Optional separate alpha animation
981
1189
  if (alphaTween) {
982
- section.timeline.to(
983
- target,
984
- {
985
- duration: alphaTween.duration,
986
- opacity: 1,
987
- ease: alphaTween.ease,
988
- delay: alphaTween.delay ? alphaTween.delay : 0,
989
- },
990
- '<'
991
- )
1190
+ logMoonwalk('TweenJS', 'Adding alpha tween', {
1191
+ target: targetId,
1192
+ duration: alphaTween.duration,
1193
+ delay: (delay + (alphaTween.delay || 0)).toFixed(3),
1194
+ })
1195
+ animate(target, { opacity: 1 }, {
1196
+ duration: alphaTween.duration,
1197
+ ease: convertEasing(alphaTween.ease || 'easeIn'),
1198
+ delay: delay + (alphaTween.delay || 0),
1199
+ })
992
1200
  }
1201
+
1202
+ // Update animation state for next element
1203
+ this.updateAnimationState(section, delay, tweenDuration)
993
1204
  }
994
1205
 
995
1206
  /**
@@ -1009,47 +1220,35 @@ export default class Moonwalk {
1009
1220
  tweenTransition,
1010
1221
  tweenOverlap
1011
1222
  ) {
1012
- let tweenPosition
1013
- const startingPoint = tweenDuration - tweenOverlap * -1
1014
-
1015
1223
  if (Dom.hasAttribute(target, 'data-moonwalked')) {
1016
1224
  return
1017
1225
  }
1018
1226
 
1019
- if (section.timeline.isActive() && section.timeline.recent()) {
1020
- const currentTime = section.timeline.time()
1021
- const lastTweenTime = section.timeline.recent().time()
1022
- const lastTweenEndTime = section.timeline.recent().endTime()
1023
- if (lastTweenTime > startingPoint) {
1024
- /* We're late for this tween if it was supposed to be sequential,
1025
- so insert at current time in timeline instead */
1026
- tweenPosition = () => section.timeline.time()
1027
- } else {
1028
- if (currentTime + tweenOverlap * -1 < lastTweenEndTime) {
1029
- /* Still time, add as normal overlap at the end */
1030
- tweenPosition = () => `>${tweenOverlap}`
1031
- } else {
1032
- /* Won't make it */
1033
- tweenPosition = () => section.timeline.time()
1034
- }
1035
- }
1036
- } else {
1037
- tweenPosition = () => '>'
1038
- }
1227
+ // Calculate delay using our helper method for stagger effect
1228
+ const calculatedDelay = this.calculateDelay(
1229
+ section,
1230
+ tweenDuration,
1231
+ tweenOverlap
1232
+ )
1039
1233
 
1040
- section.timeline
1041
- .to(
1042
- target,
1043
- {
1044
- css: {
1045
- className: target.className
1046
- ? `${target.className} moonwalked`
1047
- : 'moonwalked',
1048
- },
1049
- duration: tweenDuration,
1050
- },
1051
- tweenPosition()
1052
- )
1053
- .call(() => target.setAttribute('data-moonwalked', ''), null, '>')
1234
+ const targetId = target.getAttribute('data-testid') || target.className
1235
+
1236
+ logMoonwalk('TweenCSS', 'Scheduling CSS animation', {
1237
+ target: targetId,
1238
+ delay: calculatedDelay.toFixed(3),
1239
+ duration: tweenDuration,
1240
+ })
1241
+
1242
+ // Add class after delay to trigger CSS transition
1243
+ delayedCall(calculatedDelay, () => {
1244
+ logMoonwalk('TweenCSS', 'Adding moonwalked attribute', {
1245
+ target: targetId,
1246
+ })
1247
+ target.classList.add('moonwalked')
1248
+ target.setAttribute('data-moonwalked', '')
1249
+ })
1250
+
1251
+ // Update animation state for next element in section
1252
+ this.updateAnimationState(section, calculatedDelay, tweenDuration)
1054
1253
  }
1055
1254
  }