@telus-uds/components-base 3.25.0 → 3.27.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/lib/cjs/Card/Card.js +34 -13
  3. package/lib/cjs/Card/CardBase.js +78 -11
  4. package/lib/cjs/Card/PressableCardBase.js +147 -8
  5. package/lib/cjs/Carousel/Carousel.js +161 -77
  6. package/lib/cjs/Carousel/CarouselContext.js +10 -4
  7. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
  8. package/lib/cjs/Carousel/Constants.js +22 -2
  9. package/lib/cjs/Checkbox/Checkbox.js +43 -13
  10. package/lib/cjs/InputSupports/InputSupports.js +2 -1
  11. package/lib/cjs/List/List.js +24 -9
  12. package/lib/cjs/List/ListItem.js +18 -1
  13. package/lib/cjs/List/ListItemBase.js +27 -8
  14. package/lib/cjs/List/ListItemMark.js +33 -62
  15. package/lib/cjs/List/PressableListItemBase.js +1 -0
  16. package/lib/cjs/PriceLockup/PriceLockup.js +1 -1
  17. package/lib/esm/Card/Card.js +34 -13
  18. package/lib/esm/Card/CardBase.js +78 -11
  19. package/lib/esm/Card/PressableCardBase.js +148 -9
  20. package/lib/esm/Carousel/Carousel.js +153 -69
  21. package/lib/esm/Carousel/CarouselContext.js +10 -4
  22. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
  23. package/lib/esm/Carousel/Constants.js +21 -1
  24. package/lib/esm/Checkbox/Checkbox.js +43 -13
  25. package/lib/esm/InputSupports/InputSupports.js +2 -1
  26. package/lib/esm/List/List.js +24 -9
  27. package/lib/esm/List/ListItem.js +19 -2
  28. package/lib/esm/List/ListItemBase.js +27 -8
  29. package/lib/esm/List/ListItemMark.js +33 -62
  30. package/lib/esm/List/PressableListItemBase.js +1 -0
  31. package/lib/esm/PriceLockup/PriceLockup.js +1 -1
  32. package/lib/package.json +2 -2
  33. package/package.json +2 -2
  34. package/src/Card/Card.jsx +29 -7
  35. package/src/Card/CardBase.jsx +88 -8
  36. package/src/Card/PressableCardBase.jsx +135 -9
  37. package/src/Carousel/Carousel.jsx +185 -88
  38. package/src/Carousel/CarouselContext.jsx +12 -4
  39. package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
  40. package/src/Carousel/Constants.js +24 -0
  41. package/src/Checkbox/Checkbox.jsx +29 -7
  42. package/src/InputSupports/InputSupports.jsx +6 -1
  43. package/src/List/List.jsx +33 -9
  44. package/src/List/ListItem.jsx +33 -11
  45. package/src/List/ListItemBase.jsx +33 -9
  46. package/src/List/ListItemMark.jsx +32 -53
  47. package/src/List/PressableListItemBase.jsx +1 -0
  48. package/src/PriceLockup/PriceLockup.jsx +1 -1
@@ -5,6 +5,7 @@ import PanResponder from "react-native-web/dist/exports/PanResponder";
5
5
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
6
  import Platform from "react-native-web/dist/exports/Platform";
7
7
  import Dimensions from "react-native-web/dist/exports/Dimensions";
8
+ import Easing from "react-native-web/dist/exports/Easing";
8
9
  import PropTypes from 'prop-types';
9
10
  import { useThemeTokens, useTheme } from '../ThemeProvider';
10
11
  import { useViewport } from '../ViewportProvider';
@@ -21,13 +22,8 @@ import CarouselTabsPanel from './CarouselTabs/CarouselTabsPanel';
21
22
  import CarouselTabsPanelItem from './CarouselTabs/CarouselTabsPanelItem';
22
23
  import dictionary from './dictionary';
23
24
  import Box from '../Box';
