@telus-uds/components-base 3.23.0 → 3.24.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 (51) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/lib/cjs/Card/CardBase.js +97 -17
  3. package/lib/cjs/Card/PressableCardBase.js +12 -8
  4. package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
  5. package/lib/cjs/Icon/Icon.js +3 -0
  6. package/lib/cjs/Listbox/GroupControl.js +12 -6
  7. package/lib/cjs/Listbox/Listbox.js +41 -7
  8. package/lib/cjs/Listbox/ListboxGroup.js +139 -8
  9. package/lib/cjs/Listbox/ListboxOverlay.js +10 -5
  10. package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
  11. package/lib/cjs/Listbox/dictionary.js +14 -0
  12. package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
  13. package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
  14. package/lib/cjs/Shortcuts/index.js +16 -0
  15. package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
  16. package/lib/cjs/index.js +15 -0
  17. package/lib/esm/Card/CardBase.js +97 -17
  18. package/lib/esm/Card/PressableCardBase.js +10 -8
  19. package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
  20. package/lib/esm/Icon/Icon.js +3 -0
  21. package/lib/esm/Listbox/GroupControl.js +12 -6
  22. package/lib/esm/Listbox/Listbox.js +41 -7
  23. package/lib/esm/Listbox/ListboxGroup.js +141 -10
  24. package/lib/esm/Listbox/ListboxOverlay.js +10 -5
  25. package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
  26. package/lib/esm/Listbox/dictionary.js +8 -0
  27. package/lib/esm/Shortcuts/Shortcuts.js +160 -0
  28. package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
  29. package/lib/esm/Shortcuts/index.js +3 -0
  30. package/lib/esm/Tooltip/Tooltip.native.js +2 -0
  31. package/lib/esm/index.js +1 -0
  32. package/lib/package.json +2 -2
  33. package/package.json +2 -2
  34. package/src/Card/CardBase.jsx +113 -14
  35. package/src/Card/PressableCardBase.jsx +17 -5
  36. package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
  37. package/src/Icon/Icon.jsx +3 -0
  38. package/src/Listbox/GroupControl.jsx +41 -33
  39. package/src/Listbox/Listbox.jsx +41 -2
  40. package/src/Listbox/ListboxGroup.jsx +158 -26
  41. package/src/Listbox/ListboxOverlay.jsx +18 -5
  42. package/src/Listbox/SecondLevelHeader.jsx +182 -0
  43. package/src/Listbox/dictionary.js +8 -0
  44. package/src/Shortcuts/Shortcuts.jsx +174 -0
  45. package/src/Shortcuts/ShortcutsItem.jsx +297 -0
  46. package/src/Shortcuts/index.js +4 -0
  47. package/src/Tooltip/Tooltip.native.jsx +2 -1
  48. package/src/index.js +1 -0
  49. package/types/Listbox.d.ts +24 -0
  50. package/types/Shortcuts.d.ts +136 -0
  51. package/types/index.d.ts +12 -0
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _Image = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Image"));
9
+ var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
10
+ var _Pressable = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Pressable"));
11
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
12
+ var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
13
+ var _propTypes = _interopRequireDefault(require("prop-types"));
14
+ var _ThemeProvider = require("../ThemeProvider");
15
+ var _utils = require("../utils");
16
+ var _Icon = _interopRequireDefault(require("../Icon"));
17
+ var _jsxRuntime = require("react/jsx-runtime");
18
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
+ const DYNAMIC_WIDTH_VARIANT = 'dynamic';
20
+ const EQUAL_WIDTH_VARIANT = 'equal';
21
+ const [selectProps, selectedSystemPropTypes] = (0, _utils.selectSystemProps)([_utils.a11yProps, _utils.linkProps, _utils.viewProps]);
22
+ const selectPressableStyles = (tokens, widthVariant, equalWidth) => {
23
+ const styles = {
24
+ borderColor: tokens.borderColor,
25
+ borderRadius: tokens.borderRadius,
26
+ borderWidth: tokens.borderWidth,
27
+ ..._Platform.default.select({
28
+ web: {
29
+ outline: 'none'
30
+ }
31
+ })
32
+ };
33
+ if (widthVariant === DYNAMIC_WIDTH_VARIANT) {
34
+ styles.width = 'auto';
35
+ } else if (widthVariant === EQUAL_WIDTH_VARIANT) {
36
+ if (equalWidth) {
37
+ styles.width = equalWidth;
38
+ } else {
39
+ styles.minWidth = tokens.width;
40
+ }
41
+ } else {
42
+ styles.width = tokens.width;
43
+ }
44
+ return styles;
45
+ };
46
+ const selectIconContainerStyles = tokens => ({
47
+ paddingBottom: tokens.iconContainerPaddingBottom,
48
+ paddingLeft: tokens.iconContainerPaddingLeft,
49
+ paddingRight: tokens.iconContainerPaddingRight,
50
+ paddingTop: tokens.iconContainerPaddingTop
51
+ });
52
+ const selectIconVariant = () => ({
53
+ background: true,
54
+ padding: 'medium'
55
+ });
56
+ const selectIconTokens = tokens => ({
57
+ backgroundColor: tokens.iconBackgroundColor,
58
+ color: tokens.iconColor,
59
+ size: tokens.iconSize,
60
+ width: tokens.iconWidth
61
+ });
62
+ const selectImageStyles = tokens => ({
63
+ width: tokens.imageWidth,
64
+ height: tokens.imageHeight
65
+ });
66
+ const selectLabelContainerStyles = tokens => ({
67
+ paddingBottom: tokens.labelContainerPaddingBottom,
68
+ paddingLeft: tokens.labelContainerPaddingLeft,
69
+ paddingRight: tokens.labelContainerPaddingRight,
70
+ paddingTop: tokens.labelContainerPaddingTop
71
+ });
72
+ const selectTitleTextStyles = tokens => (0, _ThemeProvider.applyTextStyles)({
73
+ fontColor: tokens.labelFontColor,
74
+ fontName: tokens.labelFontName,
75
+ fontSize: tokens.labelFontSize,
76
+ fontWeight: tokens.labelFontWeight,
77
+ lineHeight: tokens.labelLineHeight,
78
+ textDecorationLine: tokens.labelUnderline,
79
+ textAlign: tokens.labelTextAlign
80
+ });
81
+
82
+ /**
83
+ * A clickable shortcut item component that displays an icon or image with an optional label.
84
+ * Can be used within a Shortcuts container to create a grid of navigation shortcuts.
85
+ *
86
+ * @component
87
+ * @param {Object} props - Component props
88
+ * @param {string} [props.icon] - Icon identifier to display
89
+ * @param {Object} [props.image={ src: '', alt: '' }] - Image object with src and alt properties
90
+ * @param {string} [props.image.src] - Image source URL
91
+ * @param {string} [props.image.alt] - Image alt text for accessibility
92
+ * @param {string|React.ReactNode} props.label - Label text or content to display below the icon/image
93
+ * @param {boolean} [props.hideLabel=false] - Whether to hide the label for this specific item
94
+ * @param {string} [props.href] - Link URL for navigation
95
+ * @param {Object} [props.iconVariant] - Icon variant to apply to this specific item
96
+ * @param {Object} [props.tokens] - Theme tokens to customize appearance
97
+ * @param {Object} [props.variant] - Variant configuration object for this specific item
98
+ * @param {string} [props.variant.width] - Width variant (e.g., 'dynamic', 'equal')
99
+ * @param {Function} [props.onPressableStateChange] - Callback function that receives the pressable state object (pressed, hovered, focused)
100
+ * @param {number} [props.maxWidth] - Maximum width for equal width variant (injected by Shortcuts container)
101
+ * @param {Function} [props.registerWidth] - Callback to register width for equal width variant (injected by Shortcuts container)
102
+ * @param {Object} [props.containerVariant] - Variant configuration from Shortcuts container (injected by Shortcuts container)
103
+ * @param {boolean} [props.containerHideLabels] - Hide labels setting from Shortcuts container (injected by Shortcuts container)
104
+ * @param {Object} [props.containerIconVariant] - Icon variant from Shortcuts container (injected by Shortcuts container)
105
+ * @param {React.Ref} ref - Forwarded ref to the Pressable component
106
+ * @returns {React.ReactElement} The rendered shortcut item
107
+ *
108
+ * @example
109
+ * <ShortcutsItem
110
+ * icon={HomeIcon}
111
+ * label="Home"
112
+ * href="/home"
113
+ * onPressableStateChange={(state) => console.log(state)}
114
+ * />
115
+ */
116
+ const ShortcutsItem = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
117
+ let {
118
+ icon,
119
+ image = {
120
+ src: '',
121
+ alt: ''
122
+ },
123
+ label,
124
+ hideLabel = false,
125
+ href,
126
+ iconVariant,
127
+ tokens,
128
+ variant,
129
+ onPressableStateChange,
130
+ maxWidth,
131
+ registerWidth,
132
+ containerVariant,
133
+ containerHideLabels,
134
+ containerIconVariant,
135
+ ...rest
136
+ } = _ref;
137
+ const mergedVariant = {
138
+ ...containerVariant,
139
+ ...variant
140
+ };
141
+ const widthVariant = mergedVariant?.width;
142
+ const shouldHideLabel = hideLabel || containerHideLabels;
143
+ const mergedIconVariant = iconVariant ?? containerIconVariant;
144
+ const getThemeTokens = (0, _ThemeProvider.useThemeTokensCallback)('ShortcutsItem', tokens, mergedVariant);
145
+ const getTokens = pressableState => getThemeTokens((0, _utils.resolvePressableState)(pressableState));
146
+ const {
147
+ onPress,
148
+ ...props
149
+ } = _utils.clickProps.toPressProps(rest);
150
+ const {
151
+ hrefAttrs,
152
+ rawRest
153
+ } = _utils.hrefAttrsProp.bundle(props);
154
+ const selectedProps = selectProps({
155
+ href,
156
+ onPress: _utils.linkProps.handleHref({
157
+ href,
158
+ onPress
159
+ }),
160
+ hrefAttrs,
161
+ ...rawRest
162
+ });
163
+ const handleLayout = event => {
164
+ if (widthVariant === EQUAL_WIDTH_VARIANT && registerWidth) {
165
+ const {
166
+ width
167
+ } = event.nativeEvent.layout;
168
+ registerWidth(width);
169
+ }
170
+ };
171
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Pressable.default, {
172
+ ref: ref,
173
+ style: pressableState => selectPressableStyles(getTokens(pressableState), widthVariant, maxWidth),
174
+ onLayout: handleLayout,
175
+ ...selectedProps,
176
+ children: pressableState => {
177
+ const themeTokens = getTokens(pressableState);
178
+ if (onPressableStateChange) {
179
+ onPressableStateChange((0, _utils.resolvePressableState)(pressableState));
180
+ }
181
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
182
+ style: staticStyles.container,
183
+ children: [icon && /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
184
+ style: selectIconContainerStyles(themeTokens),
185
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
186
+ icon: icon,
187
+ variant: mergedIconVariant ?? selectIconVariant(),
188
+ tokens: mergedIconVariant ? {} : selectIconTokens(themeTokens),
189
+ ...(_Platform.default.OS === 'web' && {
190
+ accessibilityLabel: label
191
+ })
192
+ })
193
+ }), !icon && image && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
194
+ source: image.src,
195
+ alt: image.alt,
196
+ style: selectImageStyles(themeTokens),
197
+ resizeMethod: "resize",
198
+ accessibilityIgnoresInvertColors: true
199
+ }), label && !shouldHideLabel && /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
200
+ style: [staticStyles.label, selectLabelContainerStyles(themeTokens)],
201
+ children: (0, _utils.wrapStringsInText)(label, {
202
+ style: selectTitleTextStyles(themeTokens)
203
+ })
204
+ })]
205
+ });
206
+ }
207
+ });
208
+ });
209
+ ShortcutsItem.displayName = 'ShortcutsItem';
210
+ ShortcutsItem.propTypes = {
211
+ ...selectedSystemPropTypes,
212
+ tokens: (0, _utils.getTokensPropType)('ShortcutsItem'),
213
+ variant: _utils.variantProp.propType,
214
+ /**
215
+ * Icon for the ShortcutsItem
216
+ */
217
+ icon: _propTypes.default.elementType,
218
+ /**
219
+ * Image for the ShortcutsItem
220
+ */
221
+ image: _propTypes.default.shape({
222
+ src: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number, _propTypes.default.object]),
223
+ alt: _propTypes.default.string
224
+ }),
225
+ /**
226
+ * Label for the ShortcutsItem
227
+ */
228
+ label: _propTypes.default.string,
229
+ /**
230
+ * Hide the label for this specific ShortcutsItem. When true, the label is visually hidden but remains accessible to screen readers via the icon's accessibilityLabel.
231
+ */
232
+ hideLabel: _propTypes.default.bool,
233
+ /**
234
+ * href for the ShortcutsItem
235
+ */
236
+ href: _propTypes.default.string,
237
+ /**
238
+ * Icon variant for this specific ShortcutsItem
239
+ */
240
+ iconVariant: _utils.variantProp.propType,
241
+ /**
242
+ * Callback function that receives the pressable state object containing pressed, hovered, and focused boolean properties
243
+ */
244
+ onPressableStateChange: _propTypes.default.func,
245
+ /**
246
+ * Maximum width for equal width variant (automatically injected by Shortcuts container)
247
+ * @private
248
+ */
249
+ maxWidth: _propTypes.default.number,
250
+ /**
251
+ * Callback to register width for equal width variant (automatically injected by Shortcuts container)
252
+ * @private
253
+ */
254
+ registerWidth: _propTypes.default.func,
255
+ /**
256
+ * Variant configuration from Shortcuts container (automatically injected by Shortcuts container)
257
+ * @private
258
+ */
259
+ containerVariant: _utils.variantProp.propType,
260
+ /**
261
+ * Hide labels setting from Shortcuts container (automatically injected by Shortcuts container)
262
+ * @private
263
+ */
264
+ containerHideLabels: _propTypes.default.bool,
265
+ /**
266
+ * Icon variant from Shortcuts container (automatically injected by Shortcuts container)
267
+ * @private
268
+ */
269
+ containerIconVariant: _utils.variantProp.propType
270
+ };
271
+ const staticStyles = _StyleSheet.default.create({
272
+ container: {
273
+ alignItems: 'center',
274
+ justifyContent: 'center'
275
+ },
276
+ label: {
277
+ flexWrap: 'wrap'
278
+ }
279
+ });
280
+ var _default = exports.default = ShortcutsItem;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "ShortcutsItem", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _ShortcutsItem.default;
10
+ }
11
+ });
12
+ exports.default = void 0;
13
+ var _Shortcuts = _interopRequireDefault(require("./Shortcuts"));
14
+ var _ShortcutsItem = _interopRequireDefault(require("./ShortcutsItem"));
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ var _default = exports.default = _Shortcuts.default;
@@ -169,6 +169,7 @@ const Tooltip = /*#__PURE__*/_react.default.forwardRef((_ref7, ref) => {
169
169
  nativeID,
170
170
  activateOnHover = false,
171
171
  tooltipButtonTokens,
172
+ testID,
172
173
  ...rest
173
174
  } = _ref7;
174
175
  const [isOpen, setIsOpen] = _react.default.useState(false);
@@ -312,6 +313,7 @@ const Tooltip = /*#__PURE__*/_react.default.forwardRef((_ref7, ref) => {
312
313
  display: inline ? 'inline-block' : 'flex'
313
314
  }
314
315
  })],
