@telus-uds/components-base 3.21.0 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -1
- package/lib/cjs/Button/Button.js +12 -3
- package/lib/cjs/Button/ButtonBase.js +63 -10
- package/lib/cjs/Button/ButtonDropdown.js +2 -0
- package/lib/cjs/Button/ButtonGroup.js +45 -38
- package/lib/cjs/Button/propTypes.js +6 -0
- package/lib/cjs/Card/PressableCardBase.js +3 -1
- package/lib/cjs/Carousel/Carousel.js +63 -22
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/cjs/Icon/Icon.js +8 -11
- package/lib/cjs/Icon/IconText.js +0 -1
- package/lib/cjs/Listbox/GroupControl.js +33 -39
- package/lib/cjs/Listbox/Listbox.js +22 -13
- package/lib/cjs/Listbox/ListboxGroup.js +2 -1
- package/lib/cjs/Listbox/ListboxOverlay.js +5 -2
- package/lib/cjs/Listbox/PressableItem.js +8 -4
- package/lib/cjs/TextInput/TextInputBase.js +5 -1
- package/lib/cjs/ThemeProvider/index.js +9 -1
- package/lib/cjs/ThemeProvider/useResponsiveThemeTokensCallback.js +124 -0
- package/lib/cjs/Validator/Validator.js +171 -135
- package/lib/cjs/index.js +7 -0
- package/lib/esm/Button/Button.js +13 -4
- package/lib/esm/Button/ButtonBase.js +64 -11
- package/lib/esm/Button/ButtonDropdown.js +2 -0
- package/lib/esm/Button/ButtonGroup.js +44 -39
- package/lib/esm/Button/propTypes.js +6 -0
- package/lib/esm/Card/PressableCardBase.js +3 -1
- package/lib/esm/Carousel/Carousel.js +63 -22
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +23 -3
- package/lib/esm/Icon/Icon.js +8 -11
- package/lib/esm/Icon/IconText.js +0 -1
- package/lib/esm/Listbox/GroupControl.js +33 -39
- package/lib/esm/Listbox/Listbox.js +23 -14
- package/lib/esm/Listbox/ListboxGroup.js +2 -1
- package/lib/esm/Listbox/ListboxOverlay.js +5 -2
- package/lib/esm/Listbox/PressableItem.js +8 -4
- package/lib/esm/TextInput/TextInputBase.js +5 -1
- package/lib/esm/ThemeProvider/index.js +1 -0
- package/lib/esm/ThemeProvider/useResponsiveThemeTokensCallback.js +117 -0
- package/lib/esm/Validator/Validator.js +171 -135
- package/lib/esm/index.js +1 -1
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/Button.jsx +26 -5
- package/src/Button/ButtonBase.jsx +79 -16
- package/src/Button/ButtonDropdown.jsx +2 -0
- package/src/Button/ButtonGroup.jsx +62 -45
- package/src/Button/propTypes.js +6 -0
- package/src/Card/PressableCardBase.jsx +3 -1
- package/src/Carousel/Carousel.jsx +71 -7
- package/src/Carousel/CarouselItem/CarouselItem.jsx +31 -3
- package/src/Icon/Icon.jsx +11 -14
- package/src/Icon/IconText.jsx +0 -1
- package/src/Listbox/GroupControl.jsx +41 -47
- package/src/Listbox/Listbox.jsx +26 -9
- package/src/Listbox/ListboxGroup.jsx +2 -1
- package/src/Listbox/ListboxOverlay.jsx +7 -2
- package/src/Listbox/PressableItem.jsx +8 -4
- package/src/PriceLockup/utils/renderPrice.jsx +15 -17
- package/src/TextInput/TextInputBase.jsx +5 -1
- package/src/ThemeProvider/index.js +1 -0
- package/src/ThemeProvider/useResponsiveThemeTokensCallback.js +129 -0
- package/src/Validator/Validator.jsx +180 -159
- package/src/index.js +2 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, Text, StyleSheet } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
5
|
import Icon from '../Icon'
|
|
6
6
|
import Spacer from '../Spacer'
|
|
7
7
|
import { useListboxContext } from './ListboxContext'
|
|
8
8
|
|
|
9
9
|
const styles = StyleSheet.create({
|
|
10
|
-
|
|
10
|
+
container: {
|
|
11
11
|
width: '100%',
|
|
12
12
|
flex: 1,
|
|
13
13
|
alignItems: 'center',
|
|
@@ -17,6 +17,36 @@ const styles = StyleSheet.create({
|
|
|
17
17
|
}
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
+
const selectTextStyles = (tokens) => ({
|
|
21
|
+
color: tokens.groupColor,
|
|
22
|
+
fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
|
|
23
|
+
fontSize: tokens.groupFontSize,
|
|
24
|
+
fontWeight: tokens.groupFontWeight
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const selectContainerStyles = (tokens) => ({
|
|
28
|
+
fontFamily: `${tokens.groupFontName}${tokens.groupFontWeight}normal`,
|
|
29
|
+
fontSize: tokens.groupFontSize,
|
|
30
|
+
color: tokens.groupColor,
|
|
31
|
+
textDecoration: tokens.itemTextDecoration,
|
|
32
|
+
backgroundColor: tokens.groupBackgroundColor,
|
|
33
|
+
outline: tokens.itemOutline,
|
|
34
|
+
minHeight: tokens.groupHeight,
|
|
35
|
+
borderRadius: tokens.groupBorderRadius,
|
|
36
|
+
paddingLeft: tokens.groupPaddingLeft - tokens.groupBorderLeftWidth,
|
|
37
|
+
paddingRight: tokens.groupPaddingRight - tokens.groupBorderRightWidth,
|
|
38
|
+
paddingTop: tokens.groupPaddingTop - tokens.groupBorderTopWidth,
|
|
39
|
+
paddingBottom: tokens.groupPaddingBottom - tokens.groupBorderBottomWidth,
|
|
40
|
+
borderLeftWidth: tokens.groupBorderLeftWidth,
|
|
41
|
+
borderLeftColor: tokens.groupBorderLeftColor,
|
|
42
|
+
borderRightWidth: tokens.groupBorderRightWidth,
|
|
43
|
+
borderRightColor: tokens.groupBorderRightColor,
|
|
44
|
+
borderTopWidth: tokens.groupBorderTopWidth,
|
|
45
|
+
borderTopColor: tokens.groupBorderTopColor,
|
|
46
|
+
borderBottomWidth: tokens.groupBorderBottomWidth,
|
|
47
|
+
borderBottomColor: tokens.groupBorderBottomColor
|
|
48
|
+
})
|
|
49
|
+
|
|
20
50
|
const GroupControl = React.forwardRef(({ expanded, pressed, hover, focus, label, id }, ref) => {
|
|
21
51
|
const { selectedId, setSelectedId } = useListboxContext()
|
|
22
52
|
const tokens = useThemeTokens(
|
|
@@ -31,58 +61,22 @@ const GroupControl = React.forwardRef(({ expanded, pressed, hover, focus, label,
|
|
|
31
61
|
focus
|
|
32
62
|
}
|
|
33
63
|
)
|
|
34
|
-
const {
|
|
35
|
-
groupFontName,
|
|
36
|
-
groupFontWeight,
|
|
37
|
-
groupFontSize,
|
|
38
|
-
groupColor,
|
|
39
|
-
groupBackgroundColor,
|
|
40
|
-
groupBorderColor,
|
|
41
|
-
groupBorderWidth,
|
|
42
|
-
groupBorderRadius,
|
|
43
|
-
groupPaddingLeft,
|
|
44
|
-
groupPaddingRight,
|
|
45
|
-
groupPaddingTop,
|
|
46
|
-
groupPaddingBottom,
|
|
47
|
-
itemTextDecoration,
|
|
48
|
-
itemOutline,
|
|
49
|
-
groupHeight
|
|
50
|
-
} = tokens
|
|
51
|
-
|
|
52
|
-
const getTextStyles = () => ({
|
|
53
|
-
color: groupColor
|
|
54
|
-
})
|
|
55
64
|
|
|
56
65
|
return (
|
|
57
66
|
<View
|
|
58
67
|
onPress={() => setSelectedId(id)}
|
|
59
|
-
style={[
|
|
60
|
-
styles.controlWrapper,
|
|
61
|
-
{
|
|
62
|
-
fontFamily: `${groupFontName}${groupFontWeight}normal`,
|
|
63
|
-
fontSize: groupFontSize,
|
|
64
|
-
color: groupColor,
|
|
65
|
-
textDecoration: itemTextDecoration,
|
|
66
|
-
backgroundColor: groupBackgroundColor,
|
|
67
|
-
outline: itemOutline,
|
|
68
|
-
height: groupHeight,
|
|
69
|
-
border: `${groupBorderWidth}px solid ${groupBorderColor}`,
|
|
70
|
-
borderRadius: groupBorderRadius,
|
|
71
|
-
paddingLeft: groupPaddingLeft - groupBorderWidth,
|
|
72
|
-
paddingRight: groupPaddingRight - groupBorderWidth,
|
|
73
|
-
paddingTop: groupPaddingTop - groupBorderWidth,
|
|
74
|
-
paddingBottom: groupPaddingBottom - groupBorderWidth
|
|
75
|
-
}
|
|
76
|
-
]}
|
|
68
|
+
style={[styles.container, selectContainerStyles(tokens)]}
|
|
77
69
|
ref={ref}
|
|
78
70
|
>
|
|
79
|
-
<Text style={
|
|
71
|
+
<Text style={selectTextStyles(tokens)}>{label}</Text>
|
|
80
72
|
<Spacer space={1} direction="row" />
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
{tokens.groupIcon && (
|
|
74
|
+
<Icon
|
|
75
|
+
icon={tokens.groupIcon}
|
|
76
|
+
tokens={{ color: tokens.groupColor }}
|
|
77
|
+
variant={{ size: 'micro' }}
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
86
80
|
</View>
|
|
87
81
|
)
|
|
88
82
|
})
|
package/src/Listbox/Listbox.jsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { View,
|
|
3
|
+
import { View, Platform, StyleSheet } from 'react-native'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
|
-
import { withLinkRouter, getTokensPropType } from '../utils'
|
|
5
|
+
import { withLinkRouter, getTokensPropType, variantProp } from '../utils'
|
|
6
6
|
import ExpandCollapse from '../ExpandCollapse'
|
|
7
7
|
import ListboxGroup from './ListboxGroup'
|
|
8
8
|
import ListboxItem from './ListboxItem'
|
|
@@ -10,12 +10,18 @@ import { ListboxContext } from './ListboxContext'
|
|
|
10
10
|
import DropdownOverlay from './ListboxOverlay'
|
|
11
11
|
|
|
12
12
|
const styles = StyleSheet.create({
|
|
13
|
-
|
|
13
|
+
container: {
|
|
14
14
|
padding: 0,
|
|
15
15
|
margin: 0
|
|
16
16
|
}
|
|
17
17
|
})
|
|
18
18
|
|
|
19
|
+
const selectContainerStyles = (tokens) => ({
|
|
20
|
+
minHeight: tokens.minHeight,
|
|
21
|
+
minWidth: tokens.minWidth,
|
|
22
|
+
backgroundColor: tokens.containerBackgroundColor
|
|
23
|
+
})
|
|
24
|
+
|
|
19
25
|
const getInitialOpen = (items, selectedId) =>
|
|
20
26
|
items
|
|
21
27
|
.filter(
|
|
@@ -36,15 +42,14 @@ const Listbox = React.forwardRef(
|
|
|
36
42
|
itemRouterProps,
|
|
37
43
|
onClose,
|
|
38
44
|
variant,
|
|
39
|
-
tokens
|
|
45
|
+
tokens,
|
|
46
|
+
testID
|
|
40
47
|
},
|
|
41
48
|
ref
|
|
42
49
|
) => {
|
|
43
50
|
const initialOpen = getInitialOpen(items, defaultSelectedId)
|
|
44
|
-
|
|
45
51
|
const [selectedId, setSelectedId] = React.useState(defaultSelectedId)
|
|
46
|
-
|
|
47
|
-
const { minHeight, minWidth } = useThemeTokens('Listbox', variant, tokens)
|
|
52
|
+
const listboxTokens = useThemeTokens('Listbox', tokens, variant)
|
|
48
53
|
|
|
49
54
|
// We need to keep track of each item's ref in order to be able to
|
|
50
55
|
// focus on a specific item via keyboard navigation
|
|
@@ -101,7 +106,11 @@ const Listbox = React.forwardRef(
|
|
|
101
106
|
<ListboxContext.Provider value={{ selectedId, setSelectedId }}>
|
|
102
107
|
<ExpandCollapse initialOpen={initialOpen} maxOpen={1} ref={ref}>
|
|
103
108
|
{(expandProps) => (
|
|
104
|
-
<View
|
|
109
|
+
<View
|
|
110
|
+
style={[styles.container, selectContainerStyles(listboxTokens)]}
|
|
111
|
+
accessibilityRole="combobox"
|
|
112
|
+
testID={testID}
|
|
113
|
+
>
|
|
105
114
|
{items.map((item, index) => {
|
|
106
115
|
const { id, label, items: nestedItems } = item
|
|
107
116
|
const itemId = id ?? label
|
|
@@ -169,7 +178,15 @@ Listbox.propTypes = {
|
|
|
169
178
|
/**
|
|
170
179
|
* onClose event
|
|
171
180
|
*/
|
|
172
|
-
onClose: PropTypes.func
|
|
181
|
+
onClose: PropTypes.func,
|
|
182
|
+
/**
|
|
183
|
+
* Test ID for testing
|
|
184
|
+
*/
|
|
185
|
+
testID: PropTypes.string,
|
|
186
|
+
/**
|
|
187
|
+
* Listbox variant
|
|
188
|
+
*/
|
|
189
|
+
variant: variantProp.propType
|
|
173
190
|
}
|
|
174
191
|
|
|
175
192
|
Listbox.Overlay = DropdownOverlay
|
|
@@ -22,13 +22,17 @@ const paddingVertical = 0
|
|
|
22
22
|
const paddingHorizontal = 0
|
|
23
23
|
|
|
24
24
|
const DropdownOverlay = React.forwardRef(
|
|
25
|
-
(
|
|
25
|
+
(
|
|
26
|
+
{ children, isReady = false, overlaidPosition, maxWidth, minWidth, onLayout, tokens, testID },
|
|
27
|
+
ref
|
|
28
|
+
) => {
|
|
26
29
|
const systemTokens = useThemeTokens('Listbox', {}, {})
|
|
27
30
|
|
|
28
31
|
return (
|
|
29
32
|
<View
|
|
30
33
|
ref={ref}
|
|
31
34
|
onLayout={onLayout}
|
|
35
|
+
testID={testID}
|
|
32
36
|
style={[
|
|
33
37
|
overlaidPosition,
|
|
34
38
|
{ maxWidth, minWidth },
|
|
@@ -75,7 +79,8 @@ DropdownOverlay.propTypes = {
|
|
|
75
79
|
maxWidth: PropTypes.number,
|
|
76
80
|
minWidth: PropTypes.number,
|
|
77
81
|
onLayout: PropTypes.func,
|
|
78
|
-
tokens: PropTypes.object
|
|
82
|
+
tokens: PropTypes.object,
|
|
83
|
+
testID: PropTypes.string
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
export default Platform.OS === 'web' ? withPortal(DropdownOverlay) : DropdownOverlay
|
|
@@ -39,10 +39,14 @@ const getItemStyles = ({
|
|
|
39
39
|
color: itemColor,
|
|
40
40
|
outline: itemOutline,
|
|
41
41
|
textDecoration: itemTextDecoration,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
borderLeftWidth: itemBorderLeftWidth,
|
|
43
|
+
borderLeftColor: itemBorderLeftColor,
|
|
44
|
+
borderRightWidth: itemBorderRightWidth,
|
|
45
|
+
borderRightColor: itemBorderRightColor,
|
|
46
|
+
borderTopWidth: itemBorderTopWidth,
|
|
47
|
+
borderTopColor: itemBorderTopColor,
|
|
48
|
+
borderBottomWidth: itemBorderBottomWidth,
|
|
49
|
+
borderBottomColor: itemBorderBottomColor,
|
|
46
50
|
borderRadius: itemBorderRadius,
|
|
47
51
|
justifyContent: 'center'
|
|
48
52
|
})
|
|
@@ -102,28 +102,26 @@ const renderPrice = (
|
|
|
102
102
|
)}
|
|
103
103
|
</Text>
|
|
104
104
|
) : null}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
<Text>
|
|
106
|
+
{renderAmount(
|
|
107
|
+
amount,
|
|
108
|
+
amountTypographyTokens,
|
|
109
|
+
strikeThrough,
|
|
110
|
+
a11yText,
|
|
111
|
+
fontColor,
|
|
112
|
+
themeTokens
|
|
113
|
+
)}
|
|
114
|
+
{isPriceBaseline &&
|
|
115
|
+
cents &&
|
|
116
|
+
renderAmount(
|
|
117
|
+
`${separator}${cents}`,
|
|
118
|
+
centsTypographyTokens,
|
|
110
119
|
strikeThrough,
|
|
111
120
|
a11yText,
|
|
112
121
|
fontColor,
|
|
113
122
|
themeTokens
|
|
114
123
|
)}
|
|
115
|
-
|
|
116
|
-
cents &&
|
|
117
|
-
renderAmount(
|
|
118
|
-
`${separator}${cents}`,
|
|
119
|
-
centsTypographyTokens,
|
|
120
|
-
strikeThrough,
|
|
121
|
-
a11yText,
|
|
122
|
-
fontColor,
|
|
123
|
-
themeTokens
|
|
124
|
-
)}
|
|
125
|
-
</Text>
|
|
126
|
-
}
|
|
124
|
+
</Text>
|
|
127
125
|
{cents && !isPriceBaseline
|
|
128
126
|
? renderTypography(`${separator}${cents}`, centsTypographyTokens, undefined, fontColor)
|
|
129
127
|
: null}
|
|
@@ -263,6 +263,10 @@ const TextInputBase = React.forwardRef(
|
|
|
263
263
|
// Add a space every 4 digits starting from the 5th position
|
|
264
264
|
filteredText = formattedValue.replace(regex, '$1 ').trim()
|
|
265
265
|
}
|
|
266
|
+
// Apply maxLength if provided
|
|
267
|
+
if (rest.maxLength && filteredText && filteredText.length > rest.maxLength) {
|
|
268
|
+
filteredText = filteredText.substring(0, rest.maxLength)
|
|
269
|
+
}
|
|
266
270
|
setValue(filteredText, event)
|
|
267
271
|
if (typeof onChangeText === 'function') onChangeText(filteredText, event)
|
|
268
272
|
}
|
|
@@ -340,7 +344,7 @@ const TextInputBase = React.forwardRef(
|
|
|
340
344
|
onMouseOut: handleMouseOut,
|
|
341
345
|
onChange: handleChangeText,
|
|
342
346
|
defaultValue: initialValue,
|
|
343
|
-
maxLength: type === 'card' ? 19 :
|
|
347
|
+
maxLength: type === 'card' ? 19 : rest.maxLength,
|
|
344
348
|
value: isControlled ? currentValue : undefined,
|
|
345
349
|
onKeyPress
|
|
346
350
|
}
|
|
@@ -3,6 +3,7 @@ import ThemeProvider from './ThemeProvider'
|
|
|
3
3
|
export { default as useTheme } from './useTheme'
|
|
4
4
|
export { default as useSetTheme } from './useSetTheme'
|
|
5
5
|
export { default as useResponsiveThemeTokens } from './useResponsiveThemeTokens'
|
|
6
|
+
export { default as useResponsiveThemeTokensCallback } from './useResponsiveThemeTokensCallback'
|
|
6
7
|
export * from './useThemeTokens'
|
|
7
8
|
|
|
8
9
|
export * from './utils'
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
import { viewports } from '@telus-uds/system-constants'
|
|
3
|
+
import useTheme from './useTheme'
|
|
4
|
+
import { getComponentTheme, mergeAppearances, resolveThemeTokens } from './utils'
|
|
5
|
+
|
|
6
|
+
const getResponsiveThemeTokens = (
|
|
7
|
+
{ rules = [], tokens: defaultThemeTokens = {} },
|
|
8
|
+
tokensProp,
|
|
9
|
+
variants = {},
|
|
10
|
+
states
|
|
11
|
+
) => {
|
|
12
|
+
const appearances = mergeAppearances(variants, states)
|
|
13
|
+
|
|
14
|
+
const tokensByViewport = Object.fromEntries(
|
|
15
|
+
viewports.keys.map((viewport) => [viewport, { ...defaultThemeTokens }])
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// Go through each rule and collect them for the corresponding viewport if they apply
|
|
19
|
+
rules.forEach((rule) => {
|
|
20
|
+
if (doesRuleApply(rule, appearances)) {
|
|
21
|
+
// If the rule does not have a viewport specified, we collect it in all viewports
|
|
22
|
+
let targetViewports = rule.if.viewport || viewports.keys
|
|
23
|
+
if (!Array.isArray(targetViewports)) {
|
|
24
|
+
targetViewports = [targetViewports]
|
|
25
|
+
}
|
|
26
|
+
targetViewports.forEach((viewport) => {
|
|
27
|
+
tokensByViewport[viewport] = { ...tokensByViewport[viewport], ...rule.tokens }
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
Object.keys(tokensByViewport).forEach((viewport) => {
|
|
33
|
+
tokensByViewport[viewport] = resolveThemeTokens(
|
|
34
|
+
tokensByViewport[viewport],
|
|
35
|
+
appearances,
|
|
36
|
+
tokensProp
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return tokensByViewport
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const doesRuleApply = (rule, appearances) =>
|
|
44
|
+
Object.entries(rule.if).every((condition) => doesConditionApply(condition, appearances))
|
|
45
|
+
|
|
46
|
+
const doesConditionApply = ([key, value], appearances) => {
|
|
47
|
+
if (key === 'viewport') {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
// use null rather than undefined so we can serialise the value in themes
|
|
51
|
+
const appearanceValue = appearances[key] ?? null
|
|
52
|
+
return Array.isArray(value) ? value.includes(appearanceValue) : value === appearanceValue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @typedef {import('../utils/props/tokens.js').TokensSet} TokensSet
|
|
57
|
+
* @typedef {import('../utils/props/tokens.js').TokensProp} TokensProp
|
|
58
|
+
* @typedef {import('../utils/props/variantProp').AppearanceSet} AppearanceSet
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns a memoised tokens getter function that gets responsive tokens for all viewports,
|
|
63
|
+
* similar to calling useResponsiveThemeTokens but with the callback pattern of useThemeTokensCallback.
|
|
64
|
+
*
|
|
65
|
+
* Scenarios where `useResponsiveThemeTokensCallback` should be used:
|
|
66
|
+
*
|
|
67
|
+
* - Where responsive tokens are to be obtained from state that is accessible only in scopes like callbacks
|
|
68
|
+
* and render functions, where calling useResponsiveThemeTokens directly would be disallowed by React's hook rules.
|
|
69
|
+
* - When using media query stylesheets and need to resolve tokens based on dynamic state (e.g., pressed, hovered)
|
|
70
|
+
* that changes at runtime.
|
|
71
|
+
* - Passing a responsive tokens getter down via a child component's `tokens` prop, applying rules using the
|
|
72
|
+
* child component's current state.
|
|
73
|
+
*
|
|
74
|
+
* The function returned may be called with an object of state appearances to get an object
|
|
75
|
+
* of tokens for each viewport, which can then be passed to createMediaQueryStyles.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Resolving responsive tokens inside Pressable's style function, based on Pressable state
|
|
79
|
+
* const PressMe = ({ tokens, variant, children }) => {
|
|
80
|
+
* const getResponsiveTokens = useResponsiveThemeTokensCallback('PressMe', tokens, variant)
|
|
81
|
+
* const getPressableStyle = ({ pressed }) => {
|
|
82
|
+
* const responsiveTokens = getResponsiveTokens({ pressed })
|
|
83
|
+
* const mediaQueryStyles = createMediaQueryStyles(responsiveTokens)
|
|
84
|
+
* return mediaQueryStyles
|
|
85
|
+
* }
|
|
86
|
+
* return <Pressable style={getPressableStyle}>{children}</Pressable>
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // Setting the theme in a parent and resolving it in a child based on child's state
|
|
91
|
+
* const MenuButton = ({ tokens, variant, ...buttonProps }) => {
|
|
92
|
+
* // Define what theme, variant etc we want in this component...
|
|
93
|
+
* const getResponsiveTokens = useResponsiveThemeTokensCallback('Button', tokens, variant)
|
|
94
|
+
* // ...resolve them in another component based on its state (e.g. press, hover...)
|
|
95
|
+
* return <ButtonBase tokens={getResponsiveTokens} accessibilityRole="menuitem" {...buttonProps} />
|
|
96
|
+
* }
|
|
97
|
+
*
|
|
98
|
+
* @typedef {Object} ResponsiveObject
|
|
99
|
+
* @property {TokensSet} xs
|
|
100
|
+
* @property {TokensSet} sm
|
|
101
|
+
* @property {TokensSet} md
|
|
102
|
+
* @property {TokensSet} lg
|
|
103
|
+
* @property {TokensSet} xl
|
|
104
|
+
*
|
|
105
|
+
* @param {string} componentName - the name as defined in the theme schema of the component whose theme is to be used
|
|
106
|
+
* @param {TokensProp} [tokens] - every themed component should accept a `tokens` prop allowing theme tokens to be overridden
|
|
107
|
+
* @param {AppearanceSet} [variants] - variants passed in as props that don't change dynamically
|
|
108
|
+
* @returns {(states: AppearanceSet, tokenOverrides?: TokensProp) => ResponsiveObject}
|
|
109
|
+
* - callback function that returns an overridable responsive tokens object for current state. Only pass
|
|
110
|
+
* tokenOverrides in rare cases where tokens overrides are also generated outside hook scope.
|
|
111
|
+
*/
|
|
112
|
+
const useResponsiveThemeTokensCallback = (componentName, tokens = {}, variants = {}) => {
|
|
113
|
+
const theme = useTheme()
|
|
114
|
+
const componentTheme = getComponentTheme(theme, componentName)
|
|
115
|
+
const getResponsiveThemeTokensCallback = useCallback(
|
|
116
|
+
(states, tokenOverrides) => {
|
|
117
|
+
const resolvedTokens = resolveThemeTokens(
|
|
118
|
+
tokens,
|
|
119
|
+
mergeAppearances(variants, states),
|
|
120
|
+
tokenOverrides
|
|
121
|
+
)
|
|
122
|
+
return getResponsiveThemeTokens(componentTheme, resolvedTokens, variants, states)
|
|
123
|
+
},
|
|
124
|
+
[componentTheme, tokens, variants]
|
|
125
|
+
)
|
|
126
|
+
return getResponsiveThemeTokensCallback
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default useResponsiveThemeTokensCallback
|