gsap-react-marquee 0.1.3 → 0.1.5

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/index.cjs.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var require$$0 = require('react');
6
6
  var react = require('@gsap/react');
7
- var gsap = require('gsap');
7
+ var gsap$1 = require('gsap');
8
8
 
9
9
  var jsxRuntime = {exports: {}};
10
10
 
@@ -425,6 +425,710 @@ if (process.env.NODE_ENV === 'production') {
425
425
 
426
426
  var jsxRuntimeExports = jsxRuntime.exports;
427
427
 
428
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
429
+
430
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); return Constructor; }
431
+
432
+ /*!
433
+ * Observer 3.13.0
434
+ * https://gsap.com
435
+ *
436
+ * @license Copyright 2008-2025, GreenSock. All rights reserved.
437
+ * Subject to the terms at https://gsap.com/standard-license
438
+ * @author: Jack Doyle, jack@greensock.com
439
+ */
440
+
441
+ /* eslint-disable */
442
+ var gsap,
443
+ _coreInitted,
444
+ _win,
445
+ _doc,
446
+ _docEl,
447
+ _body,
448
+ _isTouch,
449
+ _pointerType,
450
+ ScrollTrigger,
451
+ _root,
452
+ _normalizer,
453
+ _eventTypes,
454
+ _context,
455
+ _getGSAP = function _getGSAP() {
456
+ return gsap || typeof window !== "undefined" && (gsap = window.gsap) && gsap.registerPlugin && gsap;
457
+ },
458
+ _startup = 1,
459
+ _observers = [],
460
+ _scrollers = [],
461
+ _proxies = [],
462
+ _getTime = Date.now,
463
+ _bridge = function _bridge(name, value) {
464
+ return value;
465
+ },
466
+ _integrate = function _integrate() {
467
+ var core = ScrollTrigger.core,
468
+ data = core.bridge || {},
469
+ scrollers = core._scrollers,
470
+ proxies = core._proxies;
471
+ scrollers.push.apply(scrollers, _scrollers);
472
+ proxies.push.apply(proxies, _proxies);
473
+ _scrollers = scrollers;
474
+ _proxies = proxies;
475
+
476
+ _bridge = function _bridge(name, value) {
477
+ return data[name](value);
478
+ };
479
+ },
480
+ _getProxyProp = function _getProxyProp(element, property) {
481
+ return ~_proxies.indexOf(element) && _proxies[_proxies.indexOf(element) + 1][property];
482
+ },
483
+ _isViewport = function _isViewport(el) {
484
+ return !!~_root.indexOf(el);
485
+ },
486
+ _addListener = function _addListener(element, type, func, passive, capture) {
487
+ return element.addEventListener(type, func, {
488
+ passive: passive !== false,
489
+ capture: !!capture
490
+ });
491
+ },
492
+ _removeListener = function _removeListener(element, type, func, capture) {
493
+ return element.removeEventListener(type, func, !!capture);
494
+ },
495
+ _scrollLeft = "scrollLeft",
496
+ _scrollTop = "scrollTop",
497
+ _onScroll = function _onScroll() {
498
+ return _normalizer && _normalizer.isPressed || _scrollers.cache++;
499
+ },
500
+ _scrollCacheFunc = function _scrollCacheFunc(f, doNotCache) {
501
+ var cachingFunc = function cachingFunc(value) {
502
+ // since reading the scrollTop/scrollLeft/pageOffsetY/pageOffsetX can trigger a layout, this function allows us to cache the value so it only gets read fresh after a "scroll" event fires (or while we're refreshing because that can lengthen the page and alter the scroll position). when "soft" is true, that means don't actually set the scroll, but cache the new value instead (useful in ScrollSmoother)
503
+ if (value || value === 0) {
504
+ _startup && (_win.history.scrollRestoration = "manual"); // otherwise the new position will get overwritten by the browser onload.
505
+
506
+ var isNormalizing = _normalizer && _normalizer.isPressed;
507
+ value = cachingFunc.v = Math.round(value) || (_normalizer && _normalizer.iOS ? 1 : 0); //TODO: iOS Bug: if you allow it to go to 0, Safari can start to report super strange (wildly inaccurate) touch positions!
508
+
509
+ f(value);
510
+ cachingFunc.cacheID = _scrollers.cache;
511
+ isNormalizing && _bridge("ss", value); // set scroll (notify ScrollTrigger so it can dispatch a "scrollStart" event if necessary
512
+ } else if (doNotCache || _scrollers.cache !== cachingFunc.cacheID || _bridge("ref")) {
513
+ cachingFunc.cacheID = _scrollers.cache;
514
+ cachingFunc.v = f();
515
+ }
516
+
517
+ return cachingFunc.v + cachingFunc.offset;
518
+ };
519
+
520
+ cachingFunc.offset = 0;
521
+ return f && cachingFunc;
522
+ },
523
+ _horizontal = {
524
+ s: _scrollLeft,
525
+ p: "left",
526
+ p2: "Left",
527
+ os: "right",
528
+ os2: "Right",
529
+ d: "width",
530
+ d2: "Width",
531
+ a: "x",
532
+ sc: _scrollCacheFunc(function (value) {
533
+ return arguments.length ? _win.scrollTo(value, _vertical.sc()) : _win.pageXOffset || _doc[_scrollLeft] || _docEl[_scrollLeft] || _body[_scrollLeft] || 0;
534
+ })
535
+ },
536
+ _vertical = {
537
+ s: _scrollTop,
538
+ p: "top",
539
+ p2: "Top",
540
+ os: "bottom",
541
+ os2: "Bottom",
542
+ d: "height",
543
+ d2: "Height",
544
+ a: "y",
545
+ op: _horizontal,
546
+ sc: _scrollCacheFunc(function (value) {
547
+ return arguments.length ? _win.scrollTo(_horizontal.sc(), value) : _win.pageYOffset || _doc[_scrollTop] || _docEl[_scrollTop] || _body[_scrollTop] || 0;
548
+ })
549
+ },
550
+ _getTarget = function _getTarget(t, self) {
551
+ return (self && self._ctx && self._ctx.selector || gsap.utils.toArray)(t)[0] || (typeof t === "string" && gsap.config().nullTargetWarn !== false ? console.warn("Element not found:", t) : null);
552
+ },
553
+ _isWithin = function _isWithin(element, list) {
554
+ // check if the element is in the list or is a descendant of an element in the list.
555
+ var i = list.length;
556
+
557
+ while (i--) {
558
+ if (list[i] === element || list[i].contains(element)) {
559
+ return true;
560
+ }
561
+ }
562
+
563
+ return false;
564
+ },
565
+ _getScrollFunc = function _getScrollFunc(element, _ref) {
566
+ var s = _ref.s,
567
+ sc = _ref.sc;
568
+ // we store the scroller functions in an alternating sequenced Array like [element, verticalScrollFunc, horizontalScrollFunc, ...] so that we can minimize memory, maximize performance, and we also record the last position as a ".rec" property in order to revert to that after refreshing to ensure things don't shift around.
569
+ _isViewport(element) && (element = _doc.scrollingElement || _docEl);
570
+
571
+ var i = _scrollers.indexOf(element),
572
+ offset = sc === _vertical.sc ? 1 : 2;
573
+
574
+ !~i && (i = _scrollers.push(element) - 1);
575
+ _scrollers[i + offset] || _addListener(element, "scroll", _onScroll); // clear the cache when a scroll occurs
576
+
577
+ var prev = _scrollers[i + offset],
578
+ func = prev || (_scrollers[i + offset] = _scrollCacheFunc(_getProxyProp(element, s), true) || (_isViewport(element) ? sc : _scrollCacheFunc(function (value) {
579
+ return arguments.length ? element[s] = value : element[s];
580
+ })));
581
+ func.target = element;
582
+ prev || (func.smooth = gsap.getProperty(element, "scrollBehavior") === "smooth"); // only set it the first time (don't reset every time a scrollFunc is requested because perhaps it happens during a refresh() when it's disabled in ScrollTrigger.
583
+
584
+ return func;
585
+ },
586
+ _getVelocityProp = function _getVelocityProp(value, minTimeRefresh, useDelta) {
587
+ var v1 = value,
588
+ v2 = value,
589
+ t1 = _getTime(),
590
+ t2 = t1,
591
+ min = minTimeRefresh,
592
+ dropToZeroTime = Math.max(500, min * 3),
593
+ update = function update(value, force) {
594
+ var t = _getTime();
595
+
596
+ if (force || t - t1 > min) {
597
+ v2 = v1;
598
+ v1 = value;
599
+ t2 = t1;
600
+ t1 = t;
601
+ } else {
602
+ v1 += value;
603
+ }
604
+ },
605
+ reset = function reset() {
606
+ v2 = v1 = 0 ;
607
+ t2 = t1 = 0;
608
+ },
609
+ getVelocity = function getVelocity(latestValue) {
610
+ var tOld = t2,
611
+ vOld = v2,
612
+ t = _getTime();
613
+
614
+ (latestValue || latestValue === 0) && latestValue !== v1 && update(latestValue);
615
+ return t1 === t2 || t - t2 > dropToZeroTime ? 0 : (v1 + (vOld )) / ((t ) - tOld) * 1000;
616
+ };
617
+
618
+ return {
619
+ update: update,
620
+ reset: reset,
621
+ getVelocity: getVelocity
622
+ };
623
+ },
624
+ _getEvent = function _getEvent(e, preventDefault) {
625
+ preventDefault && !e._gsapAllow && e.preventDefault();
626
+ return e.changedTouches ? e.changedTouches[0] : e;
627
+ },
628
+ _getAbsoluteMax = function _getAbsoluteMax(a) {
629
+ var max = Math.max.apply(Math, a),
630
+ min = Math.min.apply(Math, a);
631
+ return Math.abs(max) >= Math.abs(min) ? max : min;
632
+ },
633
+ _setScrollTrigger = function _setScrollTrigger() {
634
+ ScrollTrigger = gsap.core.globals().ScrollTrigger;
635
+ ScrollTrigger && ScrollTrigger.core && _integrate();
636
+ },
637
+ _initCore = function _initCore(core) {
638
+ gsap = core || _getGSAP();
639
+
640
+ if (!_coreInitted && gsap && typeof document !== "undefined" && document.body) {
641
+ _win = window;
642
+ _doc = document;
643
+ _docEl = _doc.documentElement;
644
+ _body = _doc.body;
645
+ _root = [_win, _doc, _docEl, _body];
646
+ gsap.utils.clamp;
647
+
648
+ _context = gsap.core.context || function () {};
649
+
650
+ _pointerType = "onpointerenter" in _body ? "pointer" : "mouse"; // isTouch is 0 if no touch, 1 if ONLY touch, and 2 if it can accommodate touch but also other types like mouse/pointer.
651
+
652
+ _isTouch = Observer.isTouch = _win.matchMedia && _win.matchMedia("(hover: none), (pointer: coarse)").matches ? 1 : "ontouchstart" in _win || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 ? 2 : 0;
653
+ _eventTypes = Observer.eventTypes = ("ontouchstart" in _docEl ? "touchstart,touchmove,touchcancel,touchend" : !("onpointerdown" in _docEl) ? "mousedown,mousemove,mouseup,mouseup" : "pointerdown,pointermove,pointercancel,pointerup").split(",");
654
+ setTimeout(function () {
655
+ return _startup = 0;
656
+ }, 500);
657
+
658
+ _setScrollTrigger();
659
+
660
+ _coreInitted = 1;
661
+ }
662
+
663
+ return _coreInitted;
664
+ };
665
+
666
+ _horizontal.op = _vertical;
667
+ _scrollers.cache = 0;
668
+ var Observer = /*#__PURE__*/function () {
669
+ function Observer(vars) {
670
+ this.init(vars);
671
+ }
672
+
673
+ var _proto = Observer.prototype;
674
+
675
+ _proto.init = function init(vars) {
676
+ _coreInitted || _initCore(gsap) || console.warn("Please gsap.registerPlugin(Observer)");
677
+ ScrollTrigger || _setScrollTrigger();
678
+ var tolerance = vars.tolerance,
679
+ dragMinimum = vars.dragMinimum,
680
+ type = vars.type,
681
+ target = vars.target,
682
+ lineHeight = vars.lineHeight,
683
+ debounce = vars.debounce,
684
+ preventDefault = vars.preventDefault,
685
+ onStop = vars.onStop,
686
+ onStopDelay = vars.onStopDelay,
687
+ ignore = vars.ignore,
688
+ wheelSpeed = vars.wheelSpeed,
689
+ event = vars.event,
690
+ onDragStart = vars.onDragStart,
691
+ onDragEnd = vars.onDragEnd,
692
+ onDrag = vars.onDrag,
693
+ onPress = vars.onPress,
694
+ onRelease = vars.onRelease,
695
+ onRight = vars.onRight,
696
+ onLeft = vars.onLeft,
697
+ onUp = vars.onUp,
698
+ onDown = vars.onDown,
699
+ onChangeX = vars.onChangeX,
700
+ onChangeY = vars.onChangeY,
701
+ onChange = vars.onChange,
702
+ onToggleX = vars.onToggleX,
703
+ onToggleY = vars.onToggleY,
704
+ onHover = vars.onHover,
705
+ onHoverEnd = vars.onHoverEnd,
706
+ onMove = vars.onMove,
707
+ ignoreCheck = vars.ignoreCheck,
708
+ isNormalizer = vars.isNormalizer,
709
+ onGestureStart = vars.onGestureStart,
710
+ onGestureEnd = vars.onGestureEnd,
711
+ onWheel = vars.onWheel,
712
+ onEnable = vars.onEnable,
713
+ onDisable = vars.onDisable,
714
+ onClick = vars.onClick,
715
+ scrollSpeed = vars.scrollSpeed,
716
+ capture = vars.capture,
717
+ allowClicks = vars.allowClicks,
718
+ lockAxis = vars.lockAxis,
719
+ onLockAxis = vars.onLockAxis;
720
+ this.target = target = _getTarget(target) || _docEl;
721
+ this.vars = vars;
722
+ ignore && (ignore = gsap.utils.toArray(ignore));
723
+ tolerance = tolerance || 1e-9;
724
+ dragMinimum = dragMinimum || 0;
725
+ wheelSpeed = wheelSpeed || 1;
726
+ scrollSpeed = scrollSpeed || 1;
727
+ type = type || "wheel,touch,pointer";
728
+ debounce = debounce !== false;
729
+ lineHeight || (lineHeight = parseFloat(_win.getComputedStyle(_body).lineHeight) || 22); // note: browser may report "normal", so default to 22.
730
+
731
+ var id,
732
+ onStopDelayedCall,
733
+ dragged,
734
+ moved,
735
+ wheeled,
736
+ locked,
737
+ axis,
738
+ self = this,
739
+ prevDeltaX = 0,
740
+ prevDeltaY = 0,
741
+ passive = vars.passive || !preventDefault && vars.passive !== false,
742
+ scrollFuncX = _getScrollFunc(target, _horizontal),
743
+ scrollFuncY = _getScrollFunc(target, _vertical),
744
+ scrollX = scrollFuncX(),
745
+ scrollY = scrollFuncY(),
746
+ limitToTouch = ~type.indexOf("touch") && !~type.indexOf("pointer") && _eventTypes[0] === "pointerdown",
747
+ // for devices that accommodate mouse events and touch events, we need to distinguish.
748
+ isViewport = _isViewport(target),
749
+ ownerDoc = target.ownerDocument || _doc,
750
+ deltaX = [0, 0, 0],
751
+ // wheel, scroll, pointer/touch
752
+ deltaY = [0, 0, 0],
753
+ onClickTime = 0,
754
+ clickCapture = function clickCapture() {
755
+ return onClickTime = _getTime();
756
+ },
757
+ _ignoreCheck = function _ignoreCheck(e, isPointerOrTouch) {
758
+ return (self.event = e) && ignore && _isWithin(e.target, ignore) || isPointerOrTouch && limitToTouch && e.pointerType !== "touch" || ignoreCheck && ignoreCheck(e, isPointerOrTouch);
759
+ },
760
+ onStopFunc = function onStopFunc() {
761
+ self._vx.reset();
762
+
763
+ self._vy.reset();
764
+
765
+ onStopDelayedCall.pause();
766
+ onStop && onStop(self);
767
+ },
768
+ update = function update() {
769
+ var dx = self.deltaX = _getAbsoluteMax(deltaX),
770
+ dy = self.deltaY = _getAbsoluteMax(deltaY),
771
+ changedX = Math.abs(dx) >= tolerance,
772
+ changedY = Math.abs(dy) >= tolerance;
773
+
774
+ onChange && (changedX || changedY) && onChange(self, dx, dy, deltaX, deltaY); // in ScrollTrigger.normalizeScroll(), we need to know if it was touch/pointer so we need access to the deltaX/deltaY Arrays before we clear them out.
775
+
776
+ if (changedX) {
777
+ onRight && self.deltaX > 0 && onRight(self);
778
+ onLeft && self.deltaX < 0 && onLeft(self);
779
+ onChangeX && onChangeX(self);
780
+ onToggleX && self.deltaX < 0 !== prevDeltaX < 0 && onToggleX(self);
781
+ prevDeltaX = self.deltaX;
782
+ deltaX[0] = deltaX[1] = deltaX[2] = 0;
783
+ }
784
+
785
+ if (changedY) {
786
+ onDown && self.deltaY > 0 && onDown(self);
787
+ onUp && self.deltaY < 0 && onUp(self);
788
+ onChangeY && onChangeY(self);
789
+ onToggleY && self.deltaY < 0 !== prevDeltaY < 0 && onToggleY(self);
790
+ prevDeltaY = self.deltaY;
791
+ deltaY[0] = deltaY[1] = deltaY[2] = 0;
792
+ }
793
+
794
+ if (moved || dragged) {
795
+ onMove && onMove(self);
796
+
797
+ if (dragged) {
798
+ onDragStart && dragged === 1 && onDragStart(self);
799
+ onDrag && onDrag(self);
800
+ dragged = 0;
801
+ }
802
+
803
+ moved = false;
804
+ }
805
+
806
+ locked && !(locked = false) && onLockAxis && onLockAxis(self);
807
+
808
+ if (wheeled) {
809
+ onWheel(self);
810
+ wheeled = false;
811
+ }
812
+
813
+ id = 0;
814
+ },
815
+ onDelta = function onDelta(x, y, index) {
816
+ deltaX[index] += x;
817
+ deltaY[index] += y;
818
+
819
+ self._vx.update(x);
820
+
821
+ self._vy.update(y);
822
+
823
+ debounce ? id || (id = requestAnimationFrame(update)) : update();
824
+ },
825
+ onTouchOrPointerDelta = function onTouchOrPointerDelta(x, y) {
826
+ if (lockAxis && !axis) {
827
+ self.axis = axis = Math.abs(x) > Math.abs(y) ? "x" : "y";
828
+ locked = true;
829
+ }
830
+
831
+ if (axis !== "y") {
832
+ deltaX[2] += x;
833
+
834
+ self._vx.update(x, true); // update the velocity as frequently as possible instead of in the debounced function so that very quick touch-scrolls (flicks) feel natural. If it's the mouse/touch/pointer, force it so that we get snappy/accurate momentum scroll.
835
+
836
+ }
837
+
838
+ if (axis !== "x") {
839
+ deltaY[2] += y;
840
+
841
+ self._vy.update(y, true);
842
+ }
843
+
844
+ debounce ? id || (id = requestAnimationFrame(update)) : update();
845
+ },
846
+ _onDrag = function _onDrag(e) {
847
+ if (_ignoreCheck(e, 1)) {
848
+ return;
849
+ }
850
+
851
+ e = _getEvent(e, preventDefault);
852
+ var x = e.clientX,
853
+ y = e.clientY,
854
+ dx = x - self.x,
855
+ dy = y - self.y,
856
+ isDragging = self.isDragging;
857
+ self.x = x;
858
+ self.y = y;
859
+
860
+ if (isDragging || (dx || dy) && (Math.abs(self.startX - x) >= dragMinimum || Math.abs(self.startY - y) >= dragMinimum)) {
861
+ dragged = isDragging ? 2 : 1; // dragged: 0 = not dragging, 1 = first drag, 2 = normal drag
862
+
863
+ isDragging || (self.isDragging = true);
864
+ onTouchOrPointerDelta(dx, dy);
865
+ }
866
+ },
867
+ _onPress = self.onPress = function (e) {
868
+ if (_ignoreCheck(e, 1) || e && e.button) {
869
+ return;
870
+ }
871
+
872
+ self.axis = axis = null;
873
+ onStopDelayedCall.pause();
874
+ self.isPressed = true;
875
+ e = _getEvent(e); // note: may need to preventDefault(?) Won't side-scroll on iOS Safari if we do, though.
876
+
877
+ prevDeltaX = prevDeltaY = 0;
878
+ self.startX = self.x = e.clientX;
879
+ self.startY = self.y = e.clientY;
880
+
881
+ self._vx.reset(); // otherwise the t2 may be stale if the user touches and flicks super fast and releases in less than 2 requestAnimationFrame ticks, causing velocity to be 0.
882
+
883
+
884
+ self._vy.reset();
885
+
886
+ _addListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, passive, true);
887
+
888
+ self.deltaX = self.deltaY = 0;
889
+ onPress && onPress(self);
890
+ },
891
+ _onRelease = self.onRelease = function (e) {
892
+ if (_ignoreCheck(e, 1)) {
893
+ return;
894
+ }
895
+
896
+ _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true);
897
+
898
+ var isTrackingDrag = !isNaN(self.y - self.startY),
899
+ wasDragging = self.isDragging,
900
+ isDragNotClick = wasDragging && (Math.abs(self.x - self.startX) > 3 || Math.abs(self.y - self.startY) > 3),
901
+ // some touch devices need some wiggle room in terms of sensing clicks - the finger may move a few pixels.
902
+ eventData = _getEvent(e);
903
+
904
+ if (!isDragNotClick && isTrackingDrag) {
905
+ self._vx.reset();
906
+
907
+ self._vy.reset(); //if (preventDefault && allowClicks && self.isPressed) { // check isPressed because in a rare edge case, the inputObserver in ScrollTrigger may stopPropagation() on the press/drag, so the onRelease may get fired without the onPress/onDrag ever getting called, thus it could trigger a click to occur on a link after scroll-dragging it.
908
+
909
+
910
+ if (preventDefault && allowClicks) {
911
+ gsap.delayedCall(0.08, function () {
912
+ // some browsers (like Firefox) won't trust script-generated clicks, so if the user tries to click on a video to play it, for example, it simply won't work. Since a regular "click" event will most likely be generated anyway (one that has its isTrusted flag set to true), we must slightly delay our script-generated click so that the "real"/trusted one is prioritized. Remember, when there are duplicate events in quick succession, we suppress all but the first one. Some browsers don't even trigger the "real" one at all, so our synthetic one is a safety valve that ensures that no matter what, a click event does get dispatched.
913
+ if (_getTime() - onClickTime > 300 && !e.defaultPrevented) {
914
+ if (e.target.click) {
915
+ //some browsers (like mobile Safari) don't properly trigger the click event
916
+ e.target.click();
917
+ } else if (ownerDoc.createEvent) {
918
+ var syntheticEvent = ownerDoc.createEvent("MouseEvents");
919
+ syntheticEvent.initMouseEvent("click", true, true, _win, 1, eventData.screenX, eventData.screenY, eventData.clientX, eventData.clientY, false, false, false, false, 0, null);
920
+ e.target.dispatchEvent(syntheticEvent);
921
+ }
922
+ }
923
+ });
924
+ }
925
+ }
926
+
927
+ self.isDragging = self.isGesturing = self.isPressed = false;
928
+ onStop && wasDragging && !isNormalizer && onStopDelayedCall.restart(true);
929
+ dragged && update(); // in case debouncing, we don't want onDrag to fire AFTER onDragEnd().
930
+
931
+ onDragEnd && wasDragging && onDragEnd(self);
932
+ onRelease && onRelease(self, isDragNotClick);
933
+ },
934
+ _onGestureStart = function _onGestureStart(e) {
935
+ return e.touches && e.touches.length > 1 && (self.isGesturing = true) && onGestureStart(e, self.isDragging);
936
+ },
937
+ _onGestureEnd = function _onGestureEnd() {
938
+ return (self.isGesturing = false) || onGestureEnd(self);
939
+ },
940
+ onScroll = function onScroll(e) {
941
+ if (_ignoreCheck(e)) {
942
+ return;
943
+ }
944
+
945
+ var x = scrollFuncX(),
946
+ y = scrollFuncY();
947
+ onDelta((x - scrollX) * scrollSpeed, (y - scrollY) * scrollSpeed, 1);
948
+ scrollX = x;
949
+ scrollY = y;
950
+ onStop && onStopDelayedCall.restart(true);
951
+ },
952
+ _onWheel = function _onWheel(e) {
953
+ if (_ignoreCheck(e)) {
954
+ return;
955
+ }
956
+
957
+ e = _getEvent(e, preventDefault);
958
+ onWheel && (wheeled = true);
959
+ var multiplier = (e.deltaMode === 1 ? lineHeight : e.deltaMode === 2 ? _win.innerHeight : 1) * wheelSpeed;
960
+ onDelta(e.deltaX * multiplier, e.deltaY * multiplier, 0);
961
+ onStop && !isNormalizer && onStopDelayedCall.restart(true);
962
+ },
963
+ _onMove = function _onMove(e) {
964
+ if (_ignoreCheck(e)) {
965
+ return;
966
+ }
967
+
968
+ var x = e.clientX,
969
+ y = e.clientY,
970
+ dx = x - self.x,
971
+ dy = y - self.y;
972
+ self.x = x;
973
+ self.y = y;
974
+ moved = true;
975
+ onStop && onStopDelayedCall.restart(true);
976
+ (dx || dy) && onTouchOrPointerDelta(dx, dy);
977
+ },
978
+ _onHover = function _onHover(e) {
979
+ self.event = e;
980
+ onHover(self);
981
+ },
982
+ _onHoverEnd = function _onHoverEnd(e) {
983
+ self.event = e;
984
+ onHoverEnd(self);
985
+ },
986
+ _onClick = function _onClick(e) {
987
+ return _ignoreCheck(e) || _getEvent(e, preventDefault) && onClick(self);
988
+ };
989
+
990
+ onStopDelayedCall = self._dc = gsap.delayedCall(onStopDelay || 0.25, onStopFunc).pause();
991
+ self.deltaX = self.deltaY = 0;
992
+ self._vx = _getVelocityProp(0, 50);
993
+ self._vy = _getVelocityProp(0, 50);
994
+ self.scrollX = scrollFuncX;
995
+ self.scrollY = scrollFuncY;
996
+ self.isDragging = self.isGesturing = self.isPressed = false;
997
+
998
+ _context(this);
999
+
1000
+ self.enable = function (e) {
1001
+ if (!self.isEnabled) {
1002
+ _addListener(isViewport ? ownerDoc : target, "scroll", _onScroll);
1003
+
1004
+ type.indexOf("scroll") >= 0 && _addListener(isViewport ? ownerDoc : target, "scroll", onScroll, passive, capture);
1005
+ type.indexOf("wheel") >= 0 && _addListener(target, "wheel", _onWheel, passive, capture);
1006
+
1007
+ if (type.indexOf("touch") >= 0 && _isTouch || type.indexOf("pointer") >= 0) {
1008
+ _addListener(target, _eventTypes[0], _onPress, passive, capture);
1009
+
1010
+ _addListener(ownerDoc, _eventTypes[2], _onRelease);
1011
+
1012
+ _addListener(ownerDoc, _eventTypes[3], _onRelease);
1013
+
1014
+ allowClicks && _addListener(target, "click", clickCapture, true, true);
1015
+ onClick && _addListener(target, "click", _onClick);
1016
+ onGestureStart && _addListener(ownerDoc, "gesturestart", _onGestureStart);
1017
+ onGestureEnd && _addListener(ownerDoc, "gestureend", _onGestureEnd);
1018
+ onHover && _addListener(target, _pointerType + "enter", _onHover);
1019
+ onHoverEnd && _addListener(target, _pointerType + "leave", _onHoverEnd);
1020
+ onMove && _addListener(target, _pointerType + "move", _onMove);
1021
+ }
1022
+
1023
+ self.isEnabled = true;
1024
+ self.isDragging = self.isGesturing = self.isPressed = moved = dragged = false;
1025
+
1026
+ self._vx.reset();
1027
+
1028
+ self._vy.reset();
1029
+
1030
+ scrollX = scrollFuncX();
1031
+ scrollY = scrollFuncY();
1032
+ e && e.type && _onPress(e);
1033
+ onEnable && onEnable(self);
1034
+ }
1035
+
1036
+ return self;
1037
+ };
1038
+
1039
+ self.disable = function () {
1040
+ if (self.isEnabled) {
1041
+ // only remove the _onScroll listener if there aren't any others that rely on the functionality.
1042
+ _observers.filter(function (o) {
1043
+ return o !== self && _isViewport(o.target);
1044
+ }).length || _removeListener(isViewport ? ownerDoc : target, "scroll", _onScroll);
1045
+
1046
+ if (self.isPressed) {
1047
+ self._vx.reset();
1048
+
1049
+ self._vy.reset();
1050
+
1051
+ _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true);
1052
+ }
1053
+
1054
+ _removeListener(isViewport ? ownerDoc : target, "scroll", onScroll, capture);
1055
+
1056
+ _removeListener(target, "wheel", _onWheel, capture);
1057
+
1058
+ _removeListener(target, _eventTypes[0], _onPress, capture);
1059
+
1060
+ _removeListener(ownerDoc, _eventTypes[2], _onRelease);
1061
+
1062
+ _removeListener(ownerDoc, _eventTypes[3], _onRelease);
1063
+
1064
+ _removeListener(target, "click", clickCapture, true);
1065
+
1066
+ _removeListener(target, "click", _onClick);
1067
+
1068
+ _removeListener(ownerDoc, "gesturestart", _onGestureStart);
1069
+
1070
+ _removeListener(ownerDoc, "gestureend", _onGestureEnd);
1071
+
1072
+ _removeListener(target, _pointerType + "enter", _onHover);
1073
+
1074
+ _removeListener(target, _pointerType + "leave", _onHoverEnd);
1075
+
1076
+ _removeListener(target, _pointerType + "move", _onMove);
1077
+
1078
+ self.isEnabled = self.isPressed = self.isDragging = false;
1079
+ onDisable && onDisable(self);
1080
+ }
1081
+ };
1082
+
1083
+ self.kill = self.revert = function () {
1084
+ self.disable();
1085
+
1086
+ var i = _observers.indexOf(self);
1087
+
1088
+ i >= 0 && _observers.splice(i, 1);
1089
+ _normalizer === self && (_normalizer = 0);
1090
+ };
1091
+
1092
+ _observers.push(self);
1093
+
1094
+ isNormalizer && _isViewport(target) && (_normalizer = self);
1095
+ self.enable(event);
1096
+ };
1097
+
1098
+ _createClass(Observer, [{
1099
+ key: "velocityX",
1100
+ get: function get() {
1101
+ return this._vx.getVelocity();
1102
+ }
1103
+ }, {
1104
+ key: "velocityY",
1105
+ get: function get() {
1106
+ return this._vy.getVelocity();
1107
+ }
1108
+ }]);
1109
+
1110
+ return Observer;
1111
+ }();
1112
+ Observer.version = "3.13.0";
1113
+
1114
+ Observer.create = function (vars) {
1115
+ return new Observer(vars);
1116
+ };
1117
+
1118
+ Observer.register = _initCore;
1119
+
1120
+ Observer.getAll = function () {
1121
+ return _observers.slice();
1122
+ };
1123
+
1124
+ Observer.getById = function (id) {
1125
+ return _observers.filter(function (o) {
1126
+ return o.vars.id === id;
1127
+ })[0];
1128
+ };
1129
+
1130
+ _getGSAP() && gsap.registerPlugin(Observer);
1131
+
428
1132
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
429
1133
 
