@telus-uds/components-base 1.93.0 → 1.95.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 (101) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/lib/Autocomplete/Autocomplete.js +2 -1
  3. package/lib/Button/ButtonGroup.js +17 -1
  4. package/lib/Card/Card.js +12 -0
  5. package/lib/Card/CardBase.js +37 -2
  6. package/lib/Carousel/Carousel.js +55 -13
  7. package/lib/Carousel/CarouselItem/CarouselItem.js +86 -12
  8. package/lib/DownloadApp/DownloadApp.js +168 -0
  9. package/lib/DownloadApp/dictionary.js +17 -0
  10. package/lib/DownloadApp/index.js +10 -0
  11. package/lib/Fieldset/FieldsetContainer.js +7 -2
  12. package/lib/Fieldset/FieldsetContainer.native.js +4 -1
  13. package/lib/FileUpload/FileUpload.js +336 -0
  14. package/lib/FileUpload/NotificationContent.js +60 -0
  15. package/lib/FileUpload/dictionary.js +47 -0
  16. package/lib/FileUpload/index.js +10 -0
  17. package/lib/Icon/IconText.js +19 -2
  18. package/lib/Link/LinkBase.js +2 -2
  19. package/lib/Link/TextButton.js +7 -17
  20. package/lib/Modal/Modal.js +1 -1
  21. package/lib/Modal/ModalContent.js +12 -6
  22. package/lib/MultiSelectFilter/ModalOverlay.js +49 -30
  23. package/lib/MultiSelectFilter/MultiSelectFilter.js +170 -131
  24. package/lib/Notification/Notification.js +11 -2
  25. package/lib/Status/Status.js +9 -4
  26. package/lib/TabBar/TabBar.js +133 -0
  27. package/lib/TabBar/TabBarItem.js +184 -0
  28. package/lib/TabBar/index.js +10 -0
  29. package/lib/TextInput/TextInputBase.js +2 -1
  30. package/lib/Tooltip/getTooltipPosition.js +8 -9
  31. package/lib/index.js +24 -0
  32. package/lib/utils/convertFromMegaByteToByte.js +16 -0
  33. package/lib/utils/formatImageSource.js +34 -0
  34. package/lib/utils/index.js +17 -1
  35. package/lib-module/Autocomplete/Autocomplete.js +2 -1
  36. package/lib-module/Button/ButtonGroup.js +17 -1
  37. package/lib-module/Card/Card.js +13 -1
  38. package/lib-module/Card/CardBase.js +38 -3
  39. package/lib-module/Carousel/Carousel.js +55 -13
  40. package/lib-module/Carousel/CarouselItem/CarouselItem.js +86 -12
  41. package/lib-module/DownloadApp/DownloadApp.js +160 -0
  42. package/lib-module/DownloadApp/dictionary.js +10 -0
  43. package/lib-module/DownloadApp/index.js +2 -0
  44. package/lib-module/Fieldset/FieldsetContainer.js +7 -2
  45. package/lib-module/Fieldset/FieldsetContainer.native.js +4 -1
  46. package/lib-module/FileUpload/FileUpload.js +329 -0
  47. package/lib-module/FileUpload/NotificationContent.js +55 -0
  48. package/lib-module/FileUpload/dictionary.js +40 -0
  49. package/lib-module/FileUpload/index.js +2 -0
  50. package/lib-module/Icon/IconText.js +19 -2
  51. package/lib-module/Link/LinkBase.js +2 -2
  52. package/lib-module/Link/TextButton.js +7 -17
  53. package/lib-module/Modal/Modal.js +1 -1
  54. package/lib-module/Modal/ModalContent.js +12 -6
  55. package/lib-module/MultiSelectFilter/ModalOverlay.js +49 -30
  56. package/lib-module/MultiSelectFilter/MultiSelectFilter.js +171 -132
  57. package/lib-module/Notification/Notification.js +11 -2
  58. package/lib-module/Status/Status.js +9 -4
  59. package/lib-module/TabBar/TabBar.js +125 -0
  60. package/lib-module/TabBar/TabBarItem.js +177 -0
  61. package/lib-module/TabBar/index.js +2 -0
  62. package/lib-module/TextInput/TextInputBase.js +2 -1
  63. package/lib-module/Tooltip/getTooltipPosition.js +8 -9
  64. package/lib-module/index.js +3 -0
  65. package/lib-module/utils/convertFromMegaByteToByte.js +10 -0
  66. package/lib-module/utils/formatImageSource.js +27 -0
  67. package/lib-module/utils/index.js +3 -1
  68. package/package.json +4 -3
  69. package/src/Autocomplete/Autocomplete.jsx +2 -1
  70. package/src/Button/ButtonGroup.jsx +9 -1
  71. package/src/Card/Card.jsx +18 -2
  72. package/src/Card/CardBase.jsx +47 -12
  73. package/src/Carousel/Carousel.jsx +59 -13
  74. package/src/Carousel/CarouselItem/CarouselItem.jsx +94 -9
  75. package/src/DownloadApp/DownloadApp.jsx +165 -0
  76. package/src/DownloadApp/dictionary.js +10 -0
  77. package/src/DownloadApp/index.js +3 -0
  78. package/src/Fieldset/FieldsetContainer.jsx +4 -3
  79. package/src/Fieldset/FieldsetContainer.native.jsx +9 -6
  80. package/src/FileUpload/FileUpload.jsx +396 -0
  81. package/src/FileUpload/NotificationContent.jsx +44 -0
  82. package/src/FileUpload/dictionary.js +40 -0
  83. package/src/FileUpload/index.js +3 -0
  84. package/src/Icon/IconText.jsx +21 -4
  85. package/src/Link/LinkBase.jsx +2 -2
  86. package/src/Link/TextButton.jsx +10 -17
  87. package/src/Modal/Modal.jsx +1 -1
  88. package/src/Modal/ModalContent.jsx +8 -3
  89. package/src/MultiSelectFilter/ModalOverlay.jsx +44 -18
  90. package/src/MultiSelectFilter/MultiSelectFilter.jsx +188 -126
  91. package/src/Notification/Notification.jsx +12 -4
  92. package/src/Status/Status.jsx +15 -4
  93. package/src/TabBar/TabBar.jsx +123 -0
  94. package/src/TabBar/TabBarItem.jsx +149 -0
  95. package/src/TabBar/index.js +3 -0
  96. package/src/TextInput/TextInputBase.jsx +1 -1
  97. package/src/Tooltip/getTooltipPosition.js +11 -12
  98. package/src/index.js +3 -0
  99. package/src/utils/convertFromMegaByteToByte.js +11 -0
  100. package/src/utils/formatImageSource.js +29 -0
  101. package/src/utils/index.js +2 -0
