@telus-uds/components-base 3.25.0 → 3.26.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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,19 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Mon, 12 Jan 2026 14:55:21 GMT and should not be manually modified.
3
+ This log was last generated on Mon, 19 Jan 2026 20:39:50 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 3.26.0
8
+
9
+ Mon, 19 Jan 2026 20:39:50 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - `PriceLockup`: Add inverse variant (david.melara1@telus.com)
14
+ - `Carousel`: add new swipe animation (sergio.ramirez@telus.com)
15
+ - `TextInput`: Add ARIA feedback ID for improved accessibility (david.melara1@telus.com)
16
+
7
17
  ## 3.25.0
8
18
 
9
19
  Mon, 12 Jan 2026 14:55:21 GMT
@@ -11,6 +11,7 @@ var _PanResponder = _interopRequireDefault(require("react-native-web/dist/cjs/ex
11
11
  var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
12
12
  var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
13
13
  var _Dimensions = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Dimensions"));
14
+ var _Easing = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Easing"));
14
15
  var _propTypes = _interopRequireDefault(require("prop-types"));
15
16
  var _ThemeProvider = require("../ThemeProvider");
16
17
  var _ViewportProvider = require("../ViewportProvider");
@@ -30,11 +31,6 @@ var _Box = _interopRequireDefault(require("../Box"));
30
31
  var _Constants = require("./Constants");
31
32
  var _jsxRuntime = require("react/jsx-runtime");
32
33
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
33
- const TRANSITION_MODES = {
34
- MANUAL: 'manual',
35
- AUTOMATIC: 'automatic',
36
- SWIPE: 'swipe'
37
- };
38
34
  const staticStyles = _StyleSheet.default.create({
39
35
  root: {
40
36
  backgroundColor: 'transparent',
@@ -369,6 +365,8 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
369
365
  autoPlay = false,
370
366
  enablePeeking = false,
371
367
  contentMaxWidth,
368
+ swipeReleaseStyle = _Constants.SWIPE_RELEASE_STYLES.INSTANT,
369
+ swipeReleaseDuration = _Constants.DEFAULT_SWIPE_RELEASE_DURATION,
372
370
  ...rest
373
371
  } = _ref3;
374
372
  let childrenArray = (0, _utils.unpackFragment)(children);
@@ -497,7 +495,8 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
497
495
  });
498
496
  }
499
497
  }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