24
- import { ITEMS_PER_VIEWPORT_XS_SM, ITEMS_PER_VIEWPORT_MD, ITEMS_PER_VIEWPORT_LG_XL, DEFAULT_POSITION_OFFSET, LARGE_VIEWPORT_MARGIN, DEFAULT_VIEWPORT_MARGIN, PEEKING_MULTIPLIER, ACTIVE_INDEX_OFFSET_MULTIPLIER, NEGATIVE_MULTIPLIER } from './Constants';
25
+ import { ITEMS_PER_VIEWPORT_XS_SM, ITEMS_PER_VIEWPORT_MD, ITEMS_PER_VIEWPORT_LG_XL, DEFAULT_POSITION_OFFSET, LARGE_VIEWPORT_MARGIN, DEFAULT_VIEWPORT_MARGIN, PEEKING_MULTIPLIER, NEGATIVE_MULTIPLIER, TRANSITION_MODES, SWIPE_RELEASE_STYLES, INSTANT_ANIMATION_DURATION, DEFAULT_SWIPE_RELEASE_DURATION, POSITION_VARIANTS, POSITION_PROPERTIES } from './Constants';
25
26
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
- const TRANSITION_MODES = {
27
- MANUAL: 'manual',
28
- AUTOMATIC: 'automatic',
29
- SWIPE: 'swipe'
30
- };
31
27
  const staticStyles = StyleSheet.create({
32
28
  root: {
33
29
  backgroundColor: 'transparent',
@@ -76,7 +72,7 @@ const selectHeroContainerStyles = (width, hidden) => ({
76
72
  width,
77
73
  visibility: hidden ? 'hidden' : 'visible'
78
74
  });
79
- const getDynamicPositionProperty = areStylesAppliedOnPreviousButton => areStylesAppliedOnPreviousButton ? 'left' : 'right';
75
+ const getDynamicPositionProperty = areStylesAppliedOnPreviousButton => areStylesAppliedOnPreviousButton ? POSITION_PROPERTIES.LEFT : POSITION_PROPERTIES.RIGHT;
80
76
  const selectControlButtonPositionStyles = _ref => {
81
77
  let {
82
78
  positionVariant,
@@ -86,23 +82,36 @@ const selectControlButtonPositionStyles = _ref => {
86
82
  enablePeeking,
87
83
  enableDisplayMultipleItemsPerSlide,
88
84
  isAutoPlayEnabled,
89
- viewport
85
+ viewport,
86
+ maxWidth,
87
+ viewportWidth
90
88
  } = _ref;
91
89
  const styles = {};
92
- if (positionVariant === 'edge') {
93
- styles[positionProperty] = -1 * (buttonWidth / 2);
94
- } else if (positionVariant === 'inside') {
95
- styles[positionProperty] = DEFAULT_POSITION_OFFSET;
96
- } else if (positionVariant === 'outside') {
90
+ let positionOffset = 0;
91
+ if (positionVariant === POSITION_VARIANTS.EDGE) {
92
+ positionOffset = -1 * (buttonWidth / 2);
93
+ } else if (positionVariant === POSITION_VARIANTS.INSIDE) {
94
+ positionOffset = DEFAULT_POSITION_OFFSET;
95
+ } else if (positionVariant === POSITION_VARIANTS.OUTSIDE) {
97
96
  if (enablePeeking || enableDisplayMultipleItemsPerSlide || isAutoPlayEnabled && viewport === 'xs') {
98
- styles[positionProperty] = 0;
97
+ positionOffset = 0;
99
98
  } else {
100
- styles[positionProperty] = -1 * (spaceBetweenSlideAndButton + buttonWidth);
99
+ positionOffset = -1 * (spaceBetweenSlideAndButton + buttonWidth);
100
+ }
101
+ }
102
+ if (enablePeeking) {
103
+ if (positionProperty === POSITION_PROPERTIES.RIGHT) {
104
+ const rightMargin = (viewportWidth - maxWidth) / 2;
105
+ positionOffset += rightMargin;
106
+ } else if (positionProperty === POSITION_PROPERTIES.LEFT) {
107
+ const leftMargin = (viewportWidth - maxWidth) / 2;
108
+ positionOffset += leftMargin;
101
109
  }
102
110
  }
111
+ styles[positionProperty] = positionOffset;
103
112
  return styles;
104
113
  };
105
- const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, areStylesAppliedOnPreviousButton, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport) => {
114
+ const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, areStylesAppliedOnPreviousButton, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, viewportWidth) => {
106
115
  const styles = {
107
116
  zIndex: 1,
108
117
  position: 'absolute'
@@ -124,7 +133,9 @@ const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWi
124
133
  enablePeeking,
125
134
  enableDisplayMultipleItemsPerSlide,
126
135
  isAutoPlayEnabled,
127
- viewport
136
+ viewport,
137
+ maxWidth,
138
+ viewportWidth
128
139
  })
129
140
  };
130
141
  };
@@ -190,7 +201,7 @@ const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) =
190
201
  }
