@telus-uds/components-base 3.24.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/lib/cjs/Button/ButtonGroup.js +9 -2
  3. package/lib/cjs/Carousel/Carousel.js +90 -30
  4. package/lib/cjs/Carousel/Constants.js +13 -2
  5. package/lib/cjs/FlexGrid/FlexGrid.js +31 -35
  6. package/lib/cjs/IconButton/IconButton.js +15 -5
  7. package/lib/cjs/InputSupports/InputSupports.js +2 -1
  8. package/lib/cjs/PriceLockup/PriceLockup.js +1 -1
  9. package/lib/cjs/TextInput/TextInputBase.js +2 -3
  10. package/lib/cjs/utils/index.js +9 -1
  11. package/lib/cjs/utils/resolveContentMaxWidth.js +30 -0
  12. package/lib/esm/Button/ButtonGroup.js +9 -2
  13. package/lib/esm/Carousel/Carousel.js +84 -24
  14. package/lib/esm/Carousel/Constants.js +12 -1
  15. package/lib/esm/FlexGrid/FlexGrid.js +31 -35
  16. package/lib/esm/IconButton/IconButton.js +15 -5
  17. package/lib/esm/InputSupports/InputSupports.js +2 -1
  18. package/lib/esm/PriceLockup/PriceLockup.js +1 -1
  19. package/lib/esm/TextInput/TextInputBase.js +2 -3
  20. package/lib/esm/utils/index.js +2 -1
  21. package/lib/esm/utils/resolveContentMaxWidth.js +24 -0
  22. package/lib/package.json +2 -2
  23. package/package.json +2 -2
  24. package/src/Button/ButtonGroup.jsx +20 -3
  25. package/src/Carousel/Carousel.jsx +104 -30
  26. package/src/Carousel/Constants.js +14 -0
  27. package/src/FlexGrid/FlexGrid.jsx +30 -39
  28. package/src/IconButton/IconButton.jsx +12 -5
  29. package/src/InputSupports/InputSupports.jsx +6 -1
  30. package/src/PriceLockup/PriceLockup.jsx +1 -1
  31. package/src/TextInput/TextInputBase.jsx +2 -2
  32. package/src/utils/index.js +1 -0
  33. package/src/utils/resolveContentMaxWidth.js +28 -0
  34. package/types/Status.d.ts +42 -0
  35. package/types/index.d.ts +3 -0
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ const CONTENT_MAX_WIDTH = 'max';
8
+ const CONTENT_FULL_WIDTH = 'full';
9
+
10
+ /**
11
+ * Resolves the maximum width for content based on the provided value and responsive width.
12
+ *
13
+ * @param {number|string|null|undefined} contentMaxWidthValue - The maximum width value for the content.
14
+ * Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
15
+ * @param {number} responsiveWidth - The responsive width to use when contentMaxWidthValue is CONTENT_MAX_WIDTH.
16
+ * @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
17
+ */
18
+ const resolveContentMaxWidth = (contentMaxWidthValue, responsiveWidth) => {
19
+ if (!contentMaxWidthValue || contentMaxWidthValue === CONTENT_FULL_WIDTH) {
20
+ return null;
21
+ }
22
+ if (Number.isFinite(contentMaxWidthValue)) {
23
+ return contentMaxWidthValue;
24
+ }
25
+ if (contentMaxWidthValue === CONTENT_MAX_WIDTH) {
26
+ return responsiveWidth;
27
+ }
28
+ return contentMaxWidthValue;
29
+ };
30
+ var _default = exports.default = resolveContentMaxWidth;
@@ -129,6 +129,7 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
129
129
  id = label,
130
130
  accessibilityLabel,
131
131
  ref: itemRef,
132
+ inactive: itemInactive,
132
133
  ...itemRest
133
134
  } = _ref2;
134
135
  const isSelected = currentValues.includes(id);
@@ -152,6 +153,7 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
152
153
  accessibilityLabel,