316
+ testID: testID,
315
317
  ...selectProps(rest),
316
318
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Pressable.default, {
317
319
  onPress: toggleIsOpen,
package/lib/cjs/index.js CHANGED
@@ -59,6 +59,8 @@ var _exportNames = {
59
59
  Responsive: true,
60
60
  Search: true,
61
61
  Select: true,
62
+ Shortcuts: true,
63
+ ShortcutsItem: true,
62
64
  SideNav: true,
63
65
  Skeleton: true,
64
66
  SkipLink: true,
@@ -432,6 +434,18 @@ Object.defineProperty(exports, "Select", {
432
434
  return _Select.default;
433
435
  }
434
436
  });
437
+ Object.defineProperty(exports, "Shortcuts", {
438
+ enumerable: true,
439
+ get: function () {
440
+ return _Shortcuts.default;
441
+ }
442
+ });
443
+ Object.defineProperty(exports, "ShortcutsItem", {
444
+ enumerable: true,
445
+ get: function () {
446
+ return _Shortcuts.ShortcutsItem;
447
+ }
448
+ });
435
449
  Object.defineProperty(exports, "SideNav", {
436
450
  enumerable: true,
437
451
  get: function () {
@@ -744,6 +758,7 @@ var _RadioCard = _interopRequireWildcard(require("./RadioCard"));
744
758
  var _Responsive = _interopRequireDefault(require("./Responsive"));
745
759
  var _Search = _interopRequireDefault(require("./Search"));
746
760
  var _Select = _interopRequireDefault(require("./Select"));
761
+ var _Shortcuts = _interopRequireWildcard(require("./Shortcuts"));
747
762
  var _SideNav = _interopRequireDefault(require("./SideNav"));
748
763
  var _Skeleton = _interopRequireDefault(require("./Skeleton"));
749
764
  var _SkipLink = _interopRequireDefault(require("./SkipLink"));
@@ -9,8 +9,15 @@ import { applyShadowToken } from '../ThemeProvider';
9
9
  import { getTokensPropType, responsiveProps, useResponsiveProp, formatImageSource } from '../utils';
10
10
  import { a11yProps, viewProps, selectSystemProps } from '../utils/props';
11
11
  import backgroundImageStylesMap from './backgroundImageStylesMap';
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import FlexGrid from '../FlexGrid/FlexGrid';
13
+ import FlexGridRow from '../FlexGrid/Row';
14
+ import FlexGridCol from '../FlexGrid/Col';
15
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
16
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
17
+ const GRID_COLUMNS = 12;
18
+ const isOverlayColor = color => {
19
+ return color && typeof color === 'string' && color.startsWith('rgba(');
20
+ };
14
21
  const setBackgroundImage = _ref => {
15
22
  let {
16
23
  src,
@@ -24,12 +31,9 @@ const setBackgroundImage = _ref => {
24
31
  const borderRadius = cardStyle?.borderRadius || 0;
25
32
  const borderWidth = cardStyle?.borderWidth || 0;
26
33
  const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth);
27
-
28
- // For contain mode with position and align, use CSS background properties for web
29
34
  if (backgroundImageResizeMode === 'contain' && backgroundImagePosition && backgroundImageAlign) {
30
35
  const positionKey = `${backgroundImagePosition}-${backgroundImageAlign}`;
31
36
  if (Platform.OS === 'web') {
32
- // Create background position based on position and align
33
37
  let backgroundPosition;
34
38
  switch (positionKey) {
35
39
  case 'top-start':
@@ -73,7 +77,6 @@ const setBackgroundImage = _ref => {
73
77
  children: content
74
78
  });
75
79
  }
76
- // For React Native, apply positioning styles with full dimensions
77
80
  const positionStyles = backgroundImageStylesMap[positionKey] || {};
78
81
  return /*#__PURE__*/_jsxs(View, {
79
82
  style: [staticStyles.containContainer, {
@@ -92,8 +95,6 @@ const setBackgroundImage = _ref => {
92
95
  })]
93
96
  });
94
97
  }
95
-
96
- // Use ImageBackground for all other resize modes and React Native
97
98
  return /*#__PURE__*/_jsx(ImageBackground, {
98
99
  source: src,
99
100
  imageStyle: {
@@ -119,6 +120,10 @@ export const selectStyles = _ref2 => {
119
120
  paddingLeft,
120
121
  paddingRight,
121
122
  paddingTop,
123
+ marginTop,
124
+ marginBottom,
125
+ marginLeft,
126
+ marginRight,
122
127
  minWidth,
123
128
  shadow,
124
129
  backgroundGradient,
@@ -126,9 +131,18 @@ export const selectStyles = _ref2 => {
126
131
  maxHeight,
127
132
  overflowY
128
133
  } = _ref2;
134
+ const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web';
135
+ let backgroundImageValue = null;
136
+ if (hasGradient) {
137
+ const gradientObj = gradient || backgroundGradient;
138
+ const gradientString = `linear-gradient(${gradientObj.angle}deg, ${gradientObj.stops[0].color}, ${gradientObj.stops[1].color})`;
139
+ const shouldApplyOverlay = (gradient || backgroundGradient && backgroundColor && backgroundColor !== 'transparent') && isOverlayColor(backgroundColor);
140
+ backgroundImageValue = shouldApplyOverlay ? `linear-gradient(${backgroundColor}, ${backgroundColor}), ${gradientString}` : gradientString;
141
+ }
142
+ const boxShadowColor = isOverlayColor(backgroundColor) ? backgroundColor : 'white';
129
143
  return {
130
144
  flex,
131
- backgroundColor,
145
+ backgroundColor: hasGradient ? 'transparent' : backgroundColor,
132
146
  borderColor,
133
147
  borderRadius,
134
148
  borderWidth,
@@ -136,16 +150,20 @@ export const selectStyles = _ref2 => {
136
150
  paddingLeft,
137
151
  paddingRight,
138
152
  paddingTop,
153
+ marginTop,
154
+ marginBottom,
155
+ marginLeft,
156
+ marginRight,
139
157
  minWidth,
140
158
  ...applyShadowToken(shadow),
141
159
  ...(gradient && Platform.OS === 'web' ? {
142
- backgroundImage: `linear-gradient(${gradient.angle}deg, ${gradient.stops[0].color}, ${gradient.stops[1].color})`,
160
+ backgroundImage: backgroundImageValue,
143
161
  backgroundOrigin: `border-box`,
144
- boxShadow: `inset 0 1000px white`,
162
+ boxShadow: `inset 0 1000px ${boxShadowColor}`,
145
163
  border: `${borderWidth}px solid transparent`
146
164
  } : {}),
147
165
  ...(backgroundGradient && Platform.OS === 'web' ? {
148
- backgroundImage: `linear-gradient(${backgroundGradient.angle}deg, ${backgroundGradient.stops[0].color}, ${backgroundGradient.stops[1].color})`
166
+ backgroundImage: backgroundImageValue
149
167
  } : {}),
150
168
  ...(Platform.OS === 'web' ? {
151
169
  maxHeight,
@@ -164,9 +182,11 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
164
182
  tokens,
165
183
  dataSet,
166
184
  backgroundImage,
185
+ fullBleedContent,
186
+ cardState,
167
187
  ...rest
168
188
  } = _ref3;
169
- const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens);
189
+ const cardStyle = selectStyles(typeof tokens === 'function' ? tokens(cardState) : tokens);
170
190
  const props = selectProps(rest);
171
191
  let content = children;
172
192
  const {
@@ -180,9 +200,13 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
180
200
  const backgroundImagePosition = useResponsiveProp(position);
181
201
  const backgroundImageAlign = useResponsiveProp(align);
182
202
  const imageSourceViewport = formatImageSource(useResponsiveProp(src));
203
+ const {
204
+ content: fullBleedImageContent,
205
+ position: fullBleedContentPosition,
206
+ imgCol
207
+ } = fullBleedContent || {};
208
+ const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom');
183
209
  if (backgroundImage && src) {
184
- // When there's a background image, separate the padding from the container style
185
- // so the image can fill the entire container without padding interference
186
210
  const {
187
211
  paddingTop,
188
212
  paddingBottom,
@@ -190,8 +214,6 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
190
214
  paddingRight,
191
215
  ...containerStyle
192
216
  } = cardStyle;
193
-
194
- // Only create padding wrapper if there's actually padding defined
195
217
  const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight;
196
218
  const paddedContent = hasPadding ? /*#__PURE__*/_jsx(View, {
197
219
  style: {
@@ -219,6 +241,53 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
219
241
  children: content
220
242
  });
221
243
  }
244
+ if (fullBleedContent && fullBleedImageContent) {
245
+ const {
246
+ paddingTop,
247
+ paddingBottom,
248
+ paddingLeft,
249
+ paddingRight,
250
+ ...containerStyle
251
+ } = cardStyle;
252
+ const imageColumns = imgCol || {
253
+ xs: GRID_COLUMNS
254
+ };
255
+ const textColumns = {};
256
+ Object.keys(imageColumns).forEach(breakpoint => {
257
+ textColumns[breakpoint] = GRID_COLUMNS - (imageColumns[breakpoint] || GRID_COLUMNS);
258
+ });
259
+ const imageFirst = fullBleedPosition === 'top' || fullBleedPosition === 'left';
260
+ const imageColContent = /*#__PURE__*/_jsx(FlexGridCol, {
261
+ ...imageColumns,
262
+ style: staticStyles.fullBleedImageCol,
263
+ children: fullBleedImageContent
264
+ });
265
+ const textColContent = /*#__PURE__*/_jsx(FlexGridCol, {
266
+ ...textColumns,
267
+ style: {
268
+ paddingTop,
269
+ paddingBottom,
270
+ paddingLeft,
271
+ paddingRight
272
+ },
273
+ children: children
274
+ });
275
+ return /*#__PURE__*/_jsx(View, {
276
+ style: containerStyle,
277
+ dataSet: dataSet,
278
+ ref: ref,
279
+ ...props,
280
+ children: /*#__PURE__*/_jsx(FlexGrid, {
281
+ children: /*#__PURE__*/_jsx(FlexGridRow, {
282
+ children: imageFirst ? /*#__PURE__*/_jsxs(_Fragment, {
283
+ children: [imageColContent, textColContent]
284
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
285
+ children: [textColContent, imageColContent]
286
+ })
287
+ })
288
+ })
289
+ });
290
+ }
222
291
  return /*#__PURE__*/_jsx(View, {
223
292
  style: cardStyle,
224
293
  dataSet: dataSet,
@@ -228,6 +297,13 @@ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
228
297
  });
229
298
  });
230
299
  CardBase.displayName = 'CardBase';
300
+ export const fullBleedContentPropTypes = PropTypes.shape({
301
+ content: PropTypes.node.isRequired,
302
+ alt: PropTypes.string,
303
+ position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['bottom', 'left', 'right', 'top'])),
304
+ align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['start', 'end', 'center', 'stretch'])),
305
+ imgCol: PropTypes.object
306
+ });
231
307
  const staticStyles = StyleSheet.create({
232
308
  imageBackground: {
233
309
  width: '100%',
@@ -249,6 +325,9 @@ const staticStyles = StyleSheet.create({
249
325
  position: 'absolute',
250
326
  width: '100%',
251
327
  height: '100%'
328
+ },
329
+ fullBleedImageCol: {
330
+ padding: 0
252
331
  }
253
332
  });
254
333
  CardBase.propTypes = {
@@ -266,6 +345,7 @@ CardBase.propTypes = {
266
345
  resizeMode: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])),
267
346
  position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['bottom', 'left', 'right', 'top'])),
268
347
  align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['start', 'end', 'center', 'stretch']))
269
- })
348
+ }),
349
+ fullBleedContent: fullBleedContentPropTypes
270
350
  };
