@telus-uds/components-base 3.12.1 → 3.13.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 (45) hide show
  1. package/CHANGELOG.md +29 -2
  2. package/lib/cjs/Button/ButtonDropdown.js +105 -12
  3. package/lib/cjs/Carousel/Carousel.js +26 -0
  4. package/lib/cjs/Carousel/CarouselContext.js +7 -4
  5. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +13 -2
  6. package/lib/cjs/Carousel/Constants.js +2 -1
  7. package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
  8. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +1 -1
  9. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  10. package/lib/cjs/FlexGrid/FlexGrid.js +54 -5
  11. package/lib/cjs/Icon/Icon.js +3 -1
  12. package/lib/cjs/InputLabel/InputLabel.js +1 -1
  13. package/lib/cjs/InputSupports/InputSupports.js +1 -1
  14. package/lib/cjs/Notification/Notification.js +27 -8
  15. package/lib/cjs/utils/props/inputSupportsProps.js +1 -1
  16. package/lib/esm/Button/ButtonDropdown.js +107 -14
  17. package/lib/esm/Carousel/Carousel.js +27 -1
  18. package/lib/esm/Carousel/CarouselContext.js +7 -4
  19. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +13 -2
  20. package/lib/esm/Carousel/Constants.js +1 -0
  21. package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
  22. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +2 -2
  23. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  24. package/lib/esm/FlexGrid/FlexGrid.js +55 -6
  25. package/lib/esm/Icon/Icon.js +3 -1
  26. package/lib/esm/InputLabel/InputLabel.js +1 -1
  27. package/lib/esm/InputSupports/InputSupports.js +1 -1
  28. package/lib/esm/Notification/Notification.js +27 -8
  29. package/lib/esm/utils/props/inputSupportsProps.js +1 -1
  30. package/lib/package.json +2 -2
  31. package/package.json +2 -2
  32. package/src/Button/ButtonDropdown.jsx +109 -16
  33. package/src/Carousel/Carousel.jsx +30 -0
  34. package/src/Carousel/CarouselContext.jsx +17 -4
  35. package/src/Carousel/CarouselItem/CarouselItem.jsx +17 -2
  36. package/src/Carousel/Constants.js +1 -0
  37. package/src/ExpandCollapse/ExpandCollapse.jsx +5 -2
  38. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +2 -2
  39. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +39 -9
  40. package/src/FlexGrid/FlexGrid.jsx +62 -6
  41. package/src/Icon/Icon.jsx +3 -1
  42. package/src/InputLabel/InputLabel.jsx +1 -1
  43. package/src/InputSupports/InputSupports.jsx +1 -1
  44. package/src/Notification/Notification.jsx +58 -9
  45. package/src/utils/props/inputSupportsProps.js +1 -1
@@ -16,6 +16,7 @@ var _dictionary = _interopRequireDefault(require("./dictionary"));
16
16
  var _ViewportProvider = require("../ViewportProvider");
17
17
  var _jsxRuntime = require("react/jsx-runtime");
18
18
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
+ const CONTENT_MAX_WIDTH = 'max';
19
20
  const [selectProps, selectedSystemPropTypes] = (0, _utils.selectSystemProps)([_utils.a11yProps, _utils.viewProps]);
