@telus-uds/components-base 3.27.0 → 3.28.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 +21 -5
- package/lib/cjs/Card/CardBase.js +12 -3
- package/lib/cjs/ExpandCollapse/Control.js +5 -1
- package/lib/cjs/ExpandCollapse/ExpandCollapse.js +17 -8
- package/lib/cjs/ExpandCollapse/Panel.js +7 -2
- package/lib/cjs/IconButton/IconButton.js +10 -5
- package/lib/cjs/Modal/Modal.js +21 -11
- package/lib/cjs/Progress/Progress.js +19 -5
- package/lib/cjs/Progress/ProgressBar.js +22 -4
- package/lib/cjs/Progress/ProgressContext.js +11 -0
- package/lib/cjs/SideNav/Item.js +3 -3
- package/lib/cjs/SideNav/ItemsGroup.js +46 -19
- package/lib/cjs/SideNav/SideNav.js +29 -13
- package/lib/esm/Card/CardBase.js +12 -3
- package/lib/esm/ExpandCollapse/Control.js +5 -1
- package/lib/esm/ExpandCollapse/ExpandCollapse.js +17 -8
- package/lib/esm/ExpandCollapse/Panel.js +7 -2
- package/lib/esm/IconButton/IconButton.js +10 -5
- package/lib/esm/Modal/Modal.js +21 -11
- package/lib/esm/Progress/Progress.js +19 -5
- package/lib/esm/Progress/ProgressBar.js +22 -4
- package/lib/esm/Progress/ProgressContext.js +5 -0
- package/lib/esm/SideNav/Item.js +3 -3
- package/lib/esm/SideNav/ItemsGroup.js +45 -20
- package/lib/esm/SideNav/SideNav.js +29 -13
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Card/CardBase.jsx +9 -3
- package/src/ExpandCollapse/Control.jsx +1 -1
- package/src/ExpandCollapse/ExpandCollapse.jsx +9 -8
- package/src/ExpandCollapse/Panel.jsx +10 -2
- package/src/IconButton/IconButton.jsx +40 -28
- package/src/Modal/Modal.jsx +23 -11
- package/src/Progress/Progress.jsx +18 -7
- package/src/Progress/ProgressBar.jsx +19 -14
- package/src/Progress/ProgressContext.js +5 -0
- package/src/SideNav/Item.jsx +3 -3
- package/src/SideNav/ItemsGroup.jsx +36 -16
- package/src/SideNav/SideNav.jsx +22 -8
|
@@ -92,34 +92,46 @@ const selectInnerStyle = (
|
|
|
92
92
|
height
|
|
93
93
|
},
|
|
94
94
|
password
|
|
95
|
-
) =>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
95
|
+
) => {
|
|
96
|
+
const basePadding = calculatePadding(padding, borderWidth)
|
|
97
|
+
|
|
98
|
+
const calculateSpecificPadding = (specificPadding, specificBorderWidth) => {
|
|
99
|
+
const calculated = calculatePadding(
|
|
100
|
+
specificPadding ?? padding,
|
|
101
|
+
specificBorderWidth ?? borderWidth
|
|
102
|
+
)
|
|
103
|
+
return calculated !== basePadding && calculated !== undefined ? calculated : undefined
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
// Inner borders animate with the icon and should be treated like a themable feature of the icon
|
|
108
|
+
borderColor,
|
|
109
|
+
borderRadius,
|
|
110
|
+
borderWidth,
|
|
111
|
+
borderTopLeftRadius,
|
|
112
|
+
borderTopRightRadius,
|
|
113
|
+
borderBottomLeftRadius,
|
|
114
|
+
borderBottomRightRadius,
|
|
115
|
+
borderTopWidth,
|
|
116
|
+
borderRightWidth,
|
|
117
|
+
borderBottomWidth,
|
|
118
|
+
borderLeftWidth,
|
|
119
|
+
padding: basePadding,
|
|
120
|
+
paddingLeft: calculateSpecificPadding(paddingLeft, borderLeftWidth),
|
|
121
|
+
paddingRight: calculateSpecificPadding(paddingRight, borderRightWidth),
|
|
122
|
+
paddingTop: calculateSpecificPadding(paddingTop, borderTopWidth),
|
|
123
|
+
paddingBottom: calculateSpecificPadding(paddingBottom, borderBottomWidth),
|
|
124
|
+
...Platform.select({
|
|
125
|
+
web: {
|
|
126
|
+
pointerEvents: 'none',
|
|
127
|
+
display: 'inline-flex',
|
|
128
|
+
alignItems: 'center',
|
|
129
|
+
justifyContent: 'center'
|
|
130
|
+
}
|
|
131
|
+
}),
|
|
132
|
+
...getPasswordDimensions(password, width, height)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
123
135
|
|
|
124
136
|
/**
|
|
125
137
|
* A pressable themeless base component that handles pressable states and passes tokens
|
package/src/Modal/Modal.jsx
CHANGED
|
@@ -68,9 +68,10 @@ const selectModalStyles = ({
|
|
|
68
68
|
...applyShadowToken(shadow)
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
const selectBackdropStyles = ({ backdropColor, backdropOpacity }) => ({
|
|
71
|
+
const selectBackdropStyles = ({ backdropColor, backdropOpacity, backdropCursor }) => ({
|
|
72
72
|
backgroundColor: backdropColor,
|
|
73
|
-
opacity: backdropOpacity
|
|
73
|
+
opacity: backdropOpacity,
|
|
74
|
+
...(Platform.OS === 'web' && backdropCursor ? { cursor: backdropCursor } : {})
|
|
74
75
|
})
|
|
75
76
|
|
|
76
77
|
const selectCloseButtonContainerStyles = ({ paddingRight, paddingTop }) => ({
|
|
@@ -119,12 +120,20 @@ const Modal = React.forwardRef(
|
|
|
119
120
|
cancelButtonText,
|
|
120
121
|
cancelButtonType,
|
|
121
122
|
footer,
|
|
123
|
+
backgroundDismissible = true,
|
|
122
124
|
...rest
|
|
123
125
|
},
|
|
124
126
|
ref
|
|
125
127
|
) => {
|
|
126
128
|
const viewport = useViewport()
|
|
127
|
-
|
|
129
|
+
|
|
130
|
+
const isBackdropClickable = onClose && backgroundDismissible
|
|
131
|
+
|
|
132
|
+
const themeTokens = useThemeTokens('Modal', tokens, variant, {
|
|
133
|
+
viewport,
|
|
134
|
+
maxWidth,
|
|
135
|
+
backdropCursor: isBackdropClickable ? 'pointer' : 'default'
|
|
136
|
+
})
|
|
128
137
|
const modalRef = useScrollBlocking(isOpen)
|
|
129
138
|
const modalBodyRef = React.useRef(ref)
|
|
130
139
|
const modalContentRef = React.useRef(null)
|
|
@@ -253,7 +262,7 @@ const Modal = React.forwardRef(
|
|
|
253
262
|
</View>
|
|
254
263
|
{/* when a modal becomes open its first focusable element is being automatically focused */}
|
|
255
264
|
{/* and we prefer the close button over backdrop */}
|
|
256
|
-
<TouchableWithoutFeedback onPress={handleClose}>
|
|
265
|
+
<TouchableWithoutFeedback onPress={isBackdropClickable && handleClose}>
|
|
257
266
|
<View style={[staticStyles.backdrop, selectBackdropStyles(themeTokens)]} />
|
|
258
267
|
</TouchableWithoutFeedback>
|
|
259
268
|
</ScrollView>
|
|
@@ -349,7 +358,15 @@ Modal.propTypes = {
|
|
|
349
358
|
/**
|
|
350
359
|
* Receive a react node or an array of nodes to render at the bottom of the modal, above the action buttons.
|
|
351
360
|
*/
|
|
352
|
-
footer: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)])
|
|
361
|
+
footer: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
|
|
362
|
+
/**
|
|
363
|
+
* Controls whether the modal can be dismissed by clicking on the backdrop.
|
|
364
|
+
* When set to `false`, clicking the backdrop will not close the modal.
|
|
365
|
+
* The backdrop cursor automatically changes to 'default' to indicate it's not clickable.
|
|
366
|
+
* Note: Backdrop dismissal requires `onClose` to be defined.
|
|
367
|
+
* @default true
|
|
368
|
+
*/
|
|
369
|
+
backgroundDismissible: PropTypes.bool
|
|
353
370
|
}
|
|
354
371
|
|
|
355
372
|
export default Modal
|
|
@@ -361,12 +378,7 @@ const staticStyles = StyleSheet.create({
|
|
|
361
378
|
left: 0,
|
|
362
379
|
right: 0,
|
|
363
380
|
bottom: 0,
|
|
364
|
-
zIndex: -1
|
|
365
|
-
...Platform.select({
|
|
366
|
-
web: {
|
|
367
|
-
cursor: 'pointer'
|
|
368
|
-
}
|
|
369
|
-
})
|
|
381
|
+
zIndex: -1
|
|
370
382
|
},
|
|
371
383
|
positioningContainer: {
|
|
372
384
|
flexBasis: '100%',
|
|
@@ -4,6 +4,7 @@ import { View, StyleSheet } from 'react-native'
|
|
|
4
4
|
|
|
5
5
|
import { applyShadowToken, useThemeTokens } from '../ThemeProvider'
|
|
6
6
|
import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
|
|
7
|
+
import ProgressContext from './ProgressContext'
|
|
7
8
|
|
|
8
9
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
9
10
|
|
|
@@ -45,6 +46,12 @@ const selectProgressStyles = ({
|
|
|
45
46
|
*
|
|
46
47
|
* - Use the `size` variant to control the height of your progress bars: passing `'mini'` will make your
|
|
47
48
|
* progress bar container narrower.
|
|
49
|
+
* - Use the `layers` variant to control how multiple progress bars are positioned:
|
|
50
|
+
* - `false` (default): bars are positioned vertically one below the other.
|
|
51
|
+
* - `true`: bars overlay on top of each other (layered/stacked on z-axis).
|
|
52
|
+
* Note: The `layers` prop is deprecated. After August 2026, `layers: true` will become the permanent
|
|
53
|
+
* default behavior and the `layers` prop will be removed. To maintain vertical layout after removal,
|
|
54
|
+
* use separate individual Progress components.
|
|
48
55
|
*
|
|
49
56
|
* ## Usability and A11y guidelines
|
|
50
57
|
*
|
|
@@ -55,15 +62,19 @@ const selectProgressStyles = ({
|
|
|
55
62
|
*/
|
|
56
63
|
const Progress = React.forwardRef(({ children, tokens, variant, ...rest }, ref) => {
|
|
57
64
|
const themeTokens = useThemeTokens('Progress', tokens, variant)
|
|
65
|
+
// Default to false (vertical layout) to preserve existing behavior and avoid breaking changes
|
|
66
|
+
const layers = variant?.layers ?? false
|
|
58
67
|
|
|
59
68
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
<ProgressContext.Provider value={{ layers }}>
|
|
70
|
+
<View
|
|
71
|
+
ref={ref}
|
|
72
|
+
style={[staticStyles.progressContainer, selectProgressStyles(themeTokens)]}
|
|
73
|
+
{...selectProps(rest)}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</View>
|
|
77
|
+
</ProgressContext.Provider>
|
|
67
78
|
)
|
|
68
79
|
})
|
|
69
80
|
Progress.displayName = 'Progress'
|
|
@@ -6,22 +6,23 @@ import ProgressBarBackground from './ProgressBarBackground'
|
|
|
6
6
|
import { applyShadowToken, useThemeTokens } from '../ThemeProvider'
|
|
7
7
|
import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
|
|
8
8
|
import { MAX_PERCENT_VALUE, MIN_PERCENT_VALUE } from './constants'
|
|
9
|
+
import ProgressContext from './ProgressContext'
|
|
9
10
|
|
|
10
11
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
11
12
|
|
|
12
|
-
const selectBarStyles = (
|
|
13
|
-
{ backgroundColor, borderRadius, outlineWidth, outlineColor, shadow }
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
13
|
+
const selectBarStyles = ({ themeTokens, calculatedPercentage, barPosition, layers }) => {
|
|
14
|
+
const { backgroundColor, borderRadius, outlineWidth, outlineColor, shadow } = themeTokens
|
|
15
|
+
return {
|
|
16
|
+
backgroundColor,
|
|
17
|
+
borderRadius,
|
|
18
|
+
outlineWidth,
|
|
19
|
+
outlineColor,
|
|
20
|
+
...applyShadowToken(shadow),
|
|
21
|
+
width: `${calculatedPercentage}%`,
|
|
22
|
+
left: `${barPosition}%`,
|
|
23
|
+
...(layers ? { position: 'absolute' } : {})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* The `ProgressBar` is a visual representation of linear progression.
|
|
@@ -70,6 +71,7 @@ const ProgressBar = React.forwardRef(
|
|
|
70
71
|
},
|
|
71
72
|
ref
|
|
72
73
|
) => {
|
|
74
|
+
const { layers } = React.useContext(ProgressContext)
|
|
73
75
|
const { items, current } = offset
|
|
74
76
|
let calculatedPercentage = percentage
|
|
75
77
|
let barPosition = MIN_PERCENT_VALUE
|
|
@@ -104,7 +106,10 @@ const ProgressBar = React.forwardRef(
|
|
|
104
106
|
return percentage > MIN_PERCENT_VALUE || items ? (
|
|
105
107
|
<View
|
|
106
108
|
ref={ref}
|
|
107
|
-
style={[
|
|
109
|
+
style={[
|
|
110
|
+
staticStyles.bar,
|
|
111
|
+
selectBarStyles({ themeTokens, calculatedPercentage, barPosition, layers })
|
|
112
|
+
]}
|
|
108
113
|
{...selectedProps}
|
|
109
114
|
>
|
|
110
115
|
{children ?? <ProgressBarBackground variant={variant} />}
|
package/src/SideNav/Item.jsx
CHANGED
|
@@ -29,9 +29,9 @@ function selectItemStyles({
|
|
|
29
29
|
}) {
|
|
30
30
|
return {
|
|
31
31
|
backgroundColor,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
borderBottomColor: borderColor,
|
|
33
|
+
borderBottomWidth: borderWidth,
|
|
34
|
+
borderBottomStyle: borderStyle,
|
|
35
35
|
paddingLeft,
|
|
36
36
|
paddingRight,
|
|
37
37
|
paddingTop,
|
|
@@ -6,6 +6,12 @@ import ExpandCollapse from '../ExpandCollapse'
|
|
|
6
6
|
import { getTokensPropType, variantProp, componentPropType, selectTokens } from '../utils'
|
|
7
7
|
import { useThemeTokensCallback } from '../ThemeProvider'
|
|
8
8
|
|
|
9
|
+
const selectPanelTokens = ({ borderWidth, borderColor, backgroundColor }) => ({
|
|
10
|
+
contentPanelBackgroundColor: backgroundColor,
|
|
11
|
+
borderBottomWidth: borderWidth,
|
|
12
|
+
borderColor
|
|
13
|
+
})
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
16
|
Expandable content areas for use within `SideNav`.
|
|
11
17
|
|
|
@@ -37,27 +43,30 @@ const ItemsGroup = React.forwardRef(
|
|
|
37
43
|
const getItemAppearance = (appearance) => ({ ...getAppearance(appearance), type: 'parent' })
|
|
38
44
|
|
|
39
45
|
const getGroupTokens = useThemeTokensCallback('SideNavItemsGroup', tokens, variant)
|
|
40
|
-
const getPanelTokens = (appearance) => {
|
|
41
|
-
const { panelBorderColor, ...itemsGroupTokens } = getGroupTokens(getAppearance(appearance))
|
|
42
|
-
const groupTokens = {
|
|
43
|
-
...itemsGroupTokens,
|
|
44
|
-
borderWidth: 0,
|
|
45
|
-
marginBottom: 0
|
|
46
|
-
}
|
|
47
|
-
return selectTokens('ExpandCollapsePanel', groupTokens)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
const getItemTokens = useThemeTokensCallback('SideNavItem', itemTokens, variant)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
...
|
|
47
|
+
|
|
48
|
+
const getPanelTokens = (appearance) =>
|
|
49
|
+
selectTokens('ExpandCollapsePanel', {
|
|
50
|
+
...staticTokens.panel,
|
|
51
|
+
...getGroupTokens(getAppearance(appearance)),
|
|
52
|
+
...selectPanelTokens(getItemTokens(getItemAppearance(appearance)))
|
|
55
53
|
})
|
|
56
54
|
|
|
55
|
+
const getControlTokens = (appearance) => ({
|
|
56
|
+
...selectTokens('ExpandCollapseControl', {
|
|
57
|
+
...getItemTokens(getItemAppearance(appearance)), // main style from SideNavItem
|
|
58
|
+
...getGroupTokens(getAppearance(appearance)) // control-specific tokens like icon etc,
|
|
59
|
+
}),
|
|
60
|
+
...staticTokens.control
|
|
61
|
+
})
|
|
62
|
+
|
|
57
63
|
const controlContent = (controlState) => {
|
|
58
64
|
const currentItemTokens = getItemTokens(getItemAppearance(controlState))
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
return (
|
|
66
|
+
<ItemContent tokens={{ ...currentItemTokens, ...staticTokens.content }}>
|
|
67
|
+
{label}
|
|
68
|
+
</ItemContent>
|
|
69
|
+
)
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
return (
|
|
@@ -70,12 +79,23 @@ const ItemsGroup = React.forwardRef(
|
|
|
70
79
|
controlTokens={getControlTokens}
|
|
71
80
|
control={controlContent}
|
|
72
81
|
accessibilityState={{ active: isActive }} // ExpandCollapse.Panel handles expanded state
|
|
82
|
+
disableMobileScrollBuffer
|
|
73
83
|
>
|
|
74
84
|
{children}
|
|
75
85
|
</ExpandCollapse.Panel>
|
|
76
86
|
)
|
|
77
87
|
}
|
|
78
88
|
)
|
|
89
|
+
|
|
90
|
+
const staticTokens = {
|
|
91
|
+
panel: {
|
|
92
|
+
borderWidth: 0,
|
|
93
|
+
marginBottom: 0
|
|
94
|
+
},
|
|
95
|
+
control: { borderWidth: 0, textLine: null },
|
|
96
|
+
content: { accentWidth: 0 }
|
|
97
|
+
}
|
|
98
|
+
|
|
79
99
|
ItemsGroup.displayName = 'ItemsGroup'
|
|
80
100
|
|
|
81
101
|
ItemsGroup.propTypes = {
|
package/src/SideNav/SideNav.jsx
CHANGED
|
@@ -14,14 +14,23 @@ import {
|
|
|
14
14
|
viewProps
|
|
15
15
|
} from '../utils'
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const selectContainerTokens = ({ borderWidth, borderStyle, borderColor }) => {
|
|
18
18
|
return {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
borderTopWidth: borderWidth,
|
|
20
|
+
borderStyle,
|
|
21
|
+
borderColor
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const selectItemTokens = (tokens = {}, isLastItem = false) => ({
|
|
26
|
+
...tokens,
|
|
27
|
+
...(isLastItem
|
|
28
|
+
? {
|
|
29
|
+
borderWidth: 0
|
|
30
|
+
}
|
|
31
|
+
: {})
|
|
32
|
+
})
|
|
33
|
+
|
|
25
34
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
26
35
|
|
|
27
36
|
/**
|
|
@@ -46,11 +55,11 @@ const SideNav = React.forwardRef(
|
|
|
46
55
|
<ExpandCollapse
|
|
47
56
|
ref={ref}
|
|
48
57
|
maxOpen={accordion ? 1 : null}
|
|
49
|
-
|
|
58
|
+
tokens={selectContainerTokens(themeTokens)}
|
|
50
59
|
{...selectProps(rest)}
|
|
51
60
|
>
|
|
52
61
|
{({ openIds, onToggle }) => {
|
|
53
|
-
const renderItem = (item, index, groupId) => {
|
|
62
|
+
const renderItem = (item, index, groupId, isLastItem = false) => {
|
|
54
63
|
const { itemId = `item-${index}`, onPress } = item.props
|
|
55
64
|
const handlePress = (...args) => {
|
|
56
65
|
onItemPress(...args)
|
|
@@ -64,7 +73,7 @@ const SideNav = React.forwardRef(
|
|
|
64
73
|
itemId={itemId}
|
|
65
74
|
groupId={groupId}
|
|
66
75
|
variant={groupId ? { ...variant, type: 'child' } : variant}
|
|
67
|
-
tokens={itemTokens}
|
|
76
|
+
tokens={selectItemTokens(itemTokens, isLastItem)}
|
|
68
77
|
isActive={isItemActive(itemId, groupId)}
|
|
69
78
|
onPress={handlePress}
|
|
70
79
|
/>
|
|
@@ -93,7 +102,12 @@ const SideNav = React.forwardRef(
|
|
|
93
102
|
onToggle={handleToggle}
|
|
94
103
|
>
|
|
95
104
|
{React.Children.map(child.props.children, (item, itemIndex) =>
|
|
96
|
-
renderItem(
|
|
105
|
+
renderItem(
|
|
106
|
+
item,
|
|
107
|
+
itemIndex,
|
|
108
|
+
groupId,
|
|
109
|
+
itemIndex === child.props.children.length - 1
|
|
110
|
+
)
|
|
97
111
|
)}
|
|
98
112
|
</ItemsGroup>
|
|
99
113
|
)
|