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.
@@ -28,9 +28,25 @@ var import_react_native = require("react-native");
28
28
  var import_jsx_runtime = require("react/jsx-runtime");
29
29
  var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
30
30
  var PAGE_DOT_POSITIONS = /* @__PURE__ */ new Set(["above", "below", "overlay"]);
31
- var TAB_POSITIONS = /* @__PURE__ */ new Set(["above", "below"]);
31
+ var TABS_POSITION_MAP = {
32
+ "top-left": { side: "top", align: "start", orientation: "horizontal" },
33
+ "top-center": { side: "top", align: "center", orientation: "horizontal" },
34
+ "top-right": { side: "top", align: "end", orientation: "horizontal" },
35
+ "bottom-left": { side: "bottom", align: "start", orientation: "horizontal" },
36
+ "bottom-center": { side: "bottom", align: "center", orientation: "horizontal" },
37
+ "bottom-right": { side: "bottom", align: "end", orientation: "horizontal" },
38
+ "left-top": { side: "left", align: "start", orientation: "vertical" },
39
+ "left-center": { side: "left", align: "center", orientation: "vertical" },
40
+ "left-bottom": { side: "left", align: "end", orientation: "vertical" },
41
+ "right-top": { side: "right", align: "start", orientation: "vertical" },
42
+ "right-center": { side: "right", align: "center", orientation: "vertical" },
43
+ "right-bottom": { side: "right", align: "end", orientation: "vertical" },
44
+ "above": { side: "top", align: "center", orientation: "horizontal" },
45
+ "below": { side: "bottom", align: "center", orientation: "horizontal" }
46
+ };
47
+ var DEFAULT_TABS_POSITION = { side: "top", align: "center", orientation: "horizontal" };
48
+ var parseTabsPosition = (value) => value && TABS_POSITION_MAP[value] || DEFAULT_TABS_POSITION;
32
49
  var normalizePageDotsPosition = (value) => PAGE_DOT_POSITIONS.has(value) ? value : "below";
33
- var normalizeTabsPosition = (value) => TAB_POSITIONS.has(value) ? value : "above";
34
50
  var toNumericOffset = (value, fallback = 0) => {
35
51
  if (typeof value === "number" && Number.isFinite(value)) {
36
52
  return value;
@@ -244,7 +260,8 @@ function OverlappingCardsScrollRN(props) {
244
260
  return null;
245
261
  }, [itemsProp]);
246
262
  const cardCount = cards.length;
247
- const resolvedTabsPosition = normalizeTabsPosition(tabsPosition);
263
+ const parsedTabsPosition = parseTabsPosition(tabsPosition);
264
+ const isVerticalTabs = parsedTabsPosition.orientation === "vertical";
248
265
  const showNavigationTabs = showTabs && cardCount > 1 && cardNames !== null;
249
266
  const resolvedPageDotsOffset = toNumericOffset(pageDotsOffset, 10);
250
267
  const resolvedTabsOffset = toNumericOffset(tabsOffset, 10);
@@ -534,16 +551,21 @@ function OverlappingCardsScrollRN(props) {
534
551
  }
535
552
  );
536
553
  };
537
- const renderTabs = (position) => {
538
- if (!showNavigationTabs || resolvedTabsPosition !== position || cardNames === null) {
554
+ const renderTabs = () => {
555
+ if (!showNavigationTabs || cardNames === null) {
539
556
  return null;
540
557
  }
541
- const containerStyle = position === "above" ? [styles.tabsRow, { marginBottom: resolvedTabsOffset }] : [styles.tabsRow, { marginTop: resolvedTabsOffset }];
558
+ const { side, align, orientation } = parsedTabsPosition;
559
+ const isVertical = orientation === "vertical";
560
+ const justifyContent = align === "start" ? "flex-start" : align === "end" ? "flex-end" : "center";
561
+ const baseStyle = isVertical ? styles.tabsColumn : styles.tabsRow;
562
+ const containerStyle = side === "top" ? [baseStyle, { justifyContent, marginBottom: resolvedTabsOffset }] : side === "bottom" ? [baseStyle, { justifyContent, marginTop: resolvedTabsOffset }] : side === "left" ? [baseStyle, { justifyContent, marginRight: resolvedTabsOffset }] : [baseStyle, { justifyContent, marginLeft: resolvedTabsOffset }];
542
563
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
543
564
  TabsContainerComponent,
544
565
  {
545
- position,
546
- className: `rn-ocs-tabs rn-ocs-tabs--${position}`,
566
+ position: side,
567
+ align,
568
+ className: `rn-ocs-tabs rn-ocs-tabs--${side}`,
547
569
  style: containerStyle,
548
570
  ariaLabel: "Card tabs",
549
571
  cardNames,
@@ -564,7 +586,8 @@ function OverlappingCardsScrollRN(props) {
564
586
  {
565
587
  name,
566
588
  index,
567
- position,
589
+ position: side,
590
+ align,
568
591
  isPrincipal,
569
592
  influence,
570
593
  animate,
@@ -578,121 +601,126 @@ function OverlappingCardsScrollRN(props) {
578
601
  onPress: pressTab,
579
602
  onClick: pressTab
580
603
  },
581
- `rn-ocs-tab-${position}-${index}`
604
+ `rn-ocs-tab-${side}-${index}`
582
605
  );
583
606
  })
584
607
  }
585
608
  );
586
609
  };
