@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +35 -2
  2. package/lib/cjs/Card/Card.js +34 -13
  3. package/lib/cjs/Card/CardBase.js +90 -14
  4. package/lib/cjs/Card/PressableCardBase.js +147 -8
  5. package/lib/cjs/Carousel/Carousel.js +105 -50
  6. package/lib/cjs/Carousel/CarouselContext.js +10 -4
  7. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
  8. package/lib/cjs/Carousel/Constants.js +11 -2
  9. package/lib/cjs/Checkbox/Checkbox.js +43 -13
  10. package/lib/cjs/ExpandCollapse/Control.js +5 -1
  11. package/lib/cjs/ExpandCollapse/ExpandCollapse.js +17 -8
  12. package/lib/cjs/ExpandCollapse/Panel.js +7 -2
  13. package/lib/cjs/IconButton/IconButton.js +10 -5
  14. package/lib/cjs/List/List.js +24 -9
  15. package/lib/cjs/List/ListItem.js +18 -1
  16. package/lib/cjs/List/ListItemBase.js +27 -8
  17. package/lib/cjs/List/ListItemMark.js +33 -62
  18. package/lib/cjs/List/PressableListItemBase.js +1 -0
  19. package/lib/cjs/Modal/Modal.js +21 -11
  20. package/lib/cjs/Progress/Progress.js +19 -5
  21. package/lib/cjs/Progress/ProgressBar.js +22 -4
  22. package/lib/cjs/Progress/ProgressContext.js +11 -0
  23. package/lib/cjs/SideNav/Item.js +3 -3
  24. package/lib/cjs/SideNav/ItemsGroup.js +46 -19
  25. package/lib/cjs/SideNav/SideNav.js +29 -13
  26. package/lib/esm/Card/Card.js +34 -13
  27. package/lib/esm/Card/CardBase.js +90 -14
  28. package/lib/esm/Card/PressableCardBase.js +148 -9
  29. package/lib/esm/Carousel/Carousel.js +106 -51
  30. package/lib/esm/Carousel/CarouselContext.js +10 -4
  31. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
  32. package/lib/esm/Carousel/Constants.js +10 -1
  33. package/lib/esm/Checkbox/Checkbox.js +43 -13
  34. package/lib/esm/ExpandCollapse/Control.js +5 -1
  35. package/lib/esm/ExpandCollapse/ExpandCollapse.js +17 -8
  36. package/lib/esm/ExpandCollapse/Panel.js +7 -2
  37. package/lib/esm/IconButton/IconButton.js +10 -5
  38. package/lib/esm/List/List.js +24 -9
  39. package/lib/esm/List/ListItem.js +19 -2
  40. package/lib/esm/List/ListItemBase.js +27 -8
  41. package/lib/esm/List/ListItemMark.js +33 -62
  42. package/lib/esm/List/PressableListItemBase.js +1 -0
  43. package/lib/esm/Modal/Modal.js +21 -11
  44. package/lib/esm/Progress/Progress.js +19 -5
  45. package/lib/esm/Progress/ProgressBar.js +22 -4
  46. package/lib/esm/Progress/ProgressContext.js +5 -0
  47. package/lib/esm/SideNav/Item.js +3 -3
  48. package/lib/esm/SideNav/ItemsGroup.js +45 -20
  49. package/lib/esm/SideNav/SideNav.js +29 -13
  50. package/lib/package.json +2 -2
  51. package/package.json +2 -2
  52. package/src/Card/Card.jsx +29 -7
  53. package/src/Card/CardBase.jsx +97 -11
  54. package/src/Card/PressableCardBase.jsx +135 -9
  55. package/src/Carousel/Carousel.jsx +119 -64
  56. package/src/Carousel/CarouselContext.jsx +12 -4
  57. package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
  58. package/src/Carousel/Constants.js +10 -0
  59. package/src/Checkbox/Checkbox.jsx +29 -7
  60. package/src/ExpandCollapse/Control.jsx +1 -1
  61. package/src/ExpandCollapse/ExpandCollapse.jsx +9 -8
  62. package/src/ExpandCollapse/Panel.jsx +10 -2
  63. package/src/IconButton/IconButton.jsx +40 -28
  64. package/src/List/List.jsx +33 -9
  65. package/src/List/ListItem.jsx +33 -11
  66. package/src/List/ListItemBase.jsx +33 -9
  67. package/src/List/ListItemMark.jsx +32 -53
  68. package/src/List/PressableListItemBase.jsx +1 -0
  69. package/src/Modal/Modal.jsx +23 -11
  70. package/src/Progress/Progress.jsx +18 -7
  71. package/src/Progress/ProgressBar.jsx +19 -14
  72. package/src/Progress/ProgressContext.js +5 -0
  73. package/src/SideNav/Item.jsx +3 -3
  74. package/src/SideNav/ItemsGroup.jsx +36 -16
  75. package/src/SideNav/SideNav.jsx +22 -8
