@telus-uds/components-base 1.76.0 → 1.77.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 CHANGED
@@ -1,12 +1,26 @@
1
1
  # Change Log - @telus-uds/components-base
2
2
 
3
- This log was last generated on Wed, 07 Feb 2024 19:09:40 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 14 Feb 2024 02:21:15 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 1.77.0
8
+
9
+ Wed, 14 Feb 2024 02:21:15 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - add background-image to box component (guillermo.peitzner@telus.com)
14
+ - skeleton border radius for line shape (tim.hysniu@telus.com)
15
+ - Bump @telus-uds/system-theme-tokens to v2.51.0
16
+
17
+ ### Patches
18
+
19
+ - fix expand-collapse visual states for koodo theme (guillermo.peitzner@telus.com)
20
+
7
21
  ## 1.76.0
8
22
 
9
- Wed, 07 Feb 2024 19:09:40 GMT
23
+ Wed, 07 Feb 2024 19:14:07 GMT
10
24
 
11
25
  ### Minor changes
12
26
 
package/lib/Box/Box.js CHANGED
@@ -9,8 +9,12 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _View = _interopRequireDefault(require("react-native-web/dist/cjs/exports/View"));
10
10
  var _ScrollView = _interopRequireDefault(require("react-native-web/dist/cjs/exports/ScrollView"));
11
11
  var _Platform = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Platform"));
12
+ var _StyleSheet = _interopRequireDefault(require("react-native-web/dist/cjs/exports/StyleSheet"));
13
+ var _ImageBackground = _interopRequireDefault(require("react-native-web/dist/cjs/exports/ImageBackground"));
14
+ var _Image = _interopRequireDefault(require("react-native-web/dist/cjs/exports/Image"));
12
15
  var _ThemeProvider = require("../ThemeProvider");
13
16
  var _utils = require("../utils");
17
+ var _backgroundImageStylesMap = _interopRequireDefault(require("./backgroundImageStylesMap"));
14
18
  var _jsxRuntime = require("react/jsx-runtime");
15
19
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
20
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -65,6 +69,45 @@ const selectBoxStyles = (_ref, customGradient) => {
65
69
  });
66
70
  return styles;
67
71
  };
72
+ const setBackgroundImage = _ref2 => {
73
+ let {
74
+ src,
75
+ alt,
76
+ backgroundImageResizeMode,
77
+ backgroundImagePosition,
78
+ backgroundImageAlign,
79
+ backgroundImageWidth,
80
+ backgroundImageHeight,
81
+ content
82
+ } = _ref2;
83
+ if (backgroundImageResizeMode === 'contain') {
84
+ const containedViewStyle = {
85
+ ...staticStyles.containedView,
86
+ width: backgroundImageWidth,
87
+ height: backgroundImageHeight,
88
+ ..._backgroundImageStylesMap.default[`${backgroundImagePosition}-${backgroundImageAlign}`]
89
+ };
90
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
91
+ style: staticStyles.containedContainer,
92
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
93
+ style: containedViewStyle,
94
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
95
+ source: src,
96
+ alt: alt,
97
+ style: staticStyles.containedImage,
98
+ accessibilityIgnoresInvertColors: true
99
+ })
100
+ }), content]
101
+ });
102
+ }
103
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ImageBackground.default, {
104
+ source: src,
105
+ alt: alt,
106
+ style: staticStyles.backgroundImageContainer,
107
+ resizeMode: backgroundImageResizeMode,
108
+ children: content
109
+ });
110
+ };
68
111
 
69
112
  /**
70
113
  * A layout utility component. Use Box to create space (padding) around content.
@@ -131,7 +174,7 @@ const selectBoxStyles = (_ref, customGradient) => {
131
174
  * text content is inside a scrollable box, as screens are not scrollable by default and even very
132
175
  * short text will require scrolling on small devices at the highest accessibility text scaling settings.
133
176
  */
134
- const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
177
+ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref3, ref) => {
135
178
  let {
136
179
  space,
137
180
  horizontal = space,
@@ -150,8 +193,9 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
150
193
  testID,
151
194
  dataSet,
152
195
  customGradient,
196
+ backgroundImage,
153
197
  ...rest
154
- } = _ref2;
198
+ } = _ref3;
155
199
  const props = {
156
200
  accessibilityRole,
157
201
  ...(0, _utils.getA11yPropsFromHtmlTag)(tag, accessibilityRole),
@@ -166,7 +210,41 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
166
210
  paddingBottom: (0, _utils.useSpacingScale)(bottom),
167
211
  ...selectBoxStyles(themeTokens, customGradient)
168
212
  };