20
21
  const selectContainerStyles = tokens => ({
21
22
  ...tokens
@@ -70,13 +71,13 @@ const selectDismissButtonContainerStyles = _ref4 => {
70
71
  placeContent: 'start'
71
72
  };
72
73
  };
73
- const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport) => ({
74
- maxWidth: system && viewport === 'xl' ? maxWidth : '100%',
74
+ const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport, useContentMaxWidth) => ({
75
+ maxWidth: system && (useContentMaxWidth || viewport === 'xl') ? maxWidth : '100%',
75
76
  width: '100%',
76
77
  paddingRight: themeTokens?.containerPaddingRight,
77
78
  paddingLeft: themeTokens?.containerPaddingLeft
78
79
  });
79
- const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, system) => {
80
+ const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, system, useContentMaxWidth) => {
80
81
  const transformedSelectContainerStyles = Object.entries(themeTokens).reduce((acc, _ref5) => {
81
82
  let [vp, viewportTokens] = _ref5;
82
83
  acc[vp] = {
@@ -99,7 +100,7 @@ const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, d
99
100
  const transformedSelectContentContainerStyles = Object.entries(themeTokens).reduce((acc, _ref6) => {
100
101
  let [vp, viewportTokens] = _ref6;
101
102
  acc[vp] = {
102
- ...selectContentContainerStyle(viewportTokens, maxWidth, system, vp),
103
+ ...selectContentContainerStyle(viewportTokens, maxWidth, system, vp, useContentMaxWidth),
103
104
  flexDirection: 'row',
104
105
  flexShrink: 1,
105
106
  justifyContent: 'space-between',
@@ -176,7 +177,7 @@ const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, d
176
177
  selectDismissIconPropsStyles
177
178
  };
178
179
  };
179
- const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system) => ({
180
+ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system, useContentMaxWidth) => ({
180
181
  containerStyles: {
181
182
  container: {
182
183
  flexDirection: 'column',
@@ -188,7 +189,7 @@ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, view
188
189
  flexDirection: 'row',
189
190
  flexShrink: 1,
190
191
  justifyContent: 'space-between',
191
- ...selectContentContainerStyle(themeTokens, maxWidth, system, viewport),
192
+ ...selectContentContainerStyle(themeTokens, maxWidth, system, viewport, useContentMaxWidth),
192
193
  ...(system && {
193
194
  alignSelf: 'center'
194
195
  })
@@ -287,6 +288,7 @@ const Notification = /*#__PURE__*/_react.default.forwardRef((_ref7, ref) => {
287
288
  tokens,
288
289
  variant,
289
290
  onDismiss,
291
+ contentMinWidth,
290
292
  ...rest
291
293
  } = _ref7;
292
294
  const [isDismissed, setIsDismissed] = _react.default.useState(false);
@@ -309,6 +311,7 @@ const Notification = /*#__PURE__*/_react.default.forwardRef((_ref7, ref) => {
309
311
  system: isSystemEnabled,
310
312
  viewport
311
313
  });
314
+ const useContentMaxWidth = (0, _utils.useResponsiveProp)(contentMinWidth) === CONTENT_MAX_WIDTH;
312
315
  const maxWidth = (0, _utils.useResponsiveProp)(themeOptions?.contentMaxWidth, _systemConstants.viewports.map.get(_systemConstants.viewports.xl));
313
316
  const notificationComponentRef = _react.default.useRef({
314
317
  containerStyles: {},
@@ -331,9 +334,9 @@ const Notification = /*#__PURE__*/_react.default.forwardRef((_ref7, ref) => {
331
334
  selectDismissIconPropsIds: {}
332
335
  });
333
336
  if (enableMediaQueryStyleSheet) {
334
- notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, isSystemEnabled);
337
+ notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, isSystemEnabled, useContentMaxWidth);
335
338
  } else {
336
- notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible, viewport, isSystemEnabled);
339
+ notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible, viewport, isSystemEnabled, useContentMaxWidth);
337
340
  }
338
341
  if (isDismissed) {
339
342
  return null;
@@ -430,6 +433,22 @@ Notification.propTypes = {
430
433
  * Callback function called when the dismiss button is clicked
431
434
  */
432
435
  onDismiss: _propTypes.default.func,
436
+ /**
437
+ * The minimum width of the content in the Notification when using the system variant.
438
+ * This prop accepts responsive values for different viewports.
439
+ * - `xs`: 'max' | 'full'
440
+ * - `sm`: 'max' | 'full'
441
+ * - `md`: 'max' | 'full'
442
+ * - `lg`: 'max' | 'full'
443
+ * - `xl`: 'max' | 'full'
444
+ */
445
+ contentMinWidth: _propTypes.default.shape({
446
+ xl: _propTypes.default.oneOf(['max', 'full']),
447
+ lg: _propTypes.default.oneOf(['max', 'full']),
448
+ md: _propTypes.default.oneOf(['max', 'full']),
449
+ sm: _propTypes.default.oneOf(['max', 'full']),
450
+ xs: _propTypes.default.oneOf(['max', 'full'])
451
+ }),
433
452
  tokens: (0, _utils.getTokensPropType)('Notification'),
434
453
  variant: _utils.variantProp.propType
435
454
  };
@@ -44,7 +44,7 @@ var _default = exports.default = {
44
44
  * 1. `tooltip` as a string - The content of the tooltip.
45
45
  * 2. `tooltip` as an object - Tooltip component props to be passed.
46
46
  */
47
- tooltip: _propTypes.default.oneOfType([_shared.default, _propTypes.default.string]),
47
+ tooltip: _propTypes.default.oneOfType([_propTypes.default.shape(_shared.default), _propTypes.default.string]),
48
48
  /**
49
49
  * Use to visually mark an input as valid or invalid.
50
50
  */
@@ -1,16 +1,17 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import Platform from "react-native-web/dist/exports/Platform";
4
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
5
  import Text from "react-native-web/dist/exports/Text";
5
6
  import View from "react-native-web/dist/exports/View";
6
7
  import buttonPropTypes, { textAndA11yText } from './propTypes';
7
8
  import ButtonBase from './ButtonBase';
8
- import { useThemeTokensCallback } from '../ThemeProvider';
9
+ import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider';
9
10
  import { a11yProps, getTokensPropType, focusHandlerProps, resolvePressableState, selectTokens, useInputValue } from '../utils';
10
11
  import Icon from '../Icon';
11
- import { getStackedContent } from '../StackView';
12
12
  import { getPressHandlersWithArgs } from '../utils/pressability';
13
- import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ const FULL_WIDTH_STYLE = 'full';
14
15
  const selectIconTokens = _ref => {
15
16
  let {
16
17
  icon,
@@ -49,6 +50,44 @@ const selectIconTokens = _ref => {
49
50
  }
50
51
  };
51
52
  };
53
+ const selectDescriptionTextStyles = tokens => ({
54
+ ...applyTextStyles({
55
+ fontName: tokens?.descriptionFontName,
56
+ fontSize: tokens?.descriptionFontSize,
57
+ fontWeight: tokens?.descriptionFontWeight,
58
+ fontColor: tokens?.color
59
+ }),
60
+ paddingBottom: tokens?.descriptionTextPaddingBottom
61
+ });
62
+ const selectLeadIconTokens = tokens => ({
63
+ color: tokens?.leadIconColor,
64
+ backgroundColor: tokens?.leadIconBackgroundColor,
65
+ size: tokens?.leadIconSize,
66
+ borderRadius: tokens?.leadIconBorderRadius,
67
+ padding: tokens?.leadIconPadding
68
+ });
69
+ const selectLeadIconContainerStyles = tokens => ({
70
+ paddingTop: tokens?.leadIconContainerPaddingTop,
71
+ paddingBottom: tokens?.leadIconContainerPaddingBottom,
72
+ paddingLeft: tokens?.leadIconContainerPaddingLeft,
73
+ paddingRight: tokens?.leadIconContainerPaddingRight
74
+ });
75
+ const selectTextContainerStyles = tokens => ({
76
+ paddingLeft: tokens?.textPaddingLeft,
77
+ paddingRight: tokens?.textPaddingRight
78
+ });
79
+ const selectStackedContentStyles = (tokens, iconPosition, isFullWidth) => ({
80
+ ...staticStyles.stackedContent,
81
+ gap: tokens?.iconSpace,
82
+ flexDirection: iconPosition === 'left' ? 'row' : 'row-reverse',
83
+ ...(Platform.OS === 'web' && {
84
+ flex: 1
85
+ }),
86
+ ...(isFullWidth && {
87
+ justifyContent: 'space-between',
88
+ flex: 1
89
+ })
90
+ });
52
91
  const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
53
92
  let {
54
93
  value,
@@ -61,8 +100,11 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
61
100
  readOnly = false,
62
101
  children = null,
63
102
  accessibilityRole = 'radio',
103
+ description,
104
+ singleOption,
64
105
  ...props
65
106
  } = _ref2;
107
+ const isFullWidth = variant?.width === FULL_WIDTH_STYLE;
66
108
  const {
67
109
  currentValue: isOpen,
68
110
  setValue: setIsOpen
@@ -78,7 +120,13 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
78
120
  ...variant
79
121
  };
80
122
  const getTokens = useThemeTokensCallback('ButtonDropdown', tokens, extraState);
81
- const getButtonTokens = buttonState => selectTokens('Button', getTokens(buttonState));
123
+ const getButtonTokens = buttonState => ({
124
+ ...selectTokens('Button', getTokens(buttonState)),
125
+ iconSpace: props?.icon ? getTokens(buttonState)?.iconSpace : 0,
126
+ ...(isFullWidth && {
127
+ width: 'full'
128
+ })
129
+ });
82
130
 
83
131
  // Pass an object of relevant component state as first argument for any passed-in press handlers
84
132
  const pressHandlers = getPressHandlersWithArgs(props, [{
@@ -96,7 +144,7 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
96
144
  ...pressHandlers,
97
145
  onPress: handlePress,
98
146
  tokens: getButtonTokens,
99
- inactive: inactive,
147
+ inactive: singleOption || inactive,
100
148
  icon: () => null,
101
149
  accessibilityRole: accessibilityRole,
102
150
  ...props,
@@ -112,14 +160,14 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
112
160
  // - Token sets: https://github.com/telus/universal-design-system/issues/782
113
161
 
114
162
  const itemTokens = getTokens(buttonState);
163
+ const leadIcon = itemTokens?.leadIcon;
115
164
  const {
116
165
  iconTokens,
117
166
  iconPosition,
118
- iconSpace,
119
167
  iconWrapperStyle,
120
168
  icon: IconComponent
121
169
  } = selectIconTokens(itemTokens);
122
- const iconContent = IconComponent ? /*#__PURE__*/_jsx(View, {
170
+ const iconContent = IconComponent && !singleOption ? /*#__PURE__*/_jsx(View, {
123
171
  style: iconWrapperStyle,
124
172
  children: /*#__PURE__*/_jsx(Icon, {
125
173
  icon: IconComponent,
@@ -130,13 +178,28 @@ const ButtonDropdown = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
130
178
  ...resolvePressableState(buttonState, extraState),
131
179
  textStyles
132
180
  }) : children;
133
- const content = children ? childrenContent() : /*#__PURE__*/_jsx(Text, {
134
- style: textStyles,
135
- children: label
181
+ const content = children ? childrenContent() : /*#__PURE__*/_jsxs(View, {
182
+ style: staticStyles.contentContainer,
183
+ children: [leadIcon && /*#__PURE__*/_jsx(View, {
184
+ style: selectLeadIconContainerStyles(itemTokens),
185
+ children: /*#__PURE__*/_jsx(Icon, {
186
+ icon: leadIcon,
187
+ tokens: selectLeadIconTokens(itemTokens)
188
+ })
189
+ }), /*#__PURE__*/_jsxs(View, {
190
+ style: [staticStyles.textContainer, selectTextContainerStyles(itemTokens)],
191
+ children: [/*#__PURE__*/_jsx(Text, {
192
+ style: textStyles,
193
+ children: label
194
+ }), description && /*#__PURE__*/_jsx(Text, {
195
+ style: selectDescriptionTextStyles(itemTokens),
196
+ children: description
197
+ })]
198
+ })]
136
199
  });
137
- return getStackedContent(iconPosition === 'left' ? [iconContent, content] : [content, iconContent], {
138
- space: iconSpace,
139
- direction: 'row'
200
+ return /*#__PURE__*/_jsxs(View, {
201
+ style: selectStackedContentStyles(itemTokens, iconPosition, isFullWidth),
202
+ children: [iconContent, content]
140
203
  });
141
204
  }
142
205
  });
@@ -168,6 +231,36 @@ ButtonDropdown.propTypes = {
168
231
  /**
169
232
  * By default, `ButtonDropdown` is treated by accessibility tools as a radio button.
170
233
  */
171
- accessibilityRole: PropTypes.string
234
+ accessibilityRole: PropTypes.string,
235
+ /**
236
+ * The description of ButtonDropdown.
237
+ */
238
+ description: PropTypes.string,
239
+ /**
240
+ * Use this prop to render the ButtonDropdown as display only without any interaction when there is only one option.
241
+ */
242
+ singleOption: PropTypes.bool
172
243
  };
244
+ const staticStyles = StyleSheet.create({
245
+ textContainer: {
246
+ alignItems: 'flex-start',
247
+ flexShrink: 1,
248
+ ...(Platform.OS === 'web' && {
249
+ flex: 1
250
+ })
251
+ },
252
+ contentContainer: {
253
+ flexDirection: 'row',
254
+ alignContent: 'center',
255
+ alignItems: 'center',
256
+ ...(Platform.OS === 'web' && {
257
+ flex: 1
258
+ }),
259
+ flexShrink: 1
260
+ },
261
+ stackedContent: {
262
+ flexDirection: 'row',
263
+ alignItems: 'center'
264
+ }
265
+ });
173
266
  export default ButtonDropdown;
@@ -21,7 +21,7 @@ import CarouselTabsPanel from './CarouselTabs/CarouselTabsPanel';
21
21
  import CarouselTabsPanelItem from './CarouselTabs/CarouselTabsPanelItem';
22
22
  import dictionary from './dictionary';
23
23
  import Box from '../Box';
24
- import { 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';
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
25
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
26
26
  const TRANSITION_MODES = {
27
27
  MANUAL: 'manual',
@@ -165,6 +165,31 @@ const getTotalItems = (enableDisplayMultipleItemsPerSlide, childrenArray, viewpo
165
165
  }
166
166
  return childrenArray.length;
167
167
  };
168
+
169
+ /**
170
+ * Determines the maximum number of items that can be displayed per slide based on viewport
171
+ *
172
+ * @param {boolean} enableDisplayMultipleItemsPerSlide - Flag indicating whether multiple items per slide is enabled
173
+ * @param {string} viewport - The current viewport size ('xs', 'sm', 'md', 'lg', 'xl')
174
+ * @returns {number} The maximum number of items that can be displayed per slide
175
+ */
176
+ const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) => {
177
+ if (enableDisplayMultipleItemsPerSlide) {
178
+ switch (viewport) {
179
+ case 'xs':
180
+ case 'sm':
181
+ return ITEMS_PER_VIEWPORT_XS_SM;
182
+ case 'md':
183
+ return ITEMS_PER_VIEWPORT_MD;
184
+ case 'lg':
185
+ case 'xl':
186
+ return ITEMS_PER_VIEWPORT_LG_XL;
187
+ default:
188
+ return ITEMS_PER_VIEWPORT_XS_SM;
189
+ }
190
+ }
191
+ return ITEMS_PER_VIEWPORT_XS_SM;
192
+ };
168
193
  const selectRootContainerStyles = (enableHero, viewport) => {
169
194
  if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
170
195
  return {
@@ -860,6 +885,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
860
885
  firstFocusRef: firstFocusRef,
861
886
  refocus: refocus,
862
887
  width: containerLayout.width,
888
+ maximumItemsForSlide: getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport),
863
889
  children: [/*#__PURE__*/_jsxs(View, {
864
890
  style: [staticStyles.root, {
865
891
  ...(Platform.OS === 'web' ? {
@@ -13,7 +13,8 @@ const CarouselProvider = _ref => {
13
13
  refocus = false,
14
14
  themeTokens,
15
15
  totalItems,
16
- width
16
+ width,
17
+ maximumItemsForSlide
17
18
  } = _ref;
18
19
  const value = React.useMemo(() => ({
19
20
  activeIndex,
@@ -23,8 +24,9 @@ const CarouselProvider = _ref => {
23
24
  refocus,
24
25
  themeTokens,
25
26
  totalItems,
26
- width
27
- }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width]);
27
+ width,
28
+ maximumItemsForSlide
29
+ }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width, maximumItemsForSlide]);
28
30
  return /*#__PURE__*/_jsx(CarouselContext.Provider, {
29
31
  value: value,
30
32
  children: children
@@ -46,6 +48,7 @@ CarouselProvider.propTypes = {
46
48
  refocus: PropTypes.bool,
47
49
  themeTokens: getTokensPropType('Carousel'),
48
50
  totalItems: PropTypes.number.isRequired,
49
- width: PropTypes.number.isRequired
51
+ width: PropTypes.number.isRequired,
52
+ maximumItemsForSlide: PropTypes.number
50
53
  };
51
54
  export { CarouselProvider, useCarousel };
@@ -87,13 +87,23 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
87
87
  } = _ref2;
88
88
  const {
89
89
  width,
90
- activeIndex
90
+ activeIndex,
91
+ goTo,
92
+ maximumItemsForSlide
91
93
  } = useCarousel();
92
94
  const selectedProps = selectProps({
93
95
  ...rest,
94
96
  ...getA11yPropsFromHtmlTag(tag, rest.accessibilityRole)
95
97
  });
96
- const focusabilityProps = activeIndex === elementIndex ? {} : a11yProps.nonFocusableProps;
98
+ const focusabilityProps = activeIndex === elementIndex || enablePeeking ? {} : a11yProps.nonFocusableProps;
99
+ const handleFocus = React.useCallback(event => {
100
+ if (Platform.OS === 'web' && elementIndex >= maximumItemsForSlide * (activeIndex + 1)) {
101
+ goTo(activeIndex + 1);
102
+ }
103
+ if (rest.onFocus) {
104
+ rest.onFocus(event);
105
+ }
106
+ }, [elementIndex, activeIndex, goTo, maximumItemsForSlide, rest]);
97
107
  return /*#__PURE__*/_jsx(View, {
98
108
  style: selectContainerStyle({
99
109
  width,
@@ -107,6 +117,7 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
107
117
  ...selectedProps,
108
118
  ...focusabilityProps,
109
119
  ref: ref,
120
+ onFocus: handleFocus,
110
121
  children: children
111
122
  });
112
123
  });
@@ -1,3 +1,4 @@
1
+ export const ITEMS_PER_VIEWPORT_XS_SM = 1;
1
2
  export const ITEMS_PER_VIEWPORT_MD = 2;
2
3
  export const ITEMS_PER_VIEWPORT_LG_XL = 3;
3
4
  export const GAP_BETWEEN_ITEMS = 16;
@@ -3,7 +3,7 @@ import View from "react-native-web/dist/exports/View";
3
3
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
4
  import PropTypes from 'prop-types';
5
5
  import { useThemeTokens } from '../ThemeProvider';
6
- import { a11yProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps, contentfulProps } from '../utils';
6
+ import { a11yProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps, contentfulProps, useUniqueId } from '../utils';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
8
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps, contentfulProps]);
9
9
  function selectBorderStyles(tokens) {
@@ -33,6 +33,7 @@ const ExpandCollapse = /*#__PURE__*/React.forwardRef((_ref, ref) => {
33
33
  dataSet,
34
34
  ...rest
35
35
  } = _ref;
36
+ const instanceId = useUniqueId('ExpandCollapse');
36
37
  const {
37
38
  currentValues: openIds,
38
39
  toggleOneValue: onToggle,
@@ -58,7 +59,8 @@ const ExpandCollapse = /*#__PURE__*/React.forwardRef((_ref, ref) => {
58
59
  onToggle,
59
60
  resetValues,
60
61
  setValues,
61
- unsetValues
62
+ unsetValues,
63
+ instanceId
62
64
  }) : children
63
65
  })
64
66
  });
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import ExpandCollapse from '../ExpandCollapse';
4
- import { getTokensPropType, selectSystemProps, contentfulProps } from '../utils';
4
+ import { getTokensPropType, selectSystemProps, contentfulProps, useUniqueId } from '../utils';
5
5
  import { variantProp } from '../utils/props';
6
6
  import ExpandCollapseMiniControl from './ExpandCollapseMiniControl';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -16,7 +16,7 @@ const ExpandCollapseMini = /*#__PURE__*/React.forwardRef((_ref, ref) => {
16
16
  dataSet,
17
17
  ...rest
18
18
  } = _ref;
19
- const expandCollapeMiniPanelId = 'ExpandCollapseMiniPanel';
19
+ const expandCollapeMiniPanelId = useUniqueId('ExpandCollapseMiniPanel');
20
20
  const handleChange = (openPanels, event) => {
21
21
  if (typeof onToggle === 'function') {
22
22
  const isOpen = openPanels.length > 0;
@@ -45,7 +45,9 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
45
45
  pressed
46
46
  });
47
47
  const {
48
- size,
48
+ fontSize,
49
+ lineHeight,
50
+ iconSize,
49
51
  icon
50
52
  } = useThemeTokens('ExpandCollapseMiniControl', tokens, variant, {
51
53
  expanded,
@@ -62,18 +64,38 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
62
64
  hover: linkHover
63
65
  } = linkState || {};
64
66
  const isHovered = hover || linkHover;
67
+ const iconBaselineOffset = 0;
68
+ const hoverTranslateY = 4;
69
+
70
+ // Calculate baseline alignment to vertically center icon with text
71
+ // This combines font and icon metrics with adjustments for visual balance
72
+ const fontBaseline = fontSize / hoverTranslateY; // Quarter of font size - adjusts for text's visual center point
73
+ const iconBaseline = iconSize / hoverTranslateY; // Quarter of icon size - adjusts for icon's visual center point
74
+ const staticOffset = hoverTranslateY; // Fixed downward adjustment to fine-tune vertical alignment
75
+ const sizeCompensation = -Math.abs(iconSize - fontSize); // Compensates when icon and text sizes differ significantly
76
+
77
+ const baselineAlignment = fontBaseline + iconBaseline - staticOffset + sizeCompensation;
65
78
  if (Platform.OS !== 'web') {
79
+ // For native platforms, use baseline alignment with optional offset
66
80
  return {
67
- iconTranslateY: -1
81
+ iconTranslateY: baselineAlignment + iconBaselineOffset
68
82
  };
69
83
  }
70
84
  if (isHovered) {
71
- // Include vertical icon animation on hover alongside built-in Link theme, the size is size4
85
+ // Apply animation offset to the baseline-aligned position
86
+ // When expanded: move icon UP (1.3 the hover distance for clear movement)
87
+ // When collapsed: move icon DOWN (single hover distance)
88
+ const hoverMovementDistance = 1.3;
89
+ const animationOffset = expanded ? -(hoverTranslateY * hoverMovementDistance) : hoverTranslateY;
72
90
  return {
73
- iconTranslateY: (expanded ? -1 : 1) * size
91
+ iconTranslateY: baselineAlignment + iconBaselineOffset + animationOffset
74
92
  };
75
93
  }
76
- return {};
94
+
95
+ // Default state uses baseline alignment with optional offset
96
+ return {
97
+ iconTranslateY: baselineAlignment + iconBaselineOffset
98
+ };
77
99
  };
78
100
  return /*#__PURE__*/_jsx(Link, {
79
101
  variant: appearance,
@@ -81,7 +103,9 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
81
103
  iconPosition: iconPosition,
82
104
  tokens: linkState => ({
83
105
  ...linkTokens,
84
- ...getTokens(linkState)
106
+ ...getTokens(linkState),
107
+ iconSize,
108
+ blockLineHeight: lineHeight
85
109
  }),
86
110
  ref: ref,
87
111
  ...presentationOnly,
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { viewports } from '@telus-uds/system-constants';
4
- import { a11yProps, viewProps, getA11yPropsFromHtmlTag, layoutTags, selectSystemProps, BaseView, StyleSheet, createMediaQueryStyles } from '../utils';
4
+ import { a11yProps, viewProps, getA11yPropsFromHtmlTag, layoutTags, selectSystemProps, BaseView, StyleSheet, createMediaQueryStyles, useResponsiveProp } from '../utils';
5
5
  import Row from './Row';
6
6
  import Col from './Col';
7
7
  import GutterContext from './providers/GutterContext';
@@ -10,6 +10,32 @@ import { useTheme } from '../ThemeProvider';
10
10
  import { useViewport } from '../ViewportProvider';
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
13
+ const CONTENT_MAX_WIDTH = 'max';
14
+ const CONTENT_FULL_WIDTH = 'full';
15
+
16
+ /**
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 (pixels), a string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
21
+ * @param {number|string} responsiveWidth - The responsive width to use when contentMinWidthValue is CONTENT_MAX_WIDTH.
22
+ * @returns {number|string|null} The resolved maximum width value, which can be a number, a string (e.g., '100%'), or null.
23
+ */
24
+ const resolveContentMaxWidth = (contentMinWidthValue, responsiveWidth) => {
25
+ if (!contentMinWidthValue) {
26
+ return null;
27
+ }
28
+ if (contentMinWidthValue === CONTENT_FULL_WIDTH) {
29
+ return '100%';
30
+ }
31
+ if (Number.isFinite(contentMinWidthValue)) {
32
+ return contentMinWidthValue;
33
+ }
34
+ if (contentMinWidthValue === CONTENT_MAX_WIDTH) {
35
+ return responsiveWidth;
36
+ }
37
+ return contentMinWidthValue;
38
+ };
13
39
 
14
40
  /**
15
41
  * A mobile-first flexbox grid.
@@ -29,35 +55,41 @@ const FlexGrid = /*#__PURE__*/React.forwardRef((_ref, ref) => {
29
55
  accessibilityRole,
30
56
  children,
31
57
  dataSet,
58
+ contentMinWidth,
32
59
  ...rest
33
60
  } = _ref;
34
61
  const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse]);
35
62
  const viewport = useViewport();
36
63
  const {
64
+ themeOptions,
37
65
  themeOptions: {
38
66
  enableMediaQueryStyleSheet
39
67
  }
40
68
  } = useTheme();
41
69
  let flexgridStyles;
42
70
  let mediaIds;
71
+ const contentMinWidthValue = useResponsiveProp(contentMinWidth);
72
+ const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth);
73
+ const maxWidth = resolveContentMaxWidth(contentMinWidthValue, responsiveWidth);
43
74
  const stylesByViewport = {
44
75
  xs: {
76
+ maxWidth: limitWidth ? viewports.map.get('sm') : maxWidth,
45
77
  flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
46
78
  },
47
79
  sm: {
48
- maxWidth: limitWidth && viewports.map.get('sm'),
80
+ maxWidth: limitWidth ? viewports.map.get('sm') : maxWidth,
49
81
  flexDirection: reverseLevel[1] ? 'column-reverse' : 'column'
50
82
  },
51
83
  md: {
52
- maxWidth: limitWidth && viewports.map.get('md'),
84
+ maxWidth: limitWidth ? viewports.map.get('md') : maxWidth,
53
85
  flexDirection: reverseLevel[2] ? 'column-reverse' : 'column'
54
86
  },
55
87
  lg: {
56
- maxWidth: limitWidth && viewports.map.get('lg'),
88
+ maxWidth: limitWidth ? viewports.map.get('lg') : maxWidth,
57
89
  flexDirection: reverseLevel[3] ? 'column-reverse' : 'column'
58
90
  },
59
91
  xl: {
60
- maxWidth: limitWidth && viewports.map.get('xl'),
92
+ maxWidth: limitWidth ? viewports.map.get('xl') : maxWidth,
61
93
  flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
62
94
  }
63
95
  };
@@ -151,7 +183,24 @@ FlexGrid.propTypes = {
151
183
  /**
152
184
  * The rows and columns of the Grid. Will typically be `FlexGrid.Row` and `FlexGrid.Col` components.
153
185
  */
154
- children: PropTypes.node.isRequired
186
+ children: PropTypes.node.isRequired,
187
+ /**
188
+ * The minimum width of the content in the FlexGrid.
189
+ * This prop accepts responsive values for different viewports. If a number is provided,
190
+ * it will be the max content width for the desired viewport.
191
+ * - `xs`: 'max' | 'full' | <number>
192
+ * - `sm`: 'max' | 'full' | <number>
193
+ * - `md`: 'max' | 'full' | <number>
194
+ * - `lg`: 'max' | 'full' | <number>
195
+ * - `xl`: 'max' | 'full' | <number>
196
+ */
197
+ contentMinWidth: PropTypes.shape({
198
+ xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
199
+ lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
200
+ md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
201
+ sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
202
+ xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
203
+ })
155
204
  };
156
205
  FlexGrid.Row = Row;
157
206
  FlexGrid.Col = Col;
@@ -32,7 +32,9 @@ const Icon = /*#__PURE__*/React.forwardRef((_ref, ref) => {
32
32
  width: themeTokens.size + themeTokens.width * 2,
33
33
  // sets the diameter of the circle which is the size of the icon plus twice the general padding established to obtain a perfect circle
34
34
  height: themeTokens.size + themeTokens.width * 2
35
- } : {};
35
+ } : {
36
+ padding: themeTokens.padding
37
+ };
36
38
  const getIconContentForMobile = () => {
37
39
  if (Object.keys(paddingStyles).length) {
38
40
  return /*#__PURE__*/_jsx(View, {
@@ -142,7 +142,7 @@ InputLabel.propTypes = {
142
142
  /**
143
143
  * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
144
144
  */
145
- tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
145
+ tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
146
146
  /**
147
147
  * Current number of characterts of an input text.
148
148
  */