@rethink-js/rt-slider 1.1.0 → 1.2.0

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.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @rethink-js/rt-slider v1.1.0 | MIT */
1
+ /*! @rethink-js/rt-slider v1.2.0 | MIT */
2
2
  (() => {
3
3
  // src/index.js
4
4
  (function() {
@@ -118,6 +118,11 @@
118
118
  function clamp(i) {
119
119
  return i < 0 ? 0 : i > 1 ? 1 : i;
120
120
  }
121
+ function round(n, d) {
122
+ if (!Number.isFinite(n)) return 0;
123
+ var m = Math.pow(10, d || 0);
124
+ return Math.round(n * m) / m;
125
+ }
121
126
  function parseEasing(name) {
122
127
  var n = String(name || "").trim();
123
128
  if (!n) return null;
@@ -310,6 +315,10 @@
310
315
  this._injectedKey = null;
311
316
  this._lastGutterPx = 0;
312
317
  this._lastGapPx = 0;
318
+ this.slideState = null;
319
+ this._slideStateSig = "";
320
+ this._lastSlideStateActiveIndex = -1;
321
+ this._lastSlideStateScrollLeft = 0;
313
322
  var self = this;
314
323
  this.ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(function() {
315
324
  if (self._roTicking) return;
@@ -522,6 +531,7 @@
522
531
  this.updateScrollbar();
523
532
  this.updateButtons();
524
533
  this.updateOverlays();
534
+ this.updateSlideState(false);
525
535
  };
526
536
  Slider.prototype.safeClampScroll = function(x) {
527
537
  var clamped = Math.min(Math.max(x, 0), this.maxScroll());
@@ -546,6 +556,7 @@
546
556
  this.updateScrollbar();
547
557
  this.updateButtons();
548
558
  this.updateOverlays();
559
+ this.updateSlideState(false);
549
560
  };
550
561
  Slider.prototype.scheduleTouchClamp = function() {
551
562
  var self = this;
@@ -567,6 +578,7 @@
567
578
  self.updateScrollbar();
568
579
  self.updateButtons();
569
580
  self.updateOverlays();
581
+ self.updateSlideState(false);
570
582
  }, 90);
571
583
  };
572
584
  Slider.prototype.onScroll = function() {
@@ -575,6 +587,7 @@
575
587
  this.updateScrollbar();
576
588
  this.updateButtons();
577
589
  this.updateOverlays();
590
+ this.updateSlideState(false);
578
591
  this.scheduleTouchClamp();
579
592
  return;
580
593
  }
@@ -594,6 +607,7 @@
594
607
  self.updateScrollbar();
595
608
  self.updateButtons();
596
609
  self.updateOverlays();
610
+ self.updateSlideState(false);
597
611
  self.ticking = false;
598
612
  });
599
613
  };
@@ -644,6 +658,361 @@
644
658
  }
645
659
  return bestIdx;
646
660
  };