169
- const childrenToRender = typeof customGradient === 'function' ? customGradient(styles.colors, styles)(children) : children;
213
+ let content = children;
214
+ if (typeof customGradient === 'function') content = customGradient(styles.colors, styles)(children);
215
+ const {
216
+ src = '',
217
+ alt = '',
218
+ resizeMode = '',
219
+ position = '',
220
+ align = ''
221
+ } = backgroundImage || {};
222
+ const backgroundImageResizeMode = (0, _utils.useResponsiveProp)(resizeMode, 'cover');
223
+ const backgroundImagePosition = (0, _utils.useResponsiveProp)(position, 'none');
224
+ const backgroundImageAlign = (0, _utils.useResponsiveProp)(align, 'stretch');
225
+ const [backgroundImageWidth, setBackgroundImageWidth] = (0, _react.useState)(0);
226
+ const [backgroundImageHeight, setBackgroundImageHeight] = (0, _react.useState)(0);
227
+ if (backgroundImage) content = setBackgroundImage({
228
+ src,
229
+ alt,
230
+ backgroundImageResizeMode,
231
+ backgroundImagePosition,
232
+ backgroundImageAlign,
233
+ backgroundImageWidth,
234
+ backgroundImageHeight,
235
+ content
236
+ });
237
+ (0, _react.useEffect)(() => {
238
+ if (backgroundImage && backgroundImageWidth === 0 && backgroundImageHeight === 0) {
239
+ _Image.default.getSize(src, (width, height) => {
240
+ // Only update the state if the size has changed
241
+ if (width !== backgroundImageWidth || height !== backgroundImageHeight) {
242
+ setBackgroundImageWidth(width);
243
+ setBackgroundImageHeight(height);
244
+ }
245
+ });
246
+ }
247
+ }, [backgroundImage, backgroundImageWidth, backgroundImageHeight, src]);
170
248
  if (scroll) {
171
249
  const scrollProps = typeof scroll === 'object' ? scroll : {};
172
250
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle];
@@ -176,7 +254,7 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
176
254
  testID: testID,
177
255
  dataSet: dataSet,
178
256
  ref: ref,
179
- children: childrenToRender
257
+ children: content
180
258
  });
181
259
  }
182
260
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
@@ -185,7 +263,7 @@ const Box = /*#__PURE__*/(0, _react.forwardRef)((_ref2, ref) => {
185
263
  testID: testID,
186
264
  dataSet: dataSet,
187
265
  ref: ref,
188
- children: childrenToRender
266
+ children: content
189
267
  });
190
268
  });
191
269
  Box.displayName = 'Box';
@@ -273,7 +351,34 @@ Box.propTypes = {
273
351
  /**
274
352
  Use this prop if need to add a custom gradient for mobile
275
353
  */
276
- customGradient: _propTypes.default.func
354
+ customGradient: _propTypes.default.func,
355
+ /**
356
+ * Use this prop to add a background image to the box.
357
+ */
358
+ backgroundImage: _propTypes.default.shape({
359
+ src: _propTypes.default.string.isRequired,
360
+ alt: _propTypes.default.string,
361
+ resizeMode: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])),
362
+ position: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['none', 'bottom', 'left', 'right', 'top'])),
363
+ align: _utils.responsiveProps.getTypeOptionallyByViewport(_propTypes.default.oneOf(['start', 'end', 'center', 'stretch']))
364
+ })
277
365
  };
278
366
  var _default = Box;
279
- exports.default = _default;
367
+ exports.default = _default;
368
+ const staticStyles = _StyleSheet.default.create({
369
+ backgroundImageContainer: {
370
+ flex: 1
371
+ },
372
+ containedContainer: {
373
+ flex: 1,
374
+ overflow: 'hidden'
375
+ },
376
+ containedView: {
377
+ zIndex: -1,
378
+ position: 'absolute'
379
+ },
380
+ containedImage: {
381
+ width: '100%',
382
+ height: '100%'
383
+ }
384
+ });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _default = {
8
+ 'top-start': {
9
+ top: 0
10
+ },
11
+ 'top-center': {
12
+ left: 0,
13
+ right: 0,
14
+ marginHorizontal: 'auto'
15
+ },
16
+ 'top-end': {
17
+ top: 0,
18
+ right: 0
19
+ },
20
+ 'right-start': {
21
+ top: 0,
22
+ right: 0
23
+ },
24
+ 'left-start': {
25
+ top: 0
26
+ },
27
+ 'left-center': {
28
+ top: 0,
29
+ bottom: 0,
30
+ marginVertical: 'auto'
31
+ },
32
+ 'none-start': {
33
+ top: 0,
34
+ bottom: 0,
35
+ marginVertical: 'auto'
36
+ },
37
+ 'none-center': {
38
+ top: 0,
39
+ bottom: 0,
40
+ left: 0,
41
+ right: 0,
42
+ margin: 'auto'
43
+ },
44
+ 'right-center': {
45
+ top: 0,
46
+ bottom: 0,
47
+ right: 0,
48
+ marginVertical: 'auto'
49
+ },
50
+ 'none-end': {
51
+ top: 0,
52
+ bottom: 0,
53
+ right: 0,
54
+ marginVertical: 'auto'
55
+ },
56
+ 'bottom-start': {
57
+ bottom: 0,
58
+ left: 0
59
+ },
60
+ 'left-end': {
61
+ bottom: 0,
62
+ left: 0
63
+ },
64
+ 'bottom-center': {
65
+ left: 0,
66
+ right: 0,
67
+ bottom: 0,
68
+ marginHorizontal: 'auto'
69
+ },
70
+ 'bottom-end': {
71
+ right: 0,
72
+ bottom: 0
73
+ },
74
+ 'right-end': {
75
+ right: 0,
76
+ bottom: 0
77
+ },
78
+ 'top-stretch': {
79
+ left: 0,
80
+ right: 0,
81
+ width: '100%'
82
+ },
83
+ 'left-stretch': {
84
+ top: 0,
85
+ bottom: 0,
86
+ height: '100%'
87
+ },
88
+ 'right-stretch': {
89
+ top: 0,
90
+ bottom: 0,
91
+ right: 0,
92
+ height: '100%'
93
+ },
94
+ 'bottom-stretch': {
95
+ left: 0,
96
+ right: 0,
97
+ bottom: 0,
98
+ width: '100%'
99
+ }
100
+ };
101
+ exports.default = _default;
@@ -76,6 +76,14 @@ const selectContentPanelStyles = _ref3 => {
76
76
  marginBottom
77
77
  };