191
202
  return ITEMS_PER_VIEWPORT_XS_SM;
192
203
  };
193
- const selectRootContainerStyles = (enableHero, viewport) => {
204
+ const selectRootContainerStyles = (enableHero, viewport, enablePeeking) => {
194
205
  if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
195
206
  return {
196
207
  alignItems: 'center'
@@ -201,15 +212,25 @@ const selectRootContainerStyles = (enableHero, viewport) => {
201
212
  paddingHorizontal: 16
202
213
  };
203
214
  }
215
+ if (enablePeeking) {
216
+ return {
217
+ width: '100%'
218
+ };
219
+ }
204
220
  return {};
205
221
  };
206
- const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
207
- if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
222
+ const selectMainContainerStyles = (enableHero, viewport, maxWidth, enablePeeking) => {
223
+ if (enableHero && viewport === 'xl' && Platform.OS === 'web' && !enablePeeking) {
208
224
  return {
209
225
  width: '100%',
210
226
  maxWidth: maxWidth || 1200
211
227
  };
212
228
  }
229
+ if (enablePeeking) {
230
+ return {
231
+ width: '100%'
232
+ };
233
+ }
213
234
  if (maxWidth !== null && maxWidth !== undefined) {
214
235
  return {
215
236
  maxWidth,
@@ -219,14 +240,20 @@ const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
219
240
  }
220
241
  return {};
221
242
  };
222
- const selectNavigationStyles = (tabs, enableHero, viewport) => {
243
+ const selectNavigationStyles = (tabs, enableHero, viewport, enablePeeking, maxWidth) => {
223
244
  let marginHorizontal = 0;
224
245
  if (enableHero && tabs) {
225
246
  marginHorizontal = viewport === 'xl' ? LARGE_VIEWPORT_MARGIN : DEFAULT_VIEWPORT_MARGIN;
226
247
  }
227
- return {
248
+ const styles = {
228
249
  marginHorizontal
229
250
  };
251
+ if (enablePeeking && maxWidth) {
252
+ styles.maxWidth = maxWidth;
253
+ styles.alignSelf = 'center';
254
+ styles.width = '100%';
255
+ }
256
+ return styles;
230
257
  };
231
258
 
232
259
  /**
@@ -235,23 +262,18 @@ const selectNavigationStyles = (tabs, enableHero, viewport) => {
235
262
  * @param {number} containerWidth - The width of the carousel container.
236
263
  * @param {boolean} enablePeeking - Flag indicating whether peeking is enabled.
237
264
  * @param {Object} viewport - The viewport configuration object used to determine peeking properties.
238
- * @param {React.MutableRefObject<number>} activeIndexRef - A ref object holding the current active index of the carousel.
265
+ * @param {number} maxWidth - The maximum width constraint for the carousel content.
239
266
  * @returns {number} The calculated final width of the carousel container.
240
267
  */
241
- const calculateFinalWidth = (containerWidth, enablePeeking, viewport, activeIndexRef) => {
268
+ const calculateFinalWidth = (containerWidth, enablePeeking, viewport, maxWidth) => {
242
269
  let finalWidth = containerWidth;
243
270
  if (enablePeeking) {
244
271
  const {
245
272
  peekingGap,
246
- peekingMiddleSpace,
247
- peekingMarginLeft
273
+ peekingMiddleSpace
248
274
  } = getPeekingProps(viewport);
249
- const slideWide = containerWidth - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER);
250
- if (activeIndexRef.current === 0) {
251
- finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace;
252
- } else {
253
- finalWidth = slideWide + peekingGap;
254
- }
275
+ const baseWidth = maxWidth || containerWidth;
276
+ finalWidth = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap;
255
277
  }
256
278
  return finalWidth;
257
279
  };
@@ -362,6 +384,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
362
384
  autoPlay = false,
363
385
  enablePeeking = false,
364
386
  contentMaxWidth,
387
+ swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
388
+ swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
365
389
  ...rest
366
390
  } = _ref3;
367
391
  let childrenArray = unpackFragment(children);
@@ -372,7 +396,10 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
372
396
  } = useTheme();
373
397
  const contentMaxWidthValue = useResponsiveProp(contentMaxWidth);
374
398
  const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth);
375
- const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth);
399
+ let maxWidth = null;
400
+ if (enablePeeking || contentMaxWidth !== undefined) {
401
+ maxWidth = contentMaxWidthValue === undefined ? responsiveWidth : resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth);
402
+ }
376
403
  const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport);