500
- const animate = _react.default.useCallback((panToAnimate, toValue, toIndex) => {
498
+ const animate = _react.default.useCallback(function (panToAnimate, toValue, toIndex) {
499
+ let isSwipeRelease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
501
500
  const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
502
501
  const handleAnimationEndToIndex = function () {
503
502
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -505,10 +504,23 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
505
504
  }
506
505
  return handleAnimationEnd(toIndex, ...args);
507
506
  };
508
- if (reduceMotionRef.current || isSwiping.current) {
507
+ if (reduceMotionRef.current) {
508
+ _Animated.default.timing(panToAnimate, {
509
+ toValue,
510
+ duration: _Constants.INSTANT_ANIMATION_DURATION,
511
+ useNativeDriver: false
512
+ }).start(handleAnimationEndToIndex);
513
+ } else if (isSwipeRelease && swipeReleaseStyle === _Constants.SWIPE_RELEASE_STYLES.EASE_OUT) {
509
514
  _Animated.default.timing(panToAnimate, {
510
515
  toValue,
511
- duration: 1,
516
+ duration: swipeReleaseDuration,
517
+ easing: _Easing.default.out(_Easing.default.cubic),
518
+ useNativeDriver: false
519
+ }).start(handleAnimationEndToIndex);
520
+ } else if (isSwiping.current || isSwipeRelease) {
521
+ _Animated.default.timing(panToAnimate, {
522
+ toValue,
523
+ duration: _Constants.INSTANT_ANIMATION_DURATION,
512
524
  useNativeDriver: false
513
525
  }).start(handleAnimationEndToIndex);
514
526
  } else if (isAutoPlayEnabled) {
@@ -532,7 +544,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
532
544
  useNativeDriver: false
533
545
  }).start(handleAnimationEndToIndex);
534
546
  }
535
- }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
547
+ }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide, swipeReleaseStyle, swipeReleaseDuration]);
536
548
  const stopAutoplay = _react.default.useCallback(() => {
537
549
  if (autoPlayRef?.current) {
538
550
  clearTimeout(autoPlayRef?.current);
@@ -555,19 +567,20 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
555
567
  };
556
568
  let skipChanges = !delta;
557
569
  let calcDelta = delta;
570
+ const isSwipeRelease = transitionMode === _Constants.TRANSITION_MODES.SWIPE;
558
571
  if (activeIndexRef.current <= 0 && delta < 0) {
559
- skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC;
572
+ skipChanges = transitionMode !== _Constants.TRANSITION_MODES.AUTOMATIC;
560
573
  calcDelta = totalItems + delta;
561
574
  } else if (activeIndexRef.current + 1 >= totalItems && delta > 0) {
562
- skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC;
575
+ skipChanges = transitionMode !== _Constants.TRANSITION_MODES.AUTOMATIC;
563
576
  calcDelta = -1 * activeIndexRef.current + delta - 1;
564
577
  }
565
578
  const index = activeIndexRef.current + calcDelta;
566
579
  if (skipChanges) {
567
580
  isTransitioningRef.current = true;
568
- animate(pan, toValue, index);
581
+ animate(pan, toValue, index, isSwipeRelease);
569
582
  if (enableHero) {
570
- animate(heroPan, toValue, index);
583
+ animate(heroPan, toValue, index, isSwipeRelease);
571
584
  }
572
585
  return calcDelta;
573
586
  }
@@ -581,19 +594,19 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
581
594
  };
582
595
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta;
583
596
  isTransitioningRef.current = true;
584
- animate(pan, toValue, index);
597
+ animate(pan, toValue, index, isSwipeRelease);
585
598
  if (enableHero) {
586
- animate(heroPan, heroToValue, index);
599
+ animate(heroPan, heroToValue, index, isSwipeRelease);
587
600
  }
588
601
  if (isCarouselPlaying) {
589
602
  stopAutoplay();
590
- if (index === 0 && activeIndexRef.current + 1 === totalItems && transitionMode === TRANSITION_MODES.AUTOMATIC) {
603
+ if (index === 0 && activeIndexRef.current + 1 === totalItems && transitionMode === _Constants.TRANSITION_MODES.AUTOMATIC) {
591
604
  setisCarouselPlaying(false);
592
605
  } else if (isAutoPlayEnabled) {
593
606
  autoPlayRef.current = setTimeout(() => {
594
607
  updateOffset();
595
608
  handleAnimationStart(activeIndexRef.current);
596
- updateIndex(slideDuration < 0 ? -1 : 1, TRANSITION_MODES.AUTOMATIC);
609
+ updateIndex(slideDuration < 0 ? -1 : 1, _Constants.TRANSITION_MODES.AUTOMATIC);
597
610
  triggerRefocus();
598
611
  }, Math.abs(slideDuration) * 1000);
599
612
  }
@@ -607,7 +620,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
607
620
  autoPlayRef.current = setTimeout(() => {
608
621
  updateOffset();
609
622
  handleAnimationStart(activeIndexRef.current);
610
- updateIndex(slideDuration < 0 ? -1 : 1, TRANSITION_MODES.AUTOMATIC);
623
+ updateIndex(slideDuration < 0 ? -1 : 1, _Constants.TRANSITION_MODES.AUTOMATIC);
611
624
  triggerRefocus();
612
625
  }, Math.abs(slideDuration) * 1000);
613
626
  }
@@ -620,7 +633,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
620
633
  }, [updateIndex, updateOffset, handleAnimationStart, triggerRefocus]);
621
634
  const goToNeighboring = _react.default.useCallback(function () {
622
635
  let toPrev = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
623
- let transitionMode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TRANSITION_MODES.MANUAL;
636
+ let transitionMode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _Constants.TRANSITION_MODES.MANUAL;
624
637
  fixOffsetAndGo(toPrev ? -1 : 1, transitionMode);
625
638
  }, [fixOffsetAndGo]);
626
639
  _react.default.useEffect(() => {
@@ -780,16 +793,16 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
780
793
  startAutoplay();
781
794
  }
782
795
  const correction = gesture.moveX - gesture.x0;
796
+ isSwiping.current = false;
783
797
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
784
798
  animate(pan, {
785
799
  x: 0,
786
800
  y: 0
787
- }, 0);
801
+ }, activeIndexRef.current, true);
788
802
  } else {
789
803
  const delta = correction > 0 ? -1 : 1;
790
- updateIndex(delta, TRANSITION_MODES.SWIPE);
804
+ updateIndex(delta, _Constants.TRANSITION_MODES.SWIPE);
791
805
  }
