@telus-uds/components-base 3.5.2 → 3.7.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 (52) hide show
  1. package/CHANGELOG.md +29 -2
  2. package/lib/cjs/Box/Box.js +53 -7
  3. package/lib/cjs/Card/Card.js +12 -2
  4. package/lib/cjs/Card/PressableCardBase.js +67 -1
  5. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +5 -0
  6. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +17 -26
  7. package/lib/cjs/Icon/Icon.js +2 -1
  8. package/lib/cjs/InputLabel/InputLabel.js +9 -3
  9. package/lib/cjs/InputSupports/InputSupports.js +6 -2
  10. package/lib/cjs/List/ListItemMark.js +8 -4
  11. package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +7 -1
  12. package/lib/cjs/ToggleSwitch/ToggleSwitch.js +11 -3
  13. package/lib/cjs/ToggleSwitch/ToggleSwitchGroup.js +9 -1
  14. package/lib/cjs/Tooltip/shared.js +5 -4
  15. package/lib/cjs/utils/getSpacingScale.js +66 -0
  16. package/lib/cjs/utils/index.js +9 -1
  17. package/lib/cjs/utils/props/inputSupportsProps.js +6 -2
  18. package/lib/esm/Box/Box.js +55 -9
  19. package/lib/esm/Card/Card.js +13 -3
  20. package/lib/esm/Card/PressableCardBase.js +67 -1
  21. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +5 -0
  22. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +17 -26
  23. package/lib/esm/Icon/Icon.js +2 -1
  24. package/lib/esm/InputLabel/InputLabel.js +9 -3
  25. package/lib/esm/InputSupports/InputSupports.js +6 -2
  26. package/lib/esm/List/ListItemMark.js +9 -5
  27. package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +7 -1
  28. package/lib/esm/ToggleSwitch/ToggleSwitch.js +11 -3
  29. package/lib/esm/ToggleSwitch/ToggleSwitchGroup.js +9 -1
  30. package/lib/esm/Tooltip/shared.js +2 -1
  31. package/lib/esm/utils/getSpacingScale.js +61 -0
  32. package/lib/esm/utils/index.js +2 -1
  33. package/lib/esm/utils/props/inputSupportsProps.js +6 -2
  34. package/lib/package.json +4 -4
  35. package/package.json +4 -4
  36. package/src/Box/Box.jsx +61 -8
  37. package/src/Card/Card.jsx +20 -3
  38. package/src/Card/PressableCardBase.jsx +60 -2
  39. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +5 -0
  40. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +17 -21
  41. package/src/Icon/Icon.jsx +6 -1
  42. package/src/InputLabel/InputLabel.jsx +7 -2
  43. package/src/InputSupports/InputSupports.jsx +6 -2
  44. package/src/List/ListItemMark.jsx +5 -3
  45. package/src/MultiSelectFilter/MultiSelectFilter.jsx +7 -1
  46. package/src/ToggleSwitch/ToggleSwitch.jsx +14 -2
  47. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +9 -1
  48. package/src/Tooltip/shared.js +2 -2
  49. package/src/utils/getSpacingScale.js +50 -0
  50. package/src/utils/index.js +1 -0
  51. package/src/utils/props/inputSupportsProps.js +6 -2
  52. package/types/ToggleSwitch.d.ts +1 -0
