overlapping-cards-scroll 0.1.6 → 0.1.7

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.
@@ -32,8 +32,24 @@ var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
32
32
  var toCssDimension = (value) => typeof value === "number" ? `${value}px` : value;
33
33
  var PAGE_DOT_POSITIONS = /* @__PURE__ */ new Set(["above", "below", "overlay"]);
34
34
  var normalizePageDotsPosition = (value) => PAGE_DOT_POSITIONS.has(value) ? value : "below";
35
- var TAB_POSITIONS = /* @__PURE__ */ new Set(["above", "below"]);
36
- var normalizeTabsPosition = (value) => TAB_POSITIONS.has(value) ? value : "above";
35
+ var TABS_POSITION_MAP = {
36
+ "top-left": { side: "top", align: "start", orientation: "horizontal" },
37
+ "top-center": { side: "top", align: "center", orientation: "horizontal" },
38
+ "top-right": { side: "top", align: "end", orientation: "horizontal" },
39
+ "bottom-left": { side: "bottom", align: "start", orientation: "horizontal" },
40
+ "bottom-center": { side: "bottom", align: "center", orientation: "horizontal" },
41
+ "bottom-right": { side: "bottom", align: "end", orientation: "horizontal" },
42
+ "left-top": { side: "left", align: "start", orientation: "vertical" },
43
+ "left-center": { side: "left", align: "center", orientation: "vertical" },
44
+ "left-bottom": { side: "left", align: "end", orientation: "vertical" },
45
+ "right-top": { side: "right", align: "start", orientation: "vertical" },
46
+ "right-center": { side: "right", align: "center", orientation: "vertical" },
47
+ "right-bottom": { side: "right", align: "end", orientation: "vertical" },
48
+ "above": { side: "top", align: "center", orientation: "horizontal" },
49
+ "below": { side: "bottom", align: "center", orientation: "horizontal" }
50
+ };
51
+ var DEFAULT_TABS_POSITION = { side: "top", align: "center", orientation: "horizontal" };
52
+ var parseTabsPosition = (value) => value && TABS_POSITION_MAP[value] || DEFAULT_TABS_POSITION;
37
53
  var lastKnownViewportWidth = 1;
38
54
  function DefaultTabsContainerComponent({
39
55
  children,
@@ -567,11 +583,16 @@ function OverlappingCardsScroll(props) {
567
583
  stageElement.removeEventListener("wheel", handleWheel);
568
584
  };
569
585
  }, [handleWheel]);
570
- const containerClassName = className ? `overlapping-cards-scroll ${className}` : "overlapping-cards-scroll";
571
586
  const resolvedPageDotsPosition = normalizePageDotsPosition(pageDotsPosition);
572
587
  const showNavigationDots = showPageDots && cardCount > 1;
573
- const resolvedTabsPosition = normalizeTabsPosition(tabsPosition);
588
+ const parsedTabsPosition = parseTabsPosition(tabsPosition);
589
+ const isVerticalTabs = parsedTabsPosition.orientation === "vertical";
574
590
  const showNavigationTabs = showTabs && cardCount > 1 && cardNames !== null;
