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