@telus-uds/components-base 1.72.0 → 1.73.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 +20 -2
- package/lib/Box/Box.js +17 -6
- package/lib/FlexGrid/Col/Col.js +42 -19
- package/lib/FlexGrid/FlexGrid.js +40 -17
- package/lib/FlexGrid/Row/Row.js +45 -22
- package/lib/Listbox/ListboxGroup.js +7 -1
- package/lib/MultiSelectFilter/MultiSelectFilter.js +1 -0
- package/lib/Notification/Notification.js +13 -5
- package/lib/OrderedList/ItemBase.js +7 -1
- package/lib/Responsive/Responsive.js +24 -14
- package/lib/Responsive/ResponsiveProp.js +46 -0
- package/lib/Responsive/ResponsiveWithMediaQueryStyleSheet.js +72 -0
- package/lib/ThemeProvider/ThemeProvider.js +5 -2
- package/lib/ThemeProvider/index.js +9 -1
- package/lib/ThemeProvider/useResponsiveThemeTokens.js +89 -0
- package/lib/Typography/Typography.js +48 -22
- package/lib/server.js +40 -0
- package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +39 -6
- package/lib-module/Box/Box.js +17 -6
- package/lib-module/FlexGrid/Col/Col.js +42 -19
- package/lib-module/FlexGrid/FlexGrid.js +40 -17
- package/lib-module/FlexGrid/Row/Row.js +45 -22
- package/lib-module/Listbox/ListboxGroup.js +7 -1
- package/lib-module/MultiSelectFilter/MultiSelectFilter.js +1 -0
- package/lib-module/Notification/Notification.js +13 -5
- package/lib-module/OrderedList/ItemBase.js +7 -1
- package/lib-module/Responsive/Responsive.js +24 -15
- package/lib-module/Responsive/ResponsiveProp.js +39 -0
- package/lib-module/Responsive/ResponsiveWithMediaQueryStyleSheet.js +64 -0
- package/lib-module/ThemeProvider/ThemeProvider.js +5 -2
- package/lib-module/ThemeProvider/index.js +1 -0
- package/lib-module/ThemeProvider/useResponsiveThemeTokens.js +81 -0
- package/lib-module/Typography/Typography.js +50 -24
- package/lib-module/server.js +4 -0
- package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +36 -6
- package/package.json +13 -2
- package/src/Box/Box.jsx +35 -17
- package/src/FlexGrid/Col/Col.jsx +42 -13
- package/src/FlexGrid/FlexGrid.jsx +40 -11
- package/src/FlexGrid/Row/Row.jsx +40 -16
- package/src/Listbox/ListboxGroup.jsx +9 -2
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +2 -0
- package/src/Notification/Notification.jsx +15 -3
- package/src/OrderedList/ItemBase.jsx +14 -2
- package/src/Responsive/Responsive.jsx +24 -11
- package/src/Responsive/ResponsiveProp.jsx +33 -0
- package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +58 -0
- package/src/ThemeProvider/ThemeProvider.jsx +5 -2
- package/src/ThemeProvider/index.js +1 -0
- package/src/ThemeProvider/useResponsiveThemeTokens.js +85 -0
- package/src/Typography/Typography.jsx +72 -24
- package/src/server.js +4 -0
- package/src/utils/ssr-media-query/utils/create-media-query-styles.js +21 -6
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { viewports } from '@telus-uds/system-constants'
|
|
2
|
+
import useTheme from './useTheme'
|
|
3
|
+
import { getComponentTheme, mergeAppearances, resolveThemeTokens } from './utils'
|
|
4
|
+
|
|
5
|
+
const getResponsiveThemeTokens = (
|
|
6
|
+
{ rules = [], tokens: defaultThemeTokens = {} },
|
|
7
|
+
tokensProp,
|
|
8
|
+
variants = {},
|
|
9
|
+
states
|
|
10
|
+
) => {
|
|
11
|
+
const appearances = mergeAppearances(variants, states)
|
|
12
|
+
|
|
13
|
+
const tokensByViewport = Object.fromEntries(
|
|
14
|
+
viewports.keys.map((viewport) => [viewport, { ...defaultThemeTokens }])
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// Go through each rule and collect them for the corresponding viewport if they apply
|
|
18
|
+
rules.forEach((rule) => {
|
|
19
|
+
if (doesRuleApply(rule, appearances)) {
|
|
20
|
+
// If the rule does not have a viewport specified, we collect it in all viewports
|
|
21
|
+
let targetViewports = rule.if.viewport || viewports.keys
|
|
22
|
+
if (!Array.isArray(targetViewports)) {
|
|
23
|
+
targetViewports = [targetViewports]
|
|
24
|
+
}
|
|
25
|
+
targetViewports.forEach((viewport) => {
|
|
26
|
+
tokensByViewport[viewport] = { ...tokensByViewport[viewport], ...rule.tokens }
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
Object.keys(tokensByViewport).forEach((viewport) => {
|
|
32
|
+
tokensByViewport[viewport] = resolveThemeTokens(
|
|
33
|
+
tokensByViewport[viewport],
|
|
34
|
+
appearances,
|
|
35
|
+
tokensProp
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return tokensByViewport
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const doesRuleApply = (rule, appearances) =>
|
|
43
|
+
Object.entries(rule.if).every((condition) => doesConditionApply(condition, appearances))
|
|
44
|
+
|
|
45
|
+
const doesConditionApply = ([key, value], appearances) => {
|
|
46
|
+
if (key === 'viewport') {
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
// use null rather than undefined so we can serialise the value in themes
|
|
50
|
+
const appearanceValue = appearances[key] ?? null
|
|
51
|
+
return Array.isArray(value) ? value.includes(appearanceValue) : value === appearanceValue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @typedef {import('../utils/props/tokens.js').TokensSet} TokensSet
|
|
56
|
+
* @typedef {import('../utils/props/tokens.js').TokensProp} TokensProp
|
|
57
|
+
* @typedef {import('../utils/props/variantProp').AppearanceSet} AppearanceSet
|
|
58
|
+
*
|
|
59
|
+
* Returns a complete set of tokens for a component for each viewport based on which of the
|
|
60
|
+
* component's theme rules apply to the current set of theme appearances.
|
|
61
|
+
* Pass the returned output to createMediaQueryStyles to generate media query styles for use inside
|
|
62
|
+
* the media query stylesheet from './utils/ssr-media-query'.
|
|
63
|
+
*
|
|
64
|
+
* @typedef {Object} ResponsiveObject
|
|
65
|
+
* @property {TokensSet} xs
|
|
66
|
+
* @property {TokensSet} sm
|
|
67
|
+
* @property {TokensSet} md
|
|
68
|
+
* @property {TokensSet} lg
|
|
69
|
+
* @property {TokensSet} xl
|
|
70
|
+
*
|
|
71
|
+
* @param { string } componentName
|
|
72
|
+
* @param { TokensProp } tokens
|
|
73
|
+
* @param { AppearanceSet } variants
|
|
74
|
+
* @param { AppearanceSet } states
|
|
75
|
+
* @returns { ResponsiveObject }
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
const useResponsiveThemeTokens = (componentName, tokens = {}, variants = {}, states = {}) => {
|
|
79
|
+
const theme = useTheme()
|
|
80
|
+
const componentTheme = getComponentTheme(theme, componentName)
|
|
81
|
+
const themeTokens = getResponsiveThemeTokens(componentTheme, tokens, variants, states)
|
|
82
|
+
return themeTokens
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default useResponsiveThemeTokens
|
|
@@ -2,8 +2,7 @@ import React, { forwardRef } from 'react'
|
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { Text, View } from 'react-native'
|
|
4
4
|
|
|
5
|
-
import { useTheme, useThemeTokens } from '../ThemeProvider'
|
|
6
|
-
import { useViewport } from '../ViewportProvider'
|
|
5
|
+
import { useResponsiveThemeTokens, useTheme, useThemeTokens } from '../ThemeProvider'
|
|
7
6
|
import { applyTextStyles } from '../ThemeProvider/utils'
|
|
8
7
|
import {
|
|
9
8
|
a11yProps,
|
|
@@ -15,17 +14,29 @@ import {
|
|
|
15
14
|
textTags,
|
|
16
15
|
textProps,
|
|
17
16
|
viewProps,
|
|
18
|
-
getA11yPropsFromHtmlTag
|
|
17
|
+
getA11yPropsFromHtmlTag,
|
|
18
|
+
StyleSheet,
|
|
19
|
+
createMediaQueryStyles
|
|
19
20
|
} from '../utils'
|
|
21
|
+
import { useViewport } from '../ViewportProvider'
|
|
20
22
|
/**
|
|
21
23
|
* @typedef {import('../utils/a11y/semantics').TextTag} TextTag
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
25
27
|
const [selectTextProps, selectedTextPropTypes] = selectSystemProps([textProps])
|
|
26
|
-
|
|
27
28
|
const selectTextStyles = (
|
|
28
|
-
{
|
|
29
|
+
{
|
|
30
|
+
fontWeight,
|
|
31
|
+
fontSize,
|
|
32
|
+
color,
|
|
33
|
+
lineHeight,
|
|
34
|
+
fontName,
|
|
35
|
+
textAlign,
|
|
36
|
+
textTransform,
|
|
37
|
+
letterSpacing,
|
|
38
|
+
textDecorationLine
|
|
39
|
+
},
|
|
29
40
|
themeOptions
|
|
30
41
|
) =>
|
|
31
42
|
applyTextStyles({
|
|
@@ -37,7 +48,8 @@ const selectTextStyles = (
|
|
|
37
48
|
themeOptions,
|
|
38
49
|
textAlign,
|
|
39
50
|
textTransform,
|
|
40
|
-
letterSpacing
|
|
51
|
+
letterSpacing,
|
|
52
|
+
textDecorationLine
|
|
41
53
|
})
|
|
42
54
|
|
|
43
55
|
// General-purpose flexible theme-neutral base component for text
|
|
@@ -59,19 +71,56 @@ const Typography = forwardRef(
|
|
|
59
71
|
ref
|
|
60
72
|
) => {
|
|
61
73
|
const viewport = useViewport()
|
|
62
|
-
const { superScriptFontSize, ...themeTokens } = useThemeTokens('Typography', tokens, variant, {
|
|
63
|
-
viewport
|
|
64
|
-
})
|
|
65
74
|
const { themeOptions } = useTheme()
|
|
66
75
|
|
|
76
|
+
const { enableMediaQueryStyleSheet } = themeOptions
|
|
77
|
+
|
|
78
|
+
const useTokens = enableMediaQueryStyleSheet ? useResponsiveThemeTokens : useThemeTokens
|
|
79
|
+
const themeTokens = useTokens('Typography', tokens, variant)
|
|
80
|
+
const maxFontSizeMultiplier = enableMediaQueryStyleSheet
|
|
81
|
+
? getMaxFontMultiplier(themeTokens[viewport])
|
|
82
|
+
: getMaxFontMultiplier(themeTokens)
|
|
83
|
+
const textDecorationLine = strikeThrough ? 'line-through' : 'none'
|
|
84
|
+
|
|
85
|
+
let textStyles
|
|
86
|
+
let mediaIds
|
|
87
|
+
|
|
88
|
+
if (enableMediaQueryStyleSheet) {
|
|
89
|
+
const transformedThemeTokens = Object.entries(themeTokens).reduce(
|
|
90
|
+
(acc, [vp, viewportTokens]) => {
|
|
91
|
+
acc[vp] = selectTextStyles(
|
|
92
|
+
{
|
|
93
|
+
textAlign: align,
|
|
94
|
+
textDecorationLine,
|
|
95
|
+
...viewportTokens
|
|
96
|
+
},
|
|
97
|
+
themeOptions
|
|
98
|
+
)
|
|
99
|
+
return acc
|
|
100
|
+
},
|
|
101
|
+
{}
|
|
102
|
+
)
|
|
103
|
+
const mediaQueryStyles = createMediaQueryStyles(transformedThemeTokens)
|
|
104
|
+
const { ids, styles } = StyleSheet.create({
|
|
105
|
+
text: mediaQueryStyles
|
|
106
|
+
})
|
|
107
|
+
textStyles = styles.text
|
|
108
|
+
mediaIds = ids.text
|
|
109
|
+
} else {
|
|
110
|
+
textStyles = selectTextStyles(
|
|
111
|
+
{
|
|
112
|
+
textAlign: align,
|
|
113
|
+
textDecorationLine,
|
|
114
|
+
...themeTokens
|
|
115
|
+
},
|
|
116
|
+
themeOptions
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
67
120
|
const resolvedTextProps = {
|
|
68
121
|
...selectTextProps(rest),
|
|
69
|
-
style: selectTextStyles(
|
|
70
|
-
align ? { ...themeTokens, textAlign: align } : themeTokens,
|
|
71
|
-
themeOptions
|
|
72
|
-
),
|
|
73
122
|
dataSet,
|
|
74
|
-
maxFontSizeMultiplier
|
|
123
|
+
maxFontSizeMultiplier
|
|
75
124
|
}
|
|
76
125
|
|
|
77
126
|
const containerProps = {
|
|
@@ -83,7 +132,7 @@ const Typography = forwardRef(
|
|
|
83
132
|
const resetTagStyling = (child) => {
|
|
84
133
|
if (typeof child === 'object' && (child?.type === 'sub' || child?.type === 'sup')) {
|
|
85
134
|
const childStyles = child?.props?.style || {}
|
|
86
|
-
const supFontSize = childStyles.fontSize ?? superScriptFontSize
|
|
135
|
+
const supFontSize = childStyles.fontSize ?? themeTokens.superScriptFontSize
|
|
87
136
|
const sanitizedChild = React.cloneElement(child, {
|
|
88
137
|
style: {
|
|
89
138
|
...childStyles,
|
|
@@ -91,10 +140,8 @@ const Typography = forwardRef(
|
|
|
91
140
|
lineHeight: 0
|
|
92
141
|
}
|
|
93
142
|
})
|
|
94
|
-
|
|
95
143
|
return sanitizedChild
|
|
96
144
|
}
|
|
97
|
-
|
|
98
145
|
return child
|
|
99
146
|
}
|
|
100
147
|
|
|
@@ -106,19 +153,20 @@ const Typography = forwardRef(
|
|
|
106
153
|
return resetTagStyling(children)
|
|
107
154
|
}
|
|
108
155
|
|
|
109
|
-
const textDecorationLine = strikeThrough ? 'line-through' : 'none'
|
|
110
|
-
const textStyles = resolvedTextProps.style
|
|
111
|
-
? { ...resolvedTextProps.style, textDecorationLine }
|
|
112
|
-
: { textDecorationLine }
|
|
113
|
-
|
|
114
156
|
return block ? (
|
|
115
157
|
<View ref={ref} {...containerProps}>
|
|
116
|
-
<Text {...resolvedTextProps} style={textStyles}>
|
|
158
|
+
<Text {...resolvedTextProps} style={textStyles} dataSet={mediaIds && { media: mediaIds }}>
|
|
117
159
|
{sanitizeChildren(children)}
|
|
118
160
|
</Text>
|
|
119
161
|
</View>
|
|
120
162
|
) : (
|
|
121
|
-
<Text
|
|
163
|
+
<Text
|
|
164
|
+
ref={ref}
|
|
165
|
+
{...containerProps}
|
|
166
|
+
{...resolvedTextProps}
|
|
167
|
+
style={textStyles}
|
|
168
|
+
dataSet={mediaIds && { media: mediaIds }}
|
|
169
|
+
>
|
|
122
170
|
{sanitizeChildren(children)}
|
|
123
171
|
</Text>
|
|
124
172
|
)
|
package/src/server.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as selectSystemProps } from './utils/props/selectSystemProps'
|
|
2
|
+
export { getTokensPropType } from './utils/props/tokens'
|
|
3
|
+
export { default as htmlAttrs } from './utils/htmlAttrs'
|
|
4
|
+
export { getComponentTheme, getThemeTokens } from './ThemeProvider/utils/theme-tokens'
|
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
import { viewports } from '@telus-uds/system-constants'
|
|
2
2
|
|
|
3
|
+
const inherit = ({ xs, sm = xs, md = sm, lg = md, xl = lg }) => ({ xs, sm, md, lg, xl })
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* @typedef { Object } CssStyles
|
|
5
|
-
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef { Object } ViewportStyles
|
|
11
|
+
* @property { CssStyles } xs - The CSS styles for the "xs" viewport (required).
|
|
12
|
+
* @property { CssStyles } [sm] - Optional styles for the "sm" viewport. Inherits from "xs" if not specified.
|
|
13
|
+
* @property { CssStyles } [md] - Optional styles for the "md" viewport. Inherits from "sm" if not specified.
|
|
14
|
+
* @property { CssStyles } [lg] - Optional styles for the "lg" viewport. Inherits from "md" if not specified.
|
|
15
|
+
* @property { CssStyles } [xl] - Optional styles for the "xl" viewport. Inherits from "lg" if not specified.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
6
19
|
* @typedef { Record<String, CssStyles> } MediaQueryStyles
|
|
7
20
|
*/
|
|
8
21
|
|
|
9
22
|
/**
|
|
10
23
|
* Generates media query styles based on specified viewport styles.
|
|
11
|
-
* @param {
|
|
12
|
-
* @
|
|
24
|
+
* @param { Object } viewportStyles - The styles for different viewports.
|
|
25
|
+
* @param { boolean } [shouldInherit=true] - Flag indicating whether to inherit styles.
|
|
26
|
+
* @returns { Object } - The media query styles.
|
|
13
27
|
*/
|
|
14
28
|
|
|
15
|
-
function createMediaQueryStyles(viewportStyles) {
|
|
16
|
-
const
|
|
17
|
-
const
|
|
29
|
+
function createMediaQueryStyles(viewportStyles, shouldInherit = true) {
|
|
30
|
+
const effectiveStyles = shouldInherit ? inherit(viewportStyles) : viewportStyles
|
|
31
|
+
const viewportsArray = viewports.keys
|
|
32
|
+
const mediaQueries = Object.entries(effectiveStyles).reduce((acc, [viewport, styles]) => {
|
|
18
33
|
const minWidth = viewports.map.get(viewport)
|
|
19
34
|
const nextViewport = viewportsArray[viewportsArray.indexOf(viewport) + 1]
|
|
20
35
|
const maxWidth = viewports.map.get(nextViewport)
|