661
+ Slider.prototype.getItemAnchorScrolls = function(items) {
662
+ var current = this.safeClampScroll(this.scroller.scrollLeft);
663
+ var alignLeft = this.getAlignTargetLeft();
664
+ var anchors = [];
665
+ var last = -Infinity;
666
+ for (var i = 0; i < items.length; i++) {
667
+ var item = items[i];
668
+ item.setAttribute("data-rt-slider-item-index", String(i));
669
+ var rect = item.getBoundingClientRect();
670
+ var raw = current + (rect.left - alignLeft);
671
+ var anchor = this.safeClampScroll(raw);
672
+ if (anchor < last) anchor = last;
673
+ anchors.push(anchor);
674
+ last = anchor;
675
+ }
676
+ return anchors;
677
+ };
678
+ Slider.prototype.cloneSlideState = function(state2) {
679
+ if (!state2) return null;
680
+ return {
681
+ sliderId: state2.sliderId,
682
+ itemCount: state2.itemCount,
683
+ scroll: {
684
+ current: state2.scroll.current,
685
+ max: state2.scroll.max,
686
+ progress: state2.scroll.progress,
687
+ progressPercent: state2.scroll.progressPercent,
688
+ direction: state2.scroll.direction,
689
+ isAtStart: state2.scroll.isAtStart,
690
+ isAtEnd: state2.scroll.isAtEnd
691
+ },
692
+ active: {
693
+ index: state2.active.index,
694
+ element: state2.active.element,
695
+ anchorScrollLeft: state2.active.anchorScrollLeft,
696
+ distancePx: state2.active.distancePx
697
+ },
698
+ previous: {
699
+ index: state2.previous.index,
700
+ element: state2.previous.element
701
+ },
702
+ next: {
703
+ index: state2.next.index,
704
+ element: state2.next.element
705
+ },
706
+ segment: {
707
+ fromIndex: state2.segment.fromIndex,
708
+ fromElement: state2.segment.fromElement,
709
+ toIndex: state2.segment.toIndex,
710
+ toElement: state2.segment.toElement,
711
+ progress: state2.segment.progress,
712
+ progressPercent: state2.segment.progressPercent,
713
+ distancePx: state2.segment.distancePx,
714
+ spanPx: state2.segment.spanPx,
715
+ remainingPx: state2.segment.remainingPx,
716
+ isBetween: state2.segment.isBetween
717
+ },
718
+ slides: state2.slides.map(function(slide) {
719
+ return {
720
+ index: slide.index,
721
+ element: slide.element,
722
+ anchorScrollLeft: slide.anchorScrollLeft,
723
+ anchorProgress: slide.anchorProgress,
724
+ anchorProgressPercent: slide.anchorProgressPercent,
725
+ distanceFromCurrentPx: slide.distanceFromCurrentPx,
726
+ isActive: slide.isActive,
727
+ isPrevious: slide.isPrevious,
728
+ isNext: slide.isNext,
729
+ isFrom: slide.isFrom,
730
+ isTo: slide.isTo,
731
+ isBeforeActive: slide.isBeforeActive,
732
+ isAfterActive: slide.isAfterActive
733
+ };
734
+ })
735
+ };
736
+ };
737
+ Slider.prototype.dispatchSliderEvent = function(name, detail) {
738
+ var payload = this.cloneSlideState(detail);
739
+ try {
740
+ this.root.dispatchEvent(
741
+ new CustomEvent(name, {
742
+ bubbles: true,
743
+ detail: payload
744
+ })
745
+ );
746
+ } catch (e) {
747
+ var ev = document.createEvent("CustomEvent");
748
+ ev.initCustomEvent(name, true, false, payload);
749
+ this.root.dispatchEvent(ev);
750
+ }
751
+ };
752
+ Slider.prototype.applySlideStateAttributes = function(state2) {
753
+ var root = this.root;
754
+ root.setAttribute(
755
+ "data-rt-slider-active-index",
756
+ String(state2.active.index)
757
+ );
758
+ root.setAttribute(
759
+ "data-rt-slider-from-index",
760
+ String(state2.segment.fromIndex)
761
+ );
762
+ root.setAttribute("data-rt-slider-to-index", String(state2.segment.toIndex));
763
+ root.setAttribute(
764
+ "data-rt-slider-segment-progress",
765
+ String(round(state2.segment.progress, 6))
766
+ );
767
+ root.setAttribute(
768
+ "data-rt-slider-segment-progress-percent",
769
+ String(round(state2.segment.progressPercent, 4))
770
+ );
771
+ root.setAttribute(
772
+ "data-rt-slider-scroll-progress",
773
+ String(round(state2.scroll.progress, 6))
774
+ );
775
+ root.setAttribute(
776
+ "data-rt-slider-scroll-progress-percent",
777
+ String(round(state2.scroll.progressPercent, 4))
778
+ );
779
+ root.setAttribute(
780
+ "data-rt-slider-scroll-direction",
781
+ state2.scroll.direction
782
+ );
783
+ for (var i = 0; i < state2.slides.length; i++) {
784
+ var slide = state2.slides[i];
785
+ var el = slide.element;
786
+ if (!el) continue;
787
+ el.setAttribute("data-rt-slider-item-index", String(slide.index));
788
+ el.setAttribute(
789
+ "data-rt-slider-item-active",
790
+ slide.isActive ? "true" : "false"
791
+ );
792
+ el.setAttribute(
793
+ "data-rt-slider-item-from",
794
+ slide.isFrom ? "true" : "false"
795
+ );
796
+ el.setAttribute("data-rt-slider-item-to", slide.isTo ? "true" : "false");
797
+ el.setAttribute(
798
+ "data-rt-slider-item-previous",
799
+ slide.isPrevious ? "true" : "false"
800
+ );
801
+ el.setAttribute(
802
+ "data-rt-slider-item-next",
803
+ slide.isNext ? "true" : "false"
804
+ );
805
+ el.setAttribute(
806
+ "data-rt-slider-item-before-active",
807
+ slide.isBeforeActive ? "true" : "false"
808
+ );
809
+ el.setAttribute(
810
+ "data-rt-slider-item-after-active",
811
+ slide.isAfterActive ? "true" : "false"
812
+ );
813
+ el.setAttribute(
814
+ "data-rt-slider-item-anchor-progress",
815
+ String(round(slide.anchorProgress, 6))
816
+ );
817
+ el.setAttribute(
818
+ "data-rt-slider-item-anchor-progress-percent",
819
+ String(round(slide.anchorProgressPercent, 4))
820
+ );
821
+ el.setAttribute(
822
+ "data-rt-slider-item-distance",
823
+ String(round(slide.distanceFromCurrentPx, 4))
824
+ );
825
+ }
826
+ };
827
+ Slider.prototype.buildSlideState = function() {
828
+ var items = this.getOrderedItems();
829
+ var max = this.maxScroll();
830
+ var current = this.safeClampScroll(this.scroller.scrollLeft);
831
+ var prevScroll = this._lastSlideStateScrollLeft;
832
+ var scrollDirection = "none";
833
+ if (current > prevScroll + 0.01) scrollDirection = "forward";
834
+ else if (current < prevScroll - 0.01) scrollDirection = "backward";
835
+ if (!items.length) {
836
+ return {
837
+ sliderId: this.sliderId,
838
+ itemCount: 0,
839
+ scroll: {
840
+ current,
841
+ max,
842
+ progress: max > 0 ? clamp(current / max) : 0,
843
+ progressPercent: max > 0 ? clamp(current / max) * 100 : 0,
844
+ direction: scrollDirection,
845
+ isAtStart: current <= 0.5,
846
+ isAtEnd: current >= max - 0.5
847
+ },
848
+ active: {
849
+ index: -1,
850
+ element: null,
851
+ anchorScrollLeft: 0,
852
+ distancePx: 0
853
+ },
854
+ previous: {
855
+ index: -1,
856
+ element: null
857
+ },
858
+ next: {
859
+ index: -1,
860
+ element: null
861
+ },
862
+ segment: {
863
+ fromIndex: -1,
864
+ fromElement: null,
865
+ toIndex: -1,
866
+ toElement: null,
867
+ progress: 0,
868
+ progressPercent: 0,
869
+ distancePx: 0,
870
+ spanPx: 0,
871
+ remainingPx: 0,
872
+ isBetween: false
873
+ },
874
+ slides: []
875
+ };
876
+ }
877
+ var anchors = this.getItemAnchorScrolls(items);
878
+ var nearestIndex = 0;
879
+ var nearestDistance = Infinity;
880
+ for (var i = 0; i < anchors.length; i++) {
881
+ var distance = current - anchors[i];
882
+ var absDistance = Math.abs(distance);
883
+ if (absDistance < nearestDistance) {
884
+ nearestDistance = absDistance;
885
+ nearestIndex = i;
886
+ }
887
+ }
888
+ var fromIndex = 0;
889
+ var toIndex = 0;
890
+ var segmentProgress = 0;
891
+ var segmentDistance = 0;
892
+ var segmentSpan = 0;
893
+ var segmentRemaining = 0;
894
+ var isBetween = false;
895
+ if (current <= anchors[0]) {
896
+ fromIndex = 0;
897
+ toIndex = 0;
898
+ segmentProgress = 0;
899
+ segmentDistance = 0;
900
+ segmentSpan = 0;
901
+ segmentRemaining = 0;
902
+ isBetween = false;
903
+ } else if (current >= anchors[anchors.length - 1]) {
904
+ fromIndex = anchors.length - 1;
905
+ toIndex = anchors.length - 1;
906
+ segmentProgress = 1;
907
+ segmentDistance = 0;
908
+ segmentSpan = 0;
909
+ segmentRemaining = 0;
910
+ isBetween = false;
911
+ } else {
912
+ for (var j = 0; j < anchors.length - 1; j++) {
913
+ var a = anchors[j];
914
+ var b = anchors[j + 1];
915
+ if (current >= a && current <= b) {
916
+ fromIndex = j;
917
+ toIndex = j + 1;
918
+ segmentSpan = Math.max(0, b - a);
919
+ segmentDistance = Math.max(0, current - a);
920
+ segmentProgress = segmentSpan > 0 ? clamp(segmentDistance / segmentSpan) : 0;
921
+ segmentRemaining = Math.max(0, segmentSpan - segmentDistance);
922
+ isBetween = current > a && current < b;
923
+ break;
924
+ }
925
+ }
926
+ }
927
+ var slides = [];
928
+ for (var k = 0; k < items.length; k++) {
929
+ var anchor = anchors[k];
930
+ var anchorProgress = max > 0 ? clamp(anchor / max) : 0;
931
+ slides.push({
932
+ index: k,
933
+ element: items[k],
934
+ anchorScrollLeft: anchor,
935
+ anchorProgress,
936
+ anchorProgressPercent: anchorProgress * 100,
937
+ distanceFromCurrentPx: current - anchor,
938
+ isActive: k === nearestIndex,
939
+ isPrevious: k === nearestIndex - 1,
940
+ isNext: k === nearestIndex + 1,
941
+ isFrom: k === fromIndex,
942
+ isTo: k === toIndex,
943
+ isBeforeActive: k < nearestIndex,
944
+ isAfterActive: k > nearestIndex
945
+ });
946
+ }
947
+ return {
948
+ sliderId: this.sliderId,
949
+ itemCount: items.length,
950
+ scroll: {
951
+ current,
952
+ max,
953
+ progress: max > 0 ? clamp(current / max) : 0,
954
+ progressPercent: max > 0 ? clamp(current / max) * 100 : 0,
955
+ direction: scrollDirection,
956
+ isAtStart: current <= 0.5,
957
+ isAtEnd: current >= max - 0.5
958
+ },
959
+ active: {
960
+ index: nearestIndex,
961
+ element: items[nearestIndex] || null,
962
+ anchorScrollLeft: anchors[nearestIndex] || 0,
963
+ distancePx: current - (anchors[nearestIndex] || 0)
964
+ },
965
+ previous: {
966
+ index: nearestIndex > 0 ? nearestIndex - 1 : -1,
967
+ element: nearestIndex > 0 ? items[nearestIndex - 1] : null
968
+ },
969
+ next: {
970
+ index: nearestIndex < items.length - 1 ? nearestIndex + 1 : -1,
971
+ element: nearestIndex < items.length - 1 ? items[nearestIndex + 1] : null
972
+ },
973
+ segment: {
974
+ fromIndex,
975
+ fromElement: items[fromIndex] || null,
976
+ toIndex,
977
+ toElement: items[toIndex] || null,
978
+ progress: segmentProgress,
979
+ progressPercent: segmentProgress * 100,
980
+ distancePx: segmentDistance,
981
+ spanPx: segmentSpan,
982
+ remainingPx: segmentRemaining,
983
+ isBetween
984
+ },
985
+ slides
986
+ };
987
+ };
988
+ Slider.prototype.updateSlideState = function(force) {
989
+ var state2 = this.buildSlideState();
990
+ var prevActiveIndex = this._lastSlideStateActiveIndex;
991
+ var sig = state2.active.index + "|" + state2.segment.fromIndex + "|" + state2.segment.toIndex + "|" + round(state2.segment.progress, 4) + "|" + round(state2.scroll.progress, 4) + "|" + state2.scroll.direction;
992
+ this.slideState = state2;
993
+ this.applySlideStateAttributes(state2);
994
+ this._lastSlideStateScrollLeft = state2.scroll.current;
995
+ if (force || sig !== this._slideStateSig) {
996
+ this._slideStateSig = sig;
997
+ this.dispatchSliderEvent("rtSlider:slide", state2);
998
+ }
999
+ if (force || state2.active.index !== prevActiveIndex) {
1000
+ this._lastSlideStateActiveIndex = state2.active.index;
1001
+ this.dispatchSliderEvent("rtSlider:active", state2);
1002
+ }
1003
+ };
1004
+ Slider.prototype.getSlideState = function() {
1005
+ if (!this.slideState) this.updateSlideState(true);
1006
+ return this.cloneSlideState(this.slideState);
1007
+ };
1008
+ Slider.prototype.getActiveIndex = function() {
1009
+ var state2 = this.getSlideState();
1010
+ return state2 && state2.active ? state2.active.index : -1;
1011
+ };
1012
+ Slider.prototype.getActiveElement = function() {
1013
+ var state2 = this.getSlideState();
1014
+ return state2 && state2.active ? state2.active.element : null;
1015
+ };
647
1016
  Slider.prototype.scrollAlignToItem = function(el) {
648
1017
  if (!el) return;
649
1018
  var total = this.scroller.scrollWidth;
@@ -972,6 +1341,7 @@
972
1341
  this.updateScrollbar();
973
1342
  this.updateButtons();
974
1343
  this.updateOverlays();
1344
+ this.updateSlideState(false);
975
1345
  this.barPointerId = null;
976
1346
  };
