@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
|
@@ -4,6 +4,19 @@ import ItemContent from './ItemContent';
|
|
|
4
4
|
import ExpandCollapse from '../ExpandCollapse';
|
|
5
5
|
import { getTokensPropType, variantProp, componentPropType, selectTokens } from '../utils';
|
|
6
6
|
import { useThemeTokensCallback } from '../ThemeProvider';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const selectPanelTokens = _ref => {
|
|
9
|
+
let {
|
|
10
|
+
borderWidth,
|
|
11
|
+
borderColor,
|
|
12
|
+
backgroundColor
|
|
13
|
+
} = _ref;
|
|
14
|
+
return {
|
|
15
|
+
contentPanelBackgroundColor: backgroundColor,
|
|
16
|
+
borderBottomWidth: borderWidth,
|
|
17
|
+
borderColor
|
|
18
|
+
};
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
/**
|
|
9
22
|
Expandable content areas for use within `SideNav`.
|
|
@@ -15,8 +28,7 @@ import { useThemeTokensCallback } from '../ThemeProvider';
|
|
|
15
28
|
## Usage Criteria
|
|
16
29
|
- Use `SideNav.ItemsGroup` with large pages that have multiple sections
|
|
17
30
|
*/
|
|
18
|
-
|
|
19
|
-
const ItemsGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
31
|
+
const ItemsGroup = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
20
32
|
let {
|
|
21
33
|
children,
|
|
22
34
|
label,
|
|
@@ -27,7 +39,7 @@ const ItemsGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
27
39
|
tokens,
|
|
28
40
|
itemTokens,
|
|
29
41
|
onToggle
|
|
30
|
-
} =
|
|
42
|
+
} = _ref2;
|
|
31
43
|
// A SideNav control uses the same style and theme as SideNavItem, with a 'parent' variant,
|
|
32
44
|
// plus control-specific tokens from the SideNavItemsGroup theme (e.g. open/close icon, etc).
|
|
33
45
|
const getAppearance = appearance => ({
|
|
@@ -40,28 +52,27 @@ const ItemsGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
40
52
|
type: 'parent'
|
|
41
53
|
});
|
|
42
54
|
const getGroupTokens = useThemeTokensCallback('SideNavItemsGroup', tokens, variant);
|
|
43
|
-
const getPanelTokens = appearance => {
|
|
44
|
-
const {
|
|
45
|
-
panelBorderColor,
|
|
46
|
-
...itemsGroupTokens
|
|
47
|
-
} = getGroupTokens(getAppearance(appearance));
|
|
48
|
-
const groupTokens = {
|
|
49
|
-
...itemsGroupTokens,
|
|
50
|
-
borderWidth: 0,
|
|
51
|
-
marginBottom: 0
|
|
52
|
-
};
|
|
53
|
-
return selectTokens('ExpandCollapsePanel', groupTokens);
|
|
54
|
-
};
|
|
55
55
|
const getItemTokens = useThemeTokensCallback('SideNavItem', itemTokens, variant);
|
|
56
|
-
const
|
|
57
|
-
...
|
|
58
|
-
|
|
59
|
-
...
|
|
56
|
+
const getPanelTokens = appearance => selectTokens('ExpandCollapsePanel', {
|
|
57
|
+
...staticTokens.panel,
|
|
58
|
+
...getGroupTokens(getAppearance(appearance)),
|
|
59
|
+
...selectPanelTokens(getItemTokens(getItemAppearance(appearance)))
|
|
60
|
+
});
|
|
61
|
+
const getControlTokens = appearance => ({
|
|
62
|
+
...selectTokens('ExpandCollapseControl', {
|
|
63
|
+
...getItemTokens(getItemAppearance(appearance)),
|
|
64
|
+
// main style from SideNavItem
|
|
65
|
+
...getGroupTokens(getAppearance(appearance)) // control-specific tokens like icon etc,
|
|
66
|
+
}),
|
|
67
|
+
...staticTokens.control
|
|
60
68
|
});
|
|
61
69
|
const controlContent = controlState => {
|
|
62
70
|
const currentItemTokens = getItemTokens(getItemAppearance(controlState));
|
|
63
71
|
return /*#__PURE__*/_jsx(ItemContent, {
|
|
64
|
-
tokens:
|
|
72
|
+
tokens: {
|
|
73
|
+
...currentItemTokens,
|
|
74
|
+
...staticTokens.content
|
|
75
|
+
},
|
|
65
76
|
children: label
|
|
66
77
|
});
|
|
67
78
|
};
|
|
@@ -77,9 +88,23 @@ const ItemsGroup = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
77
88
|
active: isActive
|
|
78
89
|
} // ExpandCollapse.Panel handles expanded state
|
|
79
90
|
,
|
|
91
|
+
disableMobileScrollBuffer: true,
|
|
80
92
|
children: children
|
|
81
93
|
});
|
|
82
94
|
});
|
|
95
|
+
const staticTokens = {
|
|
96
|
+
panel: {
|
|
97
|
+
borderWidth: 0,
|
|
98
|
+
marginBottom: 0
|
|
99
|
+
},
|
|
100
|
+
control: {
|
|
101
|
+
borderWidth: 0,
|
|
102
|
+
textLine: null
|
|
103
|
+
},
|
|
104
|
+
content: {
|
|
105
|
+
accentWidth: 0
|
|
106
|
+
}
|
|
107
|
+
};
|
|
83
108
|
ItemsGroup.displayName = 'ItemsGroup';
|
|
84
109
|
ItemsGroup.propTypes = {
|
|
85
110
|
/**
|
|
@@ -6,13 +6,28 @@ import ItemsGroup from './ItemsGroup';
|
|
|
6
6
|
import { useThemeTokens } from '../ThemeProvider';
|
|
7
7
|
import { a11yProps, componentPropType, getTokensPropType, selectSystemProps, variantProp, viewProps } from '../utils';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
|
|
9
|
+
const selectContainerTokens = _ref => {
|
|
10
|
+
let {
|
|
11
|
+
borderWidth,
|
|
12
|
+
borderStyle,
|
|
13
|
+
borderColor
|
|
14
|
+
} = _ref;
|
|
10
15
|
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
borderTopWidth: borderWidth,
|
|
17
|
+
borderStyle,
|
|
18
|
+
borderColor
|
|
14
19
|
};
|
|
15
|
-
}
|
|
20
|
+
};
|
|
21
|
+
const selectItemTokens = function () {
|
|
22
|
+
let tokens = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
23
|
+
let isLastItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
24
|
+
return {
|
|
25
|
+
...tokens,
|
|
26
|
+
...(isLastItem ? {
|
|
27
|
+
borderWidth: 0
|
|
28
|
+
} : {})
|
|
29
|
+
};
|
|
30
|
+
};
|
|
16
31
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
17
32
|
|
|
18
33
|
/**
|
|
@@ -20,7 +35,7 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
|
|
|
20
35
|
- Use in conjunction with a large amount of educational / informational content
|
|
21
36
|
- Allow the user to navigate between options frequently and efficiently
|
|
22
37
|
*/
|
|
23
|
-
const SideNav = /*#__PURE__*/React.forwardRef((
|
|
38
|
+
const SideNav = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
|
|
24
39
|
let {
|
|
25
40
|
children,
|
|
26
41
|
variant = {},
|
|
@@ -29,7 +44,7 @@ const SideNav = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
29
44
|
itemTokens,
|
|
30
45
|
groupTokens,
|
|
31
46
|
...rest
|
|
32
|
-
} =
|
|
47
|
+
} = _ref2;
|
|
33
48
|
const themeTokens = useThemeTokens('SideNav', tokens, variant);
|
|
34
49
|
const [active, setActive] = React.useState({
|
|
35
50
|
groupId: undefined,
|
|
@@ -47,14 +62,15 @@ const SideNav = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
47
62
|
return /*#__PURE__*/_jsx(ExpandCollapse, {
|
|
48
63
|
ref: ref,
|
|
49
64
|
maxOpen: accordion ? 1 : null,
|
|
50
|
-
|
|
65
|
+
tokens: selectContainerTokens(themeTokens),
|
|
51
66
|
...selectProps(rest),
|
|
52
|
-
children:
|
|
67
|
+
children: _ref3 => {
|
|
53
68
|
let {
|
|
54
69
|
openIds,
|
|
55
70
|
onToggle
|
|
56
|
-
} =
|
|
57
|
-
const renderItem = (item, index, groupId)
|
|
71
|
+
} = _ref3;
|
|
72
|
+
const renderItem = function (item, index, groupId) {
|
|
73
|
+
let isLastItem = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
58
74
|
const {
|
|
59
75
|
itemId = `item-${index}`,
|
|
60
76
|
onPress
|
|
@@ -72,7 +88,7 @@ const SideNav = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
72
88
|
...variant,
|
|
73
89
|
type: 'child'
|
|
74
90
|
} : variant,
|
|
75
|
-
tokens: itemTokens,
|
|
91
|
+
tokens: selectItemTokens(itemTokens, isLastItem),
|
|
76
92
|
isActive: isItemActive(itemId, groupId),
|
|
77
93
|
onPress: handlePress
|
|
78
94
|
});
|
|
@@ -96,7 +112,7 @@ const SideNav = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
96
112
|
openGroups: openIds,
|
|
97
113
|
isActive: isGroupActive,
|
|
98
114
|
onToggle: handleToggle
|
|
99
|
-
}, React.Children.map(child.props.children, (item, itemIndex) => renderItem(item, itemIndex, groupId)));
|
|
115
|
+
}, React.Children.map(child.props.children, (item, itemIndex) => renderItem(item, itemIndex, groupId, itemIndex === child.props.children.length - 1)));
|
|
100
116
|
}
|
|
101
117
|
return null;
|
|
102
118
|
});
|
package/lib/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.20.0",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"standard-engine": {
|
|
85
85
|
"skip": true
|
|
86
86
|
},
|
|
87
|
-
"version": "3.
|
|
87
|
+
"version": "3.28.0",
|
|
88
88
|
"types": "types/index.d.ts"
|
|
89
89
|
}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.20.0",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"standard-engine": {
|
|
85
85
|
"skip": true
|
|
86
86
|
},
|
|
87
|
-
"version": "3.
|
|
87
|
+
"version": "3.28.0",
|
|
88
88
|
"types": "types/index.d.ts"
|
|
89
89
|
}
|
package/src/Card/Card.jsx
CHANGED
|
@@ -30,10 +30,10 @@ const SelectionType = {
|
|
|
30
30
|
None: undefined
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const selectInputStyle = ({ paddingTop, paddingRight }) => ({
|
|
33
|
+
const selectInputStyle = ({ paddingTop, paddingRight, paddingLeft }, { inputPositionProp }) => ({
|
|
34
34
|
position: 'absolute',
|
|
35
35
|
top: paddingTop,
|
|
36
|
-
right: paddingRight
|
|
36
|
+
...(inputPositionProp === 'left' ? { left: paddingLeft } : { right: paddingRight })
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const getInputProps = ({
|
|
@@ -108,7 +108,18 @@ const getInputProps = ({
|
|
|
108
108
|
*/
|
|
109
109
|
const Card = React.forwardRef(
|
|
110
110
|
(
|
|
111
|
-
{
|
|
111
|
+
{
|
|
112
|
+
children,
|
|
113
|
+
tokens,
|
|
114
|
+
variant,
|
|
115
|
+
dataSet,
|
|
116
|
+
onPress,
|
|
117
|
+
id,
|
|
118
|
+
interactiveCard,
|
|
119
|
+
backgroundImage,
|
|
120
|
+
testID,
|
|
121
|
+
...rest
|
|
122
|
+
},
|
|
112
123
|
ref
|
|
113
124
|
) => {
|
|
114
125
|
const viewport = useViewport()
|
|
@@ -216,6 +227,11 @@ const Card = React.forwardRef(
|
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
const renderInputPerSelectionType = (props) => {
|
|
230
|
+
const containerStyle = selectInputStyle(getThemeTokens(), {
|
|
231
|
+
inputPositionProp: interactiveCard?.inputPosition
|
|
232
|
+
})
|
|
233
|
+
const inputTestID = testID && `${testID}-selection-input`
|
|
234
|
+
|
|
219
235
|
if (!isControl) {
|
|
220
236
|
return null
|
|
221
237
|
}
|
|
@@ -223,13 +239,13 @@ const Card = React.forwardRef(
|
|
|
223
239
|
switch (selectionType) {
|
|
224
240
|
case SelectionType.Checkbox:
|
|
225
241
|
return (
|
|
226
|
-
<View style={
|
|
242
|
+
<View style={containerStyle} testID={inputTestID}>
|
|
227
243
|
<CheckboxButton {...props} tokens={{ ...checkboxTokens, ...props?.tokens }} />
|
|
228
244
|
</View>
|
|
229
245
|
)
|
|
230
246
|
case SelectionType.Radio:
|
|
231
247
|
return (
|
|
232
|
-
<View style={
|
|
248
|
+
<View style={containerStyle} testID={inputTestID}>
|
|
233
249
|
<RadioButton {...props} tokens={{ ...radioTokens, ...props?.tokens }} />
|
|
234
250
|
</View>
|
|
235
251
|
)
|
|
@@ -245,6 +261,7 @@ const Card = React.forwardRef(
|
|
|
245
261
|
tokens={interactiveCard?.body ? restOfTokens : cardStyles}
|
|
246
262
|
backgroundImage={backgroundImage}
|
|
247
263
|
dataSet={mediaIds && { media: mediaIds }}
|
|
264
|
+
testID={testID}
|
|
248
265
|
{...selectProps(rest)}
|
|
249
266
|
>
|
|
250
267
|
{interactiveCard?.body && (
|
|
@@ -342,7 +359,8 @@ Card.propTypes = {
|
|
|
342
359
|
selectionType: PropTypes.oneOf(Object.values(SelectionType)),
|
|
343
360
|
variant: variantProp.propType,
|
|
344
361
|
href: PropTypes.string,
|
|
345
|
-
hrefAttrs: PropTypes.shape(hrefAttrsProp.types)
|
|
362
|
+
hrefAttrs: PropTypes.shape(hrefAttrsProp.types),
|
|
363
|
+
inputPosition: PropTypes.oneOf(['left', 'right'])
|
|
346
364
|
}),
|
|
347
365
|
/**
|
|
348
366
|
* Apply background image to the card.
|
|
@@ -365,7 +383,11 @@ Card.propTypes = {
|
|
|
365
383
|
/**
|
|
366
384
|
* Data set for the card.
|
|
367
385
|
*/
|
|
368
|
-
dataSet: PropTypes.object
|
|
386
|
+
dataSet: PropTypes.object,
|
|
387
|
+
/**
|
|
388
|
+
* Test ID used for e2e testing.
|
|
389
|
+
*/
|
|
390
|
+
testID: PropTypes.string
|
|
369
391
|
}
|
|
370
392
|
|
|
371
393
|
export default Card
|
package/src/Card/CardBase.jsx
CHANGED
|
@@ -116,6 +116,44 @@ const setBackgroundImage = ({
|
|
|
116
116
|
)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
const selectPaddedContentStyles = ({
|
|
120
|
+
paddingTop,
|
|
121
|
+
paddingBottom,
|
|
122
|
+
paddingLeft,
|
|
123
|
+
paddingRight,
|
|
124
|
+
borderWidth,
|
|
125
|
+
borderColor,
|
|
126
|
+
borderRadius,
|
|
127
|
+
hasInteractiveBorder
|
|
128
|
+
}) => ({
|
|
129
|
+
paddingTop,
|
|
130
|
+
paddingBottom,
|
|
131
|
+
paddingLeft,
|
|
132
|
+
paddingRight,
|
|
133
|
+
...(hasInteractiveBorder
|
|
134
|
+
? {
|
|
135
|
+
borderWidth,
|
|
136
|
+
borderColor,
|
|
137
|
+
borderRadius
|
|
138
|
+
}
|
|
139
|
+
: {})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const selectInteractiveOverlayStyles = ({ backgroundColor, borderRadius, borderWidth }) => {
|
|
143
|
+
const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth)
|
|
144
|
+
return {
|
|
145
|
+
position: 'absolute',
|
|
146
|
+
top: 0,
|
|
147
|
+
left: 0,
|
|
148
|
+
right: 0,
|
|
149
|
+
bottom: 0,
|
|
150
|
+
backgroundColor,
|
|
151
|
+
borderRadius: adjustedBorderRadius,
|
|
152
|
+
pointerEvents: 'none',
|
|
153
|
+
zIndex: 1
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
119
157
|
// Ensure explicit selection of tokens
|
|
120
158
|
export const selectStyles = ({
|
|
121
159
|
flex,
|
|
@@ -195,7 +233,13 @@ export const selectStyles = ({
|
|
|
195
233
|
*/
|
|
196
234
|
const CardBase = React.forwardRef(
|
|
197
235
|
({ children, tokens, dataSet, backgroundImage, fullBleedContent, cardState, ...rest }, ref) => {
|
|
198
|
-
const
|
|
236
|
+
const resolvedTokens = typeof tokens === 'function' ? tokens(cardState) : tokens
|
|
237
|
+
const tokensToUse =
|
|
238
|
+
backgroundImage && backgroundImage.src
|
|
239
|
+
? { ...resolvedTokens, gradient: undefined, backgroundGradient: undefined }
|
|
240
|
+
: resolvedTokens
|
|
241
|
+
|
|
242
|
+
const cardStyle = selectStyles(tokensToUse)
|
|
199
243
|
const props = selectProps(rest)
|
|
200
244
|
|
|
201
245
|
let content = children
|
|
@@ -214,13 +258,55 @@ const CardBase = React.forwardRef(
|
|
|
214
258
|
const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')
|
|
215
259
|
|
|
216
260
|
if (backgroundImage && src) {
|
|
217
|
-
const {
|
|
261
|
+
const {
|
|
262
|
+
paddingTop,
|
|
263
|
+
paddingBottom,
|
|
264
|
+
paddingLeft,
|
|
265
|
+
paddingRight,
|
|
266
|
+
borderWidth,
|
|
267
|
+
borderColor,
|
|
268
|
+
borderRadius,
|
|
269
|
+
backgroundColor,
|
|
270
|
+
...containerStyle
|
|
271
|
+
} = cardStyle
|
|
218
272
|
|
|
219
273
|
const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
274
|
+
const hasInteractiveBorder = borderWidth && borderWidth > 0
|
|
275
|
+
const hasInteractiveOverlay = isOverlayColor(backgroundColor)
|
|
276
|
+
|
|
277
|
+
const paddedContent =
|
|
278
|
+
hasPadding || hasInteractiveBorder ? (
|
|
279
|
+
<View
|
|
280
|
+
style={selectPaddedContentStyles({
|
|
281
|
+
paddingTop,
|
|
282
|
+
paddingBottom,
|
|
283
|
+
paddingLeft,
|
|
284
|
+
paddingRight,
|
|
285
|
+
borderWidth,
|
|
286
|
+
borderColor,
|
|
287
|
+
borderRadius,
|
|
288
|
+
hasInteractiveBorder
|
|
289
|
+
})}
|
|
290
|
+
>
|
|
291
|
+
{children}
|
|
292
|
+
</View>
|
|
293
|
+
) : (
|
|
294
|
+
children
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
const contentWithOverlay = (
|
|
298
|
+
<>
|
|
299
|
+
{hasInteractiveOverlay && Platform.OS === 'web' && (
|
|
300
|
+
<View
|
|
301
|
+
style={selectInteractiveOverlayStyles({
|
|
302
|
+
backgroundColor,
|
|
303
|
+
borderRadius,
|
|
304
|
+
borderWidth
|
|
305
|
+
})}
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
308
|
+
<View style={staticStyles.contentOverlay}>{paddedContent}</View>
|
|
309
|
+
</>
|
|
224
310
|
)
|
|
225
311
|
|
|
226
312
|
content = setBackgroundImage({
|
|
@@ -229,12 +315,12 @@ const CardBase = React.forwardRef(
|
|
|
229
315
|
backgroundImageResizeMode,
|
|
230
316
|
backgroundImagePosition,
|
|
231
317
|
backgroundImageAlign,
|
|
232
|
-
content:
|
|
233
|
-
cardStyle: containerStyle
|
|
318
|
+
content: contentWithOverlay,
|
|
319
|
+
cardStyle: { ...containerStyle, borderRadius }
|
|
234
320
|
})
|
|
235
321
|
|
|
236
322
|
return (
|
|
237
|
-
<View style={containerStyle} dataSet={dataSet} ref={ref} {...props}>
|
|
323
|
+
<View style={{ ...containerStyle, borderRadius }} dataSet={dataSet} ref={ref} {...props}>
|
|
238
324
|
{content}
|
|
239
325
|
</View>
|
|
240
326
|
)
|
|
@@ -313,8 +399,8 @@ const staticStyles = StyleSheet.create({
|
|
|
313
399
|
contentOverlay: {
|
|
314
400
|
position: 'relative',
|
|
315
401
|
width: '100%',
|
|
316
|
-
|
|
317
|
-
zIndex:
|
|
402
|
+
minHeight: '100%',
|
|
403
|
+
zIndex: 2
|
|
318
404
|
},
|
|
319
405
|
containContainer: {
|
|
320
406
|
width: '100%',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { Pressable, Platform, View, StyleSheet } from 'react-native'
|
|
4
|
-
|
|
5
4
|
import { useViewport } from '../ViewportProvider'
|
|
6
5
|
import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider'
|
|
7
6
|
import {
|
|
@@ -26,6 +25,71 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
|
26
25
|
viewProps
|
|
27
26
|
])
|
|
28
27
|
|
|
28
|
+
const selectFocusOverlayContainerStyles = (tokens) => {
|
|
29
|
+
const { flex, minWidth, marginTop, marginBottom, marginLeft, marginRight } = tokens
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
flex: flex || 1,
|
|
33
|
+
minWidth: minWidth || 0,
|
|
34
|
+
marginTop,
|
|
35
|
+
marginBottom,
|
|
36
|
+
marginLeft,
|
|
37
|
+
marginRight
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const selectFocusBorderStyles = (tokens) => {
|
|
42
|
+
const { borderWidth, borderColor, borderRadius } = tokens
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
borderWidth,
|
|
46
|
+
borderColor,
|
|
47
|
+
borderRadius: borderRadius || 0
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const FocusBorderOverlay = ({ tokens, pressableState, children }) => {
|
|
52
|
+
const { borderWidth = 0 } = tokens
|
|
53
|
+
const showFocusBorder = pressableState.focused && borderWidth > 0
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View
|
|
57
|
+
style={[
|
|
58
|
+
staticStyles.focusOverlayContainer,
|
|
59
|
+
selectFocusOverlayContainerStyles(tokens),
|
|
60
|
+
Platform.OS === 'web' && staticStyles.webOutlineNone
|
|
61
|
+
]}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
{showFocusBorder && (
|
|
65
|
+
<View
|
|
66
|
+
style={[staticStyles.focusBorder, selectFocusBorderStyles(tokens)]}
|
|
67
|
+
accessible={false}
|
|
68
|
+
importantForAccessibility="no"
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
FocusBorderOverlay.propTypes = {
|
|
76
|
+
tokens: PropTypes.shape({
|
|
77
|
+
flex: PropTypes.number,
|
|
78
|
+
minWidth: PropTypes.number,
|
|
79
|
+
marginTop: PropTypes.number,
|
|
80
|
+
marginBottom: PropTypes.number,
|
|
81
|
+
marginLeft: PropTypes.number,
|
|
82
|
+
marginRight: PropTypes.number,
|
|
83
|
+
borderColor: PropTypes.string,
|
|
84
|
+
borderWidth: PropTypes.number,
|
|
85
|
+
borderRadius: PropTypes.number
|
|
86
|
+
}).isRequired,
|
|
87
|
+
pressableState: PropTypes.shape({
|
|
88
|
+
focused: PropTypes.bool
|
|
89
|
+
}).isRequired,
|
|
90
|
+
children: PropTypes.node
|
|
91
|
+
}
|
|
92
|
+
|
|
29
93
|
const tokenKeys = [
|
|
30
94
|
'flex',
|
|
31
95
|
'backgroundColor',
|
|
@@ -103,11 +167,53 @@ const PressableCardBase = React.forwardRef(
|
|
|
103
167
|
'PressableCard'
|
|
104
168
|
)
|
|
105
169
|
|
|
106
|
-
const getCardTokens = (pressableState) =>
|
|
170
|
+
const getCardTokens = (pressableState) => {
|
|
171
|
+
const allTokens = getTokens(pressableState)
|
|
172
|
+
const cardTokens = selectTokens('Card', allTokens)
|
|
173
|
+
|
|
174
|
+
// Handle focus border transparency to avoid double borders
|
|
175
|
+
if (pressableState.focused && allTokens.borderWidth > 0) {
|
|
176
|
+
const result = {
|
|
177
|
+
...cardTokens,
|
|
178
|
+
borderColor: 'transparent'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Also handle backgroundImage for interactive states
|
|
182
|
+
if (backgroundImage) {
|
|
183
|
+
const { hovered, pressed, focused } = pressableState || {}
|
|
184
|
+
const isInteractiveState = hovered || pressed || focused
|
|
185
|
+
|
|
186
|
+
if (!isInteractiveState) {
|
|
187
|
+
const { backgroundColor, ...restTokens } = result
|
|
188
|
+
return restTokens
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return result
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Handle backgroundImage when not in focus state
|
|
196
|
+
if (backgroundImage) {
|
|
197
|
+
const { hovered, pressed, focused } = pressableState || {}
|
|
198
|
+
const isInteractiveState = hovered || pressed || focused
|
|
199
|
+
|
|
200
|
+
if (!isInteractiveState) {
|
|
201
|
+
const { backgroundColor, ...restTokens } = cardTokens
|
|
202
|
+
return restTokens
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return cardTokens
|
|
207
|
+
}
|
|
208
|
+
|
|
107
209
|
const getOuterBorderStyle = (pressableState) => {
|
|
108
210
|
const {
|
|
109
211
|
flex,
|
|
110
212
|
minWidth,
|
|
213
|
+
marginTop,
|
|
214
|
+
marginBottom,
|
|
215
|
+
marginLeft,
|
|
216
|
+
marginRight,
|
|
111
217
|
outerBorderColor,
|
|
112
218
|
outerBorderGap = 0,
|
|
113
219
|
outerBorderWidth = 0,
|
|
@@ -116,6 +222,10 @@ const PressableCardBase = React.forwardRef(
|
|
|
116
222
|
return {
|
|
117
223
|
flex,
|
|
118
224
|
minWidth: minWidth + outerBorderGap + outerBorderWidth,
|
|
225
|
+
marginTop,
|
|
226
|
+
marginBottom,
|
|
227
|
+
marginLeft,
|
|
228
|
+
marginRight,
|
|
119
229
|
...applyOuterBorder({ outerBorderColor, outerBorderGap, outerBorderWidth, borderRadius }),
|
|
120
230
|
...Platform.select({ web: { outline: 'none' } })
|
|
121
231
|
}
|
|
@@ -194,13 +304,15 @@ const PressableCardBase = React.forwardRef(
|
|
|
194
304
|
{...selectProps({ ...rest, accessibilityRole })}
|
|
195
305
|
>
|
|
196
306
|
{(pressableState) => (
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
307
|
+
<FocusBorderOverlay tokens={getTokens(pressableState)} pressableState={pressableState}>
|
|
308
|
+
<CardBase
|
|
309
|
+
tokens={getCardTokens(pressableState)}
|
|
310
|
+
backgroundImage={backgroundImage}
|
|
311
|
+
fullBleedContent={fullBleedContent}
|
|
312
|
+
>
|
|
313
|
+
{typeof children === 'function' ? children(getCardState(pressableState)) : children}
|
|
314
|
+
</CardBase>
|
|
315
|
+
</FocusBorderOverlay>
|
|
204
316
|
)}
|
|
205
317
|
</Pressable>
|
|
206
318
|
)
|
|
@@ -218,6 +330,20 @@ const staticStyles = StyleSheet.create({
|
|
|
218
330
|
alignItems: 'stretch',
|
|
219
331
|
justifyContent: 'flex-start',
|
|
220
332
|
textDecorationLine: 'none'
|
|
333
|
+
},
|
|
334
|
+
focusOverlayContainer: {
|
|
335
|
+
position: 'relative'
|
|
336
|
+
},
|
|
337
|
+
webOutlineNone: {
|
|
338
|
+
outline: 'none'
|
|
339
|
+
},
|
|
340
|
+
focusBorder: {
|
|
341
|
+
position: 'absolute',
|
|
342
|
+
top: 0,
|
|
343
|
+
left: 0,
|
|
344
|
+
right: 0,
|
|
345
|
+
bottom: 0,
|
|
346
|
+
pointerEvents: 'none'
|
|
221
347
|
}
|
|
222
348
|
})
|
|
223
349
|
|