587
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
588
- OverlappingCardsScrollRNControllerContext.Provider,
589
- {
590
- value: controllerContextValue,
591
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [styles.shell, style], children: [
592
- renderTabs("above"),
593
- renderPageDots("above"),
594
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
595
- import_react_native.View,
596
- {
597
- style: [styles.root, { height: resolvedCardHeight }],
598
- onLayout: (event) => {
599
- const width = event.nativeEvent.layout.width || 1;
600
- setViewportWidth(Math.max(1, width));
601
- },
602
- children: [
603
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
604
- import_react_native.Animated.ScrollView,
610
+ const tabsBeforeStage = parsedTabsPosition.side === "top" || parsedTabsPosition.side === "left";
611
+ const stageAndDots = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
612
+ renderPageDots("above"),
613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
614
+ import_react_native.View,
615
+ {
616
+ style: [styles.root, { height: resolvedCardHeight }],
617
+ onLayout: (event) => {
618
+ const width = event.nativeEvent.layout.width || 1;
619
+ setViewportWidth(Math.max(1, width));
620
+ },
621
+ children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
623
+ import_react_native.Animated.ScrollView,
624
+ {
625
+ ref: scrollRef,
626
+ horizontal: true,
627
+ style: [styles.scrollRegion, { height: resolvedCardHeight }],
628
+ contentContainerStyle: {
629
+ width: layout.trackWidth,
630
+ height: resolvedCardHeight
631
+ },
632
+ onScroll,
633
+ onScrollBeginDrag: cancelFocusTransition,
634
+ onMomentumScrollBegin: cancelFocusTransition,
635
+ scrollEventThrottle: 16,
636
+ showsHorizontalScrollIndicator,
637
+ snapToInterval: shouldSnapToCard ? layout.stepDistance : void 0,
638
+ snapToAlignment: shouldSnapToCard ? "start" : void 0,
639
+ decelerationRate: shouldSnapToCard ? snapDecelerationRate : "normal",
640
+ disableIntervalMomentum: shouldSnapToCard ? snapDisableIntervalMomentum : false,
641
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
642
+ import_react_native.View,
605
643
  {
606
- ref: scrollRef,
607
- horizontal: true,
608
- style: [styles.scrollRegion, { height: resolvedCardHeight }],
609
- contentContainerStyle: {
610
- width: layout.trackWidth,
611
- height: resolvedCardHeight
612
- },
613
- onScroll,
614
- onScrollBeginDrag: cancelFocusTransition,
615
- onMomentumScrollBegin: cancelFocusTransition,
616
- scrollEventThrottle: 16,
617
- showsHorizontalScrollIndicator,
618
- snapToInterval: shouldSnapToCard ? layout.stepDistance : void 0,
619
- snapToAlignment: shouldSnapToCard ? "start" : void 0,
620
- decelerationRate: shouldSnapToCard ? snapDecelerationRate : "normal",
621
- disableIntervalMomentum: shouldSnapToCard ? snapDisableIntervalMomentum : false,
622
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
623
- import_react_native.View,
624
- {
625
- style: [
626
- styles.track,
627
- { width: layout.trackWidth, height: resolvedCardHeight }
644
+ style: [
645
+ styles.track,
646
+ { width: layout.trackWidth, height: resolvedCardHeight }
647
+ ],
648
+ children: cards.map((card, index) => {
649
+ var _a;
650
+ const restingRightX = index === 0 ? 0 : (index - 1) * layout.peek + layout.cardWidth;
651
+ const restingLeftX = index * layout.peek;
652
+ const cardXDuringNormalScroll = index === 0 ? 0 : scrollX.interpolate({
653
+ inputRange: index === 1 ? [0, layout.stepDistance] : [
654
+ (index - 1) * layout.stepDistance,
655
+ index * layout.stepDistance
656
+ ],
657
+ outputRange: [restingRightX, restingLeftX],
658
+ extrapolate: "clamp"
659
+ });
660
+ const cardXDuringFocusTransition = focusTransition ? focusTransitionProgress.interpolate({
661
+ inputRange: [0, 1],
662
+ outputRange: [
663
+ resolveCardXAtProgress(
664
+ index,
665
+ focusTransition.fromProgress,
666
+ layout
667
+ ),
668
+ resolveCardXAtProgress(
669
+ index,
670
+ focusTransition.toProgress,
671
+ layout
672
+ )
628
673
  ],
629
- children: cards.map((card, index) => {
630
- var _a;
631
- const restingRightX = index === 0 ? 0 : (index - 1) * layout.peek + layout.cardWidth;
632
- const restingLeftX = index * layout.peek;
633
- const cardXDuringNormalScroll = index === 0 ? 0 : scrollX.interpolate({
634
- inputRange: index === 1 ? [0, layout.stepDistance] : [
635
- (index - 1) * layout.stepDistance,
636
- index * layout.stepDistance
637
- ],
638
- outputRange: [restingRightX, restingLeftX],
639
- extrapolate: "clamp"
640
- });
641
- const cardXDuringFocusTransition = focusTransition ? focusTransitionProgress.interpolate({
642
- inputRange: [0, 1],
643
- outputRange: [
644
- resolveCardXAtProgress(
645
- index,
646
- focusTransition.fromProgress,
647
- layout
648
- ),
649
- resolveCardXAtProgress(
650
- index,
651
- focusTransition.toProgress,
652
- layout
653
- )
654
- ],
655
- extrapolate: "clamp"
656
- }) : null;
657
- const animatedCardX = cardXDuringFocusTransition != null ? cardXDuringFocusTransition : cardXDuringNormalScroll;
658
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
659
- import_react_native.Animated.View,
674
+ extrapolate: "clamp"
675
+ }) : null;
676
+ const animatedCardX = cardXDuringFocusTransition != null ? cardXDuringFocusTransition : cardXDuringNormalScroll;
677
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
678
+ import_react_native.Animated.View,
679
+ {
680
+ pointerEvents: "box-none",
681
+ style: [
682
+ styles.card,
660
683
  {
661
- pointerEvents: "box-none",
662
- style: [
663
- styles.card,
684
+ width: layout.cardWidth,
685
+ height: resolvedCardHeight,
686
+ transform: [
664
687
  {
665
- width: layout.cardWidth,
666
- height: resolvedCardHeight,
667
- transform: [
668
- {
669
- translateX: import_react_native.Animated.add(scrollX, animatedCardX)
670
- }
671
- ]
672
- },
673
- cardContainerStyle
674
- ],
675
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { pointerEvents: "auto", style: styles.cardContent, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
676
- OverlappingCardsScrollRNCardIndexContext.Provider,
677
- {
678
- value: index,
679
- children: card
688
+ translateX: import_react_native.Animated.add(scrollX, animatedCardX)
680
689
  }
681
- ) })
690
+ ]
682
691
  },
683
- (_a = card.key) != null ? _a : `rn-ocs-card-${index}`
684
- );
685
- })
686
- }
687
- )
692
+ cardContainerStyle
693
+ ],
694
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { pointerEvents: "auto", style: styles.cardContent, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
695
+ OverlappingCardsScrollRNCardIndexContext.Provider,
696
+ {
697
+ value: index,
698
+ children: card
699
+ }
700
+ ) })
701
+ },
702
+ (_a = card.key) != null ? _a : `rn-ocs-card-${index}`
703
+ );
704
+ })
688
705
  }
