@telus-uds/components-base 3.7.1 → 3.8.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 (48) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/lib/cjs/ActivityIndicator/FullScreenIndicator.js +89 -0
  3. package/lib/cjs/ActivityIndicator/InlineIndicator.js +64 -0
  4. package/lib/cjs/ActivityIndicator/OverlayIndicator.js +156 -0
  5. package/lib/cjs/ActivityIndicator/RenderActivityIndicator.js +88 -0
  6. package/lib/cjs/ActivityIndicator/index.js +91 -23
  7. package/lib/cjs/ActivityIndicator/shared.js +12 -1
  8. package/lib/cjs/ActivityIndicator/sharedProptypes.js +67 -0
  9. package/lib/cjs/Card/Card.js +38 -45
  10. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +1 -1
  11. package/lib/cjs/List/ListItemMark.js +13 -2
  12. package/lib/cjs/MultiSelectFilter/ModalOverlay.js +12 -3
  13. package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +9 -2
  14. package/lib/cjs/utils/index.js +9 -1
  15. package/lib/cjs/utils/useDetectOutsideClick.js +39 -0
  16. package/lib/cjs/utils/useVariants.js +46 -0
  17. package/lib/esm/ActivityIndicator/FullScreenIndicator.js +82 -0
  18. package/lib/esm/ActivityIndicator/InlineIndicator.js +57 -0
  19. package/lib/esm/ActivityIndicator/OverlayIndicator.js +149 -0
  20. package/lib/esm/ActivityIndicator/RenderActivityIndicator.js +83 -0
  21. package/lib/esm/ActivityIndicator/index.js +89 -23
  22. package/lib/esm/ActivityIndicator/shared.js +11 -0
  23. package/lib/esm/ActivityIndicator/sharedProptypes.js +61 -0
  24. package/lib/esm/Card/Card.js +38 -45
  25. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +1 -1
  26. package/lib/esm/List/ListItemMark.js +13 -2
  27. package/lib/esm/MultiSelectFilter/ModalOverlay.js +12 -3
  28. package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +9 -2
  29. package/lib/esm/utils/index.js +2 -1
  30. package/lib/esm/utils/useDetectOutsideClick.js +31 -0
  31. package/lib/esm/utils/useVariants.js +41 -0
  32. package/lib/package.json +2 -2
  33. package/package.json +2 -2
  34. package/src/ActivityIndicator/FullScreenIndicator.jsx +65 -0
  35. package/src/ActivityIndicator/InlineIndicator.jsx +47 -0
  36. package/src/ActivityIndicator/OverlayIndicator.jsx +140 -0
  37. package/src/ActivityIndicator/RenderActivityIndicator.jsx +82 -0
  38. package/src/ActivityIndicator/index.jsx +113 -32
  39. package/src/ActivityIndicator/shared.js +11 -0
  40. package/src/ActivityIndicator/sharedProptypes.js +62 -0
  41. package/src/Card/Card.jsx +51 -54
  42. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +1 -1
  43. package/src/List/ListItemMark.jsx +18 -2
  44. package/src/MultiSelectFilter/ModalOverlay.jsx +15 -3
  45. package/src/MultiSelectFilter/MultiSelectFilter.jsx +9 -2
  46. package/src/utils/index.js +1 -0
  47. package/src/utils/useDetectOutsideClick.js +35 -0
  48. package/src/utils/useVariants.js +44 -0
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import Spinner from './Spinner';
4
+ import Dots from './Dots';
5
+ import { DOTS_STYLE } from './shared';
6
+
7
+ /**
8
+ * Return <Dots/> or <Spinner/> based on `variant?.dots`.
9
+ * Reused in all the variants of the component.
10
+ */
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ const RenderActivityIndicator = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13
+ let {
14
+ variant = {},
15
+ dotSize,
16
+ size,
17
+ color,
18
+ indicatorBackgroundColor,
19
+ thickness,
20
+ label,
21
+ isStatic
22
+ } = _ref;
23
+ const {
24
+ dots,
25
+ style
26
+ } = variant || {};
27
+ const isDots = dots === true || style === DOTS_STYLE;
28
+ return isDots ? /*#__PURE__*/_jsx(Dots, {
29
+ ref: ref,
30
+ size: dotSize,
31
+ color: color,
32
+ indicatorBackgroundColor: indicatorBackgroundColor,
33
+ label: label,
34
+ isStatic: isStatic
35
+ }) : /*#__PURE__*/_jsx(Spinner, {
36
+ ref: ref,
37
+ size: size,
38
+ color: color,
39
+ indicatorBackgroundColor: indicatorBackgroundColor,
40
+ thickness: thickness,
41
+ label: label,
42
+ isStatic: isStatic
43
+ });
44
+ });
45
+ RenderActivityIndicator.displayName = 'RenderActivityIndicator';
46
+ RenderActivityIndicator.propTypes = {
47
+ /**
48
+ * ActivityIndicator variants
49
+ * */
50
+ variant: PropTypes.shape({
51
+ dots: PropTypes.bool,
52
+ style: PropTypes.oneOf([DOTS_STYLE])
53
+ }),
54
+ /**
55
+ * Size of the dots
56
+ * */
57
+ dotSize: PropTypes.number,
58
+ /**
59
+ * Size of the spinner
60
+ * */
61
+ size: PropTypes.number,
62
+ /**
63
+ * Primary color
64
+ * */
65
+ color: PropTypes.string,
66
+ /**
67
+ * Secondary color (background) of the indicator
68
+ * */
69
+ indicatorBackgroundColor: PropTypes.string,
70
+ /**
71
+ * Thickness of the indicator
72
+ * */
73
+ thickness: PropTypes.number,
74
+ /**
75
+ * Label for the ActivityIndicator
76
+ * */
77
+ label: PropTypes.string,
78
+ /**
79
+ * if true, there's no animation for ActivityIndicator
80
+ * */
81
+ isStatic: PropTypes.bool
82
+ };
83
+ export default RenderActivityIndicator;
@@ -1,45 +1,83 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import Platform from "react-native-web/dist/exports/Platform";
4
+ import { useScrollBlocking } from '../utils';
3
5
  import { useThemeTokens } from '../ThemeProvider';