@@ -12,6 +12,19 @@ var _utils = require("../utils");
12
12
  var _ThemeProvider = require("../ThemeProvider");
13
13
  var _jsxRuntime = require("react/jsx-runtime");
14
14
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
+ const selectPanelTokens = _ref => {
16
+ let {
17
+ borderWidth,
18
+ borderColor,
19
+ backgroundColor
20
+ } = _ref;
21
+ return {
22
+ contentPanelBackgroundColor: backgroundColor,
23
+ borderBottomWidth: borderWidth,
24
+ borderColor
25
+ };
26
+ };
27
+
15
28
  /**
16
29
  Expandable content areas for use within `SideNav`.
17
30
 
@@ -21,7 +34,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
21
34
 
22
35
  ## Usage Criteria
23
36
  - Use `SideNav.ItemsGroup` with large pages that have multiple sections
24
- */const ItemsGroup = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
37
+ */
38
+ const ItemsGroup = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
25
39
  let {
26
40
  children,
27
41
  label,
@@ -32,7 +46,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
32
46
  tokens,
33
47
  itemTokens,
34
48
  onToggle
35
- } = _ref;
49
+ } = _ref2;
36
50
  // A SideNav control uses the same style and theme as SideNavItem, with a 'parent' variant,
37
51
  // plus control-specific tokens from the SideNavItemsGroup theme (e.g. open/close icon, etc).
38
52
  const getAppearance = appearance => ({
@@ -45,28 +59,27 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
45
59
  type: 'parent'
46
60
  });
47
61
  const getGroupTokens = (0, _ThemeProvider.useThemeTokensCallback)('SideNavItemsGroup', tokens, variant);
48
- const getPanelTokens = appearance => {
49
- const {
50
- panelBorderColor,
51
- ...itemsGroupTokens
52
- } = getGroupTokens(getAppearance(appearance));
53
- const groupTokens = {
54
- ...itemsGroupTokens,
55
- borderWidth: 0,
56
- marginBottom: 0
57
- };
58
- return (0, _utils.selectTokens)('ExpandCollapsePanel', groupTokens);
59
- };
60
62
  const getItemTokens = (0, _ThemeProvider.useThemeTokensCallback)('SideNavItem', itemTokens, variant);
61
- const getControlTokens = appearance => (0, _utils.selectTokens)('ExpandCollapseControl', {
62
- ...getItemTokens(getItemAppearance(appearance)),
63
- // main style from SideNavItem
64
- ...getGroupTokens(getAppearance(appearance)) // control-specific tokens like icon etc
63
+ const getPanelTokens = appearance => (0, _utils.selectTokens)('ExpandCollapsePanel', {
64
+ ...staticTokens.panel,
65
+ ...getGroupTokens(getAppearance(appearance)),
66
+ ...selectPanelTokens(getItemTokens(getItemAppearance(appearance)))
67
+ });
68
+ const getControlTokens = appearance => ({
69
+ ...(0, _utils.selectTokens)('ExpandCollapseControl', {
70
+ ...getItemTokens(getItemAppearance(appearance)),
71
+ // main style from SideNavItem
72
+ ...getGroupTokens(getAppearance(appearance)) // control-specific tokens like icon etc,
73
+ }),
74
+ ...staticTokens.control
65
75
  });
66
76
  const controlContent = controlState => {
67
77
  const currentItemTokens = getItemTokens(getItemAppearance(controlState));
68
78
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ItemContent.default, {
69
- tokens: currentItemTokens,
79
+ tokens: {
80
+ ...currentItemTokens,
81
+ ...staticTokens.content
82
+ },
70
83
  children: label
71
84
  });
72
85
  };
@@ -82,9 +95,23 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
82
95
  active: isActive
83
96
  } // ExpandCollapse.Panel handles expanded state