377
404
  const autoPlayFeatureEnabled = autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1;
378
405
  // if `Carousel` only has one `Carousel.Item`, convert this to a single-item array
@@ -435,8 +462,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
435
462
  const [isCarouselPlaying, setisCarouselPlaying] = React.useState(autoPlayFeatureEnabled);
436
463
  const isSwiping = React.useRef(false);
437
464
  const autoPlayRef = React.useRef(null);
465
+ const [rootContainerLayout, setRootContainerLayout] = React.useState({
466
+ x: 0,
467
+ y: 0,
468
+ width: 0,
469
+ height: 0
470
+ });
471
+ const rootContainerLayoutRef = React.useRef(rootContainerLayout);
438
472
  const isFirstSlide = !activeIndex;
439
473
  const isLastSlide = activeIndex + 1 >= totalItems;
474
+ const currentViewportWidth = rootContainerLayout.width;
440
475
  const handleAnimationStart = React.useCallback(function () {
441
476
  if (typeof onAnimationStart === 'function') onAnimationStart(...arguments);
442
477
  setIsAnimating(true);
@@ -454,15 +489,15 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
454
489
  if (enablePeeking) {
455
490
  const {
456
491
  peekingGap,
457
- peekingMiddleSpace,
458
- peekingMarginLeft
492
+ peekingMiddleSpace
459
493
  } = getPeekingProps(viewport);
460
494
  let finalWidth;
461
- const slideWide = containerLayoutRef.current.width - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER);
495
+ const baseWidth = maxWidth || containerLayoutRef.current.width;
496
+ const slideWide = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER;
462
497
  if (activeIndexRef.current === 0) {
463
498
  finalWidth = 0;
464
499
  } else {
465
- finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace + (slideWide + peekingGap) * (activeIndexRef.current - ACTIVE_INDEX_OFFSET_MULTIPLIER);
500
+ finalWidth = (slideWide + peekingGap) * activeIndexRef.current;
466
501
  }
467
502
  animatedX.current = finalWidth * NEGATIVE_MULTIPLIER;
468
503
  } else {
@@ -489,8 +524,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
489
524
  y: 0
490
525
  });
491
526
  }