271
351
  export default CardBase;
@@ -7,10 +7,10 @@ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
7
7
  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
- import CardBase from './CardBase';
10
+ import CardBase, { fullBleedContentPropTypes } from './CardBase';
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
13
- const tokenKeys = ['flex', 'backgroundColor', 'borderColor', 'gradient', 'borderRadius', 'borderWidth', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'minWidth', 'shadow', 'contentAlignItems', 'contentJustifyContent', 'contentFlexGrow', 'contentFlexShrink',
13
+ const tokenKeys = ['flex', 'backgroundColor', 'borderColor', 'gradient', 'borderRadius', 'borderWidth', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight', 'minWidth', 'shadow', 'contentAlignItems', 'contentJustifyContent', 'contentFlexGrow', 'contentFlexShrink',
14
14
  // Outer border tokens. TODO: centralise common token sets like these as part of
15
15
  // https://github.com/telus/universal-design-system/issues/782
16
16
  'outerBorderColor', 'outerBorderWidth', 'outerBorderGap', 'icon', 'iconBackgroundColor', 'iconColor', 'iconSize', 'inputBackgroundColor', 'inputBorderColor', 'inputBorderRadius', 'inputBorderWidth', 'inputHeight', 'inputShadow', 'inputWidth'];
@@ -32,6 +32,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
32
32
  hrefAttrs,
33
33
  dataSet,
34
34
  backgroundImage,
35
+ fullBleedContent,
35
36
  accessibilityRole = href ? 'link' : undefined,
36
37
  ...rawRest
37
38
  } = _ref;