4
6
  import { getTokensPropType, variantProp } from '../utils/props';
5
- import Spinner from './Spinner';
6
- import Dots from './Dots';
7
+ import FullScreenIndicator from './FullScreenIndicator';
8
+ import OverlayIndicator from './OverlayIndicator';
9
+ import InlineIndicator from './InlineIndicator';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ const labelMapping = {
12
+ top: 'column-reverse',
13
+ bottom: 'column',
14
+ left: 'row-reverse',
15
+ right: 'row'
16
+ };
7
17
 
8
18
  /**
9
19
  * `ActivityIndicator` renders a visual loading state.
10
20
  * It does not handle positioning or layout of that visual loader.
11
21
  */
12
- import { jsx as _jsx } from "react/jsx-runtime";
13
22
  const ActivityIndicator = /*#__PURE__*/React.forwardRef((_ref, ref) => {
14
23
  let {
15
24
  variant,
16
25
  tokens,
17
26
  label,
18
- isStatic = false
27
+ labelPosition = 'bottom',
28
+ showLabel = true,
29
+ inline = false,
30
+ fullScreen = false,
31
+ overlay = false,
32
+ isStatic = false,
33
+ children,
34
+ fullScreenBackgroundColor
19
35
  } = _ref;
20
36
  const {
21
37
  size,
22
38
  dotSize,
23
39
  color,
24
40
  indicatorBackgroundColor,
41
+ fullScreenOverlayBackground,
25
42
  thickness
26
43
  } = useThemeTokens('ActivityIndicator', tokens, variant);
27
- return variant?.dots ? /*#__PURE__*/_jsx(Dots, {
28
- ref: ref,
29
- size: dotSize,
30
- color: color,
31
- indicatorBackgroundColor: indicatorBackgroundColor,
32
- label: label,
33
- isStatic: isStatic
34
- }) : /*#__PURE__*/_jsx(Spinner, {
35
- ref: ref,
36
- size: size,
37
- color: color,
38
- indicatorBackgroundColor: indicatorBackgroundColor,
39
- thickness: thickness,
40
- label: label,
41
- isStatic: isStatic
42
- });
44
+ useScrollBlocking([Platform.OS === 'web' && fullScreen]);
45
+ const variantProps = {
46
+ variant,
47
+ dotSize,
48
+ size,
49
+ color,
50
+ indicatorBackgroundColor,
51
+ thickness,
52
+ isStatic
53
+ };
54
+ const visibleLabel = showLabel && !!label;
55
+ const commonProps = {
56
+ ref,
57
+ variantProps,
58
+ label,
59
+ labelPosition,
60
+ labelMapping,
61
+ showLabel: visibleLabel
62
+ };
63
+ switch (true) {
64
+ case fullScreen:
65
+ return /*#__PURE__*/_jsx(FullScreenIndicator, {
66
+ ...commonProps,
67
+ backgroundColor: fullScreenBackgroundColor || fullScreenOverlayBackground
68
+ });
69
+ case overlay:
70
+ return /*#__PURE__*/_jsx(OverlayIndicator, {
71
+ ...commonProps,
72
+ inline: inline,
73
+ isActive: overlay,
74
+ children: children
75
+ });
76
+ default:
77
+ return /*#__PURE__*/_jsx(InlineIndicator, {
78
+ ...commonProps
79
+ });
80
+ }
43
81
  });