84
97
  ,
98
+ disableMobileScrollBuffer: true,
85
99
  children: children
86
100
  });
87
101
  });
102
+ const staticTokens = {
103
+ panel: {
104
+ borderWidth: 0,
105
+ marginBottom: 0
106
+ },
107
+ control: {
108
+ borderWidth: 0,
109
+ textLine: null
110
+ },
111
+ content: {
112
+ accentWidth: 0
113
+ }
114
+ };
88
115
  ItemsGroup.displayName = 'ItemsGroup';
89
116
  ItemsGroup.propTypes = {
90
117
  /**
@@ -15,13 +15,28 @@ var _jsxRuntime = require("react/jsx-runtime");
15
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
16
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
17
17
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
18
- function selectBorderStyles(tokens) {
18
+ const selectContainerTokens = _ref => {
19
+ let {
20
+ borderWidth,
21
+ borderStyle,
22
+ borderColor
23
+ } = _ref;
19
24
  return {
20
- borderBottomWidth: tokens.borderWidth,
21
- borderBottomStyle: tokens.borderStyle,
22
- borderBottomColor: tokens.borderColor
25
+ borderTopWidth: borderWidth,
26
+ borderStyle,
27
+ borderColor
23
28
  };
24
- }
29
+ };
30
+ const selectItemTokens = function () {
31
+ let tokens = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
32
+ let isLastItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
33
+ return {
34
+ ...tokens,
35
+ ...(isLastItem ? {
36
+ borderWidth: 0
37
+ } : {})
38
+ };
39
+ };
25
40
  const [selectProps, selectedSystemPropTypes] = (0, _utils.selectSystemProps)([_utils.a11yProps, _utils.viewProps]);
26
41
 
27
42
  /**
@@ -29,7 +44,7 @@ const [selectProps, selectedSystemPropTypes] = (0, _utils.selectSystemProps)([_u
29
44
  - Use in conjunction with a large amount of educational / informational content
30
45
  - Allow the user to navigate between options frequently and efficiently
31
46
  */
32
- const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
47
+ const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
33
48
  let {
34
49
  children,
35
50
  variant = {},
@@ -38,7 +53,7 @@ const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
38
53
  itemTokens,
39
54
  groupTokens,
40
55
  ...rest
41
- } = _ref;
56
+ } = _ref2;
42
57
  const themeTokens = (0, _ThemeProvider.useThemeTokens)('SideNav', tokens, variant);
