@telus-uds/components-base 3.19.0 → 3.21.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 +29 -1
- package/lib/cjs/Button/ButtonDropdown.js +1 -0
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +8 -21
- package/lib/cjs/Link/LinkBase.js +8 -9
- package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +8 -8
- package/lib/cjs/Responsive/ResponsiveWithMediaQueryStyleSheet.js +1 -1
- package/lib/cjs/Spacer/Spacer.js +65 -5
- package/lib/cjs/StackView/StackView.js +62 -12
- package/lib/cjs/Tabs/TabsDropdown.js +4 -5
- package/lib/cjs/utils/index.js +8 -0
- package/lib/cjs/utils/ssr-media-query/index.js +7 -0
- package/lib/cjs/utils/ssr-media-query/utils/use-all-viewport-tokens.js +53 -0
- package/lib/cjs/utils/useMediaQuerySpacing.js +121 -0
- package/lib/esm/Button/ButtonDropdown.js +1 -0
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +8 -21
- package/lib/esm/Link/LinkBase.js +8 -9
- package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +8 -8
- package/lib/esm/Responsive/ResponsiveWithMediaQueryStyleSheet.js +1 -1
- package/lib/esm/Spacer/Spacer.js +66 -6
- package/lib/esm/StackView/StackView.js +63 -13
- package/lib/esm/Tabs/TabsDropdown.js +4 -5
- package/lib/esm/utils/index.js +1 -0
- package/lib/esm/utils/ssr-media-query/index.js +2 -1
- package/lib/esm/utils/ssr-media-query/utils/use-all-viewport-tokens.js +48 -0
- package/lib/esm/utils/useMediaQuerySpacing.js +116 -0
- package/lib/package.json +5 -5
- package/package.json +5 -5
- package/src/Button/ButtonDropdown.jsx +1 -0
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +9 -16
- package/src/Link/LinkBase.jsx +11 -9
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +9 -8
- package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +1 -1
- package/src/Spacer/Spacer.jsx +54 -7
- package/src/StackView/StackView.jsx +62 -9
- package/src/Tabs/TabsDropdown.jsx +10 -9
- package/src/utils/index.js +1 -0
- package/src/utils/ssr-media-query/index.js +2 -1
- package/src/utils/ssr-media-query/utils/use-all-viewport-tokens.js +32 -0
- package/src/utils/useMediaQuerySpacing.js +124 -0
|
@@ -62,36 +62,28 @@ const ExpandCollapseMiniControl = React.forwardRef(
|
|
|
62
62
|
const iconBaselineOffset = 0
|
|
63
63
|
const hoverTranslateY = 4
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const staticOffset = hoverTranslateY // Fixed downward adjustment to fine-tune vertical alignment
|
|
70
|
-
const sizeCompensation = -Math.abs(iconSize - fontSize) // Compensates when icon and text sizes differ significantly
|
|
65
|
+
const fontBaseline = fontSize / hoverTranslateY
|
|
66
|
+
const iconBaseline = iconSize / hoverTranslateY
|
|
67
|
+
const staticOffset = hoverTranslateY
|
|
68
|
+
const sizeCompensation = -Math.abs(iconSize - fontSize)
|
|
71
69
|
|
|
72
70
|
const baselineAlignment = fontBaseline + iconBaseline - staticOffset + sizeCompensation
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
// For native platforms, use baseline alignment with optional offset
|
|
76
|
-
return { iconTranslateY: baselineAlignment + iconBaselineOffset }
|
|
77
|
-
}
|
|
72
|
+
const mobileAdjustment = Platform.OS !== 'web' ? -2 : 0
|
|
78
73
|
|
|
79
74
|
if (isHovered) {
|
|
80
|
-
// Apply animation offset to the baseline-aligned position
|
|
81
|
-
// When expanded: move icon UP (1.3 the hover distance for clear movement)
|
|
82
|
-
// When collapsed: move icon DOWN (single hover distance)
|
|
83
75
|
const hoverMovementDistance = 1.3
|
|
84
76
|
const animationOffset = expanded
|
|
85
77
|
? -(hoverTranslateY * hoverMovementDistance)
|
|
86
78
|
: hoverTranslateY
|
|
87
79
|
|
|
88
80
|
return {
|
|
89
|
-
iconTranslateY:
|
|
81
|
+
iconTranslateY:
|
|
82
|
+
baselineAlignment + iconBaselineOffset + animationOffset + mobileAdjustment
|
|
90
83
|
}
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
return { iconTranslateY: baselineAlignment + iconBaselineOffset }
|
|
86
|
+
return { iconTranslateY: baselineAlignment + iconBaselineOffset + mobileAdjustment }
|
|
95
87
|
}
|
|
96
88
|
|
|
97
89
|
return (
|
|
@@ -103,6 +95,7 @@ const ExpandCollapseMiniControl = React.forwardRef(
|
|
|
103
95
|
...linkTokens,
|
|
104
96
|
...getTokens(linkState),
|
|
105
97
|
iconSize,
|
|
98
|
+
blockFontSize: fontSize,
|
|
106
99
|
blockLineHeight: lineHeight
|
|
107
100
|
})}
|
|
108
101
|
ref={ref}
|
package/src/Link/LinkBase.jsx
CHANGED
|
@@ -171,9 +171,12 @@ const LinkBase = React.forwardRef(
|
|
|
171
171
|
const themeTokens = resolveLinkTokens(linkState)
|
|
172
172
|
const outerBorderStyles = selectOuterBorderStyles(themeTokens)
|
|
173
173
|
const decorationStyles = selectDecorationStyles(themeTokens)
|
|
174
|
+
|
|
175
|
+
const mobileCompensation = null
|
|
176
|
+
|
|
174
177
|
return [
|
|
175
178
|
outerBorderStyles,
|
|
176
|
-
|
|
179
|
+
mobileCompensation,
|
|
177
180
|
blockLeftStyle,
|
|
178
181
|
decorationStyles,
|
|
179
182
|
hasIcon && staticStyles.rowContainer
|
|
@@ -191,11 +194,14 @@ const LinkBase = React.forwardRef(
|
|
|
191
194
|
const IconComponent = icon || themeTokens.icon
|
|
192
195
|
const { iconSpace } = themeTokens
|
|
193
196
|
|
|
197
|
+
const isTextOnlyLink = !IconComponent && !icon && accessibilityRole === 'link'
|
|
198
|
+
const adjustedIconSpace = Platform.OS !== 'web' && isTextOnlyLink ? 0 : iconSpace
|
|
199
|
+
|
|
194
200
|
return (
|
|
195
201
|
<IconText
|
|
196
202
|
icon={IconComponent}
|
|
197
203
|
iconPosition={iconPosition}
|
|
198
|
-
space={
|
|
204
|
+
space={adjustedIconSpace}
|
|
199
205
|
iconProps={{
|
|
200
206
|
...iconProps,
|
|
201
207
|
tokens: iconTokens,
|
|
@@ -274,15 +280,11 @@ const staticStyles = StyleSheet.create({
|
|
|
274
280
|
}
|
|
275
281
|
})
|
|
276
282
|
},
|
|
277
|
-
|
|
283
|
+
outerBorderCompensation: {
|
|
278
284
|
...(Platform.OS !== 'web' && {
|
|
279
|
-
margin: 0,
|
|
280
285
|
marginHorizontal: 2,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
...(Platform.OS === 'android' && {
|
|
284
|
-
paddingHorizontal: 2,
|
|
285
|
-
paddingTop: 2
|
|
286
|
+
paddingHorizontal: Platform.OS === 'android' ? 2 : 0,
|
|
287
|
+
paddingTop: Platform.OS === 'android' ? 2 : 0
|
|
286
288
|
})
|
|
287
289
|
}
|
|
288
290
|
})
|
|
@@ -65,7 +65,6 @@ const selectContainerStyle = (windowHeight, windowWidth) => ({
|
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
const TOTAL_COLUMNS = 12
|
|
68
|
-
const MAX_ITEMS_THRESHOLD = 12
|
|
69
68
|
|
|
70
69
|
const MultiSelectFilter = React.forwardRef(
|
|
71
70
|
(
|
|
@@ -174,13 +173,15 @@ const MultiSelectFilter = React.forwardRef(
|
|
|
174
173
|
const getCopy = useCopy({ dictionary, copy })
|
|
175
174
|
const colSizeNotMobile = items.length > rowLimit ? 2 : 1
|
|
176
175
|
const colSize = viewport !== 'xs' ? colSizeNotMobile : 1
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
|
|
177
|
+
let rowLength = items.length
|
|
178
|
+
if (viewport !== 'xs' && colSize === 2) {
|
|
179
|
+
rowLength = Math.ceil(items.length / 2)
|
|
180
|
+
}
|
|
179
181
|
|
|
180
182
|
React.useEffect(() => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}, [colSize])
|
|
183
|
+
setMaxWidth(items.length >= rowLimit)
|
|
184
|
+
}, [items.length, rowLimit])
|
|
184
185
|
|
|
185
186
|
React.useEffect(() => setCheckedIds(currentValues ?? []), [currentValues])
|
|
186
187
|
|
|
@@ -414,14 +415,14 @@ const MultiSelectFilter = React.forwardRef(
|
|
|
414
415
|
dismissWhenPressedOutside={dismissWhenPressedOutside}
|
|
415
416
|
onClose={onClose}
|
|
416
417
|
overlaidPosition={overlaidPosition}
|
|
417
|
-
maxHeight={items.length
|
|
418
|
+
maxHeight={items.length >= rowLimit ? true : maxHeight}
|
|
418
419
|
maxHeightSize={maxHeightSize}
|
|
419
420
|
maxWidthSize={maxWidthSize}
|
|
420
421
|
minHeight={minHeight}
|
|
421
422
|
minWidth={minWidth}
|
|
422
423
|
tokens={{
|
|
423
424
|
...tokens,
|
|
424
|
-
maxWidth: items.length
|
|
425
|
+
maxWidth: items.length >= rowLimit ? true : maxWidth,
|
|
425
426
|
borderColor: containerBorderColor
|
|
426
427
|
}}
|
|
427
428
|
copy={copy}
|
|
@@ -59,7 +59,7 @@ ResponsiveWithMediaQueryStyleSheet.propTypes = {
|
|
|
59
59
|
/**
|
|
60
60
|
* To hide children of `Responsive` if the current viewport is larger than `max`
|
|
61
61
|
*/
|
|
62
|
-
max: PropTypes.oneOf(['sm', 'md', 'lg', 'xl']),
|
|
62
|
+
max: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
|
|
63
63
|
inheritedStyles: PropTypes.arrayOf(PropTypes.string),
|
|
64
64
|
children: PropTypes.node.isRequired
|
|
65
65
|
}
|
package/src/Spacer/Spacer.jsx
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { View, StyleSheet } from 'react-native'
|
|
4
|
+
import {
|
|
5
|
+
a11yProps,
|
|
6
|
+
selectSystemProps,
|
|
7
|
+
spacingProps,
|
|
8
|
+
useSpacingScale,
|
|
9
|
+
viewProps,
|
|
10
|
+
StyleSheet as StyleSheetUtils,
|
|
11
|
+
createMediaQueryStyles
|
|
12
|
+
} from '../utils'
|
|
13
|
+
import useMediaQuerySpacing from '../utils/useMediaQuerySpacing'
|
|
14
|
+
import useTheme from '../ThemeProvider/useTheme'
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
17
|
* @typedef {import('../utils/props/spacingProps.js').SpacingValue} SpacingValue
|
|
@@ -56,11 +66,43 @@ const selectSizeStyle = (size, direction) => ({
|
|
|
56
66
|
* Spacer has no content and is ignored by tools such as screen readers. Use `Divider` for
|
|
57
67
|
* separations between elements that may be treated as semantically meaningful on web.
|
|
58
68
|
*/
|
|
59
|
-
const Spacer = React.forwardRef(({ space = 1, direction = 'column', ...rest }, ref) => {
|
|
60
|
-
const
|
|
61
|
-
|
|
69
|
+
const Spacer = React.forwardRef(({ space = 1, direction = 'column', dataSet, ...rest }, ref) => {
|
|
70
|
+
const {
|
|
71
|
+
themeOptions: { enableMediaQueryStyleSheet }
|
|
72
|
+
} = useTheme()
|
|
62
73
|
|
|
63
|
-
|
|
74
|
+
const { sizeByViewport } = useMediaQuerySpacing(space)
|
|
75
|
+
|
|
76
|
+
const fallbackSize = useSpacingScale(space)
|
|
77
|
+
const sizeStyle = selectSizeStyle(fallbackSize, direction)
|
|
78
|
+
|
|
79
|
+
let spacerStyles
|
|
80
|
+
let dataSetValue = dataSet
|
|
81
|
+
|
|
82
|
+
if (enableMediaQueryStyleSheet) {
|
|
83
|
+
const sizeKey = direction === 'row' ? 'width' : 'height'
|
|
84
|
+
const stylesByViewport = {
|
|
85
|
+
xs: { [sizeKey]: sizeByViewport.xs, ...staticStyles.stretch },
|
|
86
|
+
sm: { [sizeKey]: sizeByViewport.sm, ...staticStyles.stretch },
|
|
87
|
+
md: { [sizeKey]: sizeByViewport.md, ...staticStyles.stretch },
|
|
88
|
+
lg: { [sizeKey]: sizeByViewport.lg, ...staticStyles.stretch },
|
|
89
|
+
xl: { [sizeKey]: sizeByViewport.xl, ...staticStyles.stretch }
|
|
90
|
+
}
|
|
91
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
92
|
+
|
|
93
|
+
const { ids, styles } = StyleSheetUtils.create({
|
|
94
|
+
spacer: {
|
|
95
|
+
...mediaQueryStyles
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
spacerStyles = styles.spacer
|
|
100
|
+
dataSetValue = { media: ids.spacer, ...dataSet }
|
|
101
|
+
} else {
|
|
102
|
+
spacerStyles = [staticStyles.stretch, sizeStyle]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return <View ref={ref} style={spacerStyles} dataSet={dataSetValue} {...selectProps(rest)} />
|
|
64
106
|
})
|
|
65
107
|
Spacer.displayName = 'Spacer'
|
|
66
108
|
|
|
@@ -78,7 +120,12 @@ Spacer.propTypes = {
|
|
|
78
120
|
* - `'column'` (default) applies space vertically; has a fixed height and not width.
|
|
79
121
|
* - `'row'` applies space horizontally; has a fixed width and not height.
|
|
80
122
|
*/
|
|
81
|
-
direction: PropTypes.oneOf(['column', 'row'])
|
|
123
|
+
direction: PropTypes.oneOf(['column', 'row']),
|
|
124
|
+
/**
|
|
125
|
+
* Data attributes to be applied to the element. When media query stylesheet is enabled,
|
|
126
|
+
* this will include media query IDs for responsive styling.
|
|
127
|
+
*/
|
|
128
|
+
dataSet: PropTypes.object
|
|
82
129
|
}
|
|
83
130
|
|
|
84
131
|
const staticStyles = StyleSheet.create({
|
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
spacingProps,
|
|
14
14
|
useResponsiveProp,
|
|
15
15
|
variantProp,
|
|
16
|
-
viewProps
|
|
16
|
+
viewProps,
|
|
17
|
+
StyleSheet as StyleSheetUtils,
|
|
18
|
+
createMediaQueryStyles,
|
|
19
|
+
useAllViewportTokens
|
|
17
20
|
} from '../utils'
|
|
18
|
-
import
|
|
19
|
-
import { useViewport } from '../ViewportProvider'
|
|
21
|
+
import useTheme from '../ThemeProvider/useTheme'
|
|
20
22
|
import getStackedContent from './getStackedContent'
|
|
21
23
|
import { staticStyles, selectFlexStyles } from './common'
|
|
22
24
|
|
|
@@ -75,24 +77,70 @@ const StackView = React.forwardRef(
|
|
|
75
77
|
tokens,
|
|
76
78
|
tag,
|
|
77
79
|
accessibilityRole,
|
|
80
|
+
dataSet,
|
|
78
81
|
...rest
|
|
79
82
|
},
|
|
80
83
|
ref
|
|
81
84
|
) => {
|
|
82
|
-
const viewport = useViewport()
|
|
83
85
|
const direction = useResponsiveProp(directionProp, 'column')
|
|
86
|
+
const {
|
|
87
|
+
themeOptions: { enableMediaQueryStyleSheet }
|
|
88
|
+
} = useTheme()
|
|
89
|
+
|
|
84
90
|
const selectedProps = selectProps({
|
|
85
91
|
accessibilityRole,
|
|
86
92
|
...getA11yPropsFromHtmlTag(tag, accessibilityRole),
|
|
87
93
|
...rest
|
|
88
94
|
})
|
|
89
95
|
const content = getStackedContent(children, { direction, divider, space })
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
96
|
+
|
|
97
|
+
const allTokens = useAllViewportTokens('StackView', tokens, variant)
|
|
98
|
+
|
|
99
|
+
let stackViewStyles
|
|
100
|
+
let dataSetValue = dataSet
|
|
101
|
+
|
|
102
|
+
if (enableMediaQueryStyleSheet) {
|
|
103
|
+
const stylesByViewport = {
|
|
104
|
+
xs: {
|
|
105
|
+
...selectFlexStyles(allTokens.xs),
|
|
106
|
+
width: allTokens.xs.width
|
|
107
|
+
},
|
|
108
|
+
sm: {
|
|
109
|
+
...selectFlexStyles(allTokens.sm),
|
|
110
|
+
width: allTokens.sm.width
|
|
111
|
+
},
|
|
112
|
+
md: {
|
|
113
|
+
...selectFlexStyles(allTokens.md),
|
|
114
|
+
width: allTokens.md.width
|
|
115
|
+
},
|
|
116
|
+
lg: {
|
|
117
|
+
...selectFlexStyles(allTokens.lg),
|
|
118
|
+
width: allTokens.lg.width
|
|
119
|
+
},
|
|
120
|
+
xl: {
|
|
121
|
+
...selectFlexStyles(allTokens.xl),
|
|
122
|
+
width: allTokens.xl.width
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
127
|
+
|
|
128
|
+
const { ids, styles } = StyleSheetUtils.create({
|
|
129
|
+
stackView: {
|
|
130
|
+
...mediaQueryStyles
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
stackViewStyles = [staticStyles[direction], styles.stackView]
|
|
135
|
+
dataSetValue = { media: ids.stackView, ...dataSet }
|
|
136
|
+
} else {
|
|
137
|
+
const flexStyles = selectFlexStyles(allTokens.current)
|
|
138
|
+
const size = { width: allTokens.current.width }
|
|
139
|
+
stackViewStyles = [flexStyles, staticStyles[direction], size]
|
|
140
|
+
}
|
|
93
141
|
|
|
94
142
|
return (
|
|
95
|
-
<View ref={ref} {...selectedProps} style={
|
|
143
|
+
<View ref={ref} {...selectedProps} style={stackViewStyles} dataSet={dataSetValue}>
|
|
96
144
|
{content}
|
|
97
145
|
</View>
|
|
98
146
|
)
|
|
@@ -132,7 +180,12 @@ StackView.propTypes = {
|
|
|
132
180
|
* A StackView may take any children, but will have no effect if it is only passed one child or is passed children
|
|
133
181
|
* wrapped in a component. If necessary, children may be wrapped in one React Fragment.
|
|
134
182
|
*/
|
|
135
|
-
children: PropTypes.node
|
|
183
|
+
children: PropTypes.node,
|
|
184
|
+
/**
|
|
185
|
+
* Data attributes to be applied to the element. When media query stylesheet is enabled,
|
|
186
|
+
* this will include media query IDs for responsive styling.
|
|
187
|
+
*/
|
|
188
|
+
dataSet: PropTypes.object
|
|
136
189
|
}
|
|
137
190
|
|
|
138
191
|
export default StackView
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
|
3
|
+
import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'
|
|
4
4
|
import { useThemeTokensCallback, applyTextStyles, useTheme } from '../ThemeProvider'
|
|
5
5
|
import {
|
|
6
6
|
a11yProps,
|
|
@@ -245,20 +245,21 @@ const styles = StyleSheet.create({
|
|
|
245
245
|
position: 'relative',
|
|
246
246
|
width: '100%'
|
|
247
247
|
},
|
|
248
|
-
pressable:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
pressable:
|
|
249
|
+
Platform.OS === 'web'
|
|
250
|
+
? {
|
|
251
|
+
outlineWidth: 0,
|
|
252
|
+
outlineStyle: 'none',
|
|
253
|
+
outlineColor: 'transparent'
|
|
254
|
+
}
|
|
255
|
+
: {},
|
|
253
256
|
buttonContent: {
|
|
254
257
|
display: 'flex',
|
|
255
258
|
flexDirection: 'row',
|
|
256
259
|
alignItems: 'center',
|
|
257
260
|
justifyContent: 'space-between',
|
|
258
261
|
width: '100%',
|
|
259
|
-
minHeight: 44
|
|
260
|
-
outline: 'none',
|
|
261
|
-
boxSizing: 'border-box'
|
|
262
|
+
minHeight: 44
|
|
262
263
|
}
|
|
263
264
|
})
|
|
264
265
|
|
package/src/utils/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { default as info } from './info'
|
|
|
9
9
|
export { default as useCopy } from './useCopy'
|
|
10
10
|
export { default as useHash } from './useHash'
|
|
11
11
|
export { default as useSpacingScale } from './useSpacingScale'
|
|
12
|
+
export { default as useMediaQuerySpacing } from './useMediaQuerySpacing'
|
|
12
13
|
export { default as useResponsiveProp } from './useResponsiveProp'
|
|
13
14
|
export { default as useOverlaidPosition } from './useOverlaidPosition'
|
|
14
15
|
export { default as useSafeLayoutEffect } from './useSafeLayoutEffect'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import createStyleSheet from './create-stylesheet'
|
|
2
2
|
import createMediaQueryStyles from './utils/create-media-query-styles'
|
|
3
|
+
import useAllViewportTokens from './utils/use-all-viewport-tokens'
|
|
3
4
|
|
|
4
5
|
const StyleSheet = {
|
|
5
6
|
create: createStyleSheet
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
export { StyleSheet, createMediaQueryStyles }
|
|
9
|
+
export { StyleSheet, createMediaQueryStyles, useAllViewportTokens }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useThemeTokens } from '../../../ThemeProvider'
|
|
2
|
+
import { useViewport } from '../../../ViewportProvider'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to get theme tokens for all viewports at once.
|
|
6
|
+
* This is useful for components that need to support React Native Media Queries (RNMQ).
|
|
7
|
+
*
|
|
8
|
+
* All hooks are called unconditionally to comply with React's Rules of Hooks.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} componentName - The name of the component to get tokens for
|
|
11
|
+
* @param {object|function} tokens - Custom tokens or token function
|
|
12
|
+
* @param {object} variant - Variant configuration
|
|
13
|
+
* @returns {object} Object with tokens for each viewport (xs, sm, md, lg, xl, current)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const allTokens = useAllViewportTokens('StackView', tokens, variant)
|
|
17
|
+
* // Returns: { xs: {...}, sm: {...}, md: {...}, lg: {...}, xl: {...}, current: {...} }
|
|
18
|
+
*/
|
|
19
|
+
const useAllViewportTokens = (componentName, tokens, variant) => {
|
|
20
|
+
const viewport = useViewport()
|
|
21
|
+
|
|
22
|
+
const xs = useThemeTokens(componentName, tokens, variant, { viewport: 'xs' })
|
|
23
|
+
const sm = useThemeTokens(componentName, tokens, variant, { viewport: 'sm' })
|
|
24
|
+
const md = useThemeTokens(componentName, tokens, variant, { viewport: 'md' })
|
|
25
|
+
const lg = useThemeTokens(componentName, tokens, variant, { viewport: 'lg' })
|
|
26
|
+
const xl = useThemeTokens(componentName, tokens, variant, { viewport: 'xl' })
|
|
27
|
+
const current = useThemeTokens(componentName, tokens, variant, { viewport })
|
|
28
|
+
|
|
29
|
+
return { xs, sm, md, lg, xl, current }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default useAllViewportTokens
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useThemeTokens } from '../ThemeProvider'
|
|
2
|
+
import { resolveResponsiveProp } from './useResponsiveProp'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('@telus-uds/system-constants/viewports').Viewport} Viewport
|
|
6
|
+
* @typedef {import('./props/spacingProps.js').SpacingValue} SpacingValue
|
|
7
|
+
* @typedef {import('./props/spacingProps.js').SpacingIndex} SpacingIndex
|
|
8
|
+
* @typedef {import('./props/spacingProps.js').SpacingObject} SpacingObject
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A utility hook that simplifies implementing media query-based responsive spacing.
|
|
13
|
+
*
|
|
14
|
+
* This hook handles the complexity of:
|
|
15
|
+
* - Detecting if a space value is responsive (has viewport keys)
|
|
16
|
+
* - Fetching theme tokens for each viewport
|
|
17
|
+
* - Resolving the correct space index for each viewport
|
|
18
|
+
* - Extracting actual pixel values from theme tokens
|
|
19
|
+
*
|
|
20
|
+
* ## Usage
|
|
21
|
+
*
|
|
22
|
+
* ```jsx
|
|
23
|
+
* const { sizeByViewport } = useMediaQuerySpacing(space, 'spacingScale')
|
|
24
|
+
*
|
|
25
|
+
* // Use sizeByViewport to create media query styles
|
|
26
|
+
* const stylesByViewport = {
|
|
27
|
+
* xs: { padding: sizeByViewport.xs },
|
|
28
|
+
* sm: { padding: sizeByViewport.sm },
|
|
29
|
+
* md: { padding: sizeByViewport.md },
|
|
30
|
+
* lg: { padding: sizeByViewport.lg },
|
|
31
|
+
* xl: { padding: sizeByViewport.xl }
|
|
32
|
+
* }
|
|
33
|
+
* const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ## Parameters
|
|
37
|
+
*
|
|
38
|
+
* @param {SpacingValue} spaceValue - A spacing value (number or responsive object with viewport keys)
|
|
39
|
+
* @param {string} tokenKey - The theme token key to use (e.g., 'spacingScale', 'Typography')
|
|
40
|
+
* @param {object} [tokens={}] - Additional tokens to pass to useThemeTokens
|
|
41
|
+
* @param {object} [variant={}] - Variant to pass to useThemeTokens
|
|
42
|
+
*
|
|
43
|
+
* ## Returns
|
|
44
|
+
*
|
|
45
|
+
* @returns {{
|
|
46
|
+
* spaceIndexByViewport: { xs: number, sm: number, md: number, lg: number, xl: number },
|
|
47
|
+
* sizeByViewport: { xs: number, sm: number, md: number, lg: number, xl: number },
|
|
48
|
+
* tokensByViewport: { xs: object, sm: object, md: object, lg: object, xl: object }
|
|
49
|
+
* }}
|
|
50
|
+
*
|
|
51
|
+
* - `spaceIndexByViewport`: The resolved space index for each viewport
|
|
52
|
+
* - `sizeByViewport`: The actual pixel/number values for each viewport
|
|
53
|
+
* - `tokensByViewport`: The full theme tokens for each viewport (for advanced use cases)
|
|
54
|
+
*/
|
|
55
|
+
const useMediaQuerySpacing = (spaceValue, tokenKey = 'spacingScale', tokens = {}, variant = {}) => {
|
|
56
|
+
const isResponsive =
|
|
57
|
+
typeof spaceValue === 'object' &&
|
|
58
|
+
spaceValue !== null &&
|
|
59
|
+
!spaceValue.space &&
|
|
60
|
+
!spaceValue.options
|
|
61
|
+
|
|
62
|
+
const getSpaceIndex = (viewport) => {
|
|
63
|
+
if (isResponsive) {
|
|
64
|
+
return resolveResponsiveProp(spaceValue, viewport)
|
|
65
|
+
}
|
|
66
|
+
if (typeof spaceValue === 'number') {
|
|
67
|
+
return spaceValue
|
|
68
|
+
}
|
|
69
|
+
return spaceValue?.space ?? 1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const spaceIndexByViewport = {
|
|
73
|
+
xs: getSpaceIndex('xs'),
|
|
74
|
+
sm: getSpaceIndex('sm'),
|
|
75
|
+
md: getSpaceIndex('md'),
|
|
76
|
+
lg: getSpaceIndex('lg'),
|
|
77
|
+
xl: getSpaceIndex('xl')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const tokensXs = useThemeTokens(tokenKey, tokens, variant, {
|
|
81
|
+
space: spaceIndexByViewport.xs,
|
|
82
|
+
viewport: 'xs'
|
|
83
|
+
})
|
|
84
|
+
const tokensSm = useThemeTokens(tokenKey, tokens, variant, {
|
|
85
|
+
space: spaceIndexByViewport.sm,
|
|
86
|
+
viewport: 'sm'
|
|
87
|
+
})
|
|
88
|
+
const tokensMd = useThemeTokens(tokenKey, tokens, variant, {
|
|
89
|
+
space: spaceIndexByViewport.md,
|
|
90
|
+
viewport: 'md'
|
|
91
|
+
})
|
|
92
|
+
const tokensLg = useThemeTokens(tokenKey, tokens, variant, {
|
|
93
|
+
space: spaceIndexByViewport.lg,
|
|
94
|
+
viewport: 'lg'
|
|
95
|
+
})
|
|
96
|
+
const tokensXl = useThemeTokens(tokenKey, tokens, variant, {
|
|
97
|
+
space: spaceIndexByViewport.xl,
|
|
98
|
+
viewport: 'xl'
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const sizeByViewport = {
|
|
102
|
+
xs: tokensXs.size ?? 0,
|
|
103
|
+
sm: tokensSm.size ?? 0,
|
|
104
|
+
md: tokensMd.size ?? 0,
|
|
105
|
+
lg: tokensLg.size ?? 0,
|
|
106
|
+
xl: tokensXl.size ?? 0
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const tokensByViewport = {
|
|
110
|
+
xs: tokensXs,
|
|
111
|
+
sm: tokensSm,
|
|
112
|
+
md: tokensMd,
|
|
113
|
+
lg: tokensLg,
|
|
114
|
+
xl: tokensXl
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
spaceIndexByViewport,
|
|
119
|
+
sizeByViewport,
|
|
120
|
+
tokensByViewport
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default useMediaQuerySpacing
|