@@ -125,10 +126,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
125
126
  setFocused(false);
126
127
  setPressed(false);
127
128
  },
128
- style: {
129
- ...staticStyles.linkContainer,
130
- textDecoration: 'none'
131
- },
129
+ style: staticStyles.linkContainer,
132
130
  ...(hrefAttrs || {}),
133
131
  role: accessibilityRole,
134
132
  children: /*#__PURE__*/_jsx(CardBase, {
@@ -138,6 +136,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
138
136
  hovered
139
137
  }),
140
138
  backgroundImage: backgroundImage,
139
+ fullBleedContent: fullBleedContent,
141
140
  children: typeof children === 'function' ? children(getCardState({
142
141
  pressed,
143
142
  focused,
@@ -162,6 +161,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
162
161
  children: pressableState => /*#__PURE__*/_jsx(CardBase, {
163
162
  tokens: getCardTokens(pressableState),
164
163
  backgroundImage: backgroundImage,
164
+ fullBleedContent: fullBleedContent,
165
165
  children: typeof children === 'function' ? children(getCardState(pressableState)) : children
166
166
  })
167
167
  });
@@ -175,7 +175,8 @@ const staticStyles = StyleSheet.create({
175
175
  flex: 1,
176
176
  display: 'flex',
177
177
  alignItems: 'stretch',
178
- justifyContent: 'flex-start'
178
+ justifyContent: 'flex-start',
179
+ textDecorationLine: 'none'
179
180
  }
180
181
  });