689
- ),
690
- renderPageDots("overlay")
691
- ]
692
- }
693
- ),
694
- renderPageDots("below"),
695
- renderTabs("below")
706
+ )
707
+ }
708
+ ),
709
+ renderPageDots("overlay")
710
+ ]
711
+ }
712
+ ),
713
+ renderPageDots("below")
714
+ ] });
715
+ const stageContent = isVerticalTabs ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: styles.mainColumn, children: stageAndDots }) : stageAndDots;
716
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
717
+ OverlappingCardsScrollRNControllerContext.Provider,
718
+ {
719
+ value: controllerContextValue,
720
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native.View, { style: [isVerticalTabs ? styles.shellRow : styles.shell, style], children: [
721
+ tabsBeforeStage ? renderTabs() : null,
722
+ stageContent,
723
+ !tabsBeforeStage ? renderTabs() : null
696
724
  ] })
697
725
  }
698
726
  );
@@ -702,6 +730,15 @@ var styles = import_react_native.StyleSheet.create({
702
730
  width: "100%",
703
731
  minWidth: 0
704
732
  },
733
+ shellRow: {
734
+ width: "100%",
735
+ minWidth: 0,
736
+ flexDirection: "row"
737
+ },
738
+ mainColumn: {
739
+ flex: 1,
740
+ minWidth: 0
741
+ },
705
742
  root: {
706
743
  width: "100%",
707
744
  minWidth: 0,
@@ -757,6 +794,12 @@ var styles = import_react_native.StyleSheet.create({
757
794
  flexWrap: "wrap",
758
795
  zIndex: 6
759
796
  },
797
+ tabsColumn: {
798
+ flexDirection: "column",
799
+ alignItems: "center",
800
+ justifyContent: "center",
801
+ zIndex: 6
802
+ },
760
803
  tab: {
761
804
  borderRadius: 999,
762
805
  borderWidth: 1,