591
+ const containerClassName = [
592
+ "overlapping-cards-scroll",
593
+ isVerticalTabs ? "overlapping-cards-scroll--vertical-tabs" : "",
594
+ className
595
+ ].filter(Boolean).join(" ");
575
596
  (0, import_react.useEffect)(() => {
576
597
  if (showTabs && cardNames === null) {
577
598
  console.warn(
@@ -579,17 +600,32 @@ function OverlappingCardsScroll(props) {
579
600
  );
580
601
  }
581
602
  }, [showTabs, cardNames]);
582
- const renderTabs = (position) => {
603
+ const renderTabs = () => {
583
604
  if (!showNavigationTabs || cardNames === null) {
584
605
  return null;
585
606
  }
586
- const containerClassName2 = tabsClassName ? `ocs-tabs ocs-tabs--${position} ${tabsClassName}` : `ocs-tabs ocs-tabs--${position}`;
587
- const containerStyle = position === "above" ? { marginBottom: toCssDimension(tabsOffset) } : { marginTop: toCssDimension(tabsOffset) };
607
+ const { side, align, orientation } = parsedTabsPosition;
608
+ const isVertical = orientation === "vertical";
609
+ const alignClass = align === "start" ? "ocs-tabs--align-start" : align === "end" ? "ocs-tabs--align-end" : "ocs-tabs--align-center";
610
+ const orientationClass = isVertical ? "ocs-tabs--vertical" : "";
611
+ const classNames = [
612
+ "ocs-tabs",
613
+ `ocs-tabs--${side}`,
614
+ alignClass,
615
+ orientationClass,
616
+ tabsClassName
617
+ ].filter(Boolean).join(" ");
618
+ const containerStyle = {};
619
+ if (side === "top") containerStyle.marginBottom = toCssDimension(tabsOffset);
620
+ else if (side === "bottom") containerStyle.marginTop = toCssDimension(tabsOffset);
621
+ else if (side === "left") containerStyle.marginRight = toCssDimension(tabsOffset);
622
+ else if (side === "right") containerStyle.marginLeft = toCssDimension(tabsOffset);
588
623
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
589
624
  TabsContainerComponent,
590
625
  {
591
- position,
592
- className: containerClassName2,
626
+ position: side,
627
+ align,
628
+ className: classNames,
593
629
  style: containerStyle,
594
630
  ariaLabel: "Card tabs",
595
631
  cardNames,
@@ -598,9 +634,7 @@ function OverlappingCardsScroll(props) {
598
634
  children: cardNames.map((name, index) => {
599
635
  const influence = clamp(1 - Math.abs(progress - index), 0, 1);
600
636
  const isPrincipal = influence > 0.98;
601
- const animate = {
602
- opacity: 0.45 + influence * 0.55
603
- };
637
+ const animate = { opacity: 0.45 + influence * 0.55 };
604
638
  const className2 = isPrincipal ? "ocs-tab ocs-tab--active" : "ocs-tab";
605
639
  const style = { opacity: animate.opacity };
606
640
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -608,7 +642,8 @@ function OverlappingCardsScroll(props) {
608
642
  {
609
643
  name,
610
644
  index,
611
- position,
645
+ position: side,
646
+ align,
612
647
  isPrincipal,
613
648
  influence,
614
649
  animate,
@@ -621,12 +656,167 @@ function OverlappingCardsScroll(props) {
621
656
  transitionMode: "swoop"
622
657
  })
623
658
  },
624
- `ocs-tab-${position}-${index}`
659
+ `ocs-tab-${side}-${index}`
625
660
  );
626
661
  })
627
662
  }
628
663
  );
629
664
  };
665
+ const tabsBeforeStage = parsedTabsPosition.side === "top" || parsedTabsPosition.side === "left";
666
+ const stageAndDots = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
667
+ showNavigationDots && resolvedPageDotsPosition === "above" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
668
+ "nav",
669
+ {
670
+ className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--above ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--above",
671
+ style: { marginBottom: toCssDimension(pageDotsOffset) },
672
+ "aria-label": "Card pages",
673
+ children: cards.map((_, index) => {
674
+ const influence = clamp(1 - Math.abs(progress - index), 0, 1);
675
+ const opacity = 0.25 + influence * 0.75;
676
+ const scale = 0.9 + influence * 0.22;
677
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
678
+ "button",
679
+ {
680
+ type: "button",
681
+ className: "ocs-page-dot",
682
+ "aria-label": `Go to card ${index + 1}`,
683
+ "aria-current": influence > 0.98 ? "page" : void 0,
684
+ onClick: () => focusCard(index, {
685
+ behavior: pageDotsBehavior,
686
+ transitionMode: "swoop"
687
+ }),
688
+ style: { opacity, transform: `scale(${scale})` }
689
+ },
690
+ `ocs-page-dot-above-${index}`
691
+ );
692
+ })
693
+ }
694
+ ) : null,
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ocs-stage-frame", children: [
696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
697
+ "div",
698
+ {
699
+ className: "ocs-stage",
700
+ ref: stageRef,
701
+ style: {
702
+ minHeight: toCssDimension(cardHeight)
703
+ },
704
+ onTouchStart: handleTouchStart,
705
+ onTouchMove: handleTouchMove,
706
+ onTouchEnd: handleTouchEnd,
707
+ onTouchCancel: handleTouchEnd,
708
+ children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ocs-track", children: cards.map((card, index) => {
710
+ var _a;
711
+ const cardX = resolveCardX(
712
+ index,
713
+ activeIndex,
714
+ transitionProgress,
715
+ layout
716
+ );
717
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
718
+ "div",
719
+ {
720
+ className: cardContainerClassName ? `${focusTransition ? "ocs-card ocs-card--focus-transition" : "ocs-card"} ${cardContainerClassName}` : focusTransition ? "ocs-card ocs-card--focus-transition" : "ocs-card",
721
+ style: {
722
+ width: `${layout.cardWidth}px`,
723
+ transform: `translate3d(${cardX}px, 0, 0)`,
724
+ transitionDuration: focusTransition ? `${focusTransition.duration}ms` : void 0,
725
+ ...cardContainerStyle,
726
+ pointerEvents: "none"
727
+ },
728
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
729
+ "div",
730
+ {
731
+ style: {
732
+ pointerEvents: "auto",
733
+ display: "flex",
734
+ flexDirection: "column"
735
+ },
736
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
737
+ OverlappingCardsScrollCardIndexContext.Provider,
738
+ {
739
+ value: index,
740
+ children: card
741
+ }
742
+ )
743
+ }
744
+ )
745
+ },
746
+ (_a = card.key) != null ? _a : `ocs-card-${index}`
747
+ );
748
+ }) }),
749
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ocs-scroll-region", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
750
+ "div",
751
+ {
752
+ className: "ocs-scroll-spacer",
753
+ style: {
754
+ width: `${layout.trackWidth}px`
755
+ }
756
+ }
757
+ ) })
758
+ ]
759
+ }
760
+ ),
761
+ showNavigationDots && resolvedPageDotsPosition === "overlay" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
762
+ "nav",
763
+ {
764
+ className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--overlay ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--overlay",
765
+ style: { bottom: toCssDimension(pageDotsOffset) },
766
+ "aria-label": "Card pages",
767
+ children: cards.map((_, index) => {
768
+ const influence = clamp(1 - Math.abs(progress - index), 0, 1);
769
+ const opacity = 0.25 + influence * 0.75;
770
+ const scale = 0.9 + influence * 0.22;
771
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
772
+ "button",
773
+ {
774
+ type: "button",
775
+ className: "ocs-page-dot",
776
+ "aria-label": `Go to card ${index + 1}`,
777
+ "aria-current": influence > 0.98 ? "page" : void 0,
778
+ onClick: () => focusCard(index, {
779
+ behavior: pageDotsBehavior,
780
+ transitionMode: "swoop"
781
+ }),
782
+ style: { opacity, transform: `scale(${scale})` }
783
+ },
784
+ `ocs-page-dot-overlay-${index}`
785
+ );
786
+ })
787
+ }
788
+ ) : null
789
+ ] }),
790
+ showNavigationDots && resolvedPageDotsPosition === "below" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
791
+ "nav",
792
+ {
793
+ className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--below ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--below",
794
+ style: { marginTop: toCssDimension(pageDotsOffset) },
795
+ "aria-label": "Card pages",
796
+ children: cards.map((_, index) => {
797
+ const influence = clamp(1 - Math.abs(progress - index), 0, 1);
798
+ const opacity = 0.25 + influence * 0.75;
799
+ const scale = 0.9 + influence * 0.22;
800
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ className: "ocs-page-dot",
805
+ "aria-label": `Go to card ${index + 1}`,
806
+ "aria-current": influence > 0.98 ? "page" : void 0,
807
+ onClick: () => focusCard(index, {
808
+ behavior: pageDotsBehavior,
809
+ transitionMode: "swoop"
810
+ }),
811
+ style: { opacity, transform: `scale(${scale})` }
812
+ },
813
+ `ocs-page-dot-below-${index}`
814
+ );
815
+ })
816
+ }
817
+ ) : null
818
+ ] });
819
+ const stageContent = isVerticalTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ocs-main-column", children: stageAndDots }) : stageAndDots;
630
820
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
631
821
  OverlappingCardsScrollControllerContext.Provider,