792
- isSwiping.current = false;
793
806
  }
794
807
  }), [pan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
795
808
  const heroPanResponder = _react.default.useMemo(() => _PanResponder.default.create({
@@ -820,16 +833,16 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
820
833
  startAutoplay();
821
834
  }
822
835
  const correction = gesture.moveX - gesture.x0;
836
+ isSwiping.current = false;
823
837
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
824
838
  animate(heroPan, {
825
839
  x: 0,
826
840
  y: 0
827
- }, 0);
841
+ }, activeIndexRef.current, true);
828
842
  } else {
829
843
  const delta = correction > 0 ? -1 : 1;
830
- updateIndex(delta, TRANSITION_MODES.SWIPE);
844
+ updateIndex(delta, _Constants.TRANSITION_MODES.SWIPE);
831
845
  }
832
- isSwiping.current = false;
833
846
  }
834
847
  }), [heroPan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
835
848
  const goToNext = _react.default.useCallback(() => {
@@ -842,7 +855,7 @@ const Carousel = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
842
855
  let index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
843
856
  const delta = index - activeIndexRef.current;
844
857
  if (delta) {
845
- fixOffsetAndGo(delta, TRANSITION_MODES.MANUAL);
858
+ fixOffsetAndGo(delta, _Constants.TRANSITION_MODES.MANUAL);
846
859
  }
847
860
  }, [fixOffsetAndGo]);
848
861
 
@@ -1285,7 +1298,23 @@ Carousel.propTypes = {
1285
1298
  md: _propTypes.default.oneOfType([_propTypes.default.oneOf(['max', 'full']), _propTypes.default.number]),
1286
1299
  sm: _propTypes.default.oneOfType([_propTypes.default.oneOf(['max', 'full']), _propTypes.default.number]),
1287
1300
  xs: _propTypes.default.oneOfType([_propTypes.default.oneOf(['max', 'full']), _propTypes.default.number])
1288
- })
1301
+ }),
1302
+ /**
1303
+ * Animation style for swipe release transitions.
1304
+ * - `'instant'`: Immediate snap to position (current default)
1305
+ * - `'ease-out'`: Smooth deceleration animation
1306
+ * - Default value is `'instant'`
1307
+ * - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
1308
+ *
1309
+ * @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
1310
+ */
1311
+ swipeReleaseStyle: _propTypes.default.oneOf(['instant', 'ease-out']),
1312
+ /**
1313
+ * Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
1314
+ * Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
1315
+ * - Default value is `500` (500ms)
1316
+ */
1317
+ swipeReleaseDuration: _propTypes.default.number
1289
1318
  };
1290
1319
  Carousel.Item = _CarouselItem.default;
1291
1320
  Carousel.displayName = 'Carousel';
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.PEEKING_MULTIPLIER = exports.NEGATIVE_MULTIPLIER = exports.LARGE_VIEWPORT_MARGIN = exports.ITEMS_PER_VIEWPORT_XS_SM = exports.ITEMS_PER_VIEWPORT_MD = exports.ITEMS_PER_VIEWPORT_LG_XL = exports.HERO_POSITION_OFFSET = exports.GAP_BETWEEN_ITEMS = exports.DEFAULT_VIEWPORT_MARGIN = exports.DEFAULT_POSITION_OFFSET = exports.ACTIVE_INDEX_OFFSET_MULTIPLIER = void 0;
6
+ exports.TRANSITION_MODES = exports.SWIPE_RELEASE_STYLES = exports.PEEKING_MULTIPLIER = exports.NEGATIVE_MULTIPLIER = exports.LARGE_VIEWPORT_MARGIN = exports.ITEMS_PER_VIEWPORT_XS_SM = exports.ITEMS_PER_VIEWPORT_MD = exports.ITEMS_PER_VIEWPORT_LG_XL = exports.INSTANT_ANIMATION_DURATION = exports.HERO_POSITION_OFFSET = exports.GAP_BETWEEN_ITEMS = exports.DEFAULT_VIEWPORT_MARGIN = exports.DEFAULT_SWIPE_RELEASE_DURATION = exports.DEFAULT_POSITION_OFFSET = exports.ACTIVE_INDEX_OFFSET_MULTIPLIER = void 0;
7
7
  const ITEMS_PER_VIEWPORT_XS_SM = exports.ITEMS_PER_VIEWPORT_XS_SM = 1;