181
182
  PressableCardBase.displayName = 'PressableCardBase';
@@ -195,6 +196,7 @@ PressableCardBase.propTypes = {
195
196
  resizeMode: PropTypes.oneOfType([PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center']), PropTypes.object]),
196
197
  position: PropTypes.oneOfType([PropTypes.oneOf(['bottom', 'left', 'right', 'top']), PropTypes.object]),
197
198
  align: PropTypes.oneOfType([PropTypes.oneOf(['start', 'end', 'center', 'stretch']), PropTypes.object])
198
- })
199
+ }),
200
+ fullBleedContent: fullBleedContentPropTypes
199
201
  };
200
202
  export default withLinkRouter(PressableCardBase);
@@ -4,7 +4,7 @@ import Platform from "react-native-web/dist/exports/Platform";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
5
  import View from "react-native-web/dist/exports/View";
6
6
  import { validateThemeTokens, resolveThemeTokens } from '../ThemeProvider';
7
- import { a11yProps, getTokensSetPropType, selectSystemProps, selectTokens, viewProps } from '../utils';
7
+ import { a11yProps, getTokensSetPropType, selectSystemProps, selectTokens, variantProp, viewProps } from '../utils';
8
8
  import ScrollViewEnd from './ScrollViewEnd';
9
9
  import { getItemPositionScrollTarget, itemPositionsPropType } from './itemPositions';
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -27,6 +27,7 @@ const HorizontalScroll = /*#__PURE__*/React.forwardRef((_ref, ref) => {
27
27
  ScrollButton,
28
28
  tokens,
29
29
  itemPositions,
30
+ variant,
30
31
  children,
31
32
  ...rest
32
33
  } = _ref;
@@ -70,8 +71,9 @@ const HorizontalScroll = /*#__PURE__*/React.forwardRef((_ref, ref) => {
70
71
  setScrollOffset(x);
71
72
  };
72
73
  const scrollMax = Math.max(0, contentWidth - containerWidth);
73
- const showNextButton = scrollOffset < scrollMax;
74
- const showPreviousButton = scrollOffset > 0;
74
+ const hideNavigationButtons = variant?.hideNavigationButtons || false;
75
+ const showNextButton = scrollOffset < scrollMax && !hideNavigationButtons;
76
+ const showPreviousButton = scrollOffset > 0 && !hideNavigationButtons;
75
77
  const scrollRef = React.useRef(null);
76
78
  const scrollTo = targetX => {
77
79
  if (typeof scrollRef.current?.scrollTo === 'function') {
@@ -162,6 +164,7 @@ HorizontalScroll.propTypes = {
162
164
  tokens: getTokensSetPropType(tokenKeys, {
163
165
  allowFunction: true
164
166
  }),
167
+ variant: variantProp.propType,
165
168
  children: PropTypes.node
166
169
  };
167
170
  export default HorizontalScroll;