@@ -1,4 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
+ import tooltipPropTypes from '../../Tooltip/shared';
2
3
  export default {
3
4
  types: {
4
5
  /**
@@ -31,9 +32,12 @@ export default {
31
32
  */
32
33
  feedbackProps: PropTypes.object,
33
34
  /**
34
- * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
35
+ * an optional `Tooltip`. If set, tooltip will be shown next to the label.
36
+ * Props can be passed to the tooltip in one of the following ways.
37
+ * 1. `tooltip` as a string - The content of the tooltip.
38
+ * 2. `tooltip` as an object - Tooltip component props to be passed.
35
39
  */
36
- tooltip: PropTypes.string,
40
+ tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
37
41
  /**
38
42
  * Use to visually mark an input as valid or invalid.
39
43
  */
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.3.0",
15
+ "@telus-uds/system-theme-tokens": "^4.5.0",
16
16
  "airbnb-prop-types": "^2.16.0",
17
17
  "css-mediaquery": "^0.1.2",
18
18
  "expo-document-picker": "^13.0.1",
@@ -56,8 +56,8 @@
56
56
  "module": "lib/esm/index.js",
57
57
  "name": "@telus-uds/components-base",
58
58
  "peerDependencies": {
59
- "react": "^18.2.0",
60
- "react-dom": "^18.2.0",
59
+ "react": ">=18.2.0 <19.0.0",
60
+ "react-dom": ">=18.2.0 <19.0.0",
61
61
  "react-native": "^0.74.5",
62
62
  "react-native-web": "^0.19.10",
63
63
  "react-native-svg": "15.7.1"
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.5.2",
87
+ "version": "3.7.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.3.0",
15
+ "@telus-uds/system-theme-tokens": "^4.5.0",
16
16
  "airbnb-prop-types": "^2.16.0",
17
17
  "css-mediaquery": "^0.1.2",
18
18
  "expo-document-picker": "^13.0.1",
@@ -56,8 +56,8 @@
56
56
  "module": "lib/esm/index.js",
57
57
  "name": "@telus-uds/components-base",
58
58
  "peerDependencies": {
59
- "react": "^18.2.0",
60
- "react-dom": "^18.2.0",
59
+ "react": ">=18.2.0 <19.0.0",
60
+ "react-dom": ">=18.2.0 <19.0.0",
61
61
  "react-native": "^0.74.5",
62
62
  "react-native-web": "^0.19.10",
63
63
  "react-native-svg": "15.7.1"
@@ -84,6 +84,6 @@
84
84
  "standard-engine": {
85
85
  "skip": true
86
86
  },
87
- "version": "3.5.2",
87
+ "version": "3.7.0",
88
88
  "types": "types/index.d.ts"
89
89
  }
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
  )
package/src/Card/Card.jsx CHANGED
@@ -9,7 +9,14 @@ import {
9
9
  } from '../ThemeProvider'
10
10
  import { getTokensPropType, variantProp, StyleSheet, createMediaQueryStyles } from '../utils'
11
11
  import { useViewport } from '../ViewportProvider'
12
- import { a11yProps, linkProps, selectSystemProps, viewProps, responsiveProps } from '../utils/props'
12
+ import {
13
+ a11yProps,
14
+ linkProps,
15
+ selectSystemProps,
16
+ viewProps,
17
+ responsiveProps,
18
+ hrefAttrsProp
19
+ } from '../utils/props'
13
20
  import CardBase from './CardBase'
14
21
  import PressableCardBase from './PressableCardBase'
15
22
  import CheckboxButton from '../Checkbox/CheckboxButton'
@@ -227,6 +234,8 @@ const Card = React.forwardRef(
227
234
  tokens={getThemeTokens}
228
235
  dataSet={dataSet}
229
236
  onPress={onPress}
237
+ href={interactiveCard?.href}
238
+ hrefAttrs={interactiveCard?.hrefAttrs}
230
239
  {...selectProps(rest)}
231
240
  >
232
241
  {(cardState) => {
@@ -308,12 +317,16 @@ Card.propTypes = {
308
317
  - `radiogroup`
309
318
  - `checkbox`
310
319
  * - variant: The variant to be used for the interactive card
320
+ * - href: The href to be used for the interactive card
321
+ * - hrefAttrs: The href attributes to be used for the interactive card
311
322
  */
312
323
  interactiveCard: PropTypes.shape({
313
324
  body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
314
325
  tokens: getTokensPropType('Card'),
315
326
  selectionType: PropTypes.oneOf(Object.values(SelectionType)),
316
- variant: variantProp.propType
327
+ variant: variantProp.propType,
328
+ href: PropTypes.string,
329
+ hrefAttrs: PropTypes.shape(hrefAttrsProp.types)
317
330
  }),
318
331
  /**
319
332
  * Apply background image to the card.
@@ -326,7 +339,11 @@ Card.propTypes = {
326
339
  resizeMode: responsiveProps.getTypeOptionallyByViewport(
327
340
  PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
328
341
  )
329
- })
342
+ }),
343
+ /**
344
+ * Data set for the card.
345
+ */
346
+ dataSet: PropTypes.object
330
347
  }
331
348
 
332
349
  export default Card
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { Pressable, Platform } from 'react-native'
3
+ import { Pressable, Platform, View, StyleSheet } from 'react-native'
4
4
 
5
5
  import { useViewport } from '../ViewportProvider'
6
6
  import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider'
@@ -118,7 +118,57 @@ const PressableCardBase = React.forwardRef(
118
118
  const handleChange = linkProps.handleHref({ href, onPress })
119
119
  const handleKeyDown = (event) => {
120
120
  // The expected keyboard shortcut for selecting a focused item is the Space key
121
- if (event?.key === ' ') handleChange(event)
121
+ if (event?.key === ' ') handleChange?.(event)
122
+ }
123
+
124
+ const [hovered, setHovered] = React.useState(false)
125
+ const [focused, setFocused] = React.useState(false)
126
+ const [pressed, setPressed] = React.useState(false)
127
+
128
+ if (Platform.OS === 'web' && href) {
129
+ return (
130
+ <View
131
+ ref={ref}
132
+ style={getOuterBorderStyle({ pressed, focused, hovered })}
133
+ dataSet={dataSet}
134
+ onMouseEnter={() => setHovered(true)}
135
+ onMouseLeave={() => setHovered(false)}
136
+ {...selectProps({ ...rest, accessibilityRole })}
137
+ >
138
+ <a
139
+ href={href}
140
+ tabIndex={0}
141
+ onClick={(event) => {
142
+ setPressed(false)
143
+ handleChange?.(event)
144
+ }}
145
+ onKeyDown={(event) => {
146
+ if (event.key === ' ') {
147
+ setPressed(true)
148
+ handleKeyDown(event)
149
+ }
150
+ }}
151
+ onKeyUp={(event) => {
152
+ if (event.key === ' ') setPressed(false)
153
+ }}
154
+ onMouseDown={() => setPressed(true)}
155
+ onMouseUp={() => setPressed(false)}
156
+ onFocus={() => setFocused(true)}
157
+ onBlur={() => {
158
+ setFocused(false)
159
+ setPressed(false)
160
+ }}
161
+ style={staticStyles.container}
162
+ {...(hrefAttrs || {})}
163
+ >
164
+ <CardBase tokens={getCardTokens({ pressed, focused, hovered })}>
165
+ {typeof children === 'function'
166
+ ? children(getCardState({ pressed, focused, hovered }))
167
+ : children}
168
+ </CardBase>
169
+ </a>
170
+ </View>
171
+ )
122
172
  }
123
173
 
124
174
  return (
@@ -141,6 +191,14 @@ const PressableCardBase = React.forwardRef(
141
191
  )
142
192
  }
143
193
  )
194
+
195
+ const staticStyles = StyleSheet.create({
196
+ container: {
197
+ flex: 1,
198
+ display: 'flex'
199
+ }
200
+ })
201
+
144
202
  PressableCardBase.displayName = 'PressableCardBase'
145
203
 
146
204
  PressableCardBase.propTypes = {
@@ -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}
package/src/Icon/Icon.jsx CHANGED
@@ -24,7 +24,12 @@ const Icon = React.forwardRef(
24
24
  const size = scalesWithText ? themeTokens.size * fontScale : themeTokens.size
25
25
 
26
26
  const iconContent = (
27
- <IconComponent title={accessibilityLabel} size={size} color={themeTokens.color} />
27
+ <IconComponent
28
+ title={accessibilityLabel}
29
+ size={size}
30
+ color={themeTokens.color}
31
+ gradient={themeTokens.gradient}
32
+ />
28
33
  )
29
34
 
30
35
  const paddingStyles = variant?.padding
@@ -14,6 +14,7 @@ import {
14
14
 
15
15
  import LabelContent from './LabelContent'
16
16
  import Tooltip from '../Tooltip'
17
+ import tooltipPropTypes from '../Tooltip/shared'
17
18
 
18
19
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
19
20
 
@@ -56,10 +57,14 @@ const InputLabel = React.forwardRef(
56
57
  const themeTokens = useThemeTokens('InputLabel', tokens, variant)
57
58
 
58
59
  const hasTooltip = tooltip !== undefined
60
+
59
61
  const isHintInline = hintPosition === 'inline'
60
62
 
61
63
  const { themeOptions } = useTheme()
62
64
 
65
+ const getTooltipProps = () => {
66
+ return typeof tooltip === 'string' ? { content: tooltip } : tooltip
67
+ }
63
68
  return (
64
69
  <>
65
70
  <View ref={ref} style={staticStyles.container} {...selectProps(rest)}>
@@ -94,7 +99,7 @@ const InputLabel = React.forwardRef(
94
99
  }
95
100
  ]}
96
101
  >
97
- <Tooltip content={tooltip} copy={copy} />
102
+ <Tooltip copy={copy} {...getTooltipProps()} />
98
103
  </View>
99
104
  )}
100
105
  {maxCharacterAllowed && isHintInline && (
@@ -156,7 +161,7 @@ InputLabel.propTypes = {
156
161
  /**
157
162
  * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
158
163
  */
159
- tooltip: PropTypes.string,
164
+ tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
160
165
  /**
161
166
  * Current number of characterts of an input text.
162
167
  */
@@ -8,6 +8,7 @@ import { useThemeTokens } from '../ThemeProvider'
8
8
  import useInputSupports from './useInputSupports'
9
9
  import { getTokensPropType, useCopy } from '../utils'
10
10
  import dictionary from './dictionary'
11
+ import tooltipPropTypes from '../Tooltip/shared'
11
12
 
12
13
  const InputSupports = React.forwardRef(
13
14
  (
@@ -115,9 +116,12 @@ InputSupports.propTypes = {
115
116
  */
116
117
  feedbackProps: PropTypes.object,
117
118
  /**
118
- * Content of an optional `Tooltip`. If set, a tooltip button will be shown next to the label.
119
+ * an optional `Tooltip`. If set, tooltip will be shown next to the label.
120
+ * Props can be passed to the tooltip in one of the following ways.
121
+ * 1. `tooltip` as a string - The content of the tooltip.
122
+ * 2. `tooltip` as an object - Tooltip component props to be passed.
119
123
  */
120
- tooltip: PropTypes.string,
124
+ tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
121
125
  /**
122
126
  * Use to visually mark an input as valid or invalid.
123
127
  */
@@ -45,8 +45,6 @@ const selectBulletContainerStyles = ({
45
45
  * icon or bullet nicely against the first line of text in a ListIconContent.
46
46
  */
47
47
  const ListItemMark = React.forwardRef(({ icon, iconColor, iconSize, tokens = {} }, ref) => {
48
- const IconComponent = icon || <></>
49
-
50
48
  const themeTokens = typeof tokens === 'function' ? tokens() : tokens
51
49
 
52
50
  const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
@@ -56,7 +54,11 @@ const ListItemMark = React.forwardRef(({ icon, iconColor, iconSize, tokens = {}
56
54
  const iconTokens = selectItemIconTokens(themeTokens)
57
55
  return (
58
56
  <View style={[sideItemContainerStyles, bulletContainerStyles]}>
59
- <IconComponent size={iconSize || iconTokens.size} color={iconColor || iconTokens.color} />
57
+ <Icon
58
+ icon={icon}
59
+ tokens={{ size: iconSize ?? iconTokens.size }}
60
+ variant={{ color: iconColor ?? iconTokens.color }}
61
+ />
60
62
  </View>
61
63
  )
62
64
  }
@@ -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',
@@ -107,6 +107,7 @@ const ToggleSwitch = React.forwardRef(
107
107
  variant,
108
108
  accessibilityRole = 'switch',
109
109
  accessibilityLabel = label,
110
+ togglePosition = 'end',
110
111
  ...rest
111
112
  },
112
113
  ref
@@ -125,8 +126,13 @@ const ToggleSwitch = React.forwardRef(
125
126
  selectButtonTokens(getTokens(buttonState), getTokens(themeTokens))
126
127
  const uniqueId = useUniqueId('toggleSwitch')
127
128
  const inputId = id ?? uniqueId
129
+
128
130
  return (
129
- <StackView space={themeTokens.space} direction="row" tokens={{ alignItems: 'center' }}>
131
+ <StackView
132
+ space={themeTokens.space}
133
+ direction={togglePosition === 'start' ? 'row-reverse' : 'row'}
134
+ tokens={{ alignItems: 'center', justifyContent: 'start' }}
135
+ >
130
136
  {Boolean(label) && (
131
137
  <View style={[selectLabelStyles(themeTokens), staticStyles.containText]}>
132
138
  <InputLabel
@@ -225,7 +231,13 @@ ToggleSwitch.propTypes = {
225
231
  /**
226
232
  * Content of an optional Tooltip. If set, a tooltip button will be shown next to the label.
227
233
  */
228
- tooltip: PropTypes.string
234
+ tooltip: PropTypes.string,
235
+ /**
236
+ * Controls the position of the switch in relation to the label.
237
+ * - 'start': switch on the start, before the `label` and `tooltip`.
238
+ * - 'end': switch to the end, after the `label` and `tooltip` (default).
239
+ */
240
+ togglePosition: PropTypes.oneOf(['start', 'end'])
229
241
  }
230
242
 
231
243
  const staticStyles = StyleSheet.create({
@@ -87,6 +87,7 @@ const ToggleSwitchGroup = React.forwardRef(
87
87
  onChange: itemOnChange,
88
88
  ref: itemRef,
89
89
  tooltip: itemTooltip,
90
+ togglePosition,
90
91
  ...itemRest
91
92
  },
92
93
  index
@@ -117,6 +118,7 @@ const ToggleSwitchGroup = React.forwardRef(
117
118
  inactive={inactive}
118
119
  label={label}
119
120
  tooltip={itemTooltip}
121
+ togglePosition={togglePosition}
120
122
  {...itemA11y}
121
123
  {...selectItemProps(itemRest)}
122
124
  />
@@ -182,7 +184,13 @@ ToggleSwitchGroup.propTypes = {
182
184
  /**
183
185
  * An optional ref for one individual ToggleSwitch in the ToggleSwitchGroup
184
186
  */
185
- ref: ABBPropTypes.ref()
187
+ ref: ABBPropTypes.ref(),
188
+ /**
189
+ * Controls the position of the switch in relation to the label.
190
+ * - 'start': switch on the start, before the `label` and `tooltip`.
191
+ * - 'end': switch to the end, after the `label` and `tooltip` (default).
192
+ */
193
+ togglePosition: PropTypes.oneOf(['start', 'end'])
186
194
  })
187
195
  ),
188
196
  /**
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types'
2
-
3
- import { getTokensPropType, variantProp } from '../utils'
2
+ import { getTokensPropType } from '../utils/props/tokens'
3
+ import variantProp from '../utils/props/variantProp'
4
4
 
5
5
  const propTypes = {
6
6
  /**
@@ -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'