8
8
  const ITEMS_PER_VIEWPORT_MD = exports.ITEMS_PER_VIEWPORT_MD = 2;
9
9
  const ITEMS_PER_VIEWPORT_LG_XL = exports.ITEMS_PER_VIEWPORT_LG_XL = 3;
@@ -14,4 +14,15 @@ const LARGE_VIEWPORT_MARGIN = exports.LARGE_VIEWPORT_MARGIN = 40;
14
14
  const DEFAULT_VIEWPORT_MARGIN = exports.DEFAULT_VIEWPORT_MARGIN = 10;
15
15
  const PEEKING_MULTIPLIER = exports.PEEKING_MULTIPLIER = 2;
16
16
  const ACTIVE_INDEX_OFFSET_MULTIPLIER = exports.ACTIVE_INDEX_OFFSET_MULTIPLIER = 1;
17
- const NEGATIVE_MULTIPLIER = exports.NEGATIVE_MULTIPLIER = -1;
17
+ const NEGATIVE_MULTIPLIER = exports.NEGATIVE_MULTIPLIER = -1;
18
+ const TRANSITION_MODES = exports.TRANSITION_MODES = {
19
+ MANUAL: 'manual',
20
+ AUTOMATIC: 'automatic',
21
+ SWIPE: 'swipe'
22
+ };
23
+ const SWIPE_RELEASE_STYLES = exports.SWIPE_RELEASE_STYLES = {
24
+ INSTANT: 'instant',
25
+ EASE_OUT: 'ease-out'
26
+ };
27
+ const INSTANT_ANIMATION_DURATION = exports.INSTANT_ANIMATION_DURATION = 1;
28
+ const DEFAULT_SWIPE_RELEASE_DURATION = exports.DEFAULT_SWIPE_RELEASE_DURATION = 500;
@@ -72,7 +72,8 @@ const InputSupports = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
72
72
  }), typeof children === 'function' ? children({
73
73
  inputId,
74
74
  ...a11yProps,
75
- validation: feedbackValidation
75
+ validation: feedbackValidation,
76
+ accessibilityDescribedBy: feedbackId
76
77
  }) : children, feedback || maxCharsReachedErrorMessage ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Feedback.default, {
77
78
  id: feedbackId,
78
79
  title: feedback || maxCharsReachedErrorMessage,
@@ -149,7 +149,7 @@ const PriceLockup = /*#__PURE__*/_react.default.forwardRef((_ref8, ref) => {
149
149
  ...selectProps(rest),
150
150
  children: [topText ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
151
151
  style: staticStyles.topText,
152
- children: (0, _renderTypography.default)(topText, topTextTypographyTokens)
152
+ children: (0, _renderTypography.default)(topText, topTextTypographyTokens, undefined, fontColor)
153
153
  }) : null, (0, _renderPrice.default)(price, rateText, ratePosition, signDirection, currencySymbol, currencySymbolTypographyTokens, amountTypographyTokens, centsTypographyTokens, rateTypographyTokens, fontColor, strikeThrough, a11yText, bottomText, bottomLinksMarginLeft, footnoteLinks, onClickFootnote, themeTokens, isPriceBaseline), bottomText ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
154
154
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Divider.default, {
155
155
  testID: "price-lockup-divider",
@@ -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, ACTIVE_INDEX_OFFSET_MULTIPLIER, NEGATIVE_MULTIPLIER, TRANSITION_MODES, SWIPE_RELEASE_STYLES, INSTANT_ANIMATION_DURATION, DEFAULT_SWIPE_RELEASE_DURATION } 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',
@@ -362,6 +358,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
362
358
  autoPlay = false,
363
359
  enablePeeking = false,
364
360
  contentMaxWidth,
361
+ swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
362
+ swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
365
363
  ...rest
366
364
  } = _ref3;
367
365
  let childrenArray = unpackFragment(children);
@@ -490,7 +488,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
490
488
  });
491
489
  }
492
490
  }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