43
58
  const [active, setActive] = _react.default.useState({
44
59
  groupId: undefined,
@@ -56,14 +71,15 @@ const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
56
71
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandCollapse.default, {
57
72
  ref: ref,
58
73
  maxOpen: accordion ? 1 : null,
59
- style: selectBorderStyles(themeTokens),
74
+ tokens: selectContainerTokens(themeTokens),
60
75
  ...selectProps(rest),
61
- children: _ref2 => {
76
+ children: _ref3 => {
62
77
  let {
63
78
  openIds,
64
79
  onToggle
65
- } = _ref2;
66
- const renderItem = (item, index, groupId) => {
80
+ } = _ref3;
81
+ const renderItem = function (item, index, groupId) {
82
+ let isLastItem = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
67
83
  const {
68
84
  itemId = `item-${index}`,
69
85
  onPress
@@ -81,7 +97,7 @@ const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
81
97
  ...variant,
82
98
  type: 'child'
83
99
  } : variant,
84
- tokens: itemTokens,
100
+ tokens: selectItemTokens(itemTokens, isLastItem),
85
101
  isActive: isItemActive(itemId, groupId),
86
102
  onPress: handlePress
87
103
  });
@@ -105,7 +121,7 @@ const SideNav = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
105
121
  openGroups: openIds,
106
122
  isActive: isGroupActive,
107
123
  onToggle: handleToggle
108
- }, _react.default.Children.map(child.props.children, (item, itemIndex) => renderItem(item, itemIndex, groupId)));
124
+ }, _react.default.Children.map(child.props.children, (item, itemIndex) => renderItem(item, itemIndex, groupId, itemIndex === child.props.children.length - 1)));
109
125
  }
110
126
  return null;
111
127
  });
@@ -16,18 +16,26 @@ const SelectionType = {
16
16
  Radio: 'radiogroup',
17
17
  None: undefined
18
18
  };