492
- }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
493
- const animate = React.useCallback((panToAnimate, toValue, toIndex) => {
527
+ }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking, maxWidth]);
528
+ const animate = React.useCallback(function (panToAnimate, toValue, toIndex) {
529
+ let isSwipeRelease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
494
530
  const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
495
531
  const handleAnimationEndToIndex = function () {
496
532
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -498,10 +534,23 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
498
534
  }
499
535
  return handleAnimationEnd(toIndex, ...args);
500
536
  };
501
- if (reduceMotionRef.current || isSwiping.current) {
537
+ if (reduceMotionRef.current) {
538
+ Animated.timing(panToAnimate, {
539
+ toValue,
540
+ duration: INSTANT_ANIMATION_DURATION,
541
+ useNativeDriver: false
542
+ }).start(handleAnimationEndToIndex);
543
+ } else if (isSwipeRelease && swipeReleaseStyle === SWIPE_RELEASE_STYLES.EASE_OUT) {
502
544
  Animated.timing(panToAnimate, {
503
545
  toValue,
504
- duration: 1,
546
+ duration: swipeReleaseDuration,
547
+ easing: Easing.out(Easing.cubic),
548
+ useNativeDriver: false
549
+ }).start(handleAnimationEndToIndex);
550
+ } else if (isSwiping.current || isSwipeRelease) {
551
+ Animated.timing(panToAnimate, {
552
+ toValue,
553
+ duration: INSTANT_ANIMATION_DURATION,
505
554
  useNativeDriver: false
506
555
  }).start(handleAnimationEndToIndex);
507
556
  } else if (isAutoPlayEnabled) {
@@ -525,7 +574,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
525
574
  useNativeDriver: false
526
575
  }).start(handleAnimationEndToIndex);
527
576
  }
528
- }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
577
+ }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide, swipeReleaseStyle, swipeReleaseDuration]);
529
578
  const stopAutoplay = React.useCallback(() => {
530
579
  if (autoPlayRef?.current) {
531
580
  clearTimeout(autoPlayRef?.current);
@@ -548,6 +597,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
548
597
  };
549
598
  let skipChanges = !delta;
550
599
  let calcDelta = delta;
600
+ const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE;
551
601
  if (activeIndexRef.current <= 0 && delta < 0) {
552
602
  skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC;
553
603
  calcDelta = totalItems + delta;
@@ -558,15 +608,15 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
558
608
  const index = activeIndexRef.current + calcDelta;
559
609
  if (skipChanges) {
560
610
  isTransitioningRef.current = true;
561
- animate(pan, toValue, index);
611
+ animate(pan, toValue, index, isSwipeRelease);
562
612
  if (enableHero) {
563
- animate(heroPan, toValue, index);
613
+ animate(heroPan, toValue, index, isSwipeRelease);
564
614
  }
565
615
  return calcDelta;
566
616
  }
567
617
  stopAutoplay();
568
618
  setActiveIndex(index);
569
- const finalWidth = calculateFinalWidth(containerLayoutRef.current.width, enablePeeking, viewport, activeIndexRef);
619
+ const finalWidth = calculateFinalWidth(containerLayoutRef.current.width, enablePeeking, viewport, maxWidth);
570
620
  toValue.x = finalWidth * -1 * calcDelta;
571
621
  const heroToValue = {
572
622
  x: 0,
@@ -574,9 +624,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
574
624
  };
575
625
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta;
576
626
  isTransitioningRef.current = true;
577
- animate(pan, toValue, index);
627
+ animate(pan, toValue, index, isSwipeRelease);
578
628
  if (enableHero) {
579
- animate(heroPan, heroToValue, index);
629
+ animate(heroPan, heroToValue, index, isSwipeRelease);
580
630
  }
581
631
  if (isCarouselPlaying) {
582
632
  stopAutoplay();
@@ -593,7 +643,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
593
643
  }
594
644
  if (onIndexChanged) onIndexChanged(calcDelta, index);
595
645
  return calcDelta;
596
- }, [handleAnimationStart, triggerRefocus, slideDuration, updateOffset, animate, totalItems, onIndexChanged, isCarouselPlaying, stopAutoplay, isAutoPlayEnabled, viewport, enablePeeking, pan, heroPan, enableHero]);
646
+ }, [handleAnimationStart, triggerRefocus, slideDuration, updateOffset, animate, totalItems, onIndexChanged, isCarouselPlaying, stopAutoplay, isAutoPlayEnabled, viewport, enablePeeking, pan, heroPan, enableHero, maxWidth]);
597
647
  const startAutoplay = React.useCallback(() => {
598
648
  stopAutoplay();
599
649
  if (isAutoPlayEnabled) {
@@ -628,6 +678,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
628
678
  React.useEffect(() => {
629
679
  heroContainerLayoutRef.current = heroContainerLayout;
630
680
  }, [heroContainerLayout]);
681
+ React.useEffect(() => {
682
+ rootContainerLayoutRef.current = rootContainerLayout;
683
+ }, [rootContainerLayout]);
631
684
  React.useEffect(() => {
632
685
  pan.x.addListener(_ref5 => {
633
686
  let {
@@ -736,6 +789,25 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
736
789
  } = _ref11;
737
790
  return setPreviousNextNavigationButtonWidth(width);
738
791
  };
792
+ const onRootContainerLayout = _ref12 => {
793
+ let {
794
+ nativeEvent: {
795
+ layout: {
796
+ x,
797
+ y,
798
+ width,
799
+ height
800
+ }
801
+ }
802
+ } = _ref12;
803
+ return setRootContainerLayout(prevState => ({
804
+ ...prevState,
805
+ x,
806
+ y,
807
+ width,
808
+ height
809
+ }));
810
+ };
739
811
  const isSwipeAllowed = React.useCallback(() => {
740
812
  if (totalItems === 1) {
741
813
  return false;
@@ -773,16 +845,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
773
845
  startAutoplay();
774
846
  }
775
847
  const correction = gesture.moveX - gesture.x0;
848
+ isSwiping.current = false;
776
849
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
777
850
  animate(pan, {
778
851
  x: 0,
779
852
  y: 0
780
- }, 0);
853
+ }, activeIndexRef.current, true);
781
854
  } else {
782
855
  const delta = correction > 0 ? -1 : 1;
783
856
  updateIndex(delta, TRANSITION_MODES.SWIPE);
784
857
  }
785
- isSwiping.current = false;
786
858
  }
787
859
  }), [pan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
788
860
  const heroPanResponder = React.useMemo(() => PanResponder.create({
@@ -813,16 +885,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
813
885
  startAutoplay();
814
886
  }
815
887
  const correction = gesture.moveX - gesture.x0;
888
+ isSwiping.current = false;
816
889
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
817
890
  animate(heroPan, {
818
891
  x: 0,
819
892
  y: 0
820
- }, 0);
893
+ }, activeIndexRef.current, true);
821
894
  } else {
822
895
  const delta = correction > 0 ? -1 : 1;
823
896
  updateIndex(delta, TRANSITION_MODES.SWIPE);
824
897
  }
825
- isSwiping.current = false;
826
898
  }
827
899
  }), [heroPan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
828
900
  const goToNext = React.useCallback(() => {
@@ -901,9 +973,10 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
901
973
  }
902
974
  }, [isTransitioningRef]);