493
- const animate = React.useCallback((panToAnimate, toValue, toIndex) => {
491
+ const animate = React.useCallback(function (panToAnimate, toValue, toIndex) {
492
+ let isSwipeRelease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
494
493
  const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
495
494
  const handleAnimationEndToIndex = function () {
496
495
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -498,10 +497,23 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
498
497
  }
499
498
  return handleAnimationEnd(toIndex, ...args);
500
499
  };
501
- if (reduceMotionRef.current || isSwiping.current) {
500
+ if (reduceMotionRef.current) {
501
+ Animated.timing(panToAnimate, {
502
+ toValue,
503
+ duration: INSTANT_ANIMATION_DURATION,
504
+ useNativeDriver: false
505
+ }).start(handleAnimationEndToIndex);
506
+ } else if (isSwipeRelease && swipeReleaseStyle === SWIPE_RELEASE_STYLES.EASE_OUT) {
502
507
  Animated.timing(panToAnimate, {
503
508
  toValue,
504
- duration: 1,
509
+ duration: swipeReleaseDuration,
510
+ easing: Easing.out(Easing.cubic),
511
+ useNativeDriver: false
512
+ }).start(handleAnimationEndToIndex);
513
+ } else if (isSwiping.current || isSwipeRelease) {
514
+ Animated.timing(panToAnimate, {
515
+ toValue,
516
+ duration: INSTANT_ANIMATION_DURATION,
505
517
  useNativeDriver: false
506
518
  }).start(handleAnimationEndToIndex);
507
519
  } else if (isAutoPlayEnabled) {
@@ -525,7 +537,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
525
537
  useNativeDriver: false
526
538
  }).start(handleAnimationEndToIndex);
527
539
  }
528
- }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
540
+ }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide, swipeReleaseStyle, swipeReleaseDuration]);
529
541
  const stopAutoplay = React.useCallback(() => {
530
542
  if (autoPlayRef?.current) {
531
543
  clearTimeout(autoPlayRef?.current);
@@ -548,6 +560,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
548
560
  };
549
561
  let skipChanges = !delta;
550
562
  let calcDelta = delta;
563
+ const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE;
551
564
  if (activeIndexRef.current <= 0 && delta < 0) {
552
565
  skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC;
553
566
  calcDelta = totalItems + delta;
@@ -558,9 +571,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
558
571
  const index = activeIndexRef.current + calcDelta;
559
572
  if (skipChanges) {
560
573
  isTransitioningRef.current = true;
561
- animate(pan, toValue, index);
574
+ animate(pan, toValue, index, isSwipeRelease);
562
575
  if (enableHero) {
563
- animate(heroPan, toValue, index);
576
+ animate(heroPan, toValue, index, isSwipeRelease);
564
577
  }
565
578
  return calcDelta;
566
579
  }
@@ -574,9 +587,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
574
587
  };
575
588
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta;
576
589
  isTransitioningRef.current = true;
577
- animate(pan, toValue, index);
590
+ animate(pan, toValue, index, isSwipeRelease);
578
591
  if (enableHero) {
579
- animate(heroPan, heroToValue, index);
592
+ animate(heroPan, heroToValue, index, isSwipeRelease);
580
593
  }
581
594
  if (isCarouselPlaying) {
582
595
  stopAutoplay();
@@ -773,16 +786,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
773
786
  startAutoplay();
774
787
  }
775
788
  const correction = gesture.moveX - gesture.x0;
789
+ isSwiping.current = false;
776
790
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
777
791
  animate(pan, {
778
792
  x: 0,
779
793
  y: 0
780
- }, 0);
794
+ }, activeIndexRef.current, true);
781
795
  } else {
782
796
  const delta = correction > 0 ? -1 : 1;
783
797
  updateIndex(delta, TRANSITION_MODES.SWIPE);
784
798
  }
785
- isSwiping.current = false;
786
799
  }
787
800
  }), [pan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
788
801
  const heroPanResponder = React.useMemo(() => PanResponder.create({
@@ -813,16 +826,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
813
826
  startAutoplay();
814
827
  }
815
828
  const correction = gesture.moveX - gesture.x0;
829
+ isSwiping.current = false;
816
830
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
817
831
  animate(heroPan, {
818
832
  x: 0,
819
833
  y: 0
820
- }, 0);
834
+ }, activeIndexRef.current, true);
821
835
  } else {
822
836
  const delta = correction > 0 ? -1 : 1;
823
837
  updateIndex(delta, TRANSITION_MODES.SWIPE);
824
838
  }