977
1347
  Slider.prototype.detectLinksInItems = function() {
@@ -1077,6 +1447,7 @@
1077
1447
  this.setupCursorMode();
1078
1448
  this.applyIOSScrollIndicatorMask();
1079
1449
  this.applyListStyles();
1450
+ this.updateSlideState(true);
1080
1451
  };
1081
1452
  Slider.prototype.onClickCapture = function(e) {
1082
1453
  if (!this.didDragTs) return;
@@ -1213,6 +1584,7 @@
1213
1584
  self.applyListStyles();
1214
1585
  self.setupCursorMode();
1215
1586
  self.rafUpdate();
1587
+ self.updateSlideState(true);
1216
1588
  };
1217
1589
  if (this.mq.addEventListener)
1218
1590
  this.mq.addEventListener("change", this._onMQ);
@@ -1225,6 +1597,7 @@
1225
1597
  self.setupCursorMode();
1226
1598
  self.applyIOSScrollIndicatorMask();
1227
1599
  self.applyListStyles();
1600
+ self.updateSlideState(true);
1228
1601
  };
1229
1602
  img.addEventListener("load", update, { once: true });
1230
1603
  img.addEventListener("error", update, { once: true });
@@ -1248,7 +1621,7 @@
1248
1621
  if (this.mq.matches) {
1249
1622
  loadLenis().then(function() {
1250
1623
  self.setupLenisInstance();
1251
- }).catch(function(e) {
1624
+ }).catch(function() {
1252
1625
  });
1253
1626
  }
1254
1627
  this.rafUpdate();
@@ -1256,10 +1629,12 @@
1256
1629
  self.rafUpdate();
1257
1630
  self.applyIOSScrollIndicatorMask();
1258
1631
  self.applyListStyles();
1632
+ self.updateSlideState(true);
1259
1633
  };
1260
1634
  window.addEventListener("load", this._onWL);
1261
1635
  this.setupCursorMode();
1262
1636
  this.bindEvents();
1637
+ this.updateSlideState(true);
1263
1638
  };
1264
1639
  Slider.prototype.destroy = function() {
1265
1640
  this.stopInertia();
@@ -1360,6 +1735,10 @@
1360
1735
  get: function(id) {
1361
1736
  return state.instances[id] || null;
1362
1737
  },
1738
+ getSlideState: function(id) {
1739
+ var inst = state.instances[id];
1740
+ return inst ? inst.getSlideState() : null;
1741
+ },
1363
1742
  refresh: function() {
1364
1743
  var keys = state.order;
1365
1744
  for (var i = 0; i < keys.length; i++) {