@telus-uds/components-base 3.5.1 → 3.6.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 (36) hide show
  1. package/CHANGELOG.md +27 -2
  2. package/lib/cjs/Box/Box.js +53 -7
  3. package/lib/cjs/Carousel/Carousel.js +34 -47
  4. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +4 -6
  5. package/lib/cjs/Carousel/Constants.js +5 -2
  6. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +5 -0
  7. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +17 -26
  8. package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +8 -2
  9. package/lib/cjs/Notification/Notification.js +26 -16
  10. package/lib/cjs/utils/getSpacingScale.js +66 -0
  11. package/lib/cjs/utils/index.js +9 -1
  12. package/lib/cjs/utils/useResponsiveProp.js +1 -1
  13. package/lib/esm/Box/Box.js +55 -9
  14. package/lib/esm/Carousel/Carousel.js +35 -48
  15. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +4 -6
  16. package/lib/esm/Carousel/Constants.js +4 -1
  17. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +5 -0
  18. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +17 -26
  19. package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +8 -2
  20. package/lib/esm/Notification/Notification.js +27 -17
  21. package/lib/esm/utils/getSpacingScale.js +61 -0
  22. package/lib/esm/utils/index.js +2 -1
  23. package/lib/esm/utils/useResponsiveProp.js +1 -1
  24. package/lib/package.json +4 -4
  25. package/package.json +4 -4
  26. package/src/Box/Box.jsx +61 -8
  27. package/src/Carousel/Carousel.jsx +47 -58
  28. package/src/Carousel/CarouselItem/CarouselItem.jsx +4 -6
  29. package/src/Carousel/Constants.js +3 -0
  30. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +5 -0
  31. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +17 -21
  32. package/src/MultiSelectFilter/MultiSelectFilter.jsx +8 -2
  33. package/src/Notification/Notification.jsx +29 -12
  34. package/src/utils/getSpacingScale.js +50 -0
  35. package/src/utils/index.js +1 -0
  36. package/src/utils/useResponsiveProp.js +1 -1