44
82
  ActivityIndicator.displayName = 'ActivityIndicator';
45
83
  ActivityIndicator.propTypes = {
@@ -48,10 +86,38 @@ ActivityIndicator.propTypes = {
48
86
  /**
49
87
  * A visually hidden accessible label describing the action taking place
50
88
  */
51
- label: PropTypes.string.isRequired,
89
+ label: PropTypes.string,
90
+ /**
91
+ * If true, it should render a static ActivityIndicator
92
+ */
93
+ isStatic: PropTypes.bool,
94
+ /**
95
+ * Position of the label relative to ActivityIndicator
96
+ */
97
+ labelPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']),
98
+ /**
99
+ * show or hide the label
100
+ */
101
+ showLabel: PropTypes.bool,
102
+ /**
103
+ * If true, the ActivityIndicator will be displayed in full screen mode
104
+ */
105
+ fullScreen: PropTypes.bool,
106
+ /**
107
+ * If true, the ActivityIndicator will be displayed inline
108
+ */
109
+ inline: PropTypes.bool,
110
+ /**
111
+ * If true, the ActivityIndicator will be displayed in overlay mode
112
+ */
113
+ overlay: PropTypes.bool,
114
+ /**
115
+ * Children to be rendered inside ActivityIndicator
116
+ */
117
+ children: PropTypes.node,
52
118
  /**
53
- * If true, it should render a static spinner
119
+ * Background color of the full screen overlay
54
120
  */
55
- isStatic: PropTypes.bool
121
+ fullScreenBackgroundColor: PropTypes.string
56
122
  };
57
123
  export default ActivityIndicator;
@@ -50,6 +50,17 @@ export const DOT_FADEOUT_OUTPUT_RANGE = [1, 1, 0, 0];
50
50
  export const DOT1_ANIMATION_INPUT_RANGE = [0, 0.1, 0.2, 1];
51
51
  export const DOT2_ANIMATION_INPUT_RANGE = [0, 0.3, 0.4, 0.5, 1];
52
52
  export const DOT3_ANIMATION_INPUT_RANGE = [0, 0.6, 0.7, 0.8, 1];
53
+
54
+ // Backdrop
55
+ export const BACKDROP_OPACITY = 0.06;
56
+ export const BACKDROP_Z_INDEX = 1400;
57
+
58
+ // Space
59
+ export const SPACE_WITH_LABEL = 3;
60
+ export const SPACE_WITHOUT_LABEL = 0;
61
+
62
+ // Style
63
+ export const DOTS_STYLE = 'dots';
53
64
  export const propTypes = {
54
65
  color: PropTypes.string.isRequired,
55
66
  baseColor: PropTypes.string,
@@ -0,0 +1,61 @@
1
+ import PropTypes from 'prop-types';
2
+
3
+ /**
4
+ * PropTypes for the 3 variants of the ActivityIndicator:
5
+ * - InlineIndicator
6
+ * - OverlayIndicator
7
+ * - FullScreenIndicator
8
+ */
9
+ export const activityIndicatorVariantProps = PropTypes.shape({
10
+ /**
11
+ * Indicates the variant style for the ActivityIndicator
12
+ * */
13
+ variant: PropTypes.object,
14
+ /**
15
+ * Size of the dots
16
+ * */
17
+ dotSize: PropTypes.number,
18
+ /**
19
+ * Size of the ActivityIndicator Spinner
20
+ * */
21
+ size: PropTypes.number,
22
+ /**
23
+ * Primary color (Spinner)
24
+ * */
25
+ color: PropTypes.string,
26
+ /**
27
+ * Secondary color (background) of the indicator
28
+ * */
29
+ indicatorBackgroundColor: PropTypes.string,
30
+ /**
31
+ * Thickness of the ActivityIndicator Spinner
32
+ * */
33
+ thickness: PropTypes.number,
34
+ /**
35
+ * Indicates if the ActivityIndicator is static
36
+ * (not animated)
37
+ * */
38
+ isStatic: PropTypes.bool
39
+ }).isRequired;
40
+ export const activityIndicatorCommonProps = {
41
+ /**
42
+ * Props used by RenderActivityIndicator
43
+ * */
44
+ variantProps: activityIndicatorVariantProps,
45
+ /**
46
+ * Shows or hide the label
47
+ * */
48
+ showLabel: PropTypes.bool,
49
+ /**
50
+ * A visually hidden accessible label describing the action taking place
51
+ */
52
+ label: PropTypes.string,
53
+ /**
54
+ * Position of the label relative to ActivityIndicator
55
+ */
56
+ labelPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']),
57
+ /**
58
+ * Map of flex-direction position for label
59
+ * */
60
+ labelMapping: PropTypes.object.isRequired
61
+ };
@@ -126,6 +126,7 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
126
126
  const selected = interactiveCard?.variant?.selected;
127
127
  const inactive = interactiveCard?.variant?.inactive;
128
128
  const selectionType = interactiveCard?.selectionType;
129
+ const isControl = interactiveCard?.variant?.isControl === true;
129
130
  const getThemeTokens = useThemeTokensCallback('Card', interactiveCard?.tokens, {
130
131
  interactive: true,
131
132
  ...(interactiveCard?.variant || {})
@@ -193,6 +194,9 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
193
194
  cardStyles = themeTokens;
194
195
  }
195
196
  const renderInputPerSelectionType = props => {
197
+ if (!isControl) {
198
+ return null;
199
+ }
196
200
  switch (selectionType) {
197
201
  case SelectionType.Checkbox:
198
202
  return /*#__PURE__*/_jsx(View, {
@@ -220,17 +224,8 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
220
224
  return null;
221
225
  }
222
226
  };
223
- const renderNoSelectionView = () => /*#__PURE__*/_jsx(View, {
224
- style: {
225
- paddingTop,
226
- paddingBottom,
227
- paddingLeft,
228
- paddingRight
229
- },
230
- children: children
231
- });
232
227
  return /*#__PURE__*/_jsx(_Fragment, {
233
- children: /*#__PURE__*/_jsx(CardBase, {
228
+ children: /*#__PURE__*/_jsxs(CardBase, {
234
229
  ref: ref,
235
230
  tokens: interactiveCard?.body ? restOfTokens : cardStyles,
236
231
  backgroundImage: backgroundImage,
@@ -238,41 +233,39 @@ const Card = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
238
233
  media: mediaIds
239
234
  },
240
235
  ...selectProps(rest),
241
- children: interactiveCard?.body ? /*#__PURE__*/_jsxs(_Fragment, {
242
- children: [/*#__PURE__*/_jsx(PressableCardBase, {
243
- ref: ref,
244
- tokens: getThemeTokens,
245
- dataSet: dataSet,
246
- onPress: onPress,
247
- href: interactiveCard?.href,
248
- hrefAttrs: interactiveCard?.hrefAttrs,
249
- ...selectProps(rest),
250
- children: cardState => {
251
- const {
252
- iconColor: checkColor,
253
- inputBackgroundColor: boxBackgroundColor,
254
- iconBackgroundColor: checkBackgroundColor
255
- } = getThemeTokens({
256
- ...cardState,
257
- selected,
258
- interactive: true,
259
- isControl: true
260
- }, interactiveCard?.tokens);
261
- return /*#__PURE__*/_jsxs(_Fragment, {
262
- children: [renderInputPerSelectionType(getInputProps({
263
- id,
264
- checkColor,
265
- boxBackgroundColor,
266
- checkBackgroundColor,
267
- isControlled: true,
268
- isChecked: selected || cardState?.hover,
269
- isInactive: inactive,
270
- onPress
271
- })), typeof interactiveCard?.body === 'function' ? interactiveCard.body(cardState) : interactiveCard.body]
272
- });
273
- }
274
- }), children && selectionType !== SelectionType.None ? renderNoSelectionView() : null]
275
- }) : children
236
+ children: [interactiveCard?.body && /*#__PURE__*/_jsx(PressableCardBase, {
237
+ ref: ref,
238
+ tokens: getThemeTokens,
239
+ dataSet: dataSet,
240
+ onPress: onPress,
241
+ href: interactiveCard?.href,
242
+ hrefAttrs: interactiveCard?.hrefAttrs,
243
+ ...selectProps(rest),
244
+ children: cardState => {
245
+ const {
246
+ iconColor: checkColor,
247
+ inputBackgroundColor: boxBackgroundColor,
248
+ iconBackgroundColor: checkBackgroundColor
249
+ } = getThemeTokens({
250
+ ...cardState,
251
+ selected,
252
+ interactive: true,
253
+ isControl
254
+ }, interactiveCard?.tokens);
255
+ return /*#__PURE__*/_jsxs(_Fragment, {
256
+ children: [renderInputPerSelectionType(getInputProps({
257
+ id,
258
+ checkColor,
259
+ boxBackgroundColor,
260
+ checkBackgroundColor,
261
+ isControlled: true,
262
+ isChecked: selected || cardState?.hover,
263
+ isInactive: inactive,
264
+ onPress
265
+ })), typeof interactiveCard?.body === 'function' ? interactiveCard.body(cardState) : interactiveCard.body]
266
+ });
267
+ }
268
+ }), children]
276
269
  })
277
270
  });
278
271
  });
@@ -38,7 +38,7 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
38
38
  const isFocusVisible = Platform.OS === 'web' ? focus && !pressed && !hover : expanded;
39
39
  const linkTokens = useThemeTokens('Link', {}, {
40
40
  ...variant,
41
- quiet: expanded ?? quiet
41
+ quiet
42
42
  }, {
43
43
  focus: isFocusVisible,
44
44
  hover,
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
5
5
  import Icon from '../Icon';
6
+ import { useVariants } from '../utils';
6
7
  import { jsx as _jsx } from "react/jsx-runtime";
7
8
  export const tokenTypes = {
8
9
  itemIconSize: PropTypes.number.isRequired,
@@ -53,6 +54,7 @@ const selectBulletContainerStyles = _ref4 => {
53
54
  alignItems: itemBulletContainerAlign
54
55
  };
55
56
  };
57
+ const getIconColorVariants = iconVariants => iconVariants?.filter(variant => variant[0] === 'color').map(variant => variant[1]);
56
58
 
57
59
  /**
58
60
  * Subcomponent used within ListItem and similar components for rendering bullets or icons
@@ -71,6 +73,10 @@ const ListItemMark = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
71
73
  const themeTokens = typeof tokens === 'function' ? tokens() : tokens;
72
74
  const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens);
73
75
  const bulletContainerStyles = selectBulletContainerStyles(themeTokens);
76
+
77
+ // TODO: Remove it when iconColor custom colors are deprecated.
78
+ const iconVariants = useVariants('Icon');
79
+ const iconColorVariants = getIconColorVariants(iconVariants);
74
80
  if (icon) {
75
81
  const iconTokens = selectItemIconTokens(themeTokens);
76
82
  return /*#__PURE__*/_jsx(View, {
@@ -78,10 +84,15 @@ const ListItemMark = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
78
84
  children: /*#__PURE__*/_jsx(Icon, {
79
85
  icon: icon,
80
86
  tokens: {
81
- size: iconSize ?? iconTokens.size
87
+ size: iconSize ?? iconTokens.size,
88
+ ...((iconColor && !iconColorVariants?.includes(iconColor) || !iconColor) && {
89
+ color: iconColor && !iconColorVariants?.includes(iconColor) ? iconColor : iconTokens.color
90
+ })
82
91
  },
83
92
  variant: {
84
- color: iconColor ?? iconTokens.color
93
+ ...(iconColorVariants?.includes(iconColor) && {
94
+ color: iconColor
95
+ })
85
96
  }
86
97
  })
87
98
  });
@@ -10,6 +10,7 @@ import { useThemeTokens } from '../ThemeProvider';
10
10
  import dictionary from './dictionary';
11
11
  import Card from '../Card';
12
12
  import IconButton from '../IconButton';
13
+ import useDetectOutsideClick from '../utils/useDetectOutsideClick';
13
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
15
  const staticStyles = StyleSheet.create({
15
16
  positioner: {
@@ -89,13 +90,16 @@ const ModalOverlay = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
89
90
  tokens,
90
91
  copy,
91
92
  onClose,
92
- enableFullscreen = false
93
+ enableFullscreen = false,
94
+ dismissWhenPressedOutside = false
93
95
  } = _ref2;
94
96
  const viewport = useViewport();
95
97
  const themeTokens = useThemeTokens('Modal', tokens, variant, {
96
98
  viewport,
97
99
  maxWidth: false
98
100
  });
101
+ const containerRef = React.useRef(ref || null);
102
+ useDetectOutsideClick(containerRef, onClose, dismissWhenPressedOutside);
99
103
  const containerWidthHeight = {
100
104
  minWidth: tokens.maxWidth ? maxWidthSize : minWidth,
101
105
  minHeight: maxHeight ? maxHeightSize : minHeight,
@@ -112,7 +116,7 @@ const ModalOverlay = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
112
116
  const closeLabel = getCopy('closeButton');
113
117
  return /*#__PURE__*/_jsx(Portal, {
114
118
  children: /*#__PURE__*/_jsx(View, {
115
- ref: ref,
119
+ ref: containerRef,
116
120
  onLayout: onLayout,
117
121
  style: [overlaidPosition, containerWidthHeight, staticStyles.positioner, !isReady && staticStyles.hidden, selectContainerStyle(enableFullscreen, themeTokens)],
118
122
  children: /*#__PURE__*/_jsxs(Card, {
@@ -150,6 +154,11 @@ ModalOverlay.propTypes = {
150
154
  tokens: getTokensPropType('Modal'),
151
155
  copy: copyPropTypes,
152
156
  onClose: PropTypes.func,
153
- enableFullscreen: PropTypes.bool
157
+ enableFullscreen: PropTypes.bool,
158
+ /**
159
+ * If true, clicking outside the content will trigger the a close callback, dismissing the content.
160
+ * @deprecated This parameter will be removed in the next major release; detection will be always enabled by default.
161
+ */
162
+ dismissWhenPressedOutside: PropTypes.bool
154
163
  };
155
164
  export default ModalOverlay;
@@ -85,6 +85,7 @@ const MultiSelectFilter = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
85
85
  inactive = false,
86
86
  rowLimit = 12,
87
87
  dictionary = defaultDictionary,
88
+ dismissWhenPressedOutside = false,
88
89
  ...rest
89
90
  } = _ref3;
90
91
  const viewport = useViewport();
@@ -396,8 +397,9 @@ const MultiSelectFilter = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
396
397
  })]
397
398
  })
398
399
  }), isOpen && viewport !== 'xs' && /*#__PURE__*/_jsxs(ModalOverlay, {
399
- overlaidPosition: overlaidPosition,
400
+ dismissWhenPressedOutside: dismissWhenPressedOutside,
400
401
  onClose: onClose,
402
+ overlaidPosition: overlaidPosition,
401
403
  maxHeight: items.length > MAX_ITEMS_THRESHOLD ? true : maxHeight,
402
404
  maxHeightSize: maxHeightSize,
403
405
  maxWidthSize: maxWidthSize,
@@ -541,6 +543,11 @@ MultiSelectFilter.propTypes = {
541
543
  * Sets the maximum number of items in one column. If number of items are more
542
544
  * than the `rowLimit`, they will be rendered in 2 columns.
543
545
  */
544
- rowLimit: PropTypes.number
546
+ rowLimit: PropTypes.number,
547
+ /**
548
+ * If true, clicking outside the content will trigger the a close callback, dismissing the content.
549
+ * @deprecated This parameter will be removed in the next major release; detection will be always enabled by default.
550
+ */
551
+ dismissWhenPressedOutside: PropTypes.bool
545
552
  };
546
553
  export default MultiSelectFilter;
@@ -23,4 +23,5 @@ export { default as htmlAttrs } from './htmlAttrs';
23
23
  export { transformGradient } from './transformGradient';
24
24
  export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte';
25
25
  export { default as formatImageSource } from './formatImageSource';
26
- export { default as getSpacingScale } from './getSpacingScale';
26
+ export { default as getSpacingScale } from './getSpacingScale';
27
+ export { default as useVariants } from './useVariants';
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import Platform from "react-native-web/dist/exports/Platform";
3
+ /**
4
+ * Hook to detect clicks outside of a ref, only on web.
5
+ *
6
+ * @param {React.RefObject<HTMLElement>} ref
7
+ * Reference to the element you want to “protect.”
8
+ * @param {() => void} onOutside
9
+ * Callback invoked when a click occurs outside that ref.
10
+ * @param {boolean} [enabled=true]
11
+ * Flag to enable or disable the outside-click detection at runtime.
12
+ * @deprecated Will be removed in next major release; detection will always be enabled.
13
+ */
14
+ function useDetectOutsideClick(ref, onOutside) {
15
+ let enabled = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
16
+ React.useEffect(() => {
17
+ if (!enabled || Platform.OS !== 'web') {
18
+ return undefined;
19
+ }
20
+ const handleClickOutside = e => {
21
+ if (ref.current && !ref.current.contains(e.target)) {
22
+ onOutside();
23
+ }
24
+ };
25
+ document.addEventListener('mousedown', handleClickOutside);
26
+ return () => {
27
+ document.removeEventListener('mousedown', handleClickOutside);
28
+ };
29
+ }, [ref, onOutside, enabled]);
30
+ }
31
+ export default useDetectOutsideClick;
@@ -0,0 +1,41 @@
1
+ import { getComponentTheme, useTheme } from '../ThemeProvider';
2
+
3
+ /**
4
+ * Generates a label string for a variant based on the provided key and value.
5
+ *
6
+ * @param {string} key - The name of the variant.
7
+ * @param {*} value - The value of the variant. If it's a string, it will be appended to the key.
8
+ * @returns {string} The formatted variant label (e.g., "color: red" or "size").
9
+ */
10
+ const getVariantLabel = (key, value) => `${key}${typeof value === 'string' ? `: ${value}` : ''}`;
11
+
12
+ /**
13
+ * Retrieves the variant options for a given component from the theme.
14
+ *
15
+ * @param {string} componentName - The name of the component to get variants for.
16
+ * @returns {Array<Array>} An array of variant tuples. Each tuple contains:
17
+ * - {string|undefined} The variant key (e.g., 'size', 'color', or undefined for default).
18
+ * - {string|undefined} The variant value (e.g., 'small', 'primary', or undefined for default).
19
+ * - {string} The human-readable label for the variant.
20
+ * Returns [['default', {}]] if no componentName is provided.
21
+ * @throws {Error} If the theme does not define appearances for the given component.
22
+ */
23
+ const useVariants = componentName => {
24
+ const theme = useTheme();
25
+ if (!componentName) return [['default', {}]];
26
+ const {
27
+ appearances
28
+ } = getComponentTheme(theme, componentName);
29
+ if (!appearances) {
30
+ throw new Error(`Theme ${theme.metadata?.name} does not have any appearances set for ${componentName}`);
31
+ }
32
+ const variants = Object.entries(appearances).reduce((pairs, _ref) => {
33
+ let [key, {
34
+ values,
35
+ type
36
+ } = {}] = _ref;
37
+ return type === 'variant' ? [...pairs, ...values.map(value => [key, value, getVariantLabel(key, value)])] : pairs;
38
+ }, [[undefined, undefined, 'default style']]);
39
+ return variants;
40
+ };
41
+ export default useVariants;
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.5.0",
15
+ "@telus-uds/system-theme-tokens": "^4.6.0",
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.7.1",
87
+ "version": "3.8.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.5.0",
15
+ "@telus-uds/system-theme-tokens": "^4.6.0",
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.7.1",
87
+ "version": "3.8.0",
88
88
  "types": "types/index.d.ts"
89
89
  }