78
78
  };
79
+ const selectControlPanelStyles = _ref4 => {
80
+ let {
81
+ contentPanelBackgroundColor
82
+ } = _ref4;
83
+ return {
84
+ backgroundColor: contentPanelBackgroundColor
85
+ };
86
+ };
79
87
 
80
88
  /**
81
89
  * An item in an `ExpandCollapse` which contains collapsible `children` and a `control` that opens
@@ -87,7 +95,7 @@ const selectContentPanelStyles = _ref3 => {
87
95
  * The panel does not need to be a direct child of the `<ExpandCollapse>` (unless this is required
88
96
  * by the chosen accessibility props for a particular accessibility tools).
89
97
  */
90
- const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) => {
98
+ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref5, ref) => {
91
99
  let {
92
100
  openIds = [],
93
101
  panelId,
@@ -101,7 +109,7 @@ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) =>
101
109
  controlRef,
102
110
  content,
103
111
  ...rest
104
- } = _ref4;
112
+ } = _ref5;
105
113
  const [containerHeight, setContainerHeight] = (0, _react.useState)(null);
106
114
  const isExpanded = openIds.includes(panelId);
107
115
  const selectedProps = selectProps({
@@ -143,14 +151,17 @@ const ExpandCollapsePanel = /*#__PURE__*/(0, _react.forwardRef)((_ref4, ref) =>
143
151
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_View.default, {
144
152
  ref: ref,
145
153
  style: themeTokens,
146
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Control.default, {
147
- ...selectedProps,
148
- isExpanded: isExpanded,
149
- tokens: controlTokens,
150
- variant: variant,
151
- onPress: handleControlPress,
152
- ref: controlRef,
153
- children: control
154
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
155
+ style: selectControlPanelStyles(themeTokens),
156
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Control.default, {
157
+ ...selectedProps,
158
+ isExpanded: isExpanded,
159
+ tokens: controlTokens,
160
+ variant: variant,
161
+ onPress: handleControlPress,
162
+ ref: controlRef,
163
+ children: control
164
+ })
154
165
  }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_View.default, {
155
166
  style: {
156
167
  borderTopColor: themeTokens.expandDividerColor,
@@ -34,11 +34,13 @@ const selectSkeletonStyles = _ref => {
34
34
  const selectLineStyles = _ref2 => {
35
35
  let {
36
36
  skeletonHeight,
37
- lineWidth
37
+ lineWidth,
38
+ radius
38
39
  } = _ref2;
39
40
  return {
40
41
  width: lineWidth,
41
- height: skeletonHeight
42
+ height: skeletonHeight,
43
+ borderRadius: radius
42
44
  };
43
45
  };
44
46
  const selectShapeStyles = _ref3 => {
@@ -121,7 +123,8 @@ const Skeleton = /*#__PURE__*/(0, _react.forwardRef)((_ref5, ref) => {
121
123
  }
122
124
  return selectLineStyles({
123
125
  skeletonHeight,
124
- lineWidth: getLineWidth()
126
+ lineWidth: getLineWidth(),
127
+ radius: themeTokens.lineRadius
125
128
  });
126
129
  };
127
130
  const renderSkeleton = function () {
@@ -1,11 +1,16 @@
1
- import React, { forwardRef } from 'react';
1
+ import React, { forwardRef, useEffect, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import ScrollView from "react-native-web/dist/exports/ScrollView";
5
5
  import Platform from "react-native-web/dist/exports/Platform";
6
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
7
+ import ImageBackground from "react-native-web/dist/exports/ImageBackground";
8
+ import Image from "react-native-web/dist/exports/Image";
6
9
  import { useThemeTokens } from '../ThemeProvider';
7
- import { a11yProps, getA11yPropsFromHtmlTag, getTokensPropType, layoutTags, selectSystemProps, spacingProps, useSpacingScale, variantProp, viewProps } from '../utils';
10
+ import { a11yProps, getA11yPropsFromHtmlTag, getTokensPropType, layoutTags, responsiveProps, selectSystemProps, spacingProps, useResponsiveProp, useSpacingScale, variantProp, viewProps } from '../utils';
11
+ import backgroundImageStylesMap from './backgroundImageStylesMap';
8
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
+ import { jsxs as _jsxs } from "react/jsx-runtime";
9
14
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
10
15
 
11
16
  /**
@@ -56,6 +61,45 @@ const selectBoxStyles = (_ref, customGradient) => {
56
61
  });
57
62
  return styles;
58
63
  };
64
+ const setBackgroundImage = _ref2 => {
65
+ let {
66
+ src,
67
+ alt,
68
+ backgroundImageResizeMode,
69
+ backgroundImagePosition,
70
+ backgroundImageAlign,
71
+ backgroundImageWidth,
72
+ backgroundImageHeight,
73
+ content
74
+ } = _ref2;
75
+ if (backgroundImageResizeMode === 'contain') {
76
+ const containedViewStyle = {
77
+ ...staticStyles.containedView,
78
+ width: backgroundImageWidth,
79
+ height: backgroundImageHeight,
80
+ ...backgroundImageStylesMap[`${backgroundImagePosition}-${backgroundImageAlign}`]
81
+ };
82
+ return /*#__PURE__*/_jsxs(View, {
83
+ style: staticStyles.containedContainer,
84
+ children: [/*#__PURE__*/_jsx(View, {
85
+ style: containedViewStyle,
86
+ children: /*#__PURE__*/_jsx(Image, {
87
+ source: src,
88
+ alt: alt,
89
+ style: staticStyles.containedImage,
90
+ accessibilityIgnoresInvertColors: true
91
+ })
92
+ }), content]
93
+ });
94
+ }
95
+ return /*#__PURE__*/_jsx(ImageBackground, {
96
+ source: src,
97
+ alt: alt,
98
+ style: staticStyles.backgroundImageContainer,
99
+ resizeMode: backgroundImageResizeMode,
100
+ children: content
101
+ });
102
+ };
59
103
 
60
104
  /**
61
105
  * A layout utility component. Use Box to create space (padding) around content.
@@ -122,7 +166,7 @@ const selectBoxStyles = (_ref, customGradient) => {
122
166
  * text content is inside a scrollable box, as screens are not scrollable by default and even very
123
167
  * short text will require scrolling on small devices at the highest accessibility text scaling settings.
124
168
  */
125
- const Box = /*#__PURE__*/forwardRef((_ref2, ref) => {
169
+ const Box = /*#__PURE__*/forwardRef((_ref3, ref) => {
126
170
  let {
127
171
  space,
128
172
  horizontal = space,
@@ -141,8 +185,9 @@ const Box = /*#__PURE__*/forwardRef((_ref2, ref) => {
141
185
  testID,
142
186
  dataSet,
143
187
  customGradient,
188
+ backgroundImage,
144
189
  ...rest
145
- } = _ref2;
190
+ } = _ref3;
146
191
  const props = {
147
192
  accessibilityRole,
148
193
  ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
@@ -157,7 +202,41 @@ const Box = /*#__PURE__*/forwardRef((_ref2, ref) => {
157
202
  paddingBottom: useSpacingScale(bottom),
158
203
  ...selectBoxStyles(themeTokens, customGradient)
159
204
  };
160
- const childrenToRender = typeof customGradient === 'function' ? customGradient(styles.colors, styles)(children) : children;
205
+ let content = children;
206
+ if (typeof customGradient === 'function') content = customGradient(styles.colors, styles)(children);
207
+ const {
208
+ src = '',
209
+ alt = '',
210
+ resizeMode = '',
211
+ position = '',
212
+ align = ''
213
+ } = backgroundImage || {};
214
+ const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover');
215
+ const backgroundImagePosition = useResponsiveProp(position, 'none');
216
+ const backgroundImageAlign = useResponsiveProp(align, 'stretch');
217
+ const [backgroundImageWidth, setBackgroundImageWidth] = useState(0);
218
+ const [backgroundImageHeight, setBackgroundImageHeight] = useState(0);
219
+ if (backgroundImage) content = setBackgroundImage({
220
+ src,
221
+ alt,
222
+ backgroundImageResizeMode,
223
+ backgroundImagePosition,
224
+ backgroundImageAlign,
225
+ backgroundImageWidth,
226
+ backgroundImageHeight,
227
+ content
228
+ });
229
+ useEffect(() => {
230
+ if (backgroundImage && backgroundImageWidth === 0 && backgroundImageHeight === 0) {
231
+ Image.getSize(src, (width, height) => {
232
+ // Only update the state if the size has changed
233
+ if (width !== backgroundImageWidth || height !== backgroundImageHeight) {
234
+ setBackgroundImageWidth(width);
235
+ setBackgroundImageHeight(height);
236
+ }
237
+ });
238
+ }
239
+ }, [backgroundImage, backgroundImageWidth, backgroundImageHeight, src]);
161
240
  if (scroll) {
162
241
  const scrollProps = typeof scroll === 'object' ? scroll : {};
163
242
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle];
@@ -167,7 +246,7 @@ const Box = /*#__PURE__*/forwardRef((_ref2, ref) => {
167
246
  testID: testID,
168
247
  dataSet: dataSet,
169
248
  ref: ref,
170
- children: childrenToRender
249
+ children: content
171
250
  });
172
251
  }
173
252
  return /*#__PURE__*/_jsx(View, {
@@ -176,7 +255,7 @@ const Box = /*#__PURE__*/forwardRef((_ref2, ref) => {
176
255
  testID: testID,
177
256
  dataSet: dataSet,
178
257
  ref: ref,
179
- children: childrenToRender
258
+ children: content
180
259
  });
181
260
  });
182
261
  Box.displayName = 'Box';
@@ -264,6 +343,33 @@ Box.propTypes = {
264
343
  /**
265
344
  Use this prop if need to add a custom gradient for mobile
266
345
  */
267
- customGradient: PropTypes.func
346
+ customGradient: PropTypes.func,
347
+ /**
348
+ * Use this prop to add a background image to the box.
349
+ */
350
+ backgroundImage: PropTypes.shape({
351
+ src: PropTypes.string.isRequired,
352
+ alt: PropTypes.string,
353
+ resizeMode: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])),
354
+ position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['none', 'bottom', 'left', 'right', 'top'])),
355
+ align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['start', 'end', 'center', 'stretch']))
356
+ })
268
357
  };
269
- export default Box;
358
+ export default Box;
359
+ const staticStyles = StyleSheet.create({
360
+ backgroundImageContainer: {
361
+ flex: 1
362
+ },
363
+ containedContainer: {
364
+ flex: 1,
365
+ overflow: 'hidden'
366
+ },
367
+ containedView: {
368
+ zIndex: -1,
369
+ position: 'absolute'
370
+ },
371
+ containedImage: {
372
+ width: '100%',
373
+ height: '100%'
374
+ }
375
+ });
@@ -0,0 +1,94 @@
1
+ export default {
2
+ 'top-start': {
3
+ top: 0
4
+ },
5
+ 'top-center': {
6
+ left: 0,
7
+ right: 0,
8
+ marginHorizontal: 'auto'
9
+ },
10
+ 'top-end': {
11
+ top: 0,
12
+ right: 0
13
+ },
14
+ 'right-start': {
15
+ top: 0,
16
+ right: 0
17
+ },
18
+ 'left-start': {
19
+ top: 0
20
+ },
21
+ 'left-center': {
22
+ top: 0,
23
+ bottom: 0,
24
+ marginVertical: 'auto'
25
+ },
26
+ 'none-start': {
27
+ top: 0,
28
+ bottom: 0,
29
+ marginVertical: 'auto'
30
+ },
31
+ 'none-center': {
32
+ top: 0,
33
+ bottom: 0,
34
+ left: 0,
35
+ right: 0,
36
+ margin: 'auto'
37
+ },
38
+ 'right-center': {
39
+ top: 0,
40
+ bottom: 0,
41
+ right: 0,
42
+ marginVertical: 'auto'
43
+ },
44
+ 'none-end': {
45
+ top: 0,
46
+ bottom: 0,
47
+ right: 0,
48
+ marginVertical: 'auto'
49
+ },
50
+ 'bottom-start': {
51
+ bottom: 0,
52
+ left: 0
53
+ },
54
+ 'left-end': {
55
+ bottom: 0,
56
+ left: 0
57
+ },
58
+ 'bottom-center': {
59
+ left: 0,
60
+ right: 0,
61
+ bottom: 0,
62
+ marginHorizontal: 'auto'
63
+ },
64
+ 'bottom-end': {
65
+ right: 0,
66
+ bottom: 0
67
+ },
68
+ 'right-end': {
69
+ right: 0,
70
+ bottom: 0
71
+ },
72
+ 'top-stretch': {
73
+ left: 0,
74
+ right: 0,
75
+ width: '100%'
76
+ },
77
+ 'left-stretch': {
78
+ top: 0,
79
+ bottom: 0,
80
+ height: '100%'
81
+ },
82
+ 'right-stretch': {
83
+ top: 0,
84
+ bottom: 0,
85
+ right: 0,
86
+ height: '100%'
87
+ },
88
+ 'bottom-stretch': {
89
+ left: 0,
90
+ right: 0,
91
+ bottom: 0,
92
+ width: '100%'
93
+ }
94
+ };
@@ -68,6 +68,14 @@ const selectContentPanelStyles = _ref3 => {
68
68
  marginBottom
69
69
  };
70
70
  };
71
+ const selectControlPanelStyles = _ref4 => {
72
+ let {
73
+ contentPanelBackgroundColor
74
+ } = _ref4;
75
+ return {
76
+ backgroundColor: contentPanelBackgroundColor
77
+ };
78
+ };
71
79
 
72
80
  /**
73
81
  * An item in an `ExpandCollapse` which contains collapsible `children` and a `control` that opens
@@ -79,7 +87,7 @@ const selectContentPanelStyles = _ref3 => {
79
87
  * The panel does not need to be a direct child of the `<ExpandCollapse>` (unless this is required
80
88
  * by the chosen accessibility props for a particular accessibility tools).
81
89
  */
82
- const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
90
+ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref5, ref) => {
83
91
  let {
84
92
  openIds = [],
85
93
  panelId,
@@ -93,7 +101,7 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
93
101
  controlRef,
94
102
  content,
95
103
  ...rest
96
- } = _ref4;
104
+ } = _ref5;
97
105
  const [containerHeight, setContainerHeight] = useState(null);
98
106
  const isExpanded = openIds.includes(panelId);
99
107
  const selectedProps = selectProps({
@@ -135,14 +143,17 @@ const ExpandCollapsePanel = /*#__PURE__*/forwardRef((_ref4, ref) => {
135
143
  }) : /*#__PURE__*/_jsxs(View, {
136
144
  ref: ref,
137
145
  style: themeTokens,
138
- children: [/*#__PURE__*/_jsx(ExpandCollapseControl, {
139
- ...selectedProps,
140
- isExpanded: isExpanded,
141
- tokens: controlTokens,
142
- variant: variant,
143
- onPress: handleControlPress,
144
- ref: controlRef,
145
- children: control
146
+ children: [/*#__PURE__*/_jsx(View, {
147
+ style: selectControlPanelStyles(themeTokens),
148
+ children: /*#__PURE__*/_jsx(ExpandCollapseControl, {
149
+ ...selectedProps,
150
+ isExpanded: isExpanded,
151
+ tokens: controlTokens,
152
+ variant: variant,
153
+ onPress: handleControlPress,
154
+ ref: controlRef,
155
+ children: control
156
+ })
146
157
  }), isExpanded && /*#__PURE__*/_jsx(View, {
147
158
  style: {
148
159
  borderTopColor: themeTokens.expandDividerColor,
@@ -25,11 +25,13 @@ const selectSkeletonStyles = _ref => {
25
25
  const selectLineStyles = _ref2 => {
26
26
  let {
27
27
  skeletonHeight,
28
- lineWidth
28
+ lineWidth,
29
+ radius
29
30
  } = _ref2;
30
31
  return {
31
32
  width: lineWidth,
32
- height: skeletonHeight
33
+ height: skeletonHeight,
34
+ borderRadius: radius
33
35
  };
34
36
  };
35
37
  const selectShapeStyles = _ref3 => {
@@ -112,7 +114,8 @@ const Skeleton = /*#__PURE__*/forwardRef((_ref5, ref) => {
112
114
  }
113
115
  return selectLineStyles({
114
116
  skeletonHeight,
115
- lineWidth: getLineWidth()
117
+ lineWidth: getLineWidth(),
118
+ radius: themeTokens.lineRadius
116
119
  });
117
120
  };
118
121
  const renderSkeleton = function () {
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.3.0",
14
- "@telus-uds/system-theme-tokens": "^2.50.1",
14
+ "@telus-uds/system-theme-tokens": "^2.51.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-linear-gradient": "^12.5.0",
@@ -85,5 +85,5 @@
85
85
  "standard-engine": {
86
86
  "skip": true
87
87
  },
88
- "version": "1.76.0"
88
+ "version": "1.77.0"
89
89
  }
package/src/Box/Box.jsx CHANGED
@@ -1,18 +1,21 @@
1
- import React, { forwardRef } from 'react'
1
+ import React, { forwardRef, useEffect, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { View, ScrollView, Platform } from 'react-native'
3
+ import { View, ScrollView, Platform, StyleSheet, ImageBackground, Image } from 'react-native'
4
4
  import { useThemeTokens } from '../ThemeProvider'
5
5
  import {
6
6
  a11yProps,
7
7
  getA11yPropsFromHtmlTag,
8
8
  getTokensPropType,
9
9
  layoutTags,
10
+ responsiveProps,
10
11
  selectSystemProps,
11
12
  spacingProps,
13
+ useResponsiveProp,
12
14
  useSpacingScale,
13
15
  variantProp,
14
16
  viewProps
15
17
  } from '../utils'
18
+ import backgroundImageStylesMap from './backgroundImageStylesMap'
16
19
 
17
20
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
18
21
 
@@ -69,6 +72,50 @@ const selectBoxStyles = (
69
72
  return styles
70
73
  }
71
74
 
75
+ const setBackgroundImage = ({
76
+ src,
77
+ alt,
78
+ backgroundImageResizeMode,
79
+ backgroundImagePosition,
80
+ backgroundImageAlign,
81
+ backgroundImageWidth,
82
+ backgroundImageHeight,
83
+ content
84
+ }) => {
85
+ if (backgroundImageResizeMode === 'contain') {
86
+ const containedViewStyle = {
87
+ ...staticStyles.containedView,
88
+ width: backgroundImageWidth,
89
+ height: backgroundImageHeight,
90
+ ...backgroundImageStylesMap[`${backgroundImagePosition}-${backgroundImageAlign}`]
91
+ }
92
+
93
+ return (
94
+ <View style={staticStyles.containedContainer}>
95
+ <View style={containedViewStyle}>
96
+ <Image
97
+ source={src}
98
+ alt={alt}
99
+ style={staticStyles.containedImage}
100
+ accessibilityIgnoresInvertColors
101
+ />
102
+ </View>
103
+ {content}
104
+ </View>
105
+ )
106
+ }
107
+ return (
108
+ <ImageBackground
109
+ source={src}
110
+ alt={alt}
111
+ style={staticStyles.backgroundImageContainer}
112
+ resizeMode={backgroundImageResizeMode}
113
+ >
114
+ {content}
115
+ </ImageBackground>
116
+ )
117
+ }
118
+
72
119
  /**
73
120
  * A layout utility component. Use Box to create space (padding) around content.
74
121
  *
@@ -154,6 +201,7 @@ const Box = forwardRef(
154
201
  testID,
155
202
  dataSet,
156
203
  customGradient,
204
+ backgroundImage,
157
205
  ...rest
158
206
  },
159
207
  ref
@@ -174,23 +222,52 @@ const Box = forwardRef(
174
222
  ...selectBoxStyles(themeTokens, customGradient)
175
223
  }
176
224
 
177
- const childrenToRender =
178
- typeof customGradient === 'function'
179
- ? customGradient(styles.colors, styles)(children)
180
- : children
225
+ let content = children
226
+ if (typeof customGradient === 'function')
227
+ content = customGradient(styles.colors, styles)(children)
228
+
229
+ const { src = '', alt = '', resizeMode = '', position = '', align = '' } = backgroundImage || {}
230
+ const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover')
231
+ const backgroundImagePosition = useResponsiveProp(position, 'none')
232
+ const backgroundImageAlign = useResponsiveProp(align, 'stretch')
233
+ const [backgroundImageWidth, setBackgroundImageWidth] = useState(0)
234
+ const [backgroundImageHeight, setBackgroundImageHeight] = useState(0)
235
+ if (backgroundImage)
236
+ content = setBackgroundImage({
237
+ src,
238
+ alt,
239
+ backgroundImageResizeMode,
240
+ backgroundImagePosition,
241
+ backgroundImageAlign,
242
+ backgroundImageWidth,
243
+ backgroundImageHeight,
244
+ content
245
+ })
246
+
247
+ useEffect(() => {
248
+ if (backgroundImage && backgroundImageWidth === 0 && backgroundImageHeight === 0) {
249
+ Image.getSize(src, (width, height) => {
250
+ // Only update the state if the size has changed
251
+ if (width !== backgroundImageWidth || height !== backgroundImageHeight) {
252
+ setBackgroundImageWidth(width)
253
+ setBackgroundImageHeight(height)
254
+ }
255
+ })
256
+ }
257
+ }, [backgroundImage, backgroundImageWidth, backgroundImageHeight, src])
181
258
 
182
259
  if (scroll) {
183
260
  const scrollProps = typeof scroll === 'object' ? scroll : {}
184
261
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
185
262
  return (
186
263
  <ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSet} ref={ref}>
187
- {childrenToRender}
264
+ {content}
188
265
  </ScrollView>
189
266
  )
190
267
  }
191
268
  return (
192
269
  <View {...props} style={styles} testID={testID} dataSet={dataSet} ref={ref}>
193
- {childrenToRender}
270
+ {content}
194
271
  </View>
195
272
  )
196
273
  }
@@ -284,7 +361,41 @@ Box.propTypes = {
284
361
  /**
285
362
  Use this prop if need to add a custom gradient for mobile
286
363
  */
287
- customGradient: PropTypes.func
364
+ customGradient: PropTypes.func,
365
+ /**
366
+ * Use this prop to add a background image to the box.
367
+ */
368
+ backgroundImage: PropTypes.shape({
369
+ src: PropTypes.string.isRequired,
370
+ alt: PropTypes.string,
371
+ resizeMode: responsiveProps.getTypeOptionallyByViewport(
372
+ PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
373
+ ),
374
+ position: responsiveProps.getTypeOptionallyByViewport(
375
+ PropTypes.oneOf(['none', 'bottom', 'left', 'right', 'top'])
376
+ ),
377
+ align: responsiveProps.getTypeOptionallyByViewport(
378
+ PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
379
+ )
380
+ })
288
381
  }
289
382
 
290
383
  export default Box
384
+
385
+ const staticStyles = StyleSheet.create({
386
+ backgroundImageContainer: {
387
+ flex: 1
388
+ },
389
+ containedContainer: {
390
+ flex: 1,
391
+ overflow: 'hidden'
392
+ },
393
+ containedView: {
394
+ zIndex: -1,
395
+ position: 'absolute'
396
+ },
397
+ containedImage: {
398
+ width: '100%',
399
+ height: '100%'
400
+ }
401
+ })
@@ -0,0 +1,21 @@
1
+ export default {
2
+ 'top-start': { top: 0 },
3
+ 'top-center': { left: 0, right: 0, marginHorizontal: 'auto' },
4
+ 'top-end': { top: 0, right: 0 },
5
+ 'right-start': { top: 0, right: 0 },
6
+ 'left-start': { top: 0 },
7
+ 'left-center': { top: 0, bottom: 0, marginVertical: 'auto' },
8
+ 'none-start': { top: 0, bottom: 0, marginVertical: 'auto' },
9
+ 'none-center': { top: 0, bottom: 0, left: 0, right: 0, margin: 'auto' },
10
+ 'right-center': { top: 0, bottom: 0, right: 0, marginVertical: 'auto' },
11
+ 'none-end': { top: 0, bottom: 0, right: 0, marginVertical: 'auto' },
12
+ 'bottom-start': { bottom: 0, left: 0 },
13
+ 'left-end': { bottom: 0, left: 0 },
14
+ 'bottom-center': { left: 0, right: 0, bottom: 0, marginHorizontal: 'auto' },
15
+ 'bottom-end': { right: 0, bottom: 0 },
16
+ 'right-end': { right: 0, bottom: 0 },
17
+ 'top-stretch': { left: 0, right: 0, width: '100%' },
18
+ 'left-stretch': { top: 0, bottom: 0, height: '100%' },
19
+ 'right-stretch': { top: 0, bottom: 0, right: 0, height: '100%' },
20
+ 'bottom-stretch': { left: 0, right: 0, bottom: 0, width: '100%' }
21
+ }
@@ -64,6 +64,10 @@ const selectContentPanelStyles = ({
64
64
  marginBottom
65
65
  })
66
66
 
67
+ const selectControlPanelStyles = ({ contentPanelBackgroundColor }) => ({
68
+ backgroundColor: contentPanelBackgroundColor
69
+ })
70
+
67
71
  /**
68
72
  * An item in an `ExpandCollapse` which contains collapsible `children` and a `control` that opens
69
73
  * and closes the collapsible children when pressed.
@@ -136,16 +140,18 @@ const ExpandCollapsePanel = forwardRef(
136
140
  </View>
137
141
  ) : (
138
142
  <View ref={ref} style={themeTokens}>
139
- <ExpandCollapseControl
140
- {...selectedProps}
141
- isExpanded={isExpanded}
142
- tokens={controlTokens}
143
- variant={variant}
144
- onPress={handleControlPress}
145
- ref={controlRef}
146
- >
147
- {control}
148
- </ExpandCollapseControl>
143
+ <View style={selectControlPanelStyles(themeTokens)}>
144
+ <ExpandCollapseControl
145
+ {...selectedProps}
146
+ isExpanded={isExpanded}
147
+ tokens={controlTokens}
148
+ variant={variant}
149
+ onPress={handleControlPress}
150
+ ref={controlRef}
151
+ >
152
+ {control}
153
+ </ExpandCollapseControl>
154
+ </View>
149
155
  {isExpanded && (
150
156
  <View
151
157
  style={{
@@ -25,9 +25,10 @@ const selectSkeletonStyles = ({ color, radius }) => ({
25
25
  maxWidth: '100%'
26
26
  })
27
27
 
28
- const selectLineStyles = ({ skeletonHeight, lineWidth }) => ({
28
+ const selectLineStyles = ({ skeletonHeight, lineWidth, radius }) => ({
29
29
  width: lineWidth,
30
- height: skeletonHeight
30
+ height: skeletonHeight,
31
+ borderRadius: radius
31
32
  })
32
33
 
33
34
  const selectShapeStyles = ({ skeletonHeight }) => ({
@@ -101,7 +102,11 @@ const Skeleton = forwardRef(
101
102
  ]
102
103
  }
103
104
 
104
- return selectLineStyles({ skeletonHeight, lineWidth: getLineWidth() })
105
+ return selectLineStyles({
106
+ skeletonHeight,
107
+ lineWidth: getLineWidth(),
108
+ radius: themeTokens.lineRadius
109
+ })
105
110
  }
106
111
 
107
112
  const renderSkeleton = (index = 0) => {
@@ -23,6 +23,7 @@ export type TypographyVariants = {
23
23
  characters?: number
24
24
  spaceBetweenLines?: number
25
25
  squareRadius?: number
26
+ lineRadius?: number
26
27
  inverse?: boolean
27
28
  }
28
29