package/src/Box/Box.jsx CHANGED
@@ -1,9 +1,15 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View, ScrollView, Platform, StyleSheet, ImageBackground, Image } from 'react-native'
4
- import { useThemeTokens } from '../ThemeProvider'
4
+ import {
5
+ useTheme,
6
+ useThemeTokens,
7
+ useResponsiveThemeTokens,
8
+ useThemeTokensCallback
9
+ } from '../ThemeProvider'
5
10
  import {
6
11
  a11yProps,
12
+ createMediaQueryStyles,
7
13
  getA11yPropsFromHtmlTag,
8
14
  getTokensPropType,
9
15
  layoutTags,
@@ -13,9 +19,12 @@ import {
13
19
  useResponsiveProp,
14
20
  useSpacingScale,
15
21
  variantProp,
16
- viewProps
22
+ viewProps,
23
+ StyleSheet as RNMQStyleSheet,
24
+ getSpacingScale
17
25
  } from '../utils'
18
26
  import backgroundImageStylesMap from './backgroundImageStylesMap'
27
+ import { useViewport } from '../ViewportProvider'
19
28
 
20
29
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
21
30
 
@@ -212,8 +221,20 @@ const Box = React.forwardRef(
212
221
  ...selectProps(rest)
213
222
  }
214
223
 
215
- const themeTokens = useThemeTokens('Box', tokens, variant)
216
- const styles = {
224
+ const viewport = useViewport()
225
+ const { themeOptions } = useTheme()
226
+ const { enableMediaQueryStyleSheet } = themeOptions
227
+
228
+ const useTokens = enableMediaQueryStyleSheet ? useResponsiveThemeTokens : useThemeTokens
229
+ const themeTokens = useTokens(
230
+ 'Box',
231
+ tokens,
232
+ variant,
233
+ !enableMediaQueryStyleSheet && { viewport }
234
+ )
235
+ const getSpacingTokens = useThemeTokensCallback('spacingScale')
236
+
237
+ let boxStyles = {
217
238
  flex,
218
239
  paddingLeft: useSpacingScale(left),
219
240
  paddingRight: useSpacingScale(right),
@@ -221,10 +242,40 @@ const Box = React.forwardRef(
221
242
  paddingBottom: useSpacingScale(bottom),
222
243
  ...selectBoxStyles(themeTokens, customGradient)
223
244
  }
245
+ let boxMediaIds
246
+
247
+ if (enableMediaQueryStyleSheet) {
248
+ const { transformedThemeTokens } = Object.entries(themeTokens).reduce(
249
+ (acc, [vp]) => {
250
+ acc.transformedThemeTokens[vp] = {
251
+ paddingLeft: getSpacingScale(left, vp, getSpacingTokens),
252
+ paddingRight: getSpacingScale(right, vp, getSpacingTokens),
253
+ paddingTop: getSpacingScale(top, vp, getSpacingTokens),
254
+ paddingBottom: getSpacingScale(bottom, vp, getSpacingTokens)
255
+ }
256
+ return acc
257
+ },
258
+ {
259
+ transformedThemeTokens: {}
260
+ }
261
+ )
262
+
263
+ const mediaQueryStyles = createMediaQueryStyles(transformedThemeTokens)
264
+ const { ids } = RNMQStyleSheet.create({
265
+ box: {
266
+ ...mediaQueryStyles
267
+ }
268
+ })
269
+ boxStyles = {
270
+ flex,
271
+ ...selectBoxStyles(themeTokens[viewport], customGradient)
272
+ }
273
+ boxMediaIds = ids.box
274
+ }
224
275
 
225
276
  let content = children
226
277
  if (typeof customGradient === 'function')
227
- content = customGradient(styles.colors, styles)(children)
278
+ content = customGradient(boxStyles.colors, boxStyles)(children)
228
279
 
229
280
  const { src = '', alt = '', resizeMode = '', position = '', align = '' } = backgroundImage || {}
230
281
  const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover')
@@ -256,17 +307,19 @@ const Box = React.forwardRef(
256
307
  }
257
308
  }, [backgroundImage, backgroundImageWidth, backgroundImageHeight, src])
258
309
 
310
+ const dataSetValue = boxMediaIds ? { media: boxMediaIds, ...dataSet } : dataSet
311
+
259
312
  if (scroll) {
260
313
  const scrollProps = typeof scroll === 'object' ? scroll : {}
261
- scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
314
+ scrollProps.contentContainerStyle = [boxStyles, scrollProps.contentContainerStyle]
262
315
  return (
263
- <ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSet} ref={ref}>
316
+ <ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSetValue} ref={ref}>
264
317
  {content}
265
318
  </ScrollView>
266
319
  )
267
320
  }
268
321
  return (
269
- <View {...props} style={styles} testID={testID} dataSet={dataSet} ref={ref}>
322
+ <View {...props} style={boxStyles} testID={testID} dataSet={dataSetValue} ref={ref}>
270
323
  {content}
271
324
  </View>
272
325
  )
@@ -31,7 +31,10 @@ import {
31
31
  ITEMS_PER_VIEWPORT_LG_XL,
32
32
  DEFAULT_POSITION_OFFSET,
33
33
  LARGE_VIEWPORT_MARGIN,
34
- DEFAULT_VIEWPORT_MARGIN
34
+ DEFAULT_VIEWPORT_MARGIN,
35
+ PEEKING_MULTIPLIER,
36
+ ACTIVE_INDEX_OFFSET_MULTIPLIER,
37
+ NEGATIVE_MULTIPLIER
35
38
  } from './Constants'
36
39
 
