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