430
1134
  const CLASS_PART_SEPARATOR = '-';
@@ -2921,124 +3625,367 @@ const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
2921
3625
 
2922
3626
  /**
2923
3627
  * Utility function to merge Tailwind classes with clsx
3628
+ *
3629
+ * Combines clsx for conditional classes with tailwind-merge to handle
3630
+ * conflicting Tailwind classes by keeping the last occurrence.
3631
+ * This prevents issues like "p-4 p-2" where both would be applied.
3632
+ *
3633
+ * @param inputs - Array of class values (strings, conditionals, objects)
3634
+ * @returns Merged and deduplicated class string
2924
3635
  */
2925
3636
  const cn = (...inputs) => {
2926
3637
  return twMerge(clsx(inputs));
2927
3638
  };
2928
3639
  /**
2929
- * Sets up container styles and rotation handling
3640
+ * Traverses the DOM tree upward to find the first non-transparent background color
3641
+ *
3642
+ * This function walks up the element hierarchy starting from the given element,
3643
+ * checking each parent's computed backgroundColor style until it finds a visible
3644
+ * (non-transparent) background color. This is useful for automatically detecting
3645
+ * the effective background behind an element for gradient overlays.
3646
+ *
3647
+ * The traversal stops at the first element with a visible background color,
3648
+ * which could be the element itself or any of its ancestors up to the document root.
3649
+ *
3650
+ * @param el - The HTMLElement to start the background color search from
3651
+ * @returns The first non-transparent background color found in the hierarchy,
3652
+ * or "transparent" if no visible background is found
3653
+ *
3654
+ * @example
3655
+ * // Element with white parent background
3656
+ * const color = getEffectiveBackgroundColor(marqueeElement);
3657
+ * // Returns: "rgb(255, 255, 255)" or "#ffffff"
3658
+ *
3659
+ * @example
3660
+ * // Element with no background set anywhere in hierarchy
3661
+ * const color = getEffectiveBackgroundColor(marqueeElement);
3662
+ * // Returns: "transparent"
3663
+ */
3664
+ const getEffectiveBackgroundColor = (el) => {
3665
+ let current = el;
3666
+ while (current) {
3667
+ const bg = window.getComputedStyle(current).backgroundColor;
3668
+ // Check if background color is visible (not transparent or rgba(0,0,0,0))
3669
+ if (bg && bg !== "rgba(0, 0, 0, 0)" && bg !== "transparent") {
3670
+ return bg;
3671
+ }
3672
+ current = current.parentElement;
3673
+ }
3674
+ return "transparent"; // fallback when no visible background is found
3675
+ };
3676
+ /**
3677
+ * Sets up container styles and rotation handling for the marquee
3678
+ *
3679
+ * This function handles the complex styling requirements for different marquee orientations:
3680
+ *
3681
+ * 1. **Basic Setup**: Applies gap spacing and rotation for vertical marquees
3682
+ * 2. **Vertical Mode**: Rotates container 90° and adjusts width to parent height
3683
+ * 3. **Rotation Alignment**: Special mode for vertical text that remains readable
3684
+ *
3685
+ * @param containerMarquee - The main container element that holds all marquee instances
3686
+ * @param marquees - Array of individual marquee wrapper elements
3687
+ * @param marqueesChildren - Array of content container elements within each marquee
3688
+ * @param isVertical - Boolean indicating if marquee moves up/down instead of left/right
3689
+ * @param props - Configuration object containing spacing and alignment options
2930
3690
  */