37
40
  const TRANSITION_MODES = {
@@ -245,63 +248,26 @@ const selectNavigationStyles = (tabs, enableHero, viewport) => {
245
248
  }
246
249
 
247
250
  /**
248
- * Calculates the final width of the carousel container for displacement purposes based on various conditions.
251
+ * Calculates the final width of a carousel container based on the provided parameters.
249
252
  *
250
- * @param {number} containerWidth - The initial width of the container.
251
- * @param {boolean} enablePeeking - Flag indicating if peeking is enabled.
252
- * @param {object} viewport - The viewport properties.
253
- * @param {object} activeIndexRef - A ref object holding the current active index.
254
- * @param {number} totalItems - The total number of items in the carousel.
255
- * @param {number} calcDelta - The delta value used for calculations.
256
- * @returns {number} - The final calculated width of the container.
257
- *
258
- * The function adjusts the container width for displacement purposes based on the peeking properties and the position
259
- * of the active item (first, middle, or last). It considers different peeking spaces and gaps
260
- * to ensure the correct width is calculated for the carousel to display properly.
253
+ * @param {number} containerWidth - The width of the carousel container.
254
+ * @param {boolean} enablePeeking - Flag indicating whether peeking is enabled.
255
+ * @param {Object} viewport - The viewport configuration object used to determine peeking properties.
256
+ * @param {React.MutableRefObject<number>} activeIndexRef - A ref object holding the current active index of the carousel.
257
+ * @returns {number} The calculated final width of the carousel container.
261
258
  */
262
- const calculateFinalWidth = (
263
- containerWidth,
264
- enablePeeking,
265
- viewport,
266
- activeIndexRef,
267
- totalItems,
268
- calcDelta
269
- ) => {
259
+ const calculateFinalWidth = (containerWidth, enablePeeking, viewport, activeIndexRef) => {
270
260
  let finalWidth = containerWidth
271
261
 
272
262
  if (enablePeeking) {
273
- const { peekingFirstSpace, peekingGap, peekingLastSpace, peekingMiddleSpace } =
274
- getPeekingProps(viewport)
263
+ const { peekingGap, peekingMiddleSpace, peekingMarginLeft } = getPeekingProps(viewport)
264
+ const slideWide =
265
+ containerWidth - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER)
275
266
 
276
- const isFirst = activeIndexRef.current === 0
277
- const isLast = activeIndexRef.current + 1 >= totalItems
278
- const isMiddle = !isFirst && !isLast
279
-
280
- const basePeekingSpace = peekingFirstSpace + peekingGap + peekingMiddleSpace
281
- const middlePeekingSpace = peekingMiddleSpace * 2 + peekingGap
282
-
283
- if (isFirst) {
284
- finalWidth -= basePeekingSpace
285
- if (activeIndexRef.current + 1 === totalItems - 1) {
286
- finalWidth -= peekingLastSpace - peekingMiddleSpace
287
- }
288
- } else if (isMiddle) {
289
- if (calcDelta > 0) {
290
- finalWidth -= basePeekingSpace + middlePeekingSpace * activeIndexRef.current
291
- if (activeIndexRef.current + 1 === totalItems - 1) {
292
- finalWidth -= peekingLastSpace - peekingMiddleSpace
293
- }
294
- } else {
295
- finalWidth += basePeekingSpace + middlePeekingSpace * (activeIndexRef.current - 2)
296
- if (activeIndexRef.current - 1 === 0) {
297
- finalWidth -= peekingFirstSpace - peekingMiddleSpace
298
- }
299
- }
300
- } else if (isLast) {
301
- finalWidth += basePeekingSpace + middlePeekingSpace * (activeIndexRef.current - 2)
302
- if (activeIndexRef.current - 1 === 0) {
303
- finalWidth -= peekingLastSpace - peekingMiddleSpace
304
- }
267
+ if (activeIndexRef.current === 0) {
268
+ finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace
269
+ } else {
270
+ finalWidth = slideWide + peekingGap
305
271
  }
306
272
  }
307
273
 
@@ -495,15 +461,40 @@ const Carousel = React.forwardRef(
495
461
  )
496
462
 
497
463
  const updateOffset = React.useCallback(() => {
498
- animatedX.current = containerLayoutRef.current.width * activeIndexRef.current * -1
464
+ if (enablePeeking) {
465
+ const { peekingGap, peekingMiddleSpace, peekingMarginLeft } = getPeekingProps(viewport)
466
+
467
+ let finalWidth
468
+ const slideWide =
469
+ containerLayoutRef.current.width -
470
+ (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER)
471
+
472
+ if (activeIndexRef.current === 0) {
473
+ finalWidth = 0
474
+ } else {
475
+ finalWidth =
476
+ slideWide +
477
+ peekingMarginLeft -
478
+ peekingMiddleSpace +
479
+ (slideWide + peekingGap) * (activeIndexRef.current - ACTIVE_INDEX_OFFSET_MULTIPLIER)
480
+ }
481
+
482
+ animatedX.current = finalWidth * NEGATIVE_MULTIPLIER
483
+ } else {
484
+ animatedX.current =
485
+ containerLayoutRef.current.width * activeIndexRef.current * NEGATIVE_MULTIPLIER
486
+ }
487
+
499
488
  animatedY.current = 0
500
489
  pan.setOffset({
501
490
  x: animatedX.current,
502
491
  y: animatedY.current
503
492
  })
504
493
  pan.setValue({ x: 0, y: 0 })
494
+
505
495
  if (enableHero) {
506
- heroAnimatedX.current = heroContainerLayoutRef.current.width * activeIndexRef.current * -1
496
+ heroAnimatedX.current =
497
+ heroContainerLayoutRef.current.width * activeIndexRef.current * NEGATIVE_MULTIPLIER
507
498
  heroAnimatedY.current = 0
508
499
  heroPan.setOffset({
509
500
  x: heroAnimatedX.current,
@@ -511,7 +502,7 @@ const Carousel = React.forwardRef(
511
502
  })
512
503
  heroPan.setValue({ x: 0, y: 0 })
513
504
  }
514
- }, [pan, animatedX, heroPan, heroAnimatedX, enableHero])
505
+ }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking])
515
506
 
516
507
  const animate = React.useCallback(
517
508
  (panToAnimate, toValue, toIndex) => {
@@ -593,9 +584,7 @@ const Carousel = React.forwardRef(
593
584
  containerLayoutRef.current.width,
594
585
  enablePeeking,
595
586
  viewport,
596
- activeIndexRef,
597
- totalItems,
598
- calcDelta
587
+ activeIndexRef
599
588
  )
600
589
 
601
590
  toValue.x = finalWidth * -1 * calcDelta
@@ -19,19 +19,18 @@ const selectContainerStyle = ({
19
19
  elementIndex,
20
20
  enablePeeking,
21
21
  peekingMarginLeft,
22
- peekingFirstSpace,
23
22
  peekingGap,
24
23
  hidden,
25
24
  enableDisplayMultipleItemsPerSlide,
26
- viewport
25
+ viewport,
26
+ peekingMiddleSpace
27
27
  }) => {
28
28
  let adjustedWidth = width
29
29
  let marginLeft = 0
30
- const marginRight = 0
31
30
 
32
31
  if (enablePeeking) {
33
32
  const isFirst = elementIndex === 0
34
- adjustedWidth = width - (peekingMarginLeft + peekingGap + peekingFirstSpace)
33
+ adjustedWidth = width - (peekingMiddleSpace * 2 + peekingGap * 2)
35
34
  if (isFirst) {
36
35
  marginLeft = peekingMarginLeft
37
36
  } else {
@@ -73,8 +72,7 @@ const selectContainerStyle = ({
73
72
 
74
73
  const style = {
75
74
  width: adjustedWidth,
76
- marginLeft,
77
- marginRight
75
+ marginLeft
78
76
  }
79
77
 
80
78
  if (hidden && Platform.OS === 'web') {
@@ -5,3 +5,6 @@ export const HERO_POSITION_OFFSET = 20
5
5
  export const DEFAULT_POSITION_OFFSET = 0
6
6
  export const LARGE_VIEWPORT_MARGIN = 40
7
7
  export const DEFAULT_VIEWPORT_MARGIN = 10
8
+ export const PEEKING_MULTIPLIER = 2
9
+ export const ACTIVE_INDEX_OFFSET_MULTIPLIER = 1
10
+ export const NEGATIVE_MULTIPLIER = -1
@@ -2,6 +2,7 @@ import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import ExpandCollapse from '../ExpandCollapse'
4
4
  import { getTokensPropType, selectSystemProps, contentfulProps } from '../utils'
5
+ import { variantProp } from '../utils/props'
5
6
  import ExpandCollapseMiniControl from './ExpandCollapseMiniControl'
6
7
 
7
8
  const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([contentfulProps])
@@ -79,6 +80,10 @@ ExpandCollapseMini.propTypes = {
79
80
  * Optional variant object to override the default theme tokens
80
81
  */
81
82
  tokens: getTokensPropType('ExpandCollapseMini'),
83
+ /**
84
+ * ExpandCollapseMini variant.
85
+ */
86
+ variant: variantProp.propType,
82
87
  /**
83
88
  * The dataSet prop allows to pass data-* attributes element to the component.
84
89
  */
@@ -15,13 +15,6 @@ const presentationOnly = {
15
15
  focusable: false // Stop RNW from setting tabIndex={0}: focus goes to Control only
16
16
  }
17
17
 
18
- const selectLinkTokens = ({ color, textLine, lineHeight, fontSize }) => ({
19
- color,
20
- textLine,
21
- blockLineHeight: lineHeight,
22
- blockFontSize: fontSize
23
- })
24
-
25
18
  const ExpandCollapseMiniControl = React.forwardRef(
26
19
  (
27
20
  {
@@ -35,20 +28,24 @@ const ExpandCollapseMiniControl = React.forwardRef(
35
28
  },
36
29
  ref
37
30
  ) => {
38
- const { expanded, hover, focus } = pressableState || {}
39
- // we only want focus outline when focusing, if user is pressing we don't want the border.
40
- const { outerBorderColor } = useThemeTokens(
31
+ const { expanded, hover, focus, pressed } = pressableState || {}
32
+ const { quiet } = variant
33
+ const isFocusVisible = Platform.OS === 'web' ? focus && !pressed && !hover : expanded
34
+
35
+ const linkTokens = useThemeTokens(
41
36
  'Link',
42
37
  {},
43
- {},
44
- { focus: Platform.OS !== 'web' ? expanded : focus }
45
- )
46
- const { size, icon, ...themeTokens } = useThemeTokens(
47
- 'ExpandCollapseMiniControl',
48
- tokens,
49
- variant,
50
- { expanded, focus }
38
+ { ...variant, quiet: expanded ?? quiet },
39
+ {
40
+ focus: isFocusVisible,
41
+ hover,
42
+ pressed
43
+ }
51
44
  )
45
+ const { size, icon } = useThemeTokens('ExpandCollapseMiniControl', tokens, variant, {
46
+ expanded,
47
+ focus
48
+ })
52
49
 
53
50
  // Choose hover styles when any part of Control is hoverred
54
51
  const appearance = { ...variant, hover }
@@ -75,9 +72,8 @@ const ExpandCollapseMiniControl = React.forwardRef(
75
72
  icon={icon}
76
73
  iconPosition={iconPosition}
77
74
  tokens={(linkState) => ({
78
- ...getTokens(linkState),
79
- ...selectLinkTokens(themeTokens),
80
- outerBorderColor
75
+ ...linkTokens,
76
+ ...getTokens(linkState)
81
77
  })}
82
78
  ref={ref}
83
79
  {...presentationOnly}
@@ -134,6 +134,10 @@ const MultiSelectFilter = React.forwardRef(
134
134
  buttonIconPadding,
135
135
  subtitleColor,
136
136
  dividerColor,
137
+ iconColor,
138
+ buttonBackgroundColor,
139
+ iconColorSelected,
140
+ buttonBackgroundColorSelected,
137
141
  ...restTokens
138
142
  } = useThemeTokens(
139
143
  'MultiSelectFilter',
@@ -154,7 +158,9 @@ const MultiSelectFilter = React.forwardRef(
154
158
  paddingBottom: labelPaddingBottom,
155
159
  paddingLeft: labelPaddingLeft,
156
160
  paddingRight: labelPaddingRight,
157
- iconBackground: buttonIconBackgroundColor
161
+ iconBackground: buttonIconBackgroundColor,
162
+ iconColor: isSelected ? iconColorSelected : iconColor,
163
+ backgroundColor: isSelected ? buttonBackgroundColorSelected : buttonBackgroundColor
158
164
  }
159
165
  const getButtonDropdownTokens = useThemeTokensCallback(
160
166
  'ButtonDropdown',
@@ -236,7 +242,7 @@ const MultiSelectFilter = React.forwardRef(
236
242
  const [isScrolling, setIsScrolling] = React.useState(false)
237
243
  const [scrollViewHeight, setScrollViewHeight] = React.useState(0)
238
244
  const [rowHeight, setRowHeight] = React.useState(0)
239
- const modalRef = useScrollBlocking(isOpen)
245
+ const modalRef = useScrollBlocking(isOpen && viewport === 'xs')
240
246
  const windowWidth = Dimensions.get('window').width
241
247
  const windowHeight = Dimensions.get('window').height
242
248
 
@@ -18,7 +18,8 @@ import {
18
18
  viewProps,
19
19
  wrapStringsInText,
20
20
  createMediaQueryStyles,
21
- StyleSheet
21
+ StyleSheet,
22
+ useResponsiveProp
22
23
  } from '../utils'
23
24
  import IconButton from '../IconButton'
24
25
  import useCopy from '../utils/useCopy'
@@ -64,8 +65,8 @@ const selectDismissButtonContainerStyles = ({ dismissButtonGap }) => ({
64
65
  placeContent: 'start'
65
66
  })
66
67
 
67
- const selectContentContainerStyle = (themeTokens, maxWidth, viewport, system) => ({
68
- maxWidth: viewport === 'xl' && system === true ? maxWidth : '100%',
68
+ const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport) => ({
69
+ maxWidth: system && viewport === 'xl' ? maxWidth : '100%',
69
70
  width: '100%',
70
71
  paddingRight: themeTokens?.containerPaddingRight,
71
72
  paddingLeft: themeTokens?.containerPaddingLeft
@@ -82,7 +83,7 @@ const getMediaQueryStyles = (
82
83
  ) => {
83
84
  const transformedSelectContainerStyles = Object.entries(themeTokens).reduce(
84
85
  (acc, [vp, viewportTokens]) => {
85
- acc[vp] = selectContainerStyles({ ...viewportTokens })
86
+ acc[vp] = { ...selectContainerStyles({ ...viewportTokens }), flexDirection: 'column' }
86
87
  return acc
87
88
  },
88
89
  {}
@@ -92,18 +93,31 @@ const getMediaQueryStyles = (
92
93
 
93
94
  const { ids: containerIds, styles: containerStyles } = StyleSheet.create({
94
95
  container: {
95
- flexDirection: 'column',
96
96
  ...selectContainerMediaQueryStyles
97
97
  }
98
98
  })
99
99
 
100
+ const transformedSelectContentContainerStyles = Object.entries(themeTokens).reduce(
101
+ (acc, [vp, viewportTokens]) => {
102
+ acc[vp] = {
103
+ ...selectContentContainerStyle(viewportTokens, maxWidth, system, vp),
104
+ flexDirection: 'row',
105
+ flexShrink: 1,
106
+ justifyContent: 'space-between',
107
+ ...(system && { alignSelf: 'center' })
108
+ }
109
+ return acc
110
+ },
111
+ {}
112
+ )
113
+
114
+ const selectContentContainerMediaQueryStyles = createMediaQueryStyles(
115
+ transformedSelectContentContainerStyles
116
+ )
117
+
100
118
  const { ids: contentContainerIds, styles: contentContainerStyles } = StyleSheet.create({
101
119
  contentContainer: {
102
- flexDirection: 'row',
103
- flexShrink: 1,
104
- justifyContent: 'space-between',
105
- ...selectContentContainerStyle(themeTokens, maxWidth, viewport, system),
106
- ...(system && { alignSelf: 'center' })
120
+ ...selectContentContainerMediaQueryStyles
107
121
  }
108
122
  })
109
123
 
@@ -167,7 +181,7 @@ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, view
167
181
  flexDirection: 'row',
168
182
  flexShrink: 1,
169
183
  justifyContent: 'space-between',
170
- ...selectContentContainerStyle(themeTokens, maxWidth, viewport, system),
184
+ ...selectContentContainerStyle(themeTokens, maxWidth, system, viewport),
171
185
  ...(system && { alignSelf: 'center' })
172
186
  }
173
187
  },
@@ -252,7 +266,10 @@ const Notification = React.forwardRef(
252
266
  system: isSystemEnabled,
253
267
  viewport
254
268
  })
255
- const maxWidth = viewports.map.get(viewports.xl)
269
+ const maxWidth = useResponsiveProp(
270
+ themeOptions?.contentMaxWidth,
271
+ viewports.map.get(viewports.xl)
272
+ )
256
273
 
257
274
  const notificationComponentRef = React.useRef({
258
275
  containerStyles: {},
@@ -0,0 +1,50 @@
1
+ import { resolveResponsiveProp } from './useResponsiveProp'
2
+
3
+ /**
4
+ * Resolves spacing options from the provided space object.
5
+ *
6
+ * @param {Object} space - The space configuration object.
7
+ * @param {Object} [space.options] - The options for spacing.
8
+ * @param {number|string} [space.options.size] - The size of the spacing. Can be a number or a string.
9
+ * @param {string} [space.options.variant] - The variant of the spacing.
10
+ * @param {number} [space.options.subtract=0] - A value to subtract from the spacing size.
11
+ * @returns {Object} An object containing resolved spacing tokens, variant, overridden flag, and subtract value.
12
+ * @property {Object} tokens - The resolved spacing tokens.
13
+ * @property {number|string} tokens.size - The size of the spacing.
14
+ * @property {string} [variant] - The variant of the spacing.
15
+ * @property {boolean} overridden - Indicates if the size is explicitly overridden as a number.
16
+ * @property {number} subtract - The value to subtract from the spacing size.
17
+ */
18
+ const resolveSpacingOptions = (space) => {
19
+ if (!space?.options) return {}
20
+
21
+ const { size, variant, subtract = 0 } = space.options
22
+ const overridden = typeof size === 'number'
23
+
24
+ return { tokens: { size }, variant, overridden, subtract }
25
+ }
26
+
27
+ /**
28
+ * Calculates the spacing scale based on the provided space value, viewport, and spacing tokens.
29
+ *
30
+ * @param {Object} spaceValue - The space value configuration, which may include responsive properties.
31
+ * @param {Object} viewport - The current viewport dimensions or configuration.
32
+ * @param {Function} getSpacingTokens - A function that retrieves spacing tokens based on the provided options.
33
+ * @returns {number} The calculated spacing scale, ensuring it is non-negative.
34
+ */
35
+ const getSpacingScale = (spaceValue, viewport, getSpacingTokens) => {
36
+ const { tokens, variant, overridden, subtract = 0 } = resolveSpacingOptions(spaceValue)
37
+ const space = !overridden && (spaceValue?.space ?? resolveResponsiveProp(spaceValue, viewport, 0))
38
+
39
+ const { size } = getSpacingTokens(
40
+ {
41
+ ...variant,
42
+ space: typeof space === 'number' ? space : 0,
43
+ viewport
44
+ },
45
+ tokens
46
+ )
47
+ return Math.max(size - subtract, 0)
48
+ }
49
+
50
+ export default getSpacingScale
@@ -23,3 +23,4 @@ export { default as htmlAttrs } from './htmlAttrs'
23
23
  export { transformGradient } from './transformGradient'
24
24
  export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte'
25
25
  export { default as formatImageSource } from './formatImageSource'
26
+ export { default as getSpacingScale } from './getSpacingScale'
@@ -25,7 +25,7 @@ export const resolveResponsiveProp = (prop, viewport, defaultValue) => {
25
25
  : // If no current viewport is available, default to smallest viewport
26
26
  prop[viewports.keys.find((key) => hasOwnProperty(prop, key))]
27
27
 
28
- return value === undefined ? defaultValue : value
28
+ return value === undefined || value === null ? defaultValue : value
29
29
  }
30
30
 
31
31
  /**