153
154
  ...a11yProps.getPositionInSet(items.length, index)
154
155
  };
156
+ const isInactive = itemInactive !== undefined ? itemInactive : inactive;
155
157
 
156
158
  // Ensure button is direct child of group as MacOS voiceover only applies "X of Y" to
157
159
  // "radio" if it's a direct child of "radiogroup", even if aria-posinset etc exists
@@ -162,7 +164,7 @@ const ButtonGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
162
164
  onPress: handlePress,
163
165
  tokens: getButtonTokens,
164
166
  selected: isSelected,
165
- inactive: inactive,
167
+ inactive: isInactive,
166
168
  icon: iconProp,
167
169
  ...selectItemProps({
168
170
  ...itemRest,
@@ -216,7 +218,12 @@ ButtonGroup.propTypes = {
216
218
  /**
217
219
  * An optional ref for one individual button in the ButtonGroup
218
220
  */
219
- ref: ABBPropTypes.ref()
221
+ ref: ABBPropTypes.ref(),
222
+ /**
223
+ * If true, this individual button cannot be interacted with. Takes precedence
224
+ * over the group-level `inactive` prop. Useful for disabling specific options.
225
+ */
226
+ inactive: PropTypes.bool
220
227
  })),
221
228
  /**
222
229
  * If provided, this function is called when the current selection is changed
@@ -5,10 +5,11 @@ 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
- import { useThemeTokens } from '../ThemeProvider';
10
+ import { useThemeTokens, useTheme } from '../ThemeProvider';
10
11
  import { useViewport } from '../ViewportProvider';
11
- import { getTokensPropType, getA11yPropsFromHtmlTag, layoutTags, variantProp, selectSystemProps, a11yProps, viewProps, useCopy, unpackFragment, isTouchDevice } from '../utils';
12
+ import { getTokensPropType, getA11yPropsFromHtmlTag, layoutTags, variantProp, selectSystemProps, a11yProps, viewProps, useCopy, unpackFragment, isTouchDevice, useResponsiveProp, resolveContentMaxWidth } from '../utils';
12
13
  import { useA11yInfo } from '../A11yInfoProvider';
13
14
  import { CarouselProvider } from './CarouselContext';
14
15
  import CarouselItem from './CarouselItem';
@@ -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',
@@ -203,11 +199,18 @@ const selectRootContainerStyles = (enableHero, viewport) => {
203
199
  }
204
200
  return {};
205
201
  };
206
- const selectMainContainerStyles = (enableHero, viewport) => {
202
+ const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
207
203
  if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
208
204
  return {
209
205
  width: '100%',
210
- maxWidth: 1200
206
+ maxWidth: maxWidth || 1200
207
+ };
208
+ }
209
+ if (maxWidth !== null && maxWidth !== undefined) {
210
+ return {
211
+ maxWidth,
212
+ alignSelf: 'center',
213
+ width: '100%'
211
214
  };
212
215
  }
213
216
  return {};
@@ -354,11 +357,20 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
354
357
  loopDuration = transitionDuration,
355
358
  autoPlay = false,
356
359
  enablePeeking = false,
360
+ contentMaxWidth,
361
+ swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
362
+ swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
357
363
  ...rest
358
364
  } = _ref3;
359
365
  let childrenArray = unpackFragment(children);
360
366
  const isTransitioningRef = React.useRef(false);
361
367
  const viewport = useViewport();
368
+ const {
369
+ themeOptions
370
+ } = useTheme();
371
+ const contentMaxWidthValue = useResponsiveProp(contentMaxWidth);
372
+ const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth);
373
+ const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth);
362
374
  const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport);
363
375
  const autoPlayFeatureEnabled = autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1;
364
376
  // if `Carousel` only has one `Carousel.Item`, convert this to a single-item array
@@ -476,7 +488,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
476
488
  });
477
489
  }
478
490
  }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
479
- 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;
480
493
  const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
481
494
  const handleAnimationEndToIndex = function () {
482
495
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
@@ -484,10 +497,23 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
484
497
  }
485
498
  return handleAnimationEnd(toIndex, ...args);
486
499
  };
487
- 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) {
507
+ Animated.timing(panToAnimate, {
508
+ toValue,
509
+ duration: swipeReleaseDuration,
510
+ easing: Easing.out(Easing.cubic),
511
+ useNativeDriver: false
512
+ }).start(handleAnimationEndToIndex);
513
+ } else if (isSwiping.current || isSwipeRelease) {
488
514
  Animated.timing(panToAnimate, {
489
515
  toValue,
490
- duration: 1,
516
+ duration: INSTANT_ANIMATION_DURATION,
491
517
  useNativeDriver: false
492
518
  }).start(handleAnimationEndToIndex);
493
519
  } else if (isAutoPlayEnabled) {
@@ -511,7 +537,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
511
537
  useNativeDriver: false
512
538
  }).start(handleAnimationEndToIndex);
513
539
  }
514
- }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide]);
540
+ }, [springConfig, handleAnimationEnd, transitionDuration, loopDuration, isLastSlide, isAutoPlayEnabled, enablePeeking, enableDisplayMultipleItemsPerSlide, swipeReleaseStyle, swipeReleaseDuration]);
515
541
  const stopAutoplay = React.useCallback(() => {
516
542
  if (autoPlayRef?.current) {
517
543
  clearTimeout(autoPlayRef?.current);
@@ -534,6 +560,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
534
560
  };
535
561
  let skipChanges = !delta;
536
562
  let calcDelta = delta;
563
+ const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE;
537
564
  if (activeIndexRef.current <= 0 && delta < 0) {
538
565
  skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC;
539
566
  calcDelta = totalItems + delta;
@@ -544,9 +571,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
544
571
  const index = activeIndexRef.current + calcDelta;
545
572
  if (skipChanges) {
546
573
  isTransitioningRef.current = true;
547
- animate(pan, toValue, index);
574
+ animate(pan, toValue, index, isSwipeRelease);
548
575
  if (enableHero) {
549
- animate(heroPan, toValue, index);
576
+ animate(heroPan, toValue, index, isSwipeRelease);
550
577
  }
551
578
  return calcDelta;
552
579
  }
@@ -560,9 +587,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
560
587
  };
561
588
  heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta;
562
589
  isTransitioningRef.current = true;
563
- animate(pan, toValue, index);
590
+ animate(pan, toValue, index, isSwipeRelease);
564
591
  if (enableHero) {
565
- animate(heroPan, heroToValue, index);
592
+ animate(heroPan, heroToValue, index, isSwipeRelease);
566
593
  }
567
594
  if (isCarouselPlaying) {
568
595
  stopAutoplay();
@@ -759,16 +786,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
759
786
  startAutoplay();
760
787
  }
761
788
  const correction = gesture.moveX - gesture.x0;
789
+ isSwiping.current = false;
762
790
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
763
791
  animate(pan, {
764
792
  x: 0,
765
793
  y: 0
766
- }, 0);
794
+ }, activeIndexRef.current, true);
767
795
  } else {
768
796
  const delta = correction > 0 ? -1 : 1;
769
797
  updateIndex(delta, TRANSITION_MODES.SWIPE);
770
798
  }
771
- isSwiping.current = false;
772
799
  }
773
800
  }), [pan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
774
801
  const heroPanResponder = React.useMemo(() => PanResponder.create({
@@ -799,16 +826,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
799
826
  startAutoplay();
800
827
  }
801
828
  const correction = gesture.moveX - gesture.x0;
829
+ isSwiping.current = false;
802
830
  if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
803
831
  animate(heroPan, {
804
832
  x: 0,
805
833
  y: 0
806
- }, 0);
834
+ }, activeIndexRef.current, true);
807
835
  } else {
808
836
  const delta = correction > 0 ? -1 : 1;
809
837
  updateIndex(delta, TRANSITION_MODES.SWIPE);
810
838
  }
811
- isSwiping.current = false;
812
839
  }
813
840
  }), [heroPan, updateIndex, updateOffset, animate, isSwipeAllowed, minDistanceForAction, handleAnimationStart, minDistanceToCapture, startAutoplay, stopAutoplay, isCarouselPlaying]);
814
841
  const goToNext = React.useCallback(() => {
@@ -889,7 +916,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
889
916
  return /*#__PURE__*/_jsxs(View, {
890
917
  style: selectRootContainerStyles(enableHero, viewport),
891
918
  children: [/*#__PURE__*/_jsx(View, {
892
- style: selectMainContainerStyles(enableHero, viewport),
919
+ style: selectMainContainerStyles(enableHero, viewport, maxWidth),
893
920
  children: /*#__PURE__*/_jsxs(CarouselProvider, {
894
921
  activeIndex: activeIndex,
895
922
  goTo: goTo,
@@ -1247,7 +1274,40 @@ Carousel.propTypes = {
1247
1274
  * If set to `true`, the Carousel will show multiple slides at once
1248
1275
  * - Default value is `false`
1249
1276
  */
1250
- enableDisplayMultipleItemsPerSlide: PropTypes.bool
1277
+ enableDisplayMultipleItemsPerSlide: PropTypes.bool,
1278
+ /**
1279
+ * The maximum width of the content in the Carousel.
1280
+ * This prop accepts responsive values for different viewports. If a number is provided,
1281
+ * it will be the max content width for the desired viewport.
1282
+ * - `xs`: 'max' | 'full' | <number>
1283
+ * - `sm`: 'max' | 'full' | <number>
1284
+ * - `md`: 'max' | 'full' | <number>
1285
+ * - `lg`: 'max' | 'full' | <number>
1286
+ * - `xl`: 'max' | 'full' | <number>
1287
+ */
1288
+ contentMaxWidth: PropTypes.shape({
1289
+ xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1290
+ lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1291
+ md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1292
+ sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
1293
+ xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
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
1251
1311
  };
1252
1312
  Carousel.Item = CarouselItem;
1253
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;
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { viewports } from '@telus-uds/system-constants';
4
4
  import { a11yProps, viewProps, getA11yPropsFromHtmlTag, layoutTags, selectSystemProps, BaseView, StyleSheet, createMediaQueryStyles, useResponsiveProp } from '../utils';
5
+ import resolveContentMaxWidth from '../utils/resolveContentMaxWidth';
5
6
  import Row from './Row';
6
7
  import Col from './Col';
7
8
  import GutterContext from './providers/GutterContext';
@@ -10,47 +11,24 @@ import { useTheme } from '../ThemeProvider';
10
11
  import { useViewport } from '../ViewportProvider';
11
12
  import { jsx as _jsx } from "react/jsx-runtime";
12
13
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
- const CONTENT_MAX_WIDTH = 'max';
14
- const CONTENT_FULL_WIDTH = 'full';
15
14
 
16
15
  /**
17
- * Resolves the maximum width for content based on the provided value and responsive width.
18
- *
19
- * @param {number|string|null|undefined} contentMinWidthValue - The minimum width value for the content.
20
- * Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
21
- * @param {number} responsiveWidth - The responsive width to use when contentMinWidthValue is CONTENT_MAX_WIDTH.
22
- * @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
23
- */
24
- const resolveContentMaxWidth = (contentMinWidthValue, responsiveWidth) => {
25
- if (!contentMinWidthValue || contentMinWidthValue === CONTENT_FULL_WIDTH) {
26
- return null;
27
- }
28
- if (Number.isFinite(contentMinWidthValue)) {
29
- return contentMinWidthValue;
30
- }
31
- if (contentMinWidthValue === CONTENT_MAX_WIDTH) {
32
- return responsiveWidth;
33
- }
34
- return contentMinWidthValue;
35
- };
36
-
37
- /**
38
- * Calculates the maximum width for a given viewport based on limitWidth and contentMinWidth settings.
16
+ * Calculates the maximum width for a given viewport based on limitWidth and contentMaxWidth settings.
39
17
  *
40
18
  * @param {string} viewportKey - The viewport key ('xs', 'sm', 'md', 'lg', 'xl')
41
19
  * @param {boolean} limitWidth - Whether to limit the width to viewport breakpoints
42
- * @param {any} contentMinWidth - The contentMinWidth prop value
20
+ * @param {any} contentWidthProp - The contentMaxWidth (or contentMinWidth) prop value
43
21
  * @param {number|string|null} maxWidth - The resolved max width value
44
22
  * @returns {number|string|null} The calculated maximum width for the viewport
45
23
  */
46
- const getMaxWidthForViewport = (viewportKey, limitWidth, contentMinWidth, maxWidth) => {
24
+ const getMaxWidthForViewport = (viewportKey, limitWidth, contentWidthProp, maxWidth) => {
47
25
  if (limitWidth) {
48
26
  if (viewportKey === 'xs') {
49
27
  return null;
50
28
  }
51
29
  return viewports.map.get(viewportKey);
52
30
  }
53
- if (contentMinWidth) {
31
+ if (contentWidthProp) {
54
32
  return maxWidth;
55
33
  }
56
34
  return null;
@@ -74,6 +52,7 @@ const FlexGrid = /*#__PURE__*/React.forwardRef((_ref, ref) => {
74
52
  accessibilityRole,
75
53
  children,
76
54
  dataSet,
55
+ contentMaxWidth,
77
56
  contentMinWidth,
78
57
  ...rest
79
58
  } = _ref;
@@ -87,28 +66,31 @@ const FlexGrid = /*#__PURE__*/React.forwardRef((_ref, ref) => {
87
66
  } = useTheme();
88
67
  let flexgridStyles;
89
68
  let mediaIds;
90
- const contentMinWidthValue = useResponsiveProp(contentMinWidth);
69
+
70
+ // Support both contentMaxWidth and deprecated contentMinWidth for backwards compatibility
71
+ const contentWidthProp = contentMaxWidth || contentMinWidth;
72
+ const contentWidthValue = useResponsiveProp(contentWidthProp);
91
73
  const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth);
92
- const maxWidth = resolveContentMaxWidth(contentMinWidthValue, responsiveWidth);
74
+ const maxWidth = resolveContentMaxWidth(contentWidthValue, responsiveWidth);
93
75
  const stylesByViewport = {
94
76
  xs: {
95
- maxWidth: getMaxWidthForViewport('xs', limitWidth, contentMinWidth, maxWidth),
77
+ maxWidth: getMaxWidthForViewport('xs', limitWidth, contentWidthProp, maxWidth),
96
78
  flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
97
79
  },
98
80
  sm: {
99
- maxWidth: getMaxWidthForViewport('sm', limitWidth, contentMinWidth, maxWidth),
81
+ maxWidth: getMaxWidthForViewport('sm', limitWidth, contentWidthProp, maxWidth),
100
82
  flexDirection: reverseLevel[1] ? 'column-reverse' : 'column'
101
83
  },
102
84
  md: {
103
- maxWidth: getMaxWidthForViewport('md', limitWidth, contentMinWidth, maxWidth),
85
+ maxWidth: getMaxWidthForViewport('md', limitWidth, contentWidthProp, maxWidth),
104
86
  flexDirection: reverseLevel[2] ? 'column-reverse' : 'column'
105
87
  },
106
88
  lg: {
107
- maxWidth: getMaxWidthForViewport('lg', limitWidth, contentMinWidth, maxWidth),
89
+ maxWidth: getMaxWidthForViewport('lg', limitWidth, contentWidthProp, maxWidth),
108
90
  flexDirection: reverseLevel[3] ? 'column-reverse' : 'column'
109
91
  },
110
92
  xl: {
111
- maxWidth: getMaxWidthForViewport('xl', limitWidth, contentMinWidth, maxWidth),
93
+ maxWidth: getMaxWidthForViewport('xl', limitWidth, contentWidthProp, maxWidth),
112
94
  flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
113
95
  }
114
96
  };
@@ -204,7 +186,7 @@ FlexGrid.propTypes = {
204
186
  */
205
187
  children: PropTypes.node.isRequired,
206
188
  /**
207
- * The minimum width of the content in the FlexGrid.
189
+ * The maximum width of the content in the FlexGrid.
208
190
  * This prop accepts responsive values for different viewports. If a number is provided,
209
191
  * it will be the max content width for the desired viewport.
210
192
  * - `xs`: 'max' | 'full' | <number>
@@ -213,6 +195,20 @@ FlexGrid.propTypes = {
213
195
  * - `lg`: 'max' | 'full' | <number>
214
196
  * - `xl`: 'max' | 'full' | <number>
215
197
  */
198
+ contentMaxWidth: PropTypes.shape({
199
+ xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
200
+ lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
201
+ md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
202
+ sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
203
+ xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
204
+ }),
205
+ /**
206
+ * @deprecated Use `contentMaxWidth` instead. This prop will be removed in a future version.
207
+ *
208
+ * The minimum width of the content in the FlexGrid.
209
+ * This prop accepts responsive values for different viewports. If a number is provided,
210
+ * it will be the max content width for the desired viewport.
211
+ */
216
212
  contentMinWidth: PropTypes.shape({
217
213
  xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
218
214
  lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
@@ -123,6 +123,7 @@ const IconButton = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
123
123
  hrefAttrs,
124
124
  testID,
125
125
  accessibilityRole = href ? 'link' : 'button',
126
+ inactive = false,
126
127
  ...rawRest
127
128
  } = _ref3;
128
129
  const {
@@ -143,8 +144,12 @@ const IconButton = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
143
144
  }
144
145
  });
145
146
  };
146
- const getTokens = useThemeTokensCallback('IconButton', tokens, variant);
147
- const getOuterStyle = pressableState => selectOuterStyle(getTokens(resolvePressableState(pressableState), variant.password));
147
+ const mergedVariant = inactive ? {
148
+ ...variant,
149
+ inactive: true
150
+ } : variant;
151
+ const getTokens = useThemeTokensCallback('IconButton', tokens, mergedVariant);
152
+ const getOuterStyle = pressableState => selectOuterStyle(getTokens(resolvePressableState(pressableState), mergedVariant.password));
148
153
  return /*#__PURE__*/_jsx(Pressable, {
149
154
  ref: ref,
150
155
  href: href,
@@ -153,15 +158,16 @@ const IconButton = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
153
158
  style: getOuterStyle,
154
159
  ...selectedProps,
155
160
  testID: testID,
161
+ disabled: inactive,
156
162
  children: pressableState => {
157
163
  const themeTokens = getTokens(resolvePressableState(pressableState));
158
164
  return /*#__PURE__*/_jsx(View, {
159
- style: selectInnerStyle(themeTokens, variant.password),
165
+ style: selectInnerStyle(themeTokens, mergedVariant.password),
160
166
  children: /*#__PURE__*/_jsx(Icon, {
161
167
  icon: IconComponent || themeTokens.icon,
162
168
  title: selectedProps.accessibilityLabel,
163
169
  tokens: selectTokens('Icon', themeTokens, 'icon'),
164
- variant: variant
170
+ variant: mergedVariant
165
171
  })
166
172
  });
167
173
  }
@@ -192,7 +198,11 @@ IconButton.propTypes = {
192
198
  /**
193
199
  * Function to execute when the `Iconbutton` is pressed
194
200
  */
195
- onPress: PropTypes.func
201
+ onPress: PropTypes.func,
202
+ /**
203
+ * When true, applies the inactive variant styling
204
+ */
205
+ inactive: PropTypes.bool
196
206
  };
197
207
  const staticStyles = StyleSheet.create({
198
208
  outer: {
@@ -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",
@@ -325,7 +325,7 @@ const TextInputBase = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
325
325
  icon: ClearButtonIcon,
326
326
  onPress: handleClear,
327
327
  variant: {
328
- compact: true
328
+ subtle: true
329
329
  }
330
330
  }, "clear"));
331
331
  }
@@ -335,8 +335,7 @@ const TextInputBase = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
335
335
  icon: !showPassword ? passwordShowButtonIcon : passwordHideButtonIcon,
336
336
  onPress: handleShowOrHide,
337
337
  variant: {
338
- compact: true,
339
- password: true,
338
+ subtle: true,
340
339
  inactive: variant.inactive,
341
340
  size: 'large'
342
341
  },
@@ -26,4 +26,5 @@ export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByt
26
26
  export { default as formatImageSource } from './formatImageSource';
27
27
  export { default as getSpacingScale } from './getSpacingScale';
28
28
  export { default as useVariants } from './useVariants';
29
- export { default as isTouchDevice } from './isTouchDevice';
29
+ export { default as isTouchDevice } from './isTouchDevice';
30
+ export { default as resolveContentMaxWidth } from './resolveContentMaxWidth';
@@ -0,0 +1,24 @@
1
+ const CONTENT_MAX_WIDTH = 'max';
2
+ const CONTENT_FULL_WIDTH = 'full';
3
+
4
+ /**
5
+ * Resolves the maximum width for content based on the provided value and responsive width.
6
+ *
7
+ * @param {number|string|null|undefined} contentMaxWidthValue - The maximum width value for the content.
8
+ * Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
9
+ * @param {number} responsiveWidth - The responsive width to use when contentMaxWidthValue is CONTENT_MAX_WIDTH.
10
+ * @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
11
+ */
12
+ const resolveContentMaxWidth = (contentMaxWidthValue, responsiveWidth) => {
13
+ if (!contentMaxWidthValue || contentMaxWidthValue === CONTENT_FULL_WIDTH) {
14
+ return null;
15
+ }
16
+ if (Number.isFinite(contentMaxWidthValue)) {
17
+ return contentMaxWidthValue;
18
+ }
19
+ if (contentMaxWidthValue === CONTENT_MAX_WIDTH) {
20
+ return responsiveWidth;
21
+ }
22
+ return contentMaxWidthValue;
23
+ };
24
+ export default resolveContentMaxWidth;
package/lib/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@react-native-picker/picker": "^2.9.0",
14
14
  "@telus-uds/system-constants": "^3.0.0",
15
- "@telus-uds/system-theme-tokens": "^4.17.0",
15
+ "@telus-uds/system-theme-tokens": "^4.18.0",
16
16
  "airbnb-prop-types": "^2.16.0",
17
17
  "css-mediaquery": "^0.1.2",
18
18
  "expo-document-picker": "^13.0.1",
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.24.0",
87
+ "version": "3.26.0",
88
88
  "types": "types/index.d.ts"
89
89
  }
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@react-native-picker/picker": "^2.9.0",
14
14
  "@telus-uds/system-constants": "^3.0.0",
15
- "@telus-uds/system-theme-tokens": "^4.17.0",
15
+ "@telus-uds/system-theme-tokens": "^4.18.0",
16
16
  "airbnb-prop-types": "^2.16.0",
17
17
  "css-mediaquery": "^0.1.2",
18
18
  "expo-document-picker": "^13.0.1",
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.24.0",
87
+ "version": "3.26.0",
88
88
  "types": "types/index.d.ts"
89
89
  }