825
- isSwiping.current = false;
826
839
  }
827
840
  }), [heroPan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
828
841
  const goToNext = React.useCallback(() => {
@@ -1278,7 +1291,23 @@ Carousel.propTypes = {
1278
1291
  md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1279
1292
  sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1280
1293
  xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
1281
- })
1294
+ }),
1295
+ /**
1296
+ * Animation style for swipe release transitions.
1297
+ * - `'instant'`: Immediate snap to position (current default)
1298
+ * - `'ease-out'`: Smooth deceleration animation
1299
+ * - Default value is `'instant'`
1300
+ * - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
1301
+ *
1302
+ * @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
1303
+ */
1304
+ swipeReleaseStyle: PropTypes.oneOf(['instant', 'ease-out']),
1305
+ /**
1306
+ * Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
1307
+ * Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
1308
+ * - Default value is `500` (500ms)
1309
+ */
1310
+ swipeReleaseDuration: PropTypes.number
1282
1311
  };
1283
1312
  Carousel.Item = CarouselItem;
1284
1313
  Carousel.displayName = 'Carousel';
@@ -8,4 +8,15 @@ 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;
@@ -65,7 +65,8 @@ const InputSupports = /*#__PURE__*/React.forwardRef((_ref, ref) => {
65
65
  }), typeof children === 'function' ? children({
66
66
  inputId,
67
67
  ...a11yProps,
68
- validation: feedbackValidation
68
+ validation: feedbackValidation,
69
+ accessibilityDescribedBy: feedbackId
69
70
  }) : children, feedback || maxCharsReachedErrorMessage ? /*#__PURE__*/_jsx(Feedback, {
70
71
  id: feedbackId,
71
72
  title: feedback || maxCharsReachedErrorMessage,
@@ -142,7 +142,7 @@ const PriceLockup = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
142
142
  ...selectProps(rest),
143
143
  children: [topText ? /*#__PURE__*/_jsx(View, {
144
144
  style: staticStyles.topText,
145
- children: renderTypography(topText, topTextTypographyTokens)
145
+ children: renderTypography(topText, topTextTypographyTokens, undefined, fontColor)
146
146
  }) : null, renderPrice(price, rateText, ratePosition, signDirection, currencySymbol, currencySymbolTypographyTokens, amountTypographyTokens, centsTypographyTokens, rateTypographyTokens, fontColor, strikeThrough, a11yText, bottomText, bottomLinksMarginLeft, footnoteLinks, onClickFootnote, themeTokens, isPriceBaseline), bottomText ? /*#__PURE__*/_jsxs(_Fragment, {
147
147
  children: [/*#__PURE__*/_jsx(Divider, {
148
148
  testID: "price-lockup-divider",
package/lib/package.json CHANGED
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.25.0",
87
+ "version": "3.26.0",
88
88
  "types": "types/index.d.ts"
89
89
  }
package/package.json CHANGED
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.25.0",
87
+ "version": "3.26.0",
88
88
  "types": "types/index.d.ts"
89
89
  }
@@ -1,5 +1,13 @@
1
1
  import React from 'react'
2
- import { View, Animated, PanResponder, StyleSheet, Platform, Dimensions } from 'react-native'
2
+ import {
3
+ View,
4
+ Animated,
5
+ PanResponder,
6
+ StyleSheet,
7
+ Platform,
8
+ Dimensions,
9
+ Easing
10
+ } from 'react-native'
3
11
  import PropTypes from 'prop-types'
4
12
  import { useThemeTokens, useTheme } from '../ThemeProvider'
5
13
  import { useViewport } from '../ViewportProvider'
@@ -38,15 +46,13 @@ import {
38
46
  DEFAULT_VIEWPORT_MARGIN,
39
47
  PEEKING_MULTIPLIER,
40
48
  ACTIVE_INDEX_OFFSET_MULTIPLIER,
41
- NEGATIVE_MULTIPLIER
49
+ NEGATIVE_MULTIPLIER,
50
+ TRANSITION_MODES,
51
+ SWIPE_RELEASE_STYLES,
52
+ INSTANT_ANIMATION_DURATION,
53
+ DEFAULT_SWIPE_RELEASE_DURATION
42
54
  } from './Constants'
43
55
 
44
- const TRANSITION_MODES = {
45
- MANUAL: 'manual',
46
- AUTOMATIC: 'automatic',
47
- SWIPE: 'swipe'
48
- }
49
-
50
56
  const staticStyles = StyleSheet.create({
51
57
  root: {
52
58
  backgroundColor: 'transparent',
@@ -412,6 +418,8 @@ const Carousel = React.forwardRef(
412
418
  autoPlay = false,
413
419
  enablePeeking = false,
414
420
  contentMaxWidth,
421
+ swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
422
+ swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
415
423
  ...rest
416
424
  },
417
425
  ref
@@ -553,14 +561,29 @@ const Carousel = React.forwardRef(
553
561
  }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking])
554
562
 
555
563
  const animate = React.useCallback(
556
- (panToAnimate, toValue, toIndex) => {
564
+ (panToAnimate, toValue, toIndex, isSwipeRelease = false) => {
557
565
  const applicableTransitionDuration =
558
566
  isLastSlide && toIndex === 0 ? loopDuration : transitionDuration
559
567
  const handleAnimationEndToIndex = (...args) => handleAnimationEnd(toIndex, ...args)
560
- if (reduceMotionRef.current || isSwiping.current) {
561
- Animated.timing(panToAnimate, { toValue, duration: 1, useNativeDriver: false }).start(
562
- handleAnimationEndToIndex
563
- )
568
+ if (reduceMotionRef.current) {
569
+ Animated.timing(panToAnimate, {
570
+ toValue,
571
+ duration: INSTANT_ANIMATION_DURATION,
572
+ useNativeDriver: false
573
+ }).start(handleAnimationEndToIndex)
574
+ } else if (isSwipeRelease && swipeReleaseStyle === SWIPE_RELEASE_STYLES.EASE_OUT) {
575
+ Animated.timing(panToAnimate, {
576
+ toValue,
577
+ duration: swipeReleaseDuration,
578
+ easing: Easing.out(Easing.cubic),
579
+ useNativeDriver: false
580
+ }).start(handleAnimationEndToIndex)
581
+ } else if (isSwiping.current || isSwipeRelease) {
582
+ Animated.timing(panToAnimate, {
583
+ toValue,
584
+ duration: INSTANT_ANIMATION_DURATION,
585
+ useNativeDriver: false
586
+ }).start(handleAnimationEndToIndex)
564
587
  } else if (isAutoPlayEnabled) {
565
588
  Animated.timing(panToAnimate, {
566
589
  ...springConfig,
@@ -591,7 +614,9 @@ const Carousel = React.forwardRef(
591
614
  isLastSlide,
592
615
  isAutoPlayEnabled,
593
616
  enablePeeking,
594
- enableDisplayMultipleItemsPerSlide
617
+ enableDisplayMultipleItemsPerSlide,
618
+ swipeReleaseStyle,
619
+ swipeReleaseDuration
595
620
  ]
596
621
  )
597
622
 
@@ -612,6 +637,7 @@ const Carousel = React.forwardRef(
612
637
  const toValue = { x: 0, y: 0 }
613
638
  let skipChanges = !delta
614
639
  let calcDelta = delta
640
+ const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE
615
641
  if (activeIndexRef.current <= 0 && delta < 0) {
616
642
  skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC
617
643
  calcDelta = totalItems + delta
@@ -623,10 +649,10 @@ const Carousel = React.forwardRef(
623
649
  const index = activeIndexRef.current + calcDelta
624
650
  if (skipChanges) {
625
651
  isTransitioningRef.current = true
626
- animate(pan, toValue, index)
652
+ animate(pan, toValue, index, isSwipeRelease)
627
653
 
628
654
  if (enableHero) {
629
- animate(heroPan, toValue, index)
655
+ animate(heroPan, toValue, index, isSwipeRelease)
630
656
  }
631
657
  return calcDelta
632
658
  }
@@ -645,9 +671,9 @@ const Carousel = React.forwardRef(
645
671
  const heroToValue = { x: 0, y: 0 }
646
672
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta
647
673
  isTransitioningRef.current = true
648
- animate(pan, toValue, index)
674
+ animate(pan, toValue, index, isSwipeRelease)
649
675
  if (enableHero) {
650
- animate(heroPan, heroToValue, index)
676
+ animate(heroPan, heroToValue, index, isSwipeRelease)
651
677
  }
652
678
  if (isCarouselPlaying) {
653
679
  stopAutoplay()
@@ -866,14 +892,14 @@ const Carousel = React.forwardRef(
866
892
  }
867
893
  const correction = gesture.moveX - gesture.x0
868
894
 
895
+ isSwiping.current = false
896
+
869
897
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
870
- animate(pan, { x: 0, y: 0 }, 0)
898
+ animate(pan, { x: 0, y: 0 }, activeIndexRef.current, true)
871
899
  } else {
872
900
  const delta = correction > 0 ? -1 : 1
873
901
  updateIndex(delta, TRANSITION_MODES.SWIPE)
874
902
  }
875
-
876
- isSwiping.current = false
877
903
  }
878
904
  }),
879
905
  [
@@ -924,14 +950,14 @@ const Carousel = React.forwardRef(
924
950
  }
925
951
  const correction = gesture.moveX - gesture.x0
926
952
 
953
+ isSwiping.current = false
954
+
927
955
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
928
- animate(heroPan, { x: 0, y: 0 }, 0)
956
+ animate(heroPan, { x: 0, y: 0 }, activeIndexRef.current, true)
929
957
  } else {
930
958
  const delta = correction > 0 ? -1 : 1
931
959
  updateIndex(delta, TRANSITION_MODES.SWIPE)
932
960
  }
933
-
934
- isSwiping.current = false
935
961
  }
936
962
  }),
937
963
  [
@@ -1495,7 +1521,23 @@ Carousel.propTypes = {
1495
1521
  md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1496
1522
  sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1497
1523
  xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
1498
- })
1524
+ }),
1525
+ /**
1526
+ * Animation style for swipe release transitions.
1527
+ * - `'instant'`: Immediate snap to position (current default)
1528
+ * - `'ease-out'`: Smooth deceleration animation
1529
+ * - Default value is `'instant'`
1530
+ * - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
1531
+ *
1532
+ * @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
1533
+ */
1534
+ swipeReleaseStyle: PropTypes.oneOf(['instant', 'ease-out']),
1535
+ /**
1536
+ * Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
1537
+ * Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
1538
+ * - Default value is `500` (500ms)
1539
+ */
1540
+ swipeReleaseDuration: PropTypes.number
1499
1541
  }
1500
1542
 
1501
1543
  Carousel.Item = CarouselItem
@@ -9,3 +9,17 @@ export const DEFAULT_VIEWPORT_MARGIN = 10
9
9
  export const PEEKING_MULTIPLIER = 2
10
10
  export const ACTIVE_INDEX_OFFSET_MULTIPLIER = 1
11
11
  export const NEGATIVE_MULTIPLIER = -1
12
+
13
+ export const TRANSITION_MODES = {
14
+ MANUAL: 'manual',
15
+ AUTOMATIC: 'automatic',
16
+ SWIPE: 'swipe'
17
+ }
18
+
19
+ export const SWIPE_RELEASE_STYLES = {
20
+ INSTANT: 'instant',
21
+ EASE_OUT: 'ease-out'
22
+ }
23
+
24
+ export const INSTANT_ANIMATION_DURATION = 1
25
+ export const DEFAULT_SWIPE_RELEASE_DURATION = 500
@@ -66,7 +66,12 @@ const InputSupports = React.forwardRef(
66
66
  />
67
67
  )}
68
68
  {typeof children === 'function'
69
- ? children({ inputId, ...a11yProps, validation: feedbackValidation })
69
+ ? children({
70
+ inputId,
71
+ ...a11yProps,
72
+ validation: feedbackValidation,
73
+ accessibilityDescribedBy: feedbackId
74
+ })
70
75
  : children}
71
76
  {feedback || maxCharsReachedErrorMessage ? (
72
77
  <Feedback
@@ -121,7 +121,7 @@ const PriceLockup = React.forwardRef(
121
121
  >
122
122
  {topText ? (
123
123
  <View style={staticStyles.topText}>
124
- {renderTypography(topText, topTextTypographyTokens)}
124
+ {renderTypography(topText, topTextTypographyTokens, undefined, fontColor)}
125
125
  </View>
126
126
  ) : null}
127
127
  {renderPrice(