903
975
  return /*#__PURE__*/_jsxs(View, {
904
- style: selectRootContainerStyles(enableHero, viewport),
976
+ style: selectRootContainerStyles(enableHero, viewport, enablePeeking),
977
+ onLayout: onRootContainerLayout,
905
978
  children: [/*#__PURE__*/_jsx(View, {
906
- style: selectMainContainerStyles(enableHero, viewport, maxWidth),
979
+ style: selectMainContainerStyles(enableHero, viewport, maxWidth, enablePeeking),
907
980
  children: /*#__PURE__*/_jsxs(CarouselProvider, {
908
981
  activeIndex: activeIndex,
909
982
  goTo: goTo,
@@ -915,6 +988,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
915
988
  refocus: refocus,
916
989
  width: containerLayout.width,
917
990
  maximumItemsForSlide: getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport),
991
+ maxWidth: maxWidth,
992
+ viewportWidth: currentViewportWidth,
918
993
  children: [/*#__PURE__*/_jsxs(View, {
919
994
  style: [staticStyles.root, {
920
995
  ...(Platform.OS === 'web' ? {
@@ -937,7 +1012,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
937
1012
  enablePeeking,
938
1013
  enableDisplayMultipleItemsPerSlide,
939
1014
  isAutoPlayEnabled,
940
- viewport
1015
+ viewport,
1016
+ maxWidth,
1017
+ viewportWidth: currentViewportWidth
941
1018
  })],
942
1019
  children: /*#__PURE__*/_jsx(IconButton, {
943
1020
  icon: isCarouselPlaying ? pauseIcon : playIcon,
@@ -946,7 +1023,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
946
1023
  onPress: onAnimationControlButtonPress
947
1024
  })
948
1025
  }) : null, showPreviousNextNavigation && totalItems > 1 ? /*#__PURE__*/_jsx(View, {
949
- style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, true, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport),
1026
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, true, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, currentViewportWidth),
950
1027
  testID: "previous-button-container",
951
1028
  children: /*#__PURE__*/_jsx(IconButton, {
952
1029
  onLayout: onPreviousNextNavigationButtonLayout,
@@ -984,16 +1061,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
984
1061
  children: childrenArray.map((element, index) => {
985
1062
  let hidden = !isAnimating && index !== activeIndex;
986
1063
  if (enablePeeking && !isAnimating) {
987
- if (enableDisplayMultipleItemsPerSlide) {
988
- const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
989
- if (index >= activeIndex * maxItemsForSlide - 1 && index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1) {
990
- hidden = false;
991
- } else {
992
- hidden = true;
993
- }
994
- } else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
995
- hidden = false;
996
- }
1064
+ hidden = false;
997
1065
  } else if (!enablePeeking && enableDisplayMultipleItemsPerSlide && !isAnimating) {
998
1066
  const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
999
1067
  if (index >= activeIndex * maxItemsForSlide && index < activeIndex * maxItemsForSlide + maxItemsForSlide) {
@@ -1015,7 +1083,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
1015
1083
  })
1016
1084
  })
1017
1085
  }), showPreviousNextNavigation && totalItems > 1 ? /*#__PURE__*/_jsx(View, {
1018
- style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, false, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport),
1086
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, false, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, currentViewportWidth),
1019
1087
  testID: "next-button-container",
1020
1088
  children: /*#__PURE__*/_jsx(IconButton, {
1021
1089
  onLayout: onPreviousNextNavigationButtonLayout,
@@ -1027,7 +1095,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
1027
1095
  })
1028
1096
  }) : null]
1029
1097
  }), /*#__PURE__*/_jsx(View, {
1030
- style: selectNavigationStyles(tabs, enableHero, viewport),
1098
+ style: selectNavigationStyles(tabs, enableHero, viewport, enablePeeking, maxWidth),
1031
1099
  children: showPanelNavigation ? activePanelNavigation : null
1032
1100
  })]
1033
1101
  })
