@telus-uds/components-base 3.26.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 +35 -2
- package/lib/cjs/Card/Card.js +34 -13
- package/lib/cjs/Card/CardBase.js +90 -14
- package/lib/cjs/Card/PressableCardBase.js +147 -8
- package/lib/cjs/Carousel/Carousel.js +105 -50
- package/lib/cjs/Carousel/CarouselContext.js +10 -4
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/cjs/Carousel/Constants.js +11 -2
- package/lib/cjs/Checkbox/Checkbox.js +43 -13
- 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/List/List.js +24 -9
- package/lib/cjs/List/ListItem.js +18 -1
- package/lib/cjs/List/ListItemBase.js +27 -8
- package/lib/cjs/List/ListItemMark.js +33 -62
- package/lib/cjs/List/PressableListItemBase.js +1 -0
- 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/Card.js +34 -13
- package/lib/esm/Card/CardBase.js +90 -14
- package/lib/esm/Card/PressableCardBase.js +148 -9
- package/lib/esm/Carousel/Carousel.js +106 -51
- package/lib/esm/Carousel/CarouselContext.js +10 -4
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/esm/Carousel/Constants.js +10 -1
- package/lib/esm/Checkbox/Checkbox.js +43 -13
- 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/List/List.js +24 -9
- package/lib/esm/List/ListItem.js +19 -2
- package/lib/esm/List/ListItemBase.js +27 -8
- package/lib/esm/List/ListItemMark.js +33 -62
- package/lib/esm/List/PressableListItemBase.js +1 -0
- 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/Card.jsx +29 -7
- package/src/Card/CardBase.jsx +97 -11
- package/src/Card/PressableCardBase.jsx +135 -9
- package/src/Carousel/Carousel.jsx +119 -64
- package/src/Carousel/CarouselContext.jsx +12 -4
- package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
- package/src/Carousel/Constants.js +10 -0
- package/src/Checkbox/Checkbox.jsx +29 -7
- 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/List/List.jsx +33 -9
- package/src/List/ListItem.jsx +33 -11
- package/src/List/ListItemBase.jsx +33 -9
- package/src/List/ListItemMark.jsx +32 -53
- package/src/List/PressableListItemBase.jsx +1 -0
- 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/List/List.jsx
CHANGED
|
@@ -5,10 +5,22 @@ import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps
|
|
|
5
5
|
|
|
6
6
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
7
7
|
|
|
8
|
+
const LIST_ITEM_TYPE = 'ListItem'
|
|
9
|
+
const LINKS_ITEM_TYPE = 'LinksItem'
|
|
10
|
+
|
|
8
11
|
const isListItem = (element) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
if (!element?.type) return false
|
|
13
|
+
|
|
14
|
+
if (element.type.__UDS_COMPONENT_TYPE__ === LIST_ITEM_TYPE) {
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const elementName = element.type.displayName || element.type.name || ''
|
|
19
|
+
return (
|
|
20
|
+
elementName === LIST_ITEM_TYPE ||
|
|
21
|
+
elementName.includes(LIST_ITEM_TYPE) ||
|
|
22
|
+
elementName.includes(LINKS_ITEM_TYPE)
|
|
23
|
+
)
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
/**
|
|
@@ -22,6 +34,7 @@ const List = React.forwardRef(
|
|
|
22
34
|
showDivider,
|
|
23
35
|
tokens,
|
|
24
36
|
variant,
|
|
37
|
+
iconVerticalAlign,
|
|
25
38
|
accessibilityRole = Platform.select({ web: 'list', default: undefined }),
|
|
26
39
|
...rest
|
|
27
40
|
},
|
|
@@ -30,13 +43,19 @@ const List = React.forwardRef(
|
|
|
30
43
|
const items = React.Children.map(children, (child, index) => {
|
|
31
44
|
// Pass ListItem-specific props to children (by name so teams can add their own ListItems)
|
|
32
45
|
if (isListItem(child)) {
|
|
33
|
-
|
|
34
|
-
showDivider,
|
|
35
|
-
isLastItem: index + 1 === React.Children.count(children),
|
|
46
|
+
const childProps = {
|
|
36
47
|
tokens,
|
|
37
48
|
variant,
|
|
38
|
-
...child.props
|
|
39
|
-
|
|
49
|
+
...child.props,
|
|
50
|
+
showDivider,
|
|
51
|
+
isLastItem: index + 1 === React.Children.count(children)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!child.props.iconVerticalAlign && iconVerticalAlign) {
|
|
55
|
+
childProps.iconVerticalAlign = iconVerticalAlign
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return React.cloneElement(child, childProps)
|
|
40
59
|
}
|
|
41
60
|
return child
|
|
42
61
|
})
|
|
@@ -76,7 +95,12 @@ List.propTypes = {
|
|
|
76
95
|
/**
|
|
77
96
|
* In case it is not the last item allow display divider
|
|
78
97
|
*/
|
|
79
|
-
showDivider: PropTypes.bool
|
|
98
|
+
showDivider: PropTypes.bool,
|
|
99
|
+
/**
|
|
100
|
+
* The vertical alignment of the icon in ListItems.
|
|
101
|
+
* This prop is passed down to ListItem components and can be overridden in individual List.Item components.
|
|
102
|
+
*/
|
|
103
|
+
iconVerticalAlign: PropTypes.oneOf(['top', 'center', 'bottom'])
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
export default List
|
package/src/List/ListItem.jsx
CHANGED
|
@@ -1,25 +1,47 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
3
|
import ListItemBase from './ListItemBase'
|
|
4
4
|
import { useThemeTokens } from '../ThemeProvider'
|
|
5
|
-
import { variantProp } from '../utils'
|
|
5
|
+
import { getTokensPropType, variantProp } from '../utils'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* ListItem is responsible for rendering icon or a bullet as side item
|
|
9
9
|
*/
|
|
10
|
-
const ListItem = React.forwardRef(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
10
|
+
const ListItem = React.forwardRef(
|
|
11
|
+
({ tokens, variant, children, title, iconVerticalAlign = 'top', ...listItemProps }, ref) => {
|
|
12
|
+
const themeTokens = useThemeTokens('List', tokens, variant)
|
|
13
|
+
return (
|
|
14
|
+
<ListItemBase
|
|
15
|
+
tokens={themeTokens}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...listItemProps}
|
|
18
|
+
title={title}
|
|
19
|
+
iconVerticalAlign={iconVerticalAlign}
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
</ListItemBase>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
)
|
|
18
26
|
ListItem.displayName = 'ListItem'
|
|
19
27
|
|
|
20
28
|
ListItem.propTypes = {
|
|
29
|
+
/** Theme tokens for styling */
|
|
30
|
+
tokens: getTokensPropType('List'),
|
|
31
|
+
/** Variant configuration for the component */
|
|
21
32
|
variant: variantProp.propType,
|
|
22
|
-
|
|
33
|
+
/** Content to be rendered within the list item */
|
|
34
|
+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
|
35
|
+
/** Title of the list item */
|
|
36
|
+
title: PropTypes.node,
|
|
37
|
+
/** Controls the vertical alignment of the icon */
|
|
38
|
+
iconVerticalAlign: PropTypes.oneOf(['top', 'center', 'bottom']),
|
|
39
|
+
/** Icon to be displayed */
|
|
40
|
+
icon: PropTypes.elementType,
|
|
41
|
+
/** Color of the icon */
|
|
42
|
+
iconColor: PropTypes.string,
|
|
43
|
+
/** Size of the icon */
|
|
44
|
+
iconSize: PropTypes.number
|
|
23
45
|
}
|
|
24
46
|
|
|
25
47
|
export default ListItem
|
|
@@ -2,7 +2,6 @@ import React from 'react'
|
|
|
2
2
|
import { View, Platform, StyleSheet } from 'react-native'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
4
|
import { a11yProps, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils'
|
|
5
|
-
|
|
6
5
|
import ListItemContent from './ListItemContent'
|
|
7
6
|
import ListItemMark from './ListItemMark'
|
|
8
7
|
import Typography from '../Typography'
|
|
@@ -10,6 +9,14 @@ import { useThemeTokens } from '../ThemeProvider'
|
|
|
10
9
|
|
|
11
10
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
12
11
|
|
|
12
|
+
const VERTICAL_CENTERING_DIVISOR = 2
|
|
13
|
+
|
|
14
|
+
const alignmentMap = {
|
|
15
|
+
top: 'flex-start',
|
|
16
|
+
center: 'center',
|
|
17
|
+
bottom: 'flex-end'
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
const selectItemBlockStyles = ({ interItemMargin }) => ({
|
|
14
21
|
marginBottom: interItemMargin
|
|
15
22
|
})
|
|
@@ -21,9 +28,17 @@ const selectDividerStyles = ({ dividerColor, dividerSize, interItemMarginWithDiv
|
|
|
21
28
|
paddingBottom: interItemMarginWithDivider
|
|
22
29
|
})
|
|
23
30
|
|
|
31
|
+
const selectAlignmentStyles = (iconVerticalAlign) => ({
|
|
32
|
+
alignItems: alignmentMap[iconVerticalAlign]
|
|
33
|
+
})
|
|
34
|
+
|
|
24
35
|
/**
|
|
25
36
|
* ListItem is responsible for rendering icon or a bullet as side item
|
|
26
37
|
*/
|
|
38
|
+
const calculateIconMarginTop = (itemIconSize, fontSize, lineHeightRatio) => {
|
|
39
|
+
return (fontSize * lineHeightRatio - itemIconSize) / VERTICAL_CENTERING_DIVISOR
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
const ListItemBase = React.forwardRef(
|
|
28
43
|
(
|
|
29
44
|
{
|
|
@@ -35,6 +50,7 @@ const ListItemBase = React.forwardRef(
|
|
|
35
50
|
children,
|
|
36
51
|
title,
|
|
37
52
|
isLastItem,
|
|
53
|
+
iconVerticalAlign = 'top',
|
|
38
54
|
accessibilityRole = Platform.select({ web: 'listitem', default: undefined }),
|
|
39
55
|
...rest
|
|
40
56
|
},
|
|
@@ -45,15 +61,15 @@ const ListItemBase = React.forwardRef(
|
|
|
45
61
|
const itemBlockStyles = selectItemBlockStyles(themeTokens)
|
|
46
62
|
const dividerStyles = selectDividerStyles(themeTokens)
|
|
47
63
|
const { iconMarginTop, itemIconSize } = themeTokens
|
|
48
|
-
let adjustedIconMarginTop = iconMarginTop
|
|
49
64
|
const { fontSize, lineHeight: lineHeightRatio } = useThemeTokens(
|
|
50
65
|
'Typography',
|
|
51
66
|
{},
|
|
52
67
|
{ size: 'h4', bold: true }
|
|
53
68
|
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
const adjustedIconMarginTop = title
|
|
70
|
+
? calculateIconMarginTop(itemIconSize, fontSize, lineHeightRatio)
|
|
71
|
+
: iconMarginTop
|
|
72
|
+
|
|
57
73
|
/**
|
|
58
74
|
* Function responsible returning styling, in case the item is the last shouldn't
|
|
59
75
|
* add extra margin on the bottom, if "showDivider" is true it should add a divider
|
|
@@ -81,14 +97,14 @@ const ListItemBase = React.forwardRef(
|
|
|
81
97
|
{typeof children === 'function' ? (
|
|
82
98
|
children({ tokens, icon, iconColor, iconSize, isLastItem })
|
|
83
99
|
) : (
|
|
84
|
-
<View style={staticStyles.
|
|
100
|
+
<View style={[staticStyles.innerContainer, selectAlignmentStyles(iconVerticalAlign)]}>
|
|
85
101
|
<ListItemMark
|
|
86
102
|
tokens={{ ...tokens, iconMarginTop: adjustedIconMarginTop }}
|
|
87
103
|
icon={icon}
|
|
88
104
|
iconColor={iconColor}
|
|
89
105
|
iconSize={iconSize}
|
|
90
106
|
/>
|
|
91
|
-
<View style={staticStyles.titleAndContentContainer}>
|
|
107
|
+
<View style={[staticStyles.titleAndContentContainer]}>
|
|
92
108
|
{Boolean(title) && (
|
|
93
109
|
<Typography variant={{ size: 'h4', bold: true }}>{title}</Typography>
|
|
94
110
|
)}
|
|
@@ -101,16 +117,22 @@ const ListItemBase = React.forwardRef(
|
|
|
101
117
|
}
|
|
102
118
|
)
|
|
103
119
|
ListItemBase.displayName = 'ListItem'
|
|
120
|
+
ListItemBase.__UDS_COMPONENT_TYPE__ = 'ListItem'
|
|
104
121
|
|
|
105
122
|
const staticStyles = StyleSheet.create({
|
|
106
123
|
container: {
|
|
124
|
+
flexDirection: 'row',
|
|
125
|
+
width: '100%'
|
|
126
|
+
},
|
|
127
|
+
innerContainer: {
|
|
107
128
|
flex: 1,
|
|
108
129
|
flexDirection: 'row'
|
|
109
130
|
},
|
|
110
131
|
titleAndContentContainer: {
|
|
111
132
|
flexDirection: 'column',
|
|
112
|
-
|
|
113
|
-
flexGrow: 1
|
|
133
|
+
flex: 1,
|
|
134
|
+
flexGrow: 1,
|
|
135
|
+
flexShrink: 1
|
|
114
136
|
}
|
|
115
137
|
})
|
|
116
138
|
|
|
@@ -119,6 +141,8 @@ ListItemBase.propTypes = {
|
|
|
119
141
|
tokens: getTokensPropType('List'),
|
|
120
142
|
variant: variantProp.propType,
|
|
121
143
|
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
|
144
|
+
/** Controls the vertical alignment of the icon */
|
|
145
|
+
iconVerticalAlign: PropTypes.oneOf(['top', 'center', 'bottom']),
|
|
122
146
|
/**
|
|
123
147
|
* Renders side item icon
|
|
124
148
|
*/
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
|
|
4
|
-
import { View, StyleSheet } from 'react-native'
|
|
3
|
+
import { View } from 'react-native'
|
|
5
4
|
import Icon from '../Icon'
|
|
6
5
|
import { useVariants } from '../utils'
|
|
7
6
|
|
|
@@ -9,33 +8,21 @@ export const tokenTypes = {
|
|
|
9
8
|
itemIconSize: PropTypes.number.isRequired,
|
|
10
9
|
itemIconColor: PropTypes.string.isRequired,
|
|
11
10
|
listGutter: PropTypes.number.isRequired,
|
|
12
|
-
iconMarginTop: PropTypes.number.isRequired
|
|
11
|
+
iconMarginTop: PropTypes.number.isRequired,
|
|
12
|
+
bulletIcon: PropTypes.elementType.isRequired
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const selectSideItemContainerStyles = ({ listGutter, iconMarginTop }) => ({
|
|
21
|
-
marginTop: iconMarginTop,
|
|
22
|
-
marginRight: listGutter
|
|
15
|
+
const selectContainerStyles = ({ listGutter }) => ({
|
|
16
|
+
marginInlineEnd: listGutter,
|
|
17
|
+
display: 'flex',
|
|
18
|
+
alignItems: 'flex-start'
|
|
23
19
|
})
|
|
24
|
-
|
|
25
|
-
// Align bullets with the top line of text the same way icons are aligned
|
|
26
|
-
const selectBulletPositioningStyles = ({ itemIconSize }) => ({
|
|
20
|
+
const selectBulletStyles = ({ itemIconSize }) => ({
|
|
27
21
|
width: itemIconSize,
|
|
28
|
-
height: itemIconSize
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
itemBulletContainerWidth,
|
|
33
|
-
itemBulletContainerHeight,
|
|
34
|
-
itemBulletContainerAlign
|
|
35
|
-
}) => ({
|
|
36
|
-
width: itemBulletContainerWidth,
|
|
37
|
-
height: itemBulletContainerHeight,
|
|
38
|
-
alignItems: itemBulletContainerAlign
|
|
22
|
+
height: itemIconSize,
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
flexShrink: 0
|
|
39
26
|
})
|
|
40
27
|
|
|
41
28
|
const getIconColorVariants = (iconVariants) =>
|
|
@@ -50,26 +37,24 @@ const getIconColorVariants = (iconVariants) =>
|
|
|
50
37
|
*/
|
|
51
38
|
const ListItemMark = React.forwardRef(({ icon, iconColor, iconSize, tokens = {} }, ref) => {
|
|
52
39
|
const themeTokens = typeof tokens === 'function' ? tokens() : tokens
|
|
40
|
+
const containerStyles = selectContainerStyles(themeTokens)
|
|
53
41
|
|
|
54
|
-
const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
|
|
55
|
-
const bulletContainerStyles = selectBulletContainerStyles(themeTokens)
|
|
56
|
-
|
|
57
|
-
// TODO: Remove it when iconColor custom colors are deprecated.
|
|
58
42
|
const iconVariants = useVariants('Icon')
|
|
59
43
|
const iconColorVariants = getIconColorVariants(iconVariants)
|
|
60
44
|
|
|
61
45
|
if (icon) {
|
|
62
|
-
const
|
|
46
|
+
const { itemIconSize, itemIconColor } = themeTokens
|
|
47
|
+
const finalIconSize = iconSize ?? itemIconSize
|
|
48
|
+
const finalIconColor =
|
|
49
|
+
iconColor && !iconColorVariants?.includes(iconColor) ? iconColor : itemIconColor
|
|
50
|
+
|
|
63
51
|
return (
|
|
64
|
-
<View style={
|
|
52
|
+
<View style={containerStyles}>
|
|
65
53
|
<Icon
|
|
66
54
|
icon={icon}
|
|
67
55
|
tokens={{
|
|
68
|
-
size:
|
|
69
|
-
|
|
70
|
-
color:
|
|
71
|
-
iconColor && !iconColorVariants?.includes(iconColor) ? iconColor : iconTokens.color
|
|
72
|
-
})
|
|
56
|
+
size: finalIconSize,
|
|
57
|
+
color: finalIconColor
|
|
73
58
|
}}
|
|
74
59
|
variant={{
|
|
75
60
|
...(iconColorVariants?.includes(iconColor) && { color: iconColor })
|
|
@@ -79,18 +64,19 @@ const ListItemMark = React.forwardRef(({ icon, iconColor, iconSize, tokens = {}
|
|
|
79
64
|
)
|
|
80
65
|
}
|
|
81
66
|
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const itemBulletContainerStyles = selectBulletContainerStyles(themeTokens)
|
|
85
|
-
const itemBulletPositioningStyles = selectBulletPositioningStyles(themeTokens)
|
|
67
|
+
const { itemIconSize, itemIconColor } = themeTokens
|
|
68
|
+
const bulletStyles = selectBulletStyles(themeTokens)
|
|
86
69
|
|
|
87
70
|
return (
|
|
88
|
-
<View style={
|
|
89
|
-
<View
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
<View style={containerStyles} ref={ref}>
|
|
72
|
+
<View style={bulletStyles}>
|
|
73
|
+
<Icon
|
|
74
|
+
icon={themeTokens.bulletIcon}
|
|
75
|
+
tokens={{
|
|
76
|
+
color: itemIconColor,
|
|
77
|
+
size: itemIconSize
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
94
80
|
</View>
|
|
95
81
|
</View>
|
|
96
82
|
)
|
|
@@ -114,11 +100,4 @@ ListItemMark.propTypes = {
|
|
|
114
100
|
iconSize: PropTypes.number
|
|
115
101
|
}
|
|
116
102
|
|
|
117
|
-
const staticStyles = StyleSheet.create({
|
|
118
|
-
bulletPositioning: {
|
|
119
|
-
alignItems: 'center',
|
|
120
|
-
justifyContent: 'center'
|
|
121
|
-
}
|
|
122
|
-
})
|
|
123
|
-
|
|
124
103
|
export default ListItemMark
|
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,
|