@telus-uds/components-base 3.18.0 → 3.20.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 +32 -1
- package/jest.config.cjs +10 -2
- package/lib/cjs/Box/Box.js +114 -62
- package/lib/cjs/Box/backgroundImageStylesMap.js +136 -28
- package/lib/cjs/Button/ButtonDropdown.js +1 -0
- package/lib/cjs/Carousel/Carousel.js +1 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +8 -21
- package/lib/cjs/Link/LinkBase.js +8 -9
- package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +10 -10
- package/lib/cjs/Spacer/Spacer.js +65 -5
- package/lib/cjs/StepTracker/Step.js +12 -1
- package/lib/cjs/StepTracker/StepTracker.js +15 -4
- package/lib/cjs/TabBar/TabBar.js +4 -2
- package/lib/cjs/TabBar/index.js +2 -0
- package/lib/cjs/Tooltip/Backdrop.js +1 -1
- package/lib/cjs/utils/index.js +17 -1
- package/lib/cjs/utils/isTouchDevice.js +34 -0
- package/lib/cjs/utils/useMediaQuerySpacing.js +121 -0
- package/lib/esm/Box/Box.js +113 -63
- package/lib/esm/Box/backgroundImageStylesMap.js +134 -27
- package/lib/esm/Button/ButtonDropdown.js +1 -0
- package/lib/esm/Carousel/Carousel.js +2 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +8 -21
- package/lib/esm/Link/LinkBase.js +8 -9
- package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +10 -10
- package/lib/esm/Spacer/Spacer.js +66 -6
- package/lib/esm/StepTracker/Step.js +12 -1
- package/lib/esm/StepTracker/StepTracker.js +15 -4
- package/lib/esm/TabBar/TabBar.js +4 -2
- package/lib/esm/TabBar/index.js +2 -0
- package/lib/esm/Tooltip/Backdrop.js +1 -1
- package/lib/esm/utils/index.js +3 -1
- package/lib/esm/utils/isTouchDevice.js +27 -0
- package/lib/esm/utils/useMediaQuerySpacing.js +116 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Box/Box.jsx +97 -55
- package/src/Box/backgroundImageStylesMap.js +48 -15
- package/src/Button/ButtonDropdown.jsx +1 -0
- package/src/Carousel/Carousel.jsx +3 -2
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +9 -16
- package/src/Link/LinkBase.jsx +11 -9
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +11 -10
- package/src/Spacer/Spacer.jsx +54 -7
- package/src/StepTracker/Step.jsx +47 -27
- package/src/StepTracker/StepTracker.jsx +9 -1
- package/src/TabBar/TabBar.jsx +3 -1
- package/src/TabBar/index.js +3 -0
- package/src/Tooltip/Backdrop.jsx +1 -1
- package/src/utils/index.js +2 -0
- package/src/utils/isTouchDevice.js +34 -0
- package/src/utils/useMediaQuerySpacing.js +124 -0
package/lib/esm/Spacer/Spacer.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
4
3
|
import View from "react-native-web/dist/exports/View";
|
|
5
|
-
import
|
|
4
|
+
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
5
|
+
import { a11yProps, selectSystemProps, spacingProps, useSpacingScale, viewProps, StyleSheet as StyleSheetUtils, createMediaQueryStyles } from '../utils';
|
|
6
|
+
import useMediaQuerySpacing from '../utils/useMediaQuerySpacing';
|
|
7
|
+
import useTheme from '../ThemeProvider/useTheme';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @typedef {import('../utils/props/spacingProps.js').SpacingValue} SpacingValue
|
|
@@ -60,13 +62,66 @@ const Spacer = /*#__PURE__*/React.forwardRef((_ref, ref) => {
|
|
|
60
62
|
let {
|
|
61
63
|
space = 1,
|
|
62
64
|
direction = 'column',
|
|
65
|
+
dataSet,
|
|
63
66
|
...rest
|
|
64
67
|
} = _ref;
|
|
65
|
-
const
|
|
66
|
-
|
|
68
|
+
const {
|
|
69
|
+
themeOptions: {
|
|
70
|
+
enableMediaQueryStyleSheet
|
|
71
|
+
}
|
|
72
|
+
} = useTheme();
|
|
73
|
+
const {
|
|
74
|
+
sizeByViewport
|
|
75
|
+
} = useMediaQuerySpacing(space);
|
|
76
|
+
const fallbackSize = useSpacingScale(space);
|
|
77
|
+
const sizeStyle = selectSizeStyle(fallbackSize, direction);
|
|
78
|
+
let spacerStyles;
|
|
79
|
+
let dataSetValue = dataSet;
|
|
80
|
+
if (enableMediaQueryStyleSheet) {
|
|
81
|
+
const sizeKey = direction === 'row' ? 'width' : 'height';
|
|
82
|
+
const stylesByViewport = {
|
|
83
|
+
xs: {
|
|
84
|
+
[sizeKey]: sizeByViewport.xs,
|
|
85
|
+
...staticStyles.stretch
|
|
86
|
+
},
|
|
87
|
+
sm: {
|
|
88
|
+
[sizeKey]: sizeByViewport.sm,
|
|
89
|
+
...staticStyles.stretch
|
|
90
|
+
},
|
|
91
|
+
md: {
|
|
92
|
+
[sizeKey]: sizeByViewport.md,
|
|
93
|
+
...staticStyles.stretch
|
|
94
|
+
},
|
|
95
|
+
lg: {
|
|
96
|
+
[sizeKey]: sizeByViewport.lg,
|
|
97
|
+
...staticStyles.stretch
|
|
98
|
+
},
|
|
99
|
+
xl: {
|
|
100
|
+
[sizeKey]: sizeByViewport.xl,
|
|
101
|
+
...staticStyles.stretch
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const mediaQueryStyles = createMediaQueryStyles(stylesByViewport);
|
|
105
|
+
const {
|
|
106
|
+
ids,
|
|
107
|
+
styles
|
|
108
|
+
} = StyleSheetUtils.create({
|
|
109
|
+
spacer: {
|
|
110
|
+
...mediaQueryStyles
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
spacerStyles = styles.spacer;
|
|
114
|
+
dataSetValue = {
|
|
115
|
+
media: ids.spacer,
|
|
116
|
+
...dataSet
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
spacerStyles = [staticStyles.stretch, sizeStyle];
|
|
120
|
+
}
|
|
67
121
|
return /*#__PURE__*/_jsx(View, {
|
|
68
122
|
ref: ref,
|
|
69
|
-
style:
|
|
123
|
+
style: spacerStyles,
|
|
124
|
+
dataSet: dataSetValue,
|
|
70
125
|
...selectProps(rest)
|
|
71
126
|
});
|
|
72
127
|
});
|
|
@@ -85,7 +140,12 @@ Spacer.propTypes = {
|
|
|
85
140
|
* - `'column'` (default) applies space vertically; has a fixed height and not width.
|
|
86
141
|
* - `'row'` applies space horizontally; has a fixed width and not height.
|
|
87
142
|
*/
|
|
88
|
-
direction: PropTypes.oneOf(['column', 'row'])
|
|
143
|
+
direction: PropTypes.oneOf(['column', 'row']),
|
|
144
|
+
/**
|
|
145
|
+
* Data attributes to be applied to the element. When media query stylesheet is enabled,
|
|
146
|
+
* this will include media query IDs for responsive styling.
|
|
147
|
+
*/
|
|
148
|
+
dataSet: PropTypes.object
|
|
89
149
|
};
|
|
90
150
|
const staticStyles = StyleSheet.create({
|
|
91
151
|
stretch: {
|
|
@@ -150,6 +150,13 @@ const getStepTestID = (isCompleted, isCurrent) => {
|
|
|
150
150
|
}
|
|
151
151
|
return testID;
|
|
152
152
|
};
|
|
153
|
+
const selectBarContainerStyles = (themeTokens, isCompleted, isCurrent) => ({
|
|
154
|
+
backgroundColor: isCompleted ? themeTokens.barCompletedBackgroundColor : themeTokens.barBackgroundColor,
|
|
155
|
+
height: themeTokens.barHeight,
|
|
156
|
+
...(isCurrent && {
|
|
157
|
+
backgroundColor: themeTokens.barCurrentBackgroundColor
|
|
158
|
+
})
|
|
159
|
+
});
|
|
153
160
|
|
|
154
161
|
/**
|
|
155
162
|
* A single step of a StepTracker.
|
|
@@ -162,6 +169,7 @@ const Step = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
|
|
|
162
169
|
stepCount = 0,
|
|
163
170
|
stepIndex = 0,
|
|
164
171
|
tokens,
|
|
172
|
+
isBarVariant,
|
|
165
173
|
...rest
|
|
166
174
|
} = _ref8;
|
|
167
175
|
const {
|
|
@@ -190,7 +198,10 @@ const Step = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
|
|
|
190
198
|
accessibilityCurrent: status === stepIndex,
|
|
191
199
|
ref: ref,
|
|
192
200
|
...selectProps(rest),
|
|
193
|
-
children: [/*#__PURE__*/
|
|
201
|
+
children: [isBarVariant && /*#__PURE__*/_jsx(View, {
|
|
202
|
+
style: selectBarContainerStyles(themeTokens, isCompleted, isCurrent),
|
|
203
|
+
testID: getStepTestID(isCompleted, isCurrent)
|
|
204
|
+
}), !isBarVariant && /*#__PURE__*/_jsxs(StackView, {
|
|
194
205
|
direction: "row",
|
|
195
206
|
space: 0,
|
|
196
207
|
tokens: {
|
|
@@ -12,6 +12,7 @@ import useCopy from '../utils/useCopy';
|
|
|
12
12
|
import Step from './Step';
|
|
13
13
|
import defaultDictionary from './dictionary';
|
|
14
14
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
|
+
const STYLE_BAR_VARIANT = 'bar';
|
|
15
16
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
16
17
|
const selectContainerStyles = _ref => {
|
|
17
18
|
let {
|
|
@@ -52,6 +53,14 @@ const selectStepTrackerLabelStyles = (_ref3, themeOptions) => {
|
|
|
52
53
|
themeOptions
|
|
53
54
|
});
|
|
54
55
|
};
|
|
56
|
+
const selectStepsContainerStyles = _ref4 => {
|
|
57
|
+
let {
|
|
58
|
+
barGap
|
|
59
|
+
} = _ref4;
|
|
60
|
+
return {
|
|
61
|
+
gap: barGap
|
|
62
|
+
};
|
|
63
|
+
};
|
|
55
64
|
|
|
56
65
|
/**
|
|
57
66
|
* StepTracker component shows the current position in a sequence of steps.
|
|
@@ -85,7 +94,7 @@ const selectStepTrackerLabelStyles = (_ref3, themeOptions) => {
|
|
|
85
94
|
* - `accessibilityValue.text` (`aria-valuetext`): `<Current Step Label>`,
|
|
86
95
|
*
|
|
87
96
|
*/
|
|
88
|
-
const StepTracker = /*#__PURE__*/React.forwardRef((
|
|
97
|
+
const StepTracker = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
|
|
89
98
|
let {
|
|
90
99
|
current = 0,
|
|
91
100
|
copy = 'en',
|
|
@@ -94,7 +103,8 @@ const StepTracker = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
|
|
|
94
103
|
tokens,
|
|
95
104
|
variant,
|
|
96
105
|
...rest
|
|
97
|
-
} =
|
|
106
|
+
} = _ref5;
|
|
107
|
+
const isBarVariant = variant?.style === STYLE_BAR_VARIANT;
|
|
98
108
|
const viewport = useViewport();
|
|
99
109
|
const {
|
|
100
110
|
showStepTrackerLabel,
|
|
@@ -140,7 +150,7 @@ const StepTracker = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
|
|
|
140
150
|
children: /*#__PURE__*/_jsxs(StackView, {
|
|
141
151
|
space: 0,
|
|
142
152
|
children: [/*#__PURE__*/_jsx(View, {
|
|
143
|
-
style: staticStyles.stepsContainer,
|
|
153
|
+
style: [staticStyles.stepsContainer, selectStepsContainerStyles(themeTokens)],
|
|
144
154
|
accessibilityRole: stepsContainerAccessibilityRole,
|
|
145
155
|
children: steps.map((label, index) => {
|
|
146
156
|
return /*#__PURE__*/_jsx(Step, {
|
|
@@ -151,7 +161,8 @@ const StepTracker = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
|
|
|
151
161
|
stepCount: steps.length,
|
|
152
162
|
tokens: themeTokens,
|
|
153
163
|
accessibilityRole: stepAccessibilityRole,
|
|
154
|
-
accessibilityCurrent: current === index && Platform.OS === 'web' && 'step'
|
|
164
|
+
accessibilityCurrent: current === index && Platform.OS === 'web' && 'step',
|
|
165
|
+
isBarVariant: isBarVariant
|
|
155
166
|
}, label);
|
|
156
167
|
})
|
|
157
168
|
}), showStepTrackerLabel && /*#__PURE__*/_jsx(View, {
|
package/lib/esm/TabBar/TabBar.js
CHANGED
|
@@ -84,7 +84,8 @@ const TabBar = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
84
84
|
iconActive: item.iconActive,
|
|
85
85
|
onPress: () => handlePress(item.id),
|
|
86
86
|
id: `tab-item-${index}`,
|
|
87
|
-
accessibilityRole: "tablist"
|
|
87
|
+
accessibilityRole: "tablist",
|
|
88
|
+
tokens: item.tokens
|
|
88
89
|
}, item.id))
|
|
89
90
|
})
|
|
90
91
|
});
|
|
@@ -98,7 +99,8 @@ TabBar.propTypes = {
|
|
|
98
99
|
icon: PropTypes.node,
|
|
99
100
|
iconActive: PropTypes.node,
|
|
100
101
|
label: PropTypes.string.isRequired,
|
|
101
|
-
href: PropTypes.string
|
|
102
|
+
href: PropTypes.string,
|
|
103
|
+
tokens: getTokensPropType('TabBarItem')
|
|
102
104
|
})).isRequired,
|
|
103
105
|
/** Id of the initially selected item. */
|
|
104
106
|
initiallySelectedItem: PropTypes.number,
|
package/lib/esm/TabBar/index.js
CHANGED
package/lib/esm/utils/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { default as info } from './info';
|
|
|
9
9
|
export { default as useCopy } from './useCopy';
|
|
10
10
|
export { default as useHash } from './useHash';
|
|
11
11
|
export { default as useSpacingScale } from './useSpacingScale';
|
|
12
|
+
export { default as useMediaQuerySpacing } from './useMediaQuerySpacing';
|
|
12
13
|
export { default as useResponsiveProp } from './useResponsiveProp';
|
|
13
14
|
export { default as useOverlaidPosition } from './useOverlaidPosition';
|
|
14
15
|
export { default as useSafeLayoutEffect } from './useSafeLayoutEffect';
|
|
@@ -24,4 +25,5 @@ export { transformGradient } from './transformGradient';
|
|
|
24
25
|
export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte';
|
|
25
26
|
export { default as formatImageSource } from './formatImageSource';
|
|
26
27
|
export { default as getSpacingScale } from './getSpacingScale';
|
|
27
|
-
export { default as useVariants } from './useVariants';
|
|
28
|
+
export { default as useVariants } from './useVariants';
|
|
29
|
+
export { default as isTouchDevice } from './isTouchDevice';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
2
|
+
/**
|
|
3
|
+
* Determines if the current device supports touch interactions
|
|
4
|
+
*
|
|
5
|
+
* @returns {boolean} True if the device supports touch, false otherwise
|
|
6
|
+
*/
|
|
7
|
+
const isTouchDevice = () => {
|
|
8
|
+
if (Platform.OS !== 'web') {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (typeof window !== 'undefined') {
|
|
12
|
+
if ('ontouchstart' in window) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (window.navigator && window.navigator.maxTouchPoints > 0) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (window.navigator && window.navigator.msMaxTouchPoints > 0) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (window.matchMedia && window.matchMedia('(pointer: coarse)').matches) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
export default isTouchDevice;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useThemeTokens } from '../ThemeProvider';
|
|
2
|
+
import { resolveResponsiveProp } from './useResponsiveProp';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('@telus-uds/system-constants/viewports').Viewport} Viewport
|
|
6
|
+
* @typedef {import('./props/spacingProps.js').SpacingValue} SpacingValue
|
|
7
|
+
* @typedef {import('./props/spacingProps.js').SpacingIndex} SpacingIndex
|
|
8
|
+
* @typedef {import('./props/spacingProps.js').SpacingObject} SpacingObject
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A utility hook that simplifies implementing media query-based responsive spacing.
|
|
13
|
+
*
|
|
14
|
+
* This hook handles the complexity of:
|
|
15
|
+
* - Detecting if a space value is responsive (has viewport keys)
|
|
16
|
+
* - Fetching theme tokens for each viewport
|
|
17
|
+
* - Resolving the correct space index for each viewport
|
|
18
|
+
* - Extracting actual pixel values from theme tokens
|
|
19
|
+
*
|
|
20
|
+
* ## Usage
|
|
21
|
+
*
|
|
22
|
+
* ```jsx
|
|
23
|
+
* const { sizeByViewport } = useMediaQuerySpacing(space, 'spacingScale')
|
|
24
|
+
*
|
|
25
|
+
* // Use sizeByViewport to create media query styles
|
|
26
|
+
* const stylesByViewport = {
|
|
27
|
+
* xs: { padding: sizeByViewport.xs },
|
|
28
|
+
* sm: { padding: sizeByViewport.sm },
|
|
29
|
+
* md: { padding: sizeByViewport.md },
|
|
30
|
+
* lg: { padding: sizeByViewport.lg },
|
|
31
|
+
* xl: { padding: sizeByViewport.xl }
|
|
32
|
+
* }
|
|
33
|
+
* const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* ## Parameters
|
|
37
|
+
*
|
|
38
|
+
* @param {SpacingValue} spaceValue - A spacing value (number or responsive object with viewport keys)
|
|
39
|
+
* @param {string} tokenKey - The theme token key to use (e.g., 'spacingScale', 'Typography')
|
|
40
|
+
* @param {object} [tokens={}] - Additional tokens to pass to useThemeTokens
|
|
41
|
+
* @param {object} [variant={}] - Variant to pass to useThemeTokens
|
|
42
|
+
*
|
|
43
|
+
* ## Returns
|
|
44
|
+
*
|
|
45
|
+
* @returns {{
|
|
46
|
+
* spaceIndexByViewport: { xs: number, sm: number, md: number, lg: number, xl: number },
|
|
47
|
+
* sizeByViewport: { xs: number, sm: number, md: number, lg: number, xl: number },
|
|
48
|
+
* tokensByViewport: { xs: object, sm: object, md: object, lg: object, xl: object }
|
|
49
|
+
* }}
|
|
50
|
+
*
|
|
51
|
+
* - `spaceIndexByViewport`: The resolved space index for each viewport
|
|
52
|
+
* - `sizeByViewport`: The actual pixel/number values for each viewport
|
|
53
|
+
* - `tokensByViewport`: The full theme tokens for each viewport (for advanced use cases)
|
|
54
|
+
*/
|
|
55
|
+
const useMediaQuerySpacing = function (spaceValue) {
|
|
56
|
+
let tokenKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'spacingScale';
|
|
57
|
+
let tokens = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
58
|
+
let variant = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
59
|
+
const isResponsive = typeof spaceValue === 'object' && spaceValue !== null && !spaceValue.space && !spaceValue.options;
|
|
60
|
+
const getSpaceIndex = viewport => {
|
|
61
|
+
if (isResponsive) {
|
|
62
|
+
return resolveResponsiveProp(spaceValue, viewport);
|
|
63
|
+
}
|
|
64
|
+
if (typeof spaceValue === 'number') {
|
|
65
|
+
return spaceValue;
|
|
66
|
+
}
|
|
67
|
+
return spaceValue?.space ?? 1;
|
|
68
|
+
};
|
|
69
|
+
const spaceIndexByViewport = {
|
|
70
|
+
xs: getSpaceIndex('xs'),
|
|
71
|
+
sm: getSpaceIndex('sm'),
|
|
72
|
+
md: getSpaceIndex('md'),
|
|
73
|
+
lg: getSpaceIndex('lg'),
|
|
74
|
+
xl: getSpaceIndex('xl')
|
|
75
|
+
};
|
|
76
|
+
const tokensXs = useThemeTokens(tokenKey, tokens, variant, {
|
|
77
|
+
space: spaceIndexByViewport.xs,
|
|
78
|
+
viewport: 'xs'
|
|
79
|
+
});
|
|
80
|
+
const tokensSm = useThemeTokens(tokenKey, tokens, variant, {
|
|
81
|
+
space: spaceIndexByViewport.sm,
|
|
82
|
+
viewport: 'sm'
|
|
83
|
+
});
|
|
84
|
+
const tokensMd = useThemeTokens(tokenKey, tokens, variant, {
|
|
85
|
+
space: spaceIndexByViewport.md,
|
|
86
|
+
viewport: 'md'
|
|
87
|
+
});
|
|
88
|
+
const tokensLg = useThemeTokens(tokenKey, tokens, variant, {
|
|
89
|
+
space: spaceIndexByViewport.lg,
|
|
90
|
+
viewport: 'lg'
|
|
91
|
+
});
|
|
92
|
+
const tokensXl = useThemeTokens(tokenKey, tokens, variant, {
|
|
93
|
+
space: spaceIndexByViewport.xl,
|
|
94
|
+
viewport: 'xl'
|
|
95
|
+
});
|
|
96
|
+
const sizeByViewport = {
|
|
97
|
+
xs: tokensXs.size ?? 0,
|
|
98
|
+
sm: tokensSm.size ?? 0,
|
|
99
|
+
md: tokensMd.size ?? 0,
|
|
100
|
+
lg: tokensLg.size ?? 0,
|
|
101
|
+
xl: tokensXl.size ?? 0
|
|
102
|
+
};
|
|
103
|
+
const tokensByViewport = {
|
|
104
|
+
xs: tokensXs,
|
|
105
|
+
sm: tokensSm,
|
|
106
|
+
md: tokensMd,
|
|
107
|
+
lg: tokensLg,
|
|
108
|
+
xl: tokensXl
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
spaceIndexByViewport,
|
|
112
|
+
sizeByViewport,
|
|
113
|
+
tokensByViewport
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
export default useMediaQuerySpacing;
|
package/lib/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.15.1",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"standard-engine": {
|
|
85
85
|
"skip": true
|
|
86
86
|
},
|
|
87
|
-
"version": "3.
|
|
87
|
+
"version": "3.20.0",
|
|
88
88
|
"types": "types/index.d.ts"
|
|
89
89
|
}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@gorhom/portal": "^1.0.14",
|
|
13
13
|
"@react-native-picker/picker": "^2.9.0",
|
|
14
14
|
"@telus-uds/system-constants": "^3.0.0",
|
|
15
|
-
"@telus-uds/system-theme-tokens": "^4.
|
|
15
|
+
"@telus-uds/system-theme-tokens": "^4.15.1",
|
|
16
16
|
"airbnb-prop-types": "^2.16.0",
|
|
17
17
|
"css-mediaquery": "^0.1.2",
|
|
18
18
|
"expo-document-picker": "^13.0.1",
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"standard-engine": {
|
|
85
85
|
"skip": true
|
|
86
86
|
},
|
|
87
|
-
"version": "3.
|
|
87
|
+
"version": "3.20.0",
|
|
88
88
|
"types": "types/index.d.ts"
|
|
89
89
|
}
|
package/src/Box/Box.jsx
CHANGED
|
@@ -21,9 +21,10 @@ import {
|
|
|
21
21
|
variantProp,
|
|
22
22
|
viewProps,
|
|
23
23
|
StyleSheet as RNMQStyleSheet,
|
|
24
|
-
getSpacingScale
|
|
24
|
+
getSpacingScale,
|
|
25
|
+
formatImageSource
|
|
25
26
|
} from '../utils'
|
|
26
|
-
import backgroundImageStylesMap from './backgroundImageStylesMap'
|
|
27
|
+
import backgroundImageStylesMap, { backgroundPositions } from './backgroundImageStylesMap'
|
|
27
28
|
import { useViewport } from '../ViewportProvider'
|
|
28
29
|
|
|
29
30
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
@@ -87,38 +88,60 @@ const setBackgroundImage = ({
|
|
|
87
88
|
backgroundImageResizeMode,
|
|
88
89
|
backgroundImagePosition,
|
|
89
90
|
backgroundImageAlign,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
content
|
|
91
|
+
content,
|
|
92
|
+
testID
|
|
93
93
|
}) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
const backgroundImageTestID = testID ? `${testID}-background-image` : undefined
|
|
95
|
+
|
|
96
|
+
if (backgroundImageResizeMode === 'contain' && backgroundImagePosition && backgroundImageAlign) {
|
|
97
|
+
const positionKey = `${backgroundImagePosition}-${backgroundImageAlign}`
|
|
98
|
+
|
|
99
|
+
if (Platform.OS === 'web') {
|
|
100
|
+
const backgroundPosition = backgroundPositions[positionKey] || 'center center'
|
|
101
|
+
|
|
102
|
+
const backgroundImageStyle = {
|
|
103
|
+
backgroundImage: `url(${src})`,
|
|
104
|
+
backgroundSize: 'contain',
|
|
105
|
+
backgroundRepeat: 'no-repeat',
|
|
106
|
+
backgroundPosition
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<View
|
|
111
|
+
style={[staticStyles.imageBackground, backgroundImageStyle]}
|
|
112
|
+
aria-label={alt}
|
|
113
|
+
testID={backgroundImageTestID}
|
|
114
|
+
>
|
|
115
|
+
{content}
|
|
116
|
+
</View>
|
|
117
|
+
)
|
|
100
118
|
}
|
|
119
|
+
const positionStyles = backgroundImageStylesMap[positionKey] || {}
|
|
101
120
|
|
|
102
121
|
return (
|
|
103
|
-
<View style={staticStyles.
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
<View style={staticStyles.containContainer}>
|
|
123
|
+
<Image
|
|
124
|
+
source={src}
|
|
125
|
+
resizeMode={backgroundImageResizeMode}
|
|
126
|
+
style={[staticStyles.containImage, positionStyles]}
|
|
127
|
+
accessible={true}
|
|
128
|
+
accessibilityLabel={alt}
|
|
129
|
+
accessibilityIgnoresInvertColors={true}
|
|
130
|
+
testID={backgroundImageTestID}
|
|
131
|
+
/>
|
|
132
|
+
<View style={staticStyles.contentOverlay}>{content}</View>
|
|
113
133
|
</View>
|
|
114
134
|
)
|
|
115
135
|
}
|
|
136
|
+
|
|
116
137
|
return (
|
|
117
138
|
<ImageBackground
|
|
118
|
-
source={
|
|
119
|
-
alt={alt}
|
|
120
|
-
style={staticStyles.backgroundImageContainer}
|
|
139
|
+
source={src}
|
|
121
140
|
resizeMode={backgroundImageResizeMode}
|
|
141
|
+
style={staticStyles.imageBackground}
|
|
142
|
+
accessible={true}
|
|
143
|
+
accessibilityLabel={alt}
|
|
144
|
+
testID={backgroundImageTestID}
|
|
122
145
|
>
|
|
123
146
|
{content}
|
|
124
147
|
</ImageBackground>
|
|
@@ -279,33 +302,47 @@ const Box = React.forwardRef(
|
|
|
279
302
|
|
|
280
303
|
const { src = '', alt = '', resizeMode = '', position = '', align = '' } = backgroundImage || {}
|
|
281
304
|
const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover')
|
|
282
|
-
const backgroundImagePosition = useResponsiveProp(position
|
|
283
|
-
const backgroundImageAlign = useResponsiveProp(align
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
if (backgroundImage)
|
|
305
|
+
const backgroundImagePosition = useResponsiveProp(position)
|
|
306
|
+
const backgroundImageAlign = useResponsiveProp(align)
|
|
307
|
+
const imageSourceViewport = formatImageSource(useResponsiveProp(src))
|
|
308
|
+
|
|
309
|
+
if (backgroundImage && src) {
|
|
310
|
+
const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = boxStyles
|
|
311
|
+
|
|
312
|
+
const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
|
|
313
|
+
const paddedContent = hasPadding ? (
|
|
314
|
+
<View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
|
|
315
|
+
) : (
|
|
316
|
+
children
|
|
317
|
+
)
|
|
318
|
+
|
|
287
319
|
content = setBackgroundImage({
|
|
288
|
-
src,
|
|
320
|
+
src: imageSourceViewport,
|
|
289
321
|
alt,
|
|
290
322
|
backgroundImageResizeMode,
|
|
291
323
|
backgroundImagePosition,
|
|
292
324
|
backgroundImageAlign,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
content
|
|
325
|
+
content: paddedContent,
|
|
326
|
+
testID
|
|
296
327
|
})
|
|
297
328
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
329
|
+
const dataSetValue = boxMediaIds ? { media: boxMediaIds, ...dataSet } : dataSet
|
|
330
|
+
|
|
331
|
+
if (scroll) {
|
|
332
|
+
const scrollProps = typeof scroll === 'object' ? scroll : {}
|
|
333
|
+
scrollProps.contentContainerStyle = [containerStyle, scrollProps.contentContainerStyle]
|
|
334
|
+
return (
|
|
335
|
+
<ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSetValue} ref={ref}>
|
|
336
|
+
{content}
|
|
337
|
+
</ScrollView>
|
|
338
|
+
)
|
|
307
339
|
}
|
|
308
|
-
|
|
340
|
+
return (
|
|
341
|
+
<View {...props} style={containerStyle} testID={testID} dataSet={dataSetValue} ref={ref}>
|
|
342
|
+
{content}
|
|
343
|
+
</View>
|
|
344
|
+
)
|
|
345
|
+
}
|
|
309
346
|
|
|
310
347
|
const dataSetValue = boxMediaIds ? { media: boxMediaIds, ...dataSet } : dataSet
|
|
311
348
|
|
|
@@ -416,10 +453,12 @@ Box.propTypes = {
|
|
|
416
453
|
*/
|
|
417
454
|
customGradient: PropTypes.func,
|
|
418
455
|
/**
|
|
419
|
-
*
|
|
456
|
+
* Apply background image to the box.
|
|
420
457
|
*/
|
|
421
458
|
backgroundImage: PropTypes.shape({
|
|
422
|
-
src
|
|
459
|
+
// The image src is either a URI string or a number (when a local image src is bundled in IOS or Android app)
|
|
460
|
+
// src is an object when used responsively to provide different image sources for different screen sizes
|
|
461
|
+
src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
|
|
423
462
|
alt: PropTypes.string,
|
|
424
463
|
resizeMode: responsiveProps.getTypeOptionallyByViewport(
|
|
425
464
|
PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
|
|
@@ -436,18 +475,21 @@ Box.propTypes = {
|
|
|
436
475
|
export default Box
|
|
437
476
|
|
|
438
477
|
const staticStyles = StyleSheet.create({
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
478
|
+
imageBackground: { width: '100%', height: '100%' },
|
|
479
|
+
contentOverlay: {
|
|
480
|
+
position: 'relative',
|
|
481
|
+
width: '100%',
|
|
482
|
+
height: '100%',
|
|
483
|
+
zIndex: 1
|
|
445
484
|
},
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
485
|
+
containContainer: {
|
|
486
|
+
width: '100%',
|
|
487
|
+
height: '100%',
|
|
488
|
+
overflow: 'hidden',
|
|
489
|
+
position: 'relative'
|
|
449
490
|
},
|
|
450
|
-
|
|
491
|
+
containImage: {
|
|
492
|
+
position: 'absolute',
|
|
451
493
|
width: '100%',
|
|
452
494
|
height: '100%'
|
|
453
495
|
}
|