@@ -1278,7 +1346,23 @@ Carousel.propTypes = {
1278
1346
  md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1279
1347
  sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1280
1348
  xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
1281
- })
1349
+ }),
1350
+ /**
1351
+ * Animation style for swipe release transitions.
1352
+ * - `'instant'`: Immediate snap to position (current default)
1353
+ * - `'ease-out'`: Smooth deceleration animation
1354
+ * - Default value is `'instant'`
1355
+ * - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
1356
+ *
1357
+ * @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
1358
+ */
1359
+ swipeReleaseStyle: PropTypes.oneOf(['instant', 'ease-out']),
1360
+ /**
1361
+ * Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
1362
+ * Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
1363
+ * - Default value is `500` (500ms)
1364
+ */
1365
+ swipeReleaseDuration: PropTypes.number
1282
1366
  };
1283
1367
  Carousel.Item = CarouselItem;
1284
1368
  Carousel.displayName = 'Carousel';
@@ -14,7 +14,9 @@ const CarouselProvider = _ref => {
14
14
  themeTokens,
15
15
  totalItems,
16
16
  width,
17
- maximumItemsForSlide
17
+ maximumItemsForSlide,
18
+ maxWidth,
19
+ viewportWidth
18
20
  } = _ref;
19
21
  const value = React.useMemo(() => ({
20
22
  activeIndex,
@@ -25,8 +27,10 @@ const CarouselProvider = _ref => {
25
27
  themeTokens,
26
28
  totalItems,
27
29
  width,
28
- maximumItemsForSlide
29
- }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width, maximumItemsForSlide]);
30
+ maximumItemsForSlide,
31
+ maxWidth,
32
+ viewportWidth
33
+ }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width, maximumItemsForSlide, maxWidth, viewportWidth]);
30
34
  return /*#__PURE__*/_jsx(CarouselContext.Provider, {
31
35
  value: value,
32
36
  children: children
@@ -49,6 +53,8 @@ CarouselProvider.propTypes = {
49
53
  themeTokens: getTokensPropType('Carousel'),
50
54
  totalItems: PropTypes.number.isRequired,
51
55
  width: PropTypes.number.isRequired,
52
- maximumItemsForSlide: PropTypes.number
56
+ maximumItemsForSlide: PropTypes.number,
57
+ maxWidth: PropTypes.number,
58
+ viewportWidth: PropTypes.number
53
59
  };
54
60
  export { CarouselProvider, useCarousel };
@@ -12,26 +12,26 @@ const selectContainerStyle = _ref => {
12
12
  width,
13
13
  elementIndex,
14
14
  enablePeeking,
15
- peekingMarginLeft,
16
15
  peekingGap,
17
16
  hidden,
18
17
  enableDisplayMultipleItemsPerSlide,
19
18
  viewport,
20
- peekingMiddleSpace
19
+ peekingMiddleSpace,
20
+ maxWidth,
21
+ viewportWidth
21
22
  } = _ref;
22
23
  let adjustedWidth = width;
23
24
  let marginLeft = 0;
24
25
  if (enablePeeking) {
25
26
  const isFirst = elementIndex === 0;
26
- adjustedWidth = width - (peekingMiddleSpace * 2 + peekingGap * 2);
27
+ const baseWidth = maxWidth || width;
28
+ adjustedWidth = baseWidth - peekingMiddleSpace * 2;
27
29
  if (isFirst) {
28
- marginLeft = peekingMarginLeft;
30
+ marginLeft = peekingMiddleSpace + (viewportWidth - maxWidth) / 2;
29
31
  } else {
30
32
  marginLeft = peekingGap;
31
33
  }
32
34
  }
33
-
34
- // Adjust width and margins for multiple items per slide.
35
35
  if (enableDisplayMultipleItemsPerSlide) {
36
36
  switch (viewport) {
37
37
  case 'xs':
@@ -89,7 +89,9 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
89
89
  width,
90
90
  activeIndex,
91
91
  goTo,
92
- maximumItemsForSlide
92
+ maximumItemsForSlide,
93
+ maxWidth,
94
+ viewportWidth
93
95
  } = useCarousel();
94
96
  const selectedProps = selectProps({
95
97
  ...rest,
@@ -132,6 +134,8 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
132
134
  enablePeeking,
133
135
  enableDisplayMultipleItemsPerSlide,
134
136
  viewport,
137
+ maxWidth,
138
+ viewportWidth,
135
139
  ...peekingProps
136
140
  }),
137
141
  ...selectedProps,
@@ -8,4 +8,24 @@ export const LARGE_VIEWPORT_MARGIN = 40;
8
8
  export const DEFAULT_VIEWPORT_MARGIN = 10;
9
9
  export const PEEKING_MULTIPLIER = 2;
10
10
  export const ACTIVE_INDEX_OFFSET_MULTIPLIER = 1;
11
- export const NEGATIVE_MULTIPLIER = -1;
11
+ export const NEGATIVE_MULTIPLIER = -1;
12
+ export const TRANSITION_MODES = {
13
+ MANUAL: 'manual',
14
+ AUTOMATIC: 'automatic',
15
+ SWIPE: 'swipe'
16
+ };
17
+ export const SWIPE_RELEASE_STYLES = {
18
+ INSTANT: 'instant',
19
+ EASE_OUT: 'ease-out'
20
+ };
21
+ export const INSTANT_ANIMATION_DURATION = 1;
22
+ export const DEFAULT_SWIPE_RELEASE_DURATION = 500;
23
+ export const POSITION_VARIANTS = {
24
+ EDGE: 'edge',
25
+ INSIDE: 'inside',
26
+ OUTSIDE: 'outside'
27
+ };
28
+ export const POSITION_PROPERTIES = {
29
+ LEFT: 'left',
30
+ RIGHT: 'right'
31
+ };