19
- const selectInputStyle = _ref => {
19
+ const selectInputStyle = (_ref, _ref2) => {
20
20
  let {
21
21
  paddingTop,
22
- paddingRight
22
+ paddingRight,
23
+ paddingLeft
23
24
  } = _ref;
25
+ let {
26
+ inputPositionProp
27
+ } = _ref2;
24
28
  return {
25
29
  position: 'absolute',
26
30
  top: paddingTop,
27
- right: paddingRight
31
+ ...(inputPositionProp === 'left' ? {
32
+ left: paddingLeft
33
+ } : {
34
+ right: paddingRight
35
+ })
28
36
  };
29
37
  };
30
- const getInputProps = _ref2 => {
38
+ const getInputProps = _ref3 => {
31
39
  let {
32
40
  id,
33
41
  checkColor,
@@ -37,7 +45,7 @@ const getInputProps = _ref2 => {
37
45
  isChecked,
38
46
  isInactive,
39
47
  handleChange
40
- } = _ref2;
48
+ } = _ref3;
41
49
  return {
42
50
  inputId: id,
43
51
  tokens: {
@@ -100,7 +108,7 @@ const getInputProps = _ref2 => {
100
108
  * you automatically make inaccessible its children, which may or may not be appropriate
101
109
  * depending on what you are trying to achieve.
102
110
  */
103
- const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
111
+ const Card = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
104
112
  let {
105
113
  children,
106
114
  tokens,
@@ -110,8 +118,9 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
110
118
  id,
111
119
  interactiveCard,
112
120
  backgroundImage,
121
+ testID,
113
122
  ...rest
114
- } = _ref3;
123
+ } = _ref4;
115
124
  const viewport = useViewport();
116
125
  const {
117
126
  themeOptions
@@ -182,8 +191,8 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
182
191
  let cardStyles;
183
192
  let mediaIds;
184
193
  if (enableMediaQueryStyleSheet) {
185
- const transformedThemeTokens = Object.entries(themeTokens).reduce((acc, _ref4) => {
186
- let [vp, viewportTokens] = _ref4;
194
+ const transformedThemeTokens = Object.entries(themeTokens).reduce((acc, _ref5) => {
195
+ let [vp, viewportTokens] = _ref5;
187
196
  const tokensToTransform = selectionType ? selectStyles({
188
197
  ...viewportTokens,
189
198
  paddingTop: 0,
@@ -210,13 +219,18 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
210
219
  cardStyles = themeTokens;
211
220
  }
212
221
  const renderInputPerSelectionType = props => {
222
+ const containerStyle = selectInputStyle(getThemeTokens(), {
223
+ inputPositionProp: interactiveCard?.inputPosition
224
+ });
225
+ const inputTestID = testID && `${testID}-selection-input`;
213
226
  if (!isControl) {
214
227
  return null;
215
228
  }
216
229
  switch (selectionType) {
217
230
  case SelectionType.Checkbox:
218
231
  return /*#__PURE__*/_jsx(View, {
219
- style: selectInputStyle(getThemeTokens()),
232
+ style: containerStyle,
233
+ testID: inputTestID,
220
234
  children: /*#__PURE__*/_jsx(CheckboxButton, {
221
235
  ...props,
222
236
  tokens: {
@@ -227,7 +241,8 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
227
241
  });
228
242
  case SelectionType.Radio:
229
243
  return /*#__PURE__*/_jsx(View, {
230
- style: selectInputStyle(getThemeTokens()),
244
+ style: containerStyle,
245
+ testID: inputTestID,
231
246
  children: /*#__PURE__*/_jsx(RadioButton, {
232
247
  ...props,
233
248
  tokens: {
@@ -248,6 +263,7 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
248
263
  dataSet: mediaIds && {
249
264
  media: mediaIds
250
265
  },
266
+ testID: testID,
251
267
  ...selectProps(rest),
252
268
  children: [interactiveCard?.body && /*#__PURE__*/_jsx(PressableCardBase, {
253
269
  ref: ref,
@@ -326,7 +342,8 @@ Card.propTypes = {
326
342
  selectionType: PropTypes.oneOf(Object.values(SelectionType)),
327
343
  variant: variantProp.propType,
328
344
  href: PropTypes.string,
329
- hrefAttrs: PropTypes.shape(hrefAttrsProp.types)
345
+ hrefAttrs: PropTypes.shape(hrefAttrsProp.types),
346
+ inputPosition: PropTypes.oneOf(['left', 'right'])
330
347
  }),
331
348
  /**
332
349
  * Apply background image to the card.
@@ -343,6 +360,10 @@ Card.propTypes = {
343
360
  /**
344
361
  * Data set for the card.
345
362
  */
346
- dataSet: PropTypes.object
363
+ dataSet: PropTypes.object,
364
+ /**
365
+ * Test ID used for e2e testing.
366
+ */
367
+ testID: PropTypes.string
347
368
  };
348
369
  export default Card;
@@ -107,9 +107,51 @@ const setBackgroundImage = _ref => {
107
107
  children: content
108
108
  });
109
109
  };
110
+ const selectPaddedContentStyles = _ref2 => {
111
+ let {
112
+ paddingTop,
113
+ paddingBottom,
114
+ paddingLeft,
115
+ paddingRight,
116
+ borderWidth,
117
+ borderColor,
118
+ borderRadius,
119
+ hasInteractiveBorder
120
+ } = _ref2;
121
+ return {
122
+ paddingTop,
123
+ paddingBottom,
124
+ paddingLeft,
125
+ paddingRight,
126
+ ...(hasInteractiveBorder ? {
127
+ borderWidth,
128
+ borderColor,
129
+ borderRadius
130
+ } : {})
131
+ };
132
+ };
133
+ const selectInteractiveOverlayStyles = _ref3 => {
134
+ let {
135
+ backgroundColor,
136
+ borderRadius,
137
+ borderWidth
138
+ } = _ref3;
139
+ const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth);
140
+ return {
141
+ position: 'absolute',
142
+ top: 0,
143
+ left: 0,
144
+ right: 0,
145
+ bottom: 0,
146
+ backgroundColor,
147
+ borderRadius: adjustedBorderRadius,
148
+ pointerEvents: 'none',
149
+ zIndex: 1
150
+ };
151
+ };
110
152
 
111
153
  // Ensure explicit selection of tokens
112
- export const selectStyles = _ref2 => {
154
+ export const selectStyles = _ref4 => {
113
155
  let {
114
156
  flex,
115
157
  backgroundColor,
@@ -130,7 +172,7 @@ export const selectStyles = _ref2 => {
130
172
  gradient,
131
173
  maxHeight,
132
174
  overflowY
133
- } = _ref2;
175
+ } = _ref4;
134
176
  const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web';
135
177
  let backgroundImageValue = null;
136
178
  if (hasGradient) {
@@ -176,7 +218,7 @@ export const selectStyles = _ref2 => {
176
218
  * A themeless base component for Card which components can apply theme tokens to. Not
177
219
  * intended to be used in apps or sites directly: build themed components on top of this.
178
220
  */
179
- const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
221
+ const CardBase = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
180
222
  let {
181
223
  children,
182
224
  tokens,
@@ -185,8 +227,14 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
185
227
  fullBleedContent,
186
228
  cardState,
187
229
  ...rest
188
- } = _ref3;
189
- const cardStyle = selectStyles(typeof tokens === 'function' ? tokens(cardState) : tokens);
230
+ } = _ref5;
231
+ const resolvedTokens = typeof tokens === 'function' ? tokens(cardState) : tokens;
232
+ const tokensToUse = backgroundImage && backgroundImage.src ? {
233
+ ...resolvedTokens,
234
+ gradient: undefined,
235
+ backgroundGradient: undefined
236
+ } : resolvedTokens;
237
+ const cardStyle = selectStyles(tokensToUse);
190
238
  const props = selectProps(rest);
191
239
  let content = children;
192
240
  const {
@@ -212,29 +260,57 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
212
260
  paddingBottom,
213
261
  paddingLeft,
214
262
  paddingRight,
263
+ borderWidth,
264
+ borderColor,
265
+ borderRadius,
266
+ backgroundColor,
215
267
  ...containerStyle
216
268
  } = cardStyle;
217
269
  const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight;
218
- const paddedContent = hasPadding ? /*#__PURE__*/_jsx(View, {
219
- style: {
270
+ const hasInteractiveBorder = borderWidth && borderWidth > 0;
271
+ const hasInteractiveOverlay = isOverlayColor(backgroundColor);
272
+ const paddedContent = hasPadding || hasInteractiveBorder ? /*#__PURE__*/_jsx(View, {
273
+ style: selectPaddedContentStyles({
220
274
  paddingTop,
221
275
  paddingBottom,
222
276
  paddingLeft,
223
- paddingRight
224
- },
277
+ paddingRight,
278
+ borderWidth,
279
+ borderColor,
280
+ borderRadius,
281
+ hasInteractiveBorder
282
+ }),
225
283
  children: children
226
284
  }) : children;
285
+ const contentWithOverlay = /*#__PURE__*/_jsxs(_Fragment, {
286
+ children: [hasInteractiveOverlay && Platform.OS === 'web' && /*#__PURE__*/_jsx(View, {
287
+ style: selectInteractiveOverlayStyles({
288
+ backgroundColor,
289
+ borderRadius,
290
+ borderWidth
291
+ })
292
+ }), /*#__PURE__*/_jsx(View, {
293
+ style: staticStyles.contentOverlay,
294
+ children: paddedContent
295
+ })]
296
+ });
227
297
  content = setBackgroundImage({
228
298
  src: imageSourceViewport,
229
299
  alt,
230
300
  backgroundImageResizeMode,
231
301
  backgroundImagePosition,
232
302
  backgroundImageAlign,
233
- content: paddedContent,
234
- cardStyle: containerStyle
303
+ content: contentWithOverlay,
304
+ cardStyle: {
305
+ ...containerStyle,
306
+ borderRadius
307
+ }
235
308
  });
236
309
  return /*#__PURE__*/_jsx(View, {
237
- style: containerStyle,
310
+ style: {
311
+ ...containerStyle,
312
+ borderRadius
313
+ },
238
314
  dataSet: dataSet,
239
315
  ref: ref,
240
316
  ...props,
@@ -312,8 +388,8 @@ const staticStyles = StyleSheet.create({
312
388
  contentOverlay: {
313
389
  position: 'relative',
314
390
  width: '100%',
315
- height: '100%',
316
- zIndex: 1
391
+ minHeight: '100%',
392
+ zIndex: 2
317
393
  },
318
394
  containContainer: {
319
395
  width: '100%',
@@ -8,8 +8,74 @@ import { useViewport } from '../ViewportProvider';
8
8
  import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider';
9
9
  import { a11yProps, clickProps, focusHandlerProps, getTokensSetPropType, linkProps, resolvePressableState, resolvePressableTokens, selectSystemProps, selectTokens, variantProp, viewProps, withLinkRouter } from '../utils';
10
10
  import CardBase, { fullBleedContentPropTypes } from './CardBase';
11
- import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
13
+ const selectFocusOverlayContainerStyles = tokens => {
14
+ const {
15
+ flex,
16
+ minWidth,
17
+ marginTop,
18
+ marginBottom,
19
+ marginLeft,
20
+ marginRight
21
+ } = tokens;
22
+ return {
23
+ flex: flex || 1,
24
+ minWidth: minWidth || 0,
25
+ marginTop,
26
+ marginBottom,
27
+ marginLeft,
28
+ marginRight
29
+ };
30
+ };
31
+ const selectFocusBorderStyles = tokens => {
32
+ const {
33
+ borderWidth,
34
+ borderColor,
35
+ borderRadius
36
+ } = tokens;
37
+ return {
38
+ borderWidth,
39
+ borderColor,
40
+ borderRadius: borderRadius || 0
41
+ };
42
+ };
43
+ const FocusBorderOverlay = _ref => {
44
+ let {
45
+ tokens,
46
+ pressableState,
47
+ children
48
+ } = _ref;
49
+ const {
50
+ borderWidth = 0
51
+ } = tokens;
52
+ const showFocusBorder = pressableState.focused && borderWidth > 0;
53
+ return /*#__PURE__*/_jsxs(View, {
54
+ style: [staticStyles.focusOverlayContainer, selectFocusOverlayContainerStyles(tokens), Platform.OS === 'web' && staticStyles.webOutlineNone],
55
+ children: [children, showFocusBorder && /*#__PURE__*/_jsx(View, {
56
+ style: [staticStyles.focusBorder, selectFocusBorderStyles(tokens)],
57
+ accessible: false,
58
+ importantForAccessibility: "no"
59
+ })]
60
+ });
61
+ };
62
+ FocusBorderOverlay.propTypes = {
63
+ tokens: PropTypes.shape({
64
+ flex: PropTypes.number,
65
+ minWidth: PropTypes.number,
66
+ marginTop: PropTypes.number,
67
+ marginBottom: PropTypes.number,
68
+ marginLeft: PropTypes.number,
69
+ marginRight: PropTypes.number,
70
+ borderColor: PropTypes.string,
71
+ borderWidth: PropTypes.number,
72
+ borderRadius: PropTypes.number
73
+ }).isRequired,
74
+ pressableState: PropTypes.shape({
75
+ focused: PropTypes.bool
76
+ }).isRequired,
77
+ children: PropTypes.node
78
+ };
13
79
  const tokenKeys = ['flex', 'backgroundColor', 'borderColor', 'gradient', 'borderRadius', 'borderWidth', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight', 'minWidth', 'shadow', 'contentAlignItems', 'contentJustifyContent', 'contentFlexGrow', 'contentFlexShrink',
14
80
  // Outer border tokens. TODO: centralise common token sets like these as part of
15
81
  // https://github.com/telus/universal-design-system/issues/782
@@ -21,7 +87,7 @@ export const selectPressableCardTokens = tokens => Object.fromEntries(tokenKeys.
21
87
  * based on these to an outer border and a base Card component. Not intended to be used in
22
88
  * apps or sites directly: build themed components on top of this.
23
89
  */
24
- const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
90
+ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
25
91
  let {
26
92
  children,
27
93
  tokens,
@@ -35,7 +101,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
35
101
  fullBleedContent,
36
102
  accessibilityRole = href ? 'link' : undefined,
37
103
  ...rawRest
38
- } = _ref;
104
+ } = _ref2;
39
105
  const {
40
106
  onPress,
41
107
  ...rest
@@ -52,11 +118,62 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
52
118
  partial: true,
53
119
  allowFunction: true
54
120
  }), 'PressableCard');
55
- const getCardTokens = pressableState => selectTokens('Card', getTokens(pressableState));
121
+ const getCardTokens = pressableState => {
122
+ const allTokens = getTokens(pressableState);
123
+ const cardTokens = selectTokens('Card', allTokens);
124
+
125
+ // Handle focus border transparency to avoid double borders
126
+ if (pressableState.focused && allTokens.borderWidth > 0) {
127
+ const result = {
128
+ ...cardTokens,
129
+ borderColor: 'transparent'
130
+ };
131
+
132
+ // Also handle backgroundImage for interactive states
133
+ if (backgroundImage) {
134
+ const {
135
+ hovered,
136
+ pressed,
137
+ focused
138
+ } = pressableState || {};
139
+ const isInteractiveState = hovered || pressed || focused;
140
+ if (!isInteractiveState) {
141
+ const {
142
+ backgroundColor,
143
+ ...restTokens
144
+ } = result;
145
+ return restTokens;
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+
151
+ // Handle backgroundImage when not in focus state
152
+ if (backgroundImage) {
153
+ const {
154
+ hovered,
155
+ pressed,
156
+ focused
157
+ } = pressableState || {};
158
+ const isInteractiveState = hovered || pressed || focused;
159
+ if (!isInteractiveState) {
160
+ const {
161
+ backgroundColor,
162
+ ...restTokens
163
+ } = cardTokens;
164
+ return restTokens;
165
+ }
166
+ }
167
+ return cardTokens;
168
+ };
56
169
  const getOuterBorderStyle = pressableState => {
57
170
  const {
58
171
  flex,
59
172
  minWidth,
173
+ marginTop,
174
+ marginBottom,
175
+ marginLeft,
176
+ marginRight,
60
177
  outerBorderColor,
61
178
  outerBorderGap = 0,
62
179
  outerBorderWidth = 0,
@@ -65,6 +182,10 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
65
182
  return {
66
183
  flex,
67
184
  minWidth: minWidth + outerBorderGap + outerBorderWidth,
185
+ marginTop,
186
+ marginBottom,
187
+ marginLeft,
188
+ marginRight,
68
189
  ...applyOuterBorder({
69
190
  outerBorderColor,
70
191
  outerBorderGap,
@@ -158,11 +279,15 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
158
279
  ...rest,
159
280
  accessibilityRole
160
281
  }),
161
- children: pressableState => /*#__PURE__*/_jsx(CardBase, {
162
- tokens: getCardTokens(pressableState),
163
- backgroundImage: backgroundImage,
164
- fullBleedContent: fullBleedContent,
165
- children: typeof children === 'function' ? children(getCardState(pressableState)) : children
282
+ children: pressableState => /*#__PURE__*/_jsx(FocusBorderOverlay, {
283
+ tokens: getTokens(pressableState),
284
+ pressableState: pressableState,
285
+ children: /*#__PURE__*/_jsx(CardBase, {
286
+ tokens: getCardTokens(pressableState),
287
+ backgroundImage: backgroundImage,
288
+ fullBleedContent: fullBleedContent,
289
+ children: typeof children === 'function' ? children(getCardState(pressableState)) : children
290
+ })
166
291
  })
167
292
  });
168
293
  });
@@ -177,6 +302,20 @@ const staticStyles = StyleSheet.create({
177
302
  alignItems: 'stretch',
178
303
  justifyContent: 'flex-start',
179
304
  textDecorationLine: 'none'
305
+ },
306
+ focusOverlayContainer: {
307
+ position: 'relative'
308
+ },
309
+ webOutlineNone: {
310
+ outline: 'none'
311
+ },
312
+ focusBorder: {
313
+ position: 'absolute',
314
+ top: 0,
315
+ left: 0,
316
+ right: 0,
317
+ bottom: 0,
318
+ pointerEvents: 'none'
180
319
  }
181
320
  });
182
321
  PressableCardBase.displayName = 'PressableCardBase';