2931
3691
  const setupContainerStyles = (containerMarquee, marquees, marqueesChildren, isVertical, props) => {
2932
3692
  const { spacing = 16, alignRotationWithY = false } = props;
2933
- gsap.set(containerMarquee, {
3693
+ /**
3694
+ * Apply base container styling
3695
+ * - gap: Space between marquee elements (prevents content overlap)
3696
+ * - rotate: 90° rotation for vertical movement (transforms horizontal motion to vertical)
3697
+ */
3698
+ gsap$1.set(containerMarquee, {
2934
3699
  gap: `${spacing}px`,
2935
3700
  rotate: isVertical ? 90 : "0",
2936
3701
  });
3702
+ /**
3703
+ * Handle vertical marquee specific adjustments
3704
+ * When isVertical is true, the container is rotated 90°, so we need to:
3705
+ * 1. Set container width to match parent height (since it's rotated)
3706
+ * 2. Allow content to overflow visible bounds for smooth transitions
3707
+ */
2937
3708
  if (isVertical) {
2938
3709
  const parent = containerMarquee.parentNode;
2939
- gsap.set(containerMarquee, {
2940
- width: parent.offsetHeight,
3710
+ gsap$1.set(containerMarquee, {
3711
+ width: parent.offsetHeight, // Width becomes the vertical space available
2941
3712
  });
2942
- gsap.set(marqueesChildren, {
2943
- overflow: "visible",
3713
+ gsap$1.set(marqueesChildren, {
3714
+ overflow: "visible", // Prevents clipping during animation
2944
3715
  });
2945
3716
  }
3717
+ /**
3718
+ * Handle special rotation alignment mode
3719
+ *
3720
+ * This creates a complex layout where:
3721
+ * - The main container is rotated for vertical movement
3722
+ * - Individual content is counter-rotated to remain readable
3723
+ * - Content is repositioned to align properly within the rotated space
3724
+ *
3725
+ * Use case: Vertical text marquee where text remains horizontally readable
3726
+ */
2946
3727
  if (alignRotationWithY && marquees.length > 0) {
2947
3728
  const marqueeHeight = marquees[0].offsetHeight;
2948
- gsap.set(containerMarquee, {
3729
+ // Center align items within the container
3730
+ gsap$1.set(containerMarquee, {
2949
3731
  alignItems: "center",
2950
3732
  });
2951
- gsap.set(marqueesChildren, {
2952
- rotate: -90,
2953
- x: (containerMarquee.offsetWidth - spacing) / 2 - spacing,
3733
+ /**
3734
+ * Counter-rotate content and reposition for proper alignment
3735
+ *
3736
+ * - rotate: -90° counters the container's 90° rotation
3737
+ * - x: Horizontal offset to center content within rotated container
3738
+ * - width: Set to marquee height since dimensions are swapped after rotation
3739
+ * - flexWrap/wordBreak/whiteSpace: Handle text flow in constrained space
3740
+ */
3741
+ gsap$1.set(marqueesChildren, {
3742
+ rotate: -90, // Counter-rotate to keep text readable
3743
+ x: (containerMarquee.offsetWidth - spacing) / 2 - spacing, // Center horizontally
2954
3744
  display: "flex",
2955
- flexWrap: "wrap",
2956
- width: marqueeHeight,
2957
- wordBreak: "break-all",
2958
- whiteSpace: "break-spaces",
3745
+ flexWrap: "wrap", // Allow text to wrap within constrained width
3746
+ width: marqueeHeight, // Width constraint for wrapped text
3747
+ wordBreak: "break-all", // Force word breaking if necessary
3748
+ whiteSpace: "break-spaces", // Preserve spaces while allowing breaks
2959
3749
  });
2960
- gsap.set(marquees, {
3750
+ /**
3751
+ * Adjust marquee height to fit within the rotated container
3752
+ * Accounts for spacing to prevent overflow
3753
+ */
3754
+ gsap$1.set(marquees, {
2961
3755
  height: containerMarquee.offsetWidth - spacing,
2962
3756
  });
2963
3757
  }
2964
3758
  };
2965
3759
  /**
2966
- * Calculates the number of duplicates needed to fill the container
3760
+ * Calculates the number of content duplicates needed for seamless looping
3761
+ *
3762
+ * For smooth infinite scrolling, we need enough content copies to fill the visible area
3763
+ * plus buffer space. This prevents gaps when content loops back to the beginning.
3764
+ *
3765
+ * Algorithm:
3766
+ * 1. If not in fill mode, only one copy is needed (content already spans container)
3767
+ * 2. Determine target width (viewport height for vertical, container width for horizontal)
3768
+ * 3. Calculate how many copies fit in the target space, rounding up for complete coverage
3769
+ *
3770
+ * @param marqueeChildrenWidth - Width of a single content instance
3771
+ * @param containerMarqueeWidth - Width of the marquee container
3772
+ * @param isVertical - Whether the marquee scrolls vertically
3773
+ * @param props - Configuration object containing fill mode setting
3774
+ * @returns Number of content duplicates needed (minimum 1)
2967
3775
  */
2968
3776
  const calculateDuplicates = (marqueeChildrenWidth, containerMarqueeWidth, isVertical, props) => {
3777
+ // If not filling, content presumably already spans the container
2969
3778
  if (!props.fill)
2970
3779
  return 1;
3780
+ /**
3781
+ * Determine the space we need to fill
3782
+ * - Vertical: Use viewport height (since container is rotated 90°)
3783
+ * - Horizontal: Use container width
3784
+ */
2971
3785
  const targetWidth = isVertical ? window.innerHeight : containerMarqueeWidth;
3786
+ /**
3787
+ * Calculate required duplicates
3788
+ * Math.ceil ensures we have enough copies to fully cover the target width
3789
+ * Even if the last copy is partially visible, it prevents gaps during looping
3790
+ */
2972
3791
  return marqueeChildrenWidth < targetWidth
2973
3792
  ? Math.ceil(targetWidth / marqueeChildrenWidth)
2974
- : 1;
3793
+ : 1; // If content is already larger than target, one copy suffices
2975
3794
  };
2976
3795
  /**
2977
- * Determines the minimum width for marquee elements
3796
+ * Determines the minimum width for marquee elements based on content and container
3797
+ *
3798
+ * This function ensures marquee elements have appropriate dimensions for their content
3799
+ * and container context, handling different modes and orientations.
3800
+ *
3801
+ * Width determination logic:
3802
+ * 1. **Fill mode**: Auto width lets content size naturally
3803
+ * 2. **Rotation alignment**: Use content height as width (rotated dimensions)
3804
+ * 3. **Undersized content**: Stretch to 100% to fill container
3805
+ * 4. **Oversized content**: Use actual content width for overflow scrolling
3806
+ *
3807
+ * @param marqueesChildren - Array of content elements for dimension measurement
3808
+ * @param totalWidth - Combined width of all content elements
3809
+ * @param containerMarqueeWidth - Available container width
3810
+ * @param props - Configuration object containing fill and alignment settings
3811
+ * @returns CSS width value (string with units or number for pixels)
2978
3812
  */
2979
3813
  const getMinWidth = (marqueesChildren, totalWidth, containerMarqueeWidth, props) => {
2980
3814
  const { fill = false, alignRotationWithY = false } = props;
3815
+ // Fill mode: Let content size itself naturally
2981
3816
  if (fill)
2982
3817
  return "auto";
3818
+ /**
3819
+ * Rotation alignment mode: Use height as width
3820
+ * Since content is rotated 90°, height becomes the effective width
3821
+ */
2983
3822
  if (alignRotationWithY && marqueesChildren.length > 0) {
2984
3823
  return `${marqueesChildren[0].offsetHeight}px`;
2985
3824
  }
3825
+ /**
3826
+ * Content smaller than container: Stretch to fill
3827
+ * Prevents awkward gaps in the marquee display
3828
+ */
2986
3829
  if (totalWidth < containerMarqueeWidth)
2987
3830
  return "100%";
3831
+ /**
3832
+ * Content larger than container: Use actual content width
3833
+ * Allows content to overflow and scroll properly
3834
+ */
2988
3835
  return `${totalWidth}px`;
2989
3836
  };
2990
3837
  /**
2991
3838
  * Creates a complex fill-based marquee animation with seamless looping
3839
+ *
3840
+ * This is the core animation engine that creates smooth, continuous scrolling.
3841
+ * It handles the complex math required for seamless looping by calculating
3842
+ * precise positions and durations for each content element.
3843
+ *
3844
+ * Animation Strategy:
3845
+ * 1. **Position Calculation**: Convert pixel positions to percentages for responsive scaling
3846
+ * 2. **Seamless Looping**: Calculate track length and loop points to prevent gaps
3847
+ * 3. **Staggered Animation**: Each element starts at different times for smooth flow
3848
+ * 4. **Direction Handling**: Support forward and reverse directions with proper timing
3849
+ *
3850
+ * Technical Details:
3851
+ * - Uses xPercent for percentage-based positioning (responsive to element width changes)
3852
+ * - Creates two-part animation: main movement + seamless loop reset
3853
+ * - Calculates precise durations based on distance and speed for consistent motion
3854
+ *
3855
+ * @param elementsToAnimate - Array of DOM elements to animate (content or containers)
3856
+ * @param startX - Starting X position reference point
3857
+ * @param tl - GSAP timeline to add animations to
3858
+ * @param isReverse - Whether animation should play in reverse direction
3859
+ * @param props - Configuration object with spacing, speed, delay, and other settings
2992
3860
  */
2993
3861
  const coreAnimation = (elementsToAnimate, startX, tl, isReverse, props) => {
2994
3862
  const { spacing = 16, speed = 100, delay = 0, paused = false, alignRotationWithY = false, } = props;
2995
- const widths = [];
2996
- const xPercents = [];
2997
- const latestPos = elementsToAnimate.length - 1;
2998
- // Set initial positions and calculate percentages
2999
- gsap.set(elementsToAnimate, {
3863
+ // Arrays to store calculated values for each element
3864
+ const widths = []; // Element widths in pixels
3865
+ const xPercents = []; // Current positions as percentages
3866
+ const latestPos = elementsToAnimate.length - 1; // Index of last element
3867
+ /**
3868
+ * Initialize positions and calculate percentage values
3869
+ *
3870
+ * GSAP's xPercent property positions elements relative to their own width:
3871
+ * - 0% = element's left edge at current x position
3872
+ * - -100% = element's right edge at current x position
3873
+ * - 100% = element positioned one full width to the right
3874
+ *
3875
+ * This approach makes animations responsive to width changes
3876
+ */
3877
+ gsap$1.set(elementsToAnimate, {
3000
3878
  xPercent: (i, el) => {
3001
- const w = (widths[i] = parseFloat(String(gsap.getProperty(el, "width", "px"))));
3879
+ // Get element width and store for later calculations
3880
+ const w = (widths[i] = parseFloat(String(gsap$1.getProperty(el, "width", "px"))));
3881
+ /**
3882
+ * Calculate current position as percentage of element width
3883
+ * Combines pixel position with any existing percentage offset
3884
+ */
3002
3885
  xPercents[i] =
3003
- (parseFloat(String(gsap.getProperty(el, "x", "px"))) / w) * 100 +
3004
- Number(gsap.getProperty(el, "xPercent"));
3886
+ (parseFloat(String(gsap$1.getProperty(el, "x", "px"))) / w) * 100 +
3887
+ Number(gsap$1.getProperty(el, "xPercent"));
3005
3888
  return xPercents[i];
3006
3889
  },
3007
3890
  });
3008
- gsap.set(elementsToAnimate, { x: 0 });
3009
- //Calculate the total track length for seamless looping
3891
+ // Reset x position to 0 since we're now using xPercent for positioning
3892
+ gsap$1.set(elementsToAnimate, { x: 0 });
3893
+ /**
3894
+ * Calculate total track length for seamless looping
3895
+ *
3896
+ * Track length is the total distance content travels before looping back.
3897
+ * It includes:
3898
+ * - Distance from start to last element's left edge
3899
+ * - Last element's offset percentage in pixels
3900
+ * - Last element's full width
3901
+ * - Spacing gap after last element
3902
+ *
3903
+ * This ensures smooth transitions when content loops back to beginning
3904
+ */
3010
3905
  const trackLength = elementsToAnimate[latestPos].offsetLeft +
3011
3906
  (xPercents[latestPos] / 100) * widths[latestPos] -
3012
3907
  startX +
3013
3908
  elementsToAnimate[latestPos].offsetWidth +
3014
3909
  spacing;
3015
- // Create animation timeline for each element
3910
+ /**
3911
+ * Create staggered animation for each element
3912
+ *
3913
+ * Each element gets a two-part animation:
3914
+ * 1. Main movement: From start position to loop point
3915
+ * 2. Reset movement: From end of track back to start (seamless loop)
3916
+ */
3016
3917
  elementsToAnimate.forEach((item, i) => {
3918
+ // Current position in pixels
3017
3919
  const curX = (xPercents[i] / 100) * widths[i];
3920
+ // Distance from element to animation start point
3018
3921
  const distanceToStart = item.offsetLeft + curX - startX;
3922
+ /**
3923
+ * Calculate distance to complete loop point
3924
+ *
3925
+ * For rotation alignment mode, use height instead of width
3926
+ * since the element dimensions are effectively swapped
3927
+ */
3019
3928
  const distanceToLoop = alignRotationWithY
3020
3929
  ? distanceToStart + item.offsetHeight - spacing
3021
3930
  : distanceToStart + widths[i];
3931
+ /**
3932
+ * Part 1: Main animation - move from current position to loop point
3933
+ *
3934
+ * - Target: Position where element should loop back
3935
+ * - Duration: Based on distance and speed for consistent motion
3936
+ * - Start time: 0 (all elements start simultaneously but from different positions)
3937
+ */
3022
3938
  tl.to(item, {
3023
3939
  xPercent: ((curX - distanceToLoop) / widths[i]) * 100,
3024
3940
  duration: distanceToLoop / speed,
3025
- }, 0).fromTo(item, {
3941
+ }, 0 // Start immediately
3942
+ ).fromTo(item, {
3943
+ /**
3944
+ * Part 2 Start: Position element at end of track
3945
+ * This creates the illusion of seamless continuation
3946
+ */
3026
3947
  xPercent: ((curX - distanceToLoop + trackLength) / widths[i]) * 100,
3027
3948
  }, {
3949
+ /**
3950
+ * Part 2 End: Move back to original start position
3951
+ * Completes the seamless loop cycle
3952
+ */
3028
3953
  xPercent: xPercents[i],
3029
3954
  duration: (curX - distanceToLoop + trackLength - curX) / speed,
3030
- immediateRender: false,
3031
- }, distanceToLoop / speed);
3955
+ immediateRender: false, // Don't render start position immediately
3956
+ }, distanceToLoop / speed // Start after main animation completes
3957
+ );
3032
3958
  });
3959
+ // Apply initial delay before starting animations
3033
3960
  tl.delay(delay);
3961
+ /**
3962
+ * Handle reverse direction animations
3963
+ *
3964
+ * For reverse marquees (right/down directions):
3965
+ * 1. Set timeline to end position
3966
+ * 2. Pause to prevent immediate playback
3967
+ * 3. Use delayed call to start reverse playback after initial delay
3968
+ * 4. Set up reverse completion handler for continuous looping
3969
+ */
3034
3970
  if (isReverse) {
3971
+ // If paused is requested, just pause and return
3035
3972
  if (paused) {
3036
3973
  tl.pause();
3037
3974
  return;
3038
3975
  }
3976
+ // Position timeline at end and pause
3039
3977
  tl.progress(1).pause();
3040
- gsap.delayedCall(delay, () => {
3041
- tl.reverse();
3978
+ /**
3979
+ * Start reverse playback after delay
3980
+ * This creates the proper reverse scrolling effect
3981
+ */
3982
+ gsap$1.delayedCall(delay, () => {
3983
+ tl.reverse(); // Start playing backwards
3984
+ /**
3985
+ * Handle seamless looping in reverse direction
3986
+ * When reverse completes, restart from end position
3987
+ * This prevents new delay in continuous reverse scrolling
3988
+ */
3042
3989
  tl.eventCallback("onReverseComplete", () => {
3043
3990
  tl.totalTime(tl.rawTime() + tl.duration() * 100);
3044
3991
  });
@@ -3047,23 +3994,30 @@ const coreAnimation = (elementsToAnimate, startX, tl, isReverse, props) => {
3047
3994
  };
3048
3995
 
3049
3996
  const GSAPReactMarquee = require$$0.forwardRef((props, ref) => {
3050
- const { children, className, dir = "left", loop = -1, paused = false, fill = false, } = props;
3997
+ const { children, className, dir = "left", loop = -1, paused = false, fill = false, followScrollDir = false, scrollSpeed = 2.5, gradient = false, gradientColor = null, } = props;
3051
3998
  const rootRef = require$$0.useRef(null) || ref;
3052
3999
  const containerRef = rootRef;
3053
4000
  const marqueeRef = require$$0.useRef(null);
3054
4001
  const [marqueeDuplicates, setMarqueeDuplicates] = require$$0.useState(1);
4002
+ const [effectivelyGradient, setEffectivelyGradient] = require$$0.useState(null);
4003
+ require$$0.useLayoutEffect(() => {
4004
+ if (!gradient || !(containerRef === null || containerRef === void 0 ? void 0 : containerRef.current))
4005
+ return;
4006
+ const effectiveBg = getEffectiveBackgroundColor(containerRef.current);
4007
+ setEffectivelyGradient(effectiveBg);
4008
+ }, [gradient]);
3055
4009
  const isVertical = dir === "up" || dir === "down";
3056
4010
  const isReverse = dir === "down" || dir === "right";
3057
4011
  react.useGSAP(() => {
3058
4012
  if (!(marqueeRef === null || marqueeRef === void 0 ? void 0 : marqueeRef.current) || !containerRef.current)
3059
4013
  return;
3060
4014
  const containerMarquee = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current;
3061
- const marquees = gsap.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee"));
3062
- const marqueesChildren = gsap.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));
4015
+ const marquees = gsap$1.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee"));
4016
+ const marqueesChildren = gsap$1.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));
3063
4017
  const marquee = marqueeRef.current;
3064
4018
  if (!marquee || !marqueesChildren)
3065
4019
  return;
3066
- const tl = gsap.timeline({
4020
+ const tl = gsap$1.timeline({
3067
4021
  paused: paused,
3068
4022
  repeat: loop,
3069
4023
  defaults: { ease: "none" },
@@ -3076,30 +4030,96 @@ const GSAPReactMarquee = require$$0.forwardRef((props, ref) => {
3076
4030
  setupContainerStyles(containerMarquee, marquees, marqueesChildren, isVertical, props);
3077
4031
  // Calculate dimensions and duplicates
3078
4032
  const containerMarqueeWidth = containerMarquee.offsetWidth;
3079
- // const marqueeHeight = marquees[0].offsetHeight;
3080
4033
  const marqueeChildrenWidth = marqueesChildren[0].offsetWidth;
3081
4034
  const startX = marqueesChildren[0].offsetLeft;
4035
+ // Clamp scrollSpeed to valid range (1.1 to 4.0)
4036
+ const clampedScrollSpeed = Math.min(4, Math.max(1.1, scrollSpeed));
3082
4037
  setMarqueeDuplicates(calculateDuplicates(marqueeChildrenWidth, containerMarqueeWidth, isVertical, props));
3083
4038
  // Calculate total width and set marquee styles
3084
- const totalWidth = gsap.utils
4039
+ const totalWidth = gsap$1.utils
3085
4040
  .toArray(marquee.children)
3086
4041
  .map((child) => child.offsetWidth)
3087
4042
  .reduce((a, b) => a + b, 0);
3088
- gsap.set(marquees, {
4043
+ gsap$1.set(marquees, {
3089
4044
  minWidth: getMinWidth(marqueesChildren, totalWidth, containerMarqueeWidth, props),
3090
4045
  flex: fill ? "0 0 auto" : "1",
3091
4046
  });
3092
4047
  // Create appropriate animation based on fill setting
3093
4048
  coreAnimation(fill ? marqueesChildren : marquees, startX, tl, isReverse, props);
4049
+ /**
4050
+ * GSAP Observer for scroll-based speed control
4051
+ *
4052
+ * This creates an interactive experience where users can control
4053
+ * the marquee speed and direction through mouse wheel scrolling.
4054
+ *
4055
+ * Behavior:
4056
+ * - Scroll down: Increases speed in normal direction
4057
+ * - Scroll up: Increases speed in reverse direction or slows normal direction
4058
+ * - Speed changes are smoothly animated with acceleration and deceleration phases
4059
+ * - ScrollSpeed multiplier is applied and clamped to valid range
4060
+ */
4061
+ Observer.create({
4062
+ onChangeY(self) {
4063
+ if (!followScrollDir)
4064
+ return;
4065
+ let factor = clampedScrollSpeed * (isReverse ? -1 : 1);
4066
+ if (self.deltaY < 0) {
4067
+ factor *= -1;
4068
+ }
4069
+ /**
4070
+ * Create smooth speed transition animation
4071
+ *
4072
+ * Phase 1: Quick acceleration to new speed (0.2s)
4073
+ * - timeScale: Controls timeline playback speed (higher = faster)
4074
+ * - factor * 2.5: Initial speed boost for responsive feel
4075
+ * - overwrite: Cancels any previous speed animations
4076
+ *
4077
+ * Phase 2: Gradual deceleration to sustained speed (1s delay + 1s duration)
4078
+ * - factor / 2.5: Settle to a more moderate sustained speed
4079
+ * - "+=0.3": Wait 0.3 seconds before starting deceleration
4080
+ */
4081
+ gsap$1
4082
+ .timeline({
4083
+ defaults: {
4084
+ ease: "none",
4085
+ },
4086
+ })
4087
+ .to(tl, {
4088
+ timeScale: factor * 2.5,
4089
+ duration: 0.2,
4090
+ overwrite: true,
4091
+ })
4092
+ .to(tl, { timeScale: factor / 2.5, duration: 1 }, "+=0.3");
4093
+ },
4094
+ });
3094
4095
  }, {
3095
4096
  dependencies: [marqueeDuplicates],
3096
4097
  });
4098
+ const getGradientColor = () => {
4099
+ // Priority order: explicit gradientColor > auto-detected > fallback
4100
+ if (gradientColor) {
4101
+ return gradientColor; // User-specified color takes precedence
4102
+ }
4103
+ if (gradient && effectivelyGradient) {
4104
+ return effectivelyGradient; // Auto-detected background color
4105
+ }
4106
+ return "transparent"; // Default fallback
4107
+ };
4108
+ /**
4109
+ * Generate cloned marquee elements for seamless looping
4110
+ *
4111
+ * Creates multiple copies of the content based on calculated duplicates.
4112
+ * Each clone maintains the same structure and styling as the original.
4113
+ * Memoized to prevent unnecessary re-renders when dependencies haven't changed.
4114
+ */
3097
4115
  const clonedMarquees = require$$0.useMemo(() => {
3098
4116
  if (!Number.isFinite(marqueeDuplicates) || marqueeDuplicates <= 0)
3099
4117
  return null;
3100
4118
  return Array.from({ length: marqueeDuplicates }, (_, i) => (jsxRuntimeExports.jsx("div", { className: cn("gsap-react-marquee"), children: jsxRuntimeExports.jsx("div", { className: cn("gsap-react-marquee-content", className), children: children }) }, i)));
3101
4119
  }, [marqueeDuplicates, className, children]);
3102
- return (jsxRuntimeExports.jsxs("div", { ref: containerRef, className: cn("gsap-react-marquee-container flex w-full overflow-hidden whitespace-nowrap"), children: [jsxRuntimeExports.jsx("div", { ref: marqueeRef, className: cn("gsap-react-marquee"), children: jsxRuntimeExports.jsx("div", { className: cn("gsap-react-marquee-content", className), children: children }) }), clonedMarquees] }));
4120
+ return (jsxRuntimeExports.jsxs("div", { ref: containerRef, style: {
4121
+ "--gradient-color": getGradientColor(),
4122
+ }, className: cn("gsap-react-marquee-container flex w-full overflow-hidden whitespace-nowrap"), children: [jsxRuntimeExports.jsx("div", { ref: marqueeRef, className: cn("gsap-react-marquee"), children: jsxRuntimeExports.jsx("div", { className: cn("gsap-react-marquee-content", className), children: children }) }), clonedMarquees] }));
3103
4123
  });
3104
4124
  GSAPReactMarquee.displayName = "GSAPReactMarquee";
3105
4125
 
@@ -3107,6 +4127,7 @@ exports.calculateDuplicates = calculateDuplicates;
3107
4127
  exports.cn = cn;
3108
4128
  exports.coreAnimation = coreAnimation;
3109
4129
  exports.default = GSAPReactMarquee;
4130
+ exports.getEffectiveBackgroundColor = getEffectiveBackgroundColor;
3110
4131
  exports.getMinWidth = getMinWidth;
3111
4132
  exports.setupContainerStyles = setupContainerStyles;
3112
4133
  //# sourceMappingURL=index.cjs.js.map