@@ -0,0 +1,125 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import View from "react-native-web/dist/exports/View";
4
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
+ import TabBarItem from './TabBarItem';
6
+ import { useThemeTokens } from '../ThemeProvider';
7
+ import { variantProp, getTokensPropType, selectSystemProps, a11yProps, viewProps } from '../utils';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ const selectTabBarContainerStyles = _ref => {
10
+ let {
11
+ paddingTop,
12
+ paddingBottom,
13
+ borderTopWidth,
14
+ borderTopColor,
15
+ backgroundColor
16
+ } = _ref;
17
+ return {
18
+ paddingTop,
19
+ paddingBottom,
20
+ borderTopWidth,
21
+ borderTopColor,
22
+ backgroundColor
23
+ };
24
+ };
25
+ const selectTabBarItemContainerStyles = _ref2 => {
26
+ let {
27
+ paddingLeft,
28
+ paddingRight,
29
+ gap
30
+ } = _ref2;
31
+ return {
32
+ paddingLeft,
33
+ paddingRight,
34
+ gap
35
+ };
36
+ };
37
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
38
+
39
+ /**
40
+ * TabBar component renders a navigation bar with multiple TabBarItems.
41
+ * It allows users to switch between different views or sections.
42
+ *
43
+ * @component
44
+ * @example
45
+ * const items = [
46
+ * { id: '1', label: 'Home', icon: <HomeIcon />, iconActive: <HomeActiveIcon /> },
47
+ * { id: '2', label: 'Profile', icon: <ProfileIcon />, iconActive: <ProfileActiveIcon /> },
48
+ * ]
49
+ * return (
50
+ * <TabBar
51
+ * items={items}
52
+ * initiallySelectedItem="1"
53
+ * onChange={(itemId) => console.log(itemId)}
54
+ * />
55
+ * )
56
+ */
57
+
58
+ const TabBar = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
59
+ let {
60
+ items = [],
61
+ initiallySelectedItem = '0',
62
+ onChange,
63
+ variant,
64
+ tokens,
65
+ ...rest
66
+ } = _ref3;
67
+ const [isSelected, setIsSelected] = React.useState(initiallySelectedItem);
68
+ const themeTokens = useThemeTokens('TabBar', tokens, variant);
69
+ const handlePress = itemId => {
70
+ setIsSelected(itemId);
71
+ onChange === null || onChange === void 0 ? void 0 : onChange(itemId);
72
+ };
73
+ return /*#__PURE__*/_jsx(View, {
74
+ ref: ref,
75
+ style: [styles.tabBar, selectTabBarContainerStyles(themeTokens)],
76
+ ...selectProps(rest),
77
+ children: /*#__PURE__*/_jsx(View, {
78
+ style: [styles.tabBarItem, selectTabBarItemContainerStyles(themeTokens)],
79
+ children: items.map((item, index) => /*#__PURE__*/_jsx(TabBarItem, {
80
+ label: item.label,
81
+ href: item.href,
82
+ isSelected: isSelected === item.id,
83
+ icon: item.icon,
84
+ iconActive: item.iconActive,
85
+ onPress: () => handlePress(item.id),
86
+ id: `tab-item-${index}`,
87
+ accessibilityRole: "tablist"
88
+ }, item.id))
89
+ })
90
+ });
91
+ });
92
+ TabBar.displayName = 'TabBar';
93
+ TabBar.propTypes = {
94
+ ...selectedSystemPropTypes,
95
+ /** Array of TabBarItems to be displayed in TabBar */
96
+ items: PropTypes.arrayOf(PropTypes.shape({
97
+ id: PropTypes.string.isRequired,
98
+ icon: PropTypes.node,
99
+ iconActive: PropTypes.node,
100
+ label: PropTypes.string.isRequired,
101
+ href: PropTypes.string
102
+ })).isRequired,
103
+ /** Id of the initially selected item. */
104
+ initiallySelectedItem: PropTypes.number,
105
+ /** Callback function to handle item selection change. */
106
+ onChange: PropTypes.func,
107
+ /** Variant of TabBar for styling purposes. */
108
+ variant: variantProp.propType,
109
+ /** Tokens for theming and styling. */
110
+ tokens: getTokensPropType('TabBar')
111
+ };
112
+ const styles = StyleSheet.create({
113
+ tabBar: {
114
+ flex: 1,
115
+ justifyContent: 'center',
116
+ alignItems: 'center'
117
+ },
118
+ tabBarItem: {
119
+ flex: 1,
120
+ flexDirection: 'row',
121
+ justifyContent: 'space-between',
122
+ width: '100%'
123
+ }
124
+ });
125
+ export default TabBar;
@@ -0,0 +1,177 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Pressable from "react-native-web/dist/exports/Pressable";
4
+ import View from "react-native-web/dist/exports/View";
5
+ import StyleSheet from "react-native-web/dist/exports/StyleSheet";
6
+ import { getTokensPropType, resolvePressableTokens, variantProp } from '../utils';
7
+ import { useThemeTokensCallback } from '../ThemeProvider';
8
+ import Typography from '../Typography';
9
+ import Spacer from '../Spacer';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { jsxs as _jsxs } from "react/jsx-runtime";
12
+ const selectTypographyStyles = (_ref, isSelected) => {
13
+ let {
14
+ fontWeight,
15
+ color,
16
+ lineHeight,
17
+ fontName,
18
+ activeColor
19
+ } = _ref;
20
+ return {
21
+ fontWeight,
22
+ color: isSelected ? activeColor : color,
23
+ lineHeight,
24
+ fontName
25
+ };
26
+ };
27
+ const selectIconContainerStyles = _ref2 => {
28
+ let {
29
+ paddingTop,
30
+ paddingBottom
31
+ } = _ref2;
32
+ return {
33
+ paddingTop,
34
+ paddingBottom,
35
+ alignItems: 'center'
36
+ };
37
+ };
38
+ const selectIconStyles = (_ref3, isSelected) => {
39
+ let {
40
+ iconSize,
41
+ iconColor,
42
+ activeColor
43
+ } = _ref3;
44
+ return {
45
+ size: iconSize,
46
+ color: isSelected ? activeColor : iconColor
47
+ };
48
+ };
49
+ const selectContainerStyles = _ref4 => {
50
+ let {
51
+ borderRadius,
52
+ backgroundColor
53
+ } = _ref4;
54
+ return {
55
+ borderRadius,
56
+ backgroundColor,
57
+ alignSelf: 'center',
58
+ alignItems: 'center'
59
+ };
60
+ };
61
+ const TabBarItem = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
62
+ let {
63
+ href,
64
+ variant,
65
+ tokens,
66
+ label,
67
+ isSelected,
68
+ id,
69
+ onPress,
70
+ icon: IconComponent,
71
+ iconActive: IconActiveComponent,
72
+ accessibilityRole = 'tab'
73
+ } = _ref5;
74
+ const getTokens = useThemeTokensCallback('TabBarItem', tokens, variant);
75
+ const getPressableStyle = _ref6 => {
76
+ let {
77
+ pressed,
78
+ focused,
79
+ hovered
80
+ } = _ref6;
81
+ const resolvedTokens = resolvePressableTokens(getTokens, {
82
+ pressed,
83
+ focused,
84
+ hovered
85
+ }, {
86
+ isSelected
87
+ });
88
+ return {
89
+ ...resolvedTokens,
90
+ outline: 'none' // removes the default browser :focus outline
91
+ };
92
+ };
93
+
94
+ return /*#__PURE__*/_jsx(Pressable, {
95
+ ref: ref,
96
+ href: href,
97
+ nativeID: id,
98
+ onPress: onPress,
99
+ style: _ref7 => {
100
+ let {
101
+ pressed,
102
+ focused,
103
+ hovered
104
+ } = _ref7;
105
+ return [styles.flexContainer, getPressableStyle({
106
+ pressed,
107
+ focused,
108
+ hovered
109
+ })];
110
+ },
111
+ accessibilityRole: accessibilityRole,
112
+ testID: id,
113
+ children: pressableState => {
114
+ const resolvedTokens = getPressableStyle(pressableState);
115
+ return /*#__PURE__*/_jsxs(View, {
116
+ style: selectContainerStyles(resolvedTokens),
117
+ children: [/*#__PURE__*/_jsx(View, {
118
+ style: [selectIconContainerStyles(resolvedTokens), styles.iconContainer],
119
+ children: isSelected ? IconActiveComponent && /*#__PURE__*/_jsx(IconActiveComponent, {
120
+ ...selectIconStyles(resolvedTokens, isSelected)
121
+ }) : IconComponent && /*#__PURE__*/_jsx(IconComponent, {
122
+ ...selectIconStyles(resolvedTokens)
123
+ })
124
+ }), /*#__PURE__*/_jsx(Spacer, {
125
+ space: 1
126
+ }), /*#__PURE__*/_jsx(Typography, {
127
+ variant: {
128
+ size: 'micro'
129
+ },
130
+ tokens: selectTypographyStyles(resolvedTokens, isSelected),
131
+ align: "center",
132
+ children: label
133
+ }), /*#__PURE__*/_jsx(Spacer, {
134
+ space: 1
135
+ })]
136
+ });
137
+ }
138
+ });
139
+ });
140
+ TabBarItem.displayName = 'TabBarItem';
141
+ TabBarItem.propTypes = {
142
+ /** The icon to be displayed when the item is not selected. */
143
+ icon: PropTypes.elementType,
144
+ /** The icon to be displayed when the item is selected. */
145
+ iconActive: PropTypes.elementType,
146
+ /** Tokens for theming and styling the TabBarItem. */
147
+ tokens: getTokensPropType('TabBarItem'),
148
+ /** Variant of the TabBarItem for styling purposes. */
149
+ variant: variantProp.propType,
150
+ /** Callback function to handle press events. */
151
+ onPress: PropTypes.func,
152
+ /** URL to navigate to when the item is pressed. */
153
+ href: PropTypes.string,
154
+ /** Indicates whether the item is selected. */
155
+ isSelected: PropTypes.bool,
156
+ /** Unique identifier for the item. */
157
+ id: PropTypes.string,
158
+ /** Label text for the item. */
159
+ label: PropTypes.string.isRequired,
160
+ /** Accessibility role for the item. */
161
+ accessibilityRole: PropTypes.string
162
+ };
163
+ const styles = StyleSheet.create({
164
+ flexContainer: {
165
+ flex: 1,
166
+ alignItems: 'center'
167
+ },
168
+ iconContainer: {
169
+ flex: 1,
170
+ justifyContent: 'center',
171
+ alignItems: 'center',
172
+ minWidth: 44,
173
+ minHeight: 40,
174
+ aspectRatio: 1.1
175
+ }
176
+ });
177
+ export default TabBarItem;
@@ -0,0 +1,2 @@
1
+ import TabBar from './TabBar';
2
+ export default TabBar;
@@ -42,7 +42,8 @@ const selectInputStyles = function (_ref, themeOptions, inactive, type) {
42
42
  // jump around if the border width changes (avoiding NaN and negative padding)
43
43
  const offsetBorder = value => typeof value === 'number' ? Math.max(0, value - borderWidth) : value;
44
44
  const textStyles = applyTextStyles({
45
- fontName,
45
+ fontName: isPassword ? undefined : fontName,
46
+ // In this case, we don't want to apply the fontName to the input if it's a password because Monotype don't have support for the masked characters in mobile.
46
47
  fontSize,
47
48
  lineHeight,
48
49
  fontWeight,
@@ -1,4 +1,4 @@
1
- function normalizePosition(position) {
1
+ function getAbsolutePosition(position) {
2
2
  const {
3
3
  left,
4
4
  right,
@@ -9,7 +9,7 @@ function normalizePosition(position) {
9
9
  } = position;
10
10
 
11
11
  // adjust the coordinates so that it fits within the window
12
- const normalized = {
12
+ const finalPosition = {
13
13
  left: Math.max(0, left),
14
14
  right: Math.max(0, right),
15
15
  top: Math.max(0, top),
@@ -19,15 +19,14 @@ function normalizePosition(position) {
19
19
  const getAbsoluteDiff = (value1, value2) => Math.abs(Math.abs(value1) - Math.abs(value2));
20
20
 
21
21
  // adjust the width by whatever has been subtracted from left or right
22
- normalized.width = width - Math.abs(getAbsoluteDiff(left, normalized.left) - getAbsoluteDiff(right, normalized.right));
23
- if (normalized.top !== top) {
24
- normalized.bottom += normalized.top - top;
22
+ finalPosition.width = width - Math.abs(getAbsoluteDiff(left, finalPosition.left) - getAbsoluteDiff(right, finalPosition.right));
23
+ if (finalPosition.top !== top) {
24
+ finalPosition.bottom += finalPosition.top - top;
25
25
  }
26
- const isNormalized = normalized.right !== right || normalized.left !== left || normalized.top !== top;
27
26
  return {
28
- ...normalized,
27
+ ...finalPosition,
29
28
  ...rest,
30
- isNormalized
29
+ isNormalized: false
31
30
  };
32
31
  }
33
32
  function invertPosition(position) {
@@ -166,6 +165,6 @@ function getTooltipPosition(position, _ref2) {
166
165
 
167
166
  // prefer 'below' over 'above', since we can always expand the document downwards,
168
167
  // and 'above' might cause issues on small viewports with large tooltips
169
- return normalizePosition(leastOverflowing.position === 'above' ? findRectByPosition('below', boundingRects) : leastOverflowing);
168
+ return getAbsolutePosition(leastOverflowing.position === 'above' ? findRectByPosition('below', boundingRects) : leastOverflowing);
170
169
  }
171
170
  export default getTooltipPosition;
@@ -8,12 +8,14 @@ export * from './Button';
8
8
  export { default as Card, PressableCardBase } from './Card';
9
9
  export { default as CardGroup } from './CardGroup';
10
10
  export * from './Carousel';
11
+ export { default as FileUpload } from './FileUpload';
11
12
  export { default as Listbox } from './Listbox';
12
13
  export { default as Checkbox } from './Checkbox';
13
14
  export * from './Checkbox';
14
15
  export { default as CheckboxCard } from './CheckboxCard';
15
16
  export { default as CheckboxCardGroup } from './CheckboxCardGroup';
16
17
  export { default as ColourToggle } from './ColourToggle';
18
+ export { default as DownloadApp } from './DownloadApp';
17
19
  export { default as Divider } from './Divider';
18
20
  export { default as ExpandCollapse, Accordion } from './ExpandCollapse';
19
21
  export { default as Feedback } from './Feedback';
@@ -56,6 +58,7 @@ export * from './StackView';
56
58
  export { default as Status } from './Status';
57
59
  export { default as StepTracker } from './StepTracker';
58
60
  export { default as Tabs } from './Tabs';
61
+ export { default as TabBar } from './TabBar';
59
62
  export { default as Tags } from './Tags';
60
63
  export * from './TextInput';
61
64
  export { default as Timeline } from './Timeline';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Converts a value from megabytes to bytes.
3
+ *
4
+ * @param {number} megaByte - The value in megabytes to be converted.
5
+ * @returns {number} The converted value in bytes.
6
+ */
7
+ const BYTES_IN_A_MEGABYTE = 1024 * 1024;
8
+ export default function convertFromMegaByteToByte(megaByte) {
9
+ return megaByte * BYTES_IN_A_MEGABYTE;
10
+ }
@@ -0,0 +1,27 @@
1
+ // react native source for Image and ImageBackground needs proper formatting to work
2
+ // remote sources that start with 'http' and base64 encoded sources that start with 'data:' need be wrapped in uri when passing source prop for Image and ImageBackground ie. source={ uri: image }
3
+ // local source for image can be passed normally without wrapping in uri ie. source={ image }
4
+
5
+ /**
6
+ * example code from react native docs: https://reactnative.dev/docs/imagebackground
7
+ *
8
+ * const image = {uri: 'https://legacy.reactjs.org/logo-og.png'}
9
+ *
10
+ * const App = () => (
11
+ * <View style={styles.container}>
12
+ * <ImageBackground source={image} resizeMode="cover" style={styles.image}>
13
+ * <Text style={styles.text}>Inside</Text>
14
+ * </ImageBackground>
15
+ * </View>
16
+ * )
17
+ */
18
+
19
+ /**
20
+ * Formats the image source for React Native Image and ImageBackground components.
21
+ * @param {string|number} source - The image source, either a URI string or a number (when a local image source is bundled in IOS or Android app).
22
+ * @returns {object|string} - The formatted image source.
23
+ */
24
+ const formatImageSource = source => typeof source === 'string' && (source.startsWith('http') || source.startsWith('data:')) ? {
25
+ uri: source
26
+ } : source;
27
+ export default formatImageSource;
@@ -20,4 +20,6 @@ export * from './ssr';
20
20
  export { default as containUniqueFields } from './containUniqueFields';
21
21
  export { default as BaseView } from './BaseView';
22
22
  export { default as htmlAttrs } from './htmlAttrs';
23
- export { transformGradient } from './transformGradient';
23
+ export { transformGradient } from './transformGradient';
24
+ export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte';
25
+ export { default as formatImageSource } from './formatImageSource';
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.62.0",
14
+ "@telus-uds/system-theme-tokens": "^2.64.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "css-mediaquery": "^0.1.2",
17
17
  "expo-linear-gradient": "^12.5.0",
@@ -21,7 +21,8 @@
21
21
  "prop-types": "^15.7.2",
22
22
  "react-native-picker-select": "^8.0.4",
23
23
  "semver": "7.5.2",
24
- "string.prototype.matchall": "^4.0.9"
24
+ "string.prototype.matchall": "^4.0.9",
25
+ "expo-document-picker": "~10.3.0"
25
26
  },
26
27
  "description": "Base components",
27
28
  "devDependencies": {
@@ -85,6 +86,6 @@
85
86
  "standard-engine": {
86
87
  "skip": true
87
88
  },
88
- "version": "1.93.0",
89
+ "version": "1.95.0",
89
90
  "types": "types/index.d.ts"
90
91
  }
@@ -102,6 +102,7 @@ const Autocomplete = React.forwardRef(
102
102
  validation,
103
103
  value,
104
104
  helpText = '',
105
+ loadingLabel,
105
106
  ...rest
106
107
  },
107
108
  ref
@@ -334,7 +335,7 @@ const Autocomplete = React.forwardRef(
334
335
  ref={openOverlayRef}
335
336
  >
336
337
  {isLoading ? (
337
- <Loading label={getCopy('loading')} />
338
+ <Loading label={loadingLabel ?? getCopy('loading')} />
338
339
  ) : (
339
340
  <Suggestions
340
341
  hasResults={getCopy('hasResults')}
@@ -63,7 +63,8 @@ const ButtonGroup = React.forwardRef(
63
63
  const viewport = useViewport()
64
64
  const themeTokens = useThemeTokens('ButtonGroup', tokens, variant, { viewport })
65
65
  const stackTokens = selectTokens('StackView', themeTokens)
66
- const { direction, space, fieldSpace } = themeTokens
66
+ const { direction, space, fieldSpace, borderRadius, backgroundColor, padding, gap } =
67
+ themeTokens
67
68
 
68
69
  const getButtonTokens = useThemeTokensCallback('ButtonGroupItem', tokens, variant)
69
70
 
@@ -105,6 +106,12 @@ const ButtonGroup = React.forwardRef(
105
106
  inactive={inactive}
106
107
  validation={validation}
107
108
  accessibilityRole={accessibilityRole}
109
+ style={{
110
+ borderRadius,
111
+ backgroundColor,
112
+ padding,
113
+ ...(Platform.OS === 'web' ? { gap, width: 'fit-content' } : { alignSelf: 'flex-start' })
114
+ }}
108
115
  {...selectProps(rest)}
109
116
  >
110
117
  <StackWrap
@@ -112,6 +119,7 @@ const ButtonGroup = React.forwardRef(
112
119
  space={space}
113
120
  direction={direction}
114
121
  tokens={stackTokens}
122
+ gap={gap}
115
123
  ref={ref}
116
124
  >
117
125
  {items.map(
package/src/Card/Card.jsx CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from '../ThemeProvider'
10
10
  import { getTokensPropType, variantProp, StyleSheet, createMediaQueryStyles } from '../utils'
11
11
  import { useViewport } from '../ViewportProvider'
12
- import { a11yProps, linkProps, selectSystemProps, viewProps } from '../utils/props'
12
+ import { a11yProps, linkProps, selectSystemProps, viewProps, responsiveProps } from '../utils/props'
13
13
  import CardBase from './CardBase'
14
14
  import PressableCardBase from './PressableCardBase'
15
15
  import CheckboxButton from '../Checkbox/CheckboxButton'
@@ -100,7 +100,10 @@ const getInputProps = ({
100
100
  * depending on what you are trying to achieve.
101
101
  */
102
102
  const Card = React.forwardRef(
103
- ({ children, tokens, variant, dataSet, onPress, id, interactiveCard, ...rest }, ref) => {
103
+ (
104
+ { children, tokens, variant, dataSet, onPress, id, interactiveCard, backgroundImage, ...rest },
105
+ ref
106
+ ) => {
104
107
  const viewport = useViewport()
105
108
  const { themeOptions } = useTheme()
106
109
 
@@ -213,6 +216,7 @@ const Card = React.forwardRef(
213
216
  <CardBase
214
217
  ref={ref}
215
218
  tokens={interactiveCard?.body ? restOfTokens : cardStyles}
219
+ backgroundImage={backgroundImage}
216
220
  dataSet={mediaIds && { media: mediaIds }}
217
221
  {...selectProps(rest)}
218
222
  >
@@ -310,6 +314,18 @@ Card.propTypes = {
310
314
  tokens: getTokensPropType('Card'),
311
315
  selectionType: PropTypes.oneOf(Object.values(SelectionType)),
312
316
  variant: variantProp.propType
317
+ }),
318
+ /**
319
+ * Apply background image to the card.
320
+ */
321
+ backgroundImage: PropTypes.shape({
322
+ // The image src is either a URI string or a number (when a local image src is bundled in IOS or Android app)
323
+ // src is an object when used responsively to provide different image sources for different screen sizes
324
+ src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
325
+ alt: PropTypes.string,
326
+ resizeMode: responsiveProps.getTypeOptionallyByViewport(
327
+ PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
328
+ )
313
329
  })
314
330
  }
315
331
 
@@ -1,9 +1,9 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { View, Platform } from 'react-native'
3
+ import { View, Platform, ImageBackground, StyleSheet } from 'react-native'
4
4
 
5
5
  import { applyShadowToken } from '../ThemeProvider'
6
- import { getTokensPropType } from '../utils'
6
+ import { getTokensPropType, responsiveProps, useResponsiveProp, formatImageSource } from '../utils'
7
7
  import { a11yProps, viewProps, selectSystemProps } from '../utils/props'
8
8
 
9
9
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
@@ -50,21 +50,56 @@ const selectStyles = ({
50
50
  * A themeless base component for Card which components can apply theme tokens to. Not
51
51
  * intended to be used in apps or sites directly: build themed components on top of this.
52
52
  */
53
- const CardBase = React.forwardRef(({ children, tokens, dataSet, ...rest }, ref) => {
54
- const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens)
55
- const props = selectProps(rest)
56
- return (
57
- <View style={cardStyle} dataSet={dataSet} ref={ref} {...props}>
58
- {children}
59
- </View>
60
- )
61
- })
53
+ const CardBase = React.forwardRef(
54
+ ({ children, tokens, dataSet, backgroundImage, ...rest }, ref) => {
55
+ const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens)
56
+ const props = selectProps(rest)
57
+
58
+ const { src = '', alt = '', resizeMode = '' } = backgroundImage || {}
59
+ const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover')
60
+ const imageSourceViewport = formatImageSource(useResponsiveProp(src))
61
+
62
+ return (
63
+ <View style={cardStyle} dataSet={dataSet} ref={ref} {...props}>
64
+ {src ? (
65
+ <ImageBackground
66
+ alt={alt}
67
+ source={imageSourceViewport}
68
+ imageStyle={{ borderRadius: cardStyle?.borderRadius - cardStyle?.borderWidth }}
69
+ resizeMode={backgroundImageResizeMode}
70
+ style={styles.imageBackground}
71
+ >
72
+ {children}
73
+ </ImageBackground>
74
+ ) : (
75
+ children
76
+ )}
77
+ </View>
78
+ )
79
+ }
80
+ )
62
81
  CardBase.displayName = 'CardBase'
63
82
 
83
+ const styles = StyleSheet.create({
84
+ imageBackground: { width: '100%', height: '100%' }
85
+ })
86
+
64
87
  CardBase.propTypes = {
65
88
  ...selectedSystemPropTypes,
66
89
  children: PropTypes.node,
67
- tokens: getTokensPropType('Card')
90
+ tokens: getTokensPropType('Card'),
91
+ /**
92
+ * Apply background image to the card.
93
+ */
94
+ backgroundImage: PropTypes.shape({
95
+ // The image src is either a URI string or a number (when a local image src is bundled in IOS or Android app)
96
+ // src is an object when used responsively to provide different image sources for different screen sizes
97
+ src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
98
+ alt: PropTypes.string,
99
+ resizeMode: responsiveProps.getTypeOptionallyByViewport(
100
+ PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])
101
+ )
102
+ })
68
103
  }
69
104
 
70
105
  export default CardBase