632
822
  {
@@ -638,159 +828,9 @@ function OverlappingCardsScroll(props) {
638
828
  "aria-label": ariaLabel,
639
829
  ref: containerRef,
640
830
  children: [
641
- resolvedTabsPosition === "above" ? renderTabs("above") : null,
642
- showNavigationDots && resolvedPageDotsPosition === "above" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
643
- "nav",
644
- {
645
- className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--above ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--above",
646
- style: { marginBottom: toCssDimension(pageDotsOffset) },
647
- "aria-label": "Card pages",
648
- children: cards.map((_, index) => {
649
- const influence = clamp(1 - Math.abs(progress - index), 0, 1);
650
- const opacity = 0.25 + influence * 0.75;
651
- const scale = 0.9 + influence * 0.22;
652
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
653
- "button",
654
- {
655
- type: "button",
656
- className: "ocs-page-dot",
657
- "aria-label": `Go to card ${index + 1}`,
658
- "aria-current": influence > 0.98 ? "page" : void 0,
659
- onClick: () => focusCard(index, {
660
- behavior: pageDotsBehavior,
661
- transitionMode: "swoop"
662
- }),
663
- style: { opacity, transform: `scale(${scale})` }
664
- },
665
- `ocs-page-dot-above-${index}`
666
- );
667
- })
668
- }
669
- ) : null,
670
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ocs-stage-frame", children: [
671
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
672
- "div",
673
- {
674
- className: "ocs-stage",
675
- ref: stageRef,
676
- style: {
677
- minHeight: toCssDimension(cardHeight)
678
- },
679
- onTouchStart: handleTouchStart,
680
- onTouchMove: handleTouchMove,
681
- onTouchEnd: handleTouchEnd,
682
- onTouchCancel: handleTouchEnd,
683
- children: [
684
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ocs-track", children: cards.map((card, index) => {
685
- var _a;
686
- const cardX = resolveCardX(
687
- index,
688
- activeIndex,
689
- transitionProgress,
690
- layout
691
- );
692
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
693
- "div",
694
- {
695
- className: cardContainerClassName ? `${focusTransition ? "ocs-card ocs-card--focus-transition" : "ocs-card"} ${cardContainerClassName}` : focusTransition ? "ocs-card ocs-card--focus-transition" : "ocs-card",
696
- style: {
697
- width: `${layout.cardWidth}px`,
698
- transform: `translate3d(${cardX}px, 0, 0)`,
699
- transitionDuration: focusTransition ? `${focusTransition.duration}ms` : void 0,
700
- ...cardContainerStyle,
701
- pointerEvents: "none"
702
- },
703
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
704
- "div",
705
- {
706
- style: {
707
- pointerEvents: "auto",
708
- display: "flex",
709
- flexDirection: "column"
710
- },
711
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
712
- OverlappingCardsScrollCardIndexContext.Provider,
713
- {
714
- value: index,
715
- children: card
716
- }
717
- )
718
- }
719
- )
720
- },
721
- (_a = card.key) != null ? _a : `ocs-card-${index}`
722
- );
723
- }) }),
724
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ocs-scroll-region", ref: scrollRef, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
725
- "div",
726
- {
727
- className: "ocs-scroll-spacer",
728
- style: {
729
- width: `${layout.trackWidth}px`
730
- }
731
- }
732
- ) })
733
- ]
734
- }
735
- ),
736
- showNavigationDots && resolvedPageDotsPosition === "overlay" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
737
- "nav",
738
- {
739
- className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--overlay ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--overlay",
740
- style: { bottom: toCssDimension(pageDotsOffset) },
741
- "aria-label": "Card pages",
742
- children: cards.map((_, index) => {
743
- const influence = clamp(1 - Math.abs(progress - index), 0, 1);
744
- const opacity = 0.25 + influence * 0.75;
745
- const scale = 0.9 + influence * 0.22;
746
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
747
- "button",
748
- {
749
- type: "button",
750
- className: "ocs-page-dot",
751
- "aria-label": `Go to card ${index + 1}`,
752
- "aria-current": influence > 0.98 ? "page" : void 0,
753
- onClick: () => focusCard(index, {
754
- behavior: pageDotsBehavior,
755
- transitionMode: "swoop"
756
- }),
757
- style: { opacity, transform: `scale(${scale})` }
758
- },
759
- `ocs-page-dot-overlay-${index}`
760
- );
761
- })
762
- }
763
- ) : null
764
- ] }),
765
- showNavigationDots && resolvedPageDotsPosition === "below" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
766
- "nav",
767
- {
768
- className: pageDotsClassName ? `ocs-page-dots ocs-page-dots--below ${pageDotsClassName}` : "ocs-page-dots ocs-page-dots--below",
769
- style: { marginTop: toCssDimension(pageDotsOffset) },
770
- "aria-label": "Card pages",
771
- children: cards.map((_, index) => {
772
- const influence = clamp(1 - Math.abs(progress - index), 0, 1);
773
- const opacity = 0.25 + influence * 0.75;
774
- const scale = 0.9 + influence * 0.22;
775
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
776
- "button",
777
- {
778
- type: "button",
779
- className: "ocs-page-dot",
780
- "aria-label": `Go to card ${index + 1}`,
781
- "aria-current": influence > 0.98 ? "page" : void 0,
782
- onClick: () => focusCard(index, {
783
- behavior: pageDotsBehavior,
784
- transitionMode: "swoop"
785
- }),
786
- style: { opacity, transform: `scale(${scale})` }
787
- },
788
- `ocs-page-dot-below-${index}`
789
- );
790
- })
791
- }
792
- ) : null,
793
- resolvedTabsPosition === "below" ? renderTabs("below") : null
831
+ tabsBeforeStage ? renderTabs() : null,
832
+ stageContent,
833
+ !tabsBeforeStage ? renderTabs() : null
794
834
  ]
795
835
  }
796
836
  )