@telus-uds/components-base 3.7.0 → 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.
- package/CHANGELOG.md +27 -2
- package/lib/cjs/ActivityIndicator/FullScreenIndicator.js +89 -0
- package/lib/cjs/ActivityIndicator/InlineIndicator.js +64 -0
- package/lib/cjs/ActivityIndicator/OverlayIndicator.js +156 -0
- package/lib/cjs/ActivityIndicator/RenderActivityIndicator.js +88 -0
- package/lib/cjs/ActivityIndicator/index.js +91 -23
- package/lib/cjs/ActivityIndicator/shared.js +12 -1
- package/lib/cjs/ActivityIndicator/sharedProptypes.js +67 -0
- package/lib/cjs/Card/Card.js +38 -45
- package/lib/cjs/Card/PressableCardBase.js +4 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +1 -1
- package/lib/cjs/List/ListItemMark.js +13 -2
- package/lib/cjs/MultiSelectFilter/ModalOverlay.js +12 -3
- package/lib/cjs/MultiSelectFilter/MultiSelectFilter.js +9 -2
- package/lib/cjs/utils/index.js +9 -1
- package/lib/cjs/utils/useDetectOutsideClick.js +39 -0
- package/lib/cjs/utils/useVariants.js +46 -0
- package/lib/esm/ActivityIndicator/FullScreenIndicator.js +82 -0
- package/lib/esm/ActivityIndicator/InlineIndicator.js +57 -0
- package/lib/esm/ActivityIndicator/OverlayIndicator.js +149 -0
- package/lib/esm/ActivityIndicator/RenderActivityIndicator.js +83 -0
- package/lib/esm/ActivityIndicator/index.js +89 -23
- package/lib/esm/ActivityIndicator/shared.js +11 -0
- package/lib/esm/ActivityIndicator/sharedProptypes.js +61 -0
- package/lib/esm/Card/Card.js +38 -45
- package/lib/esm/Card/PressableCardBase.js +4 -1
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +1 -1
- package/lib/esm/List/ListItemMark.js +13 -2
- package/lib/esm/MultiSelectFilter/ModalOverlay.js +12 -3
- package/lib/esm/MultiSelectFilter/MultiSelectFilter.js +9 -2
- package/lib/esm/utils/index.js +2 -1
- package/lib/esm/utils/useDetectOutsideClick.js +31 -0
- package/lib/esm/utils/useVariants.js +41 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/ActivityIndicator/FullScreenIndicator.jsx +65 -0
- package/src/ActivityIndicator/InlineIndicator.jsx +47 -0
- package/src/ActivityIndicator/OverlayIndicator.jsx +140 -0
- package/src/ActivityIndicator/RenderActivityIndicator.jsx +82 -0
- package/src/ActivityIndicator/index.jsx +113 -32
- package/src/ActivityIndicator/shared.js +11 -0
- package/src/ActivityIndicator/sharedProptypes.js +62 -0
- package/src/Card/Card.jsx +51 -54
- package/src/Card/PressableCardBase.jsx +1 -1
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +1 -1
- package/src/List/ListItemMark.jsx +18 -2
- package/src/MultiSelectFilter/ModalOverlay.jsx +15 -3
- package/src/MultiSelectFilter/MultiSelectFilter.jsx +9 -2
- package/src/utils/index.js +1 -0
- package/src/utils/useDetectOutsideClick.js +35 -0
- package/src/utils/useVariants.js +44 -0
package/src/Card/Card.jsx
CHANGED
|
@@ -128,6 +128,7 @@ const Card = React.forwardRef(
|
|
|
128
128
|
const selected = interactiveCard?.variant?.selected
|
|
129
129
|
const inactive = interactiveCard?.variant?.inactive
|
|
130
130
|
const selectionType = interactiveCard?.selectionType
|
|
131
|
+
const isControl = interactiveCard?.variant?.isControl === true
|
|
131
132
|
|
|
132
133
|
const getThemeTokens = useThemeTokensCallback('Card', interactiveCard?.tokens, {
|
|
133
134
|
interactive: true,
|
|
@@ -196,6 +197,10 @@ const Card = React.forwardRef(
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
const renderInputPerSelectionType = (props) => {
|
|
200
|
+
if (!isControl) {
|
|
201
|
+
return null
|
|
202
|
+
}
|
|
203
|
+
|
|
199
204
|
switch (selectionType) {
|
|
200
205
|
case SelectionType.Checkbox:
|
|
201
206
|
return (
|
|
@@ -214,10 +219,6 @@ const Card = React.forwardRef(
|
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
221
|
|
|
217
|
-
const renderNoSelectionView = () => (
|
|
218
|
-
<View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
|
|
219
|
-
)
|
|
220
|
-
|
|
221
222
|
return (
|
|
222
223
|
<>
|
|
223
224
|
<CardBase
|
|
@@ -227,57 +228,53 @@ const Card = React.forwardRef(
|
|
|
227
228
|
dataSet={mediaIds && { media: mediaIds }}
|
|
228
229
|
{...selectProps(rest)}
|
|
229
230
|
>
|
|
230
|
-
{interactiveCard?.body
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
{
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
</PressableCardBase>
|
|
276
|
-
{children && selectionType !== SelectionType.None ? renderNoSelectionView() : null}
|
|
277
|
-
</>
|
|
278
|
-
) : (
|
|
279
|
-
children
|
|
231
|
+
{interactiveCard?.body && (
|
|
232
|
+
<PressableCardBase
|
|
233
|
+
ref={ref}
|
|
234
|
+
tokens={getThemeTokens}
|
|
235
|
+
dataSet={dataSet}
|
|
236
|
+
onPress={onPress}
|
|
237
|
+
href={interactiveCard?.href}
|
|
238
|
+
hrefAttrs={interactiveCard?.hrefAttrs}
|
|
239
|
+
{...selectProps(rest)}
|
|
240
|
+
>
|
|
241
|
+
{(cardState) => {
|
|
242
|
+
const {
|
|
243
|
+
iconColor: checkColor,
|
|
244
|
+
inputBackgroundColor: boxBackgroundColor,
|
|
245
|
+
iconBackgroundColor: checkBackgroundColor
|
|
246
|
+
} = getThemeTokens(
|
|
247
|
+
{
|
|
248
|
+
...cardState,
|
|
249
|
+
selected,
|
|
250
|
+
interactive: true,
|
|
251
|
+
isControl
|
|
252
|
+
},
|
|
253
|
+
interactiveCard?.tokens
|
|
254
|
+
)
|
|
255
|
+
return (
|
|
256
|
+
<>
|
|
257
|
+
{renderInputPerSelectionType(
|
|
258
|
+
getInputProps({
|
|
259
|
+
id,
|
|
260
|
+
checkColor,
|
|
261
|
+
boxBackgroundColor,
|
|
262
|
+
checkBackgroundColor,
|
|
263
|
+
isControlled: true,
|
|
264
|
+
isChecked: selected || cardState?.hover,
|
|
265
|
+
isInactive: inactive,
|
|
266
|
+
onPress
|
|
267
|
+
})
|
|
268
|
+
)}
|
|
269
|
+
{typeof interactiveCard?.body === 'function'
|
|
270
|
+
? interactiveCard.body(cardState)
|
|
271
|
+
: interactiveCard.body}
|
|
272
|
+
</>
|
|
273
|
+
)
|
|
274
|
+
}}
|
|
275
|
+
</PressableCardBase>
|
|
280
276
|
)}
|
|
277
|
+
{children}
|
|
281
278
|
</CardBase>
|
|
282
279
|
</>
|
|
283
280
|
)
|
|
@@ -158,7 +158,7 @@ const PressableCardBase = React.forwardRef(
|
|
|
158
158
|
setFocused(false)
|
|
159
159
|
setPressed(false)
|
|
160
160
|
}}
|
|
161
|
-
style={staticStyles.container}
|
|
161
|
+
style={{ ...staticStyles.container, textDecoration: 'none' }}
|
|
162
162
|
{...(hrefAttrs || {})}
|
|
163
163
|
>
|
|
164
164
|
<CardBase tokens={getCardTokens({ pressed, focused, hovered })}>
|
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
|
|
3
3
|
|
|
4
4
|
import { View, StyleSheet } from 'react-native'
|
|
5
5
|
import Icon from '../Icon'
|
|
6
|
+
import { useVariants } from '../utils'
|
|
6
7
|
|
|
7
8
|
export const tokenTypes = {
|
|
8
9
|
itemIconSize: PropTypes.number.isRequired,
|
|
@@ -37,6 +38,9 @@ const selectBulletContainerStyles = ({
|
|
|
37
38
|
alignItems: itemBulletContainerAlign
|
|
38
39
|
})
|
|
39
40
|
|
|
41
|
+
const getIconColorVariants = (iconVariants) =>
|
|
42
|
+
iconVariants?.filter((variant) => variant[0] === 'color').map((variant) => variant[1])
|
|
43
|
+
|
|
40
44
|
/**
|
|
41
45
|
* Subcomponent used within ListItem and similar components for rendering bullets or icons
|
|
42
46
|
* that sit alongside a ListIconContent in a { flexDirection: row } container.
|
|
@@ -50,14 +54,26 @@ const ListItemMark = React.forwardRef(({ icon, iconColor, iconSize, tokens = {}
|
|
|
50
54
|
const sideItemContainerStyles = selectSideItemContainerStyles(themeTokens)
|
|
51
55
|
const bulletContainerStyles = selectBulletContainerStyles(themeTokens)
|
|
52
56
|
|
|
57
|
+
// TODO: Remove it when iconColor custom colors are deprecated.
|
|
58
|
+
const iconVariants = useVariants('Icon')
|
|
59
|
+
const iconColorVariants = getIconColorVariants(iconVariants)
|
|
60
|
+
|
|
53
61
|
if (icon) {
|
|
54
62
|
const iconTokens = selectItemIconTokens(themeTokens)
|
|
55
63
|
return (
|
|
56
64
|
<View style={[sideItemContainerStyles, bulletContainerStyles]}>
|
|
57
65
|
<Icon
|
|
58
66
|
icon={icon}
|
|
59
|
-
tokens={{
|
|
60
|
-
|
|
67
|
+
tokens={{
|
|
68
|
+
size: iconSize ?? iconTokens.size,
|
|
69
|
+
...(((iconColor && !iconColorVariants?.includes(iconColor)) || !iconColor) && {
|
|
70
|
+
color:
|
|
71
|
+
iconColor && !iconColorVariants?.includes(iconColor) ? iconColor : iconTokens.color
|
|
72
|
+
})
|
|
73
|
+
}}
|
|
74
|
+
variant={{
|
|
75
|
+
...(iconColorVariants?.includes(iconColor) && { color: iconColor })
|
|
76
|
+
}}
|
|
61
77
|
/>
|
|
62
78
|
</View>
|
|
63
79
|
)
|
|
@@ -9,6 +9,7 @@ import dictionary from './dictionary'
|
|
|
9
9
|
|
|
10
10
|
import Card from '../Card'
|
|
11
11
|
import IconButton from '../IconButton'
|
|
12
|
+
import useDetectOutsideClick from '../utils/useDetectOutsideClick'
|
|
12
13
|
|
|
13
14
|
const staticStyles = StyleSheet.create({
|
|
14
15
|
positioner: {
|
|
@@ -80,12 +81,17 @@ const ModalOverlay = React.forwardRef(
|
|
|
80
81
|
tokens,
|
|
81
82
|
copy,
|
|
82
83
|
onClose,
|
|
83
|
-
enableFullscreen = false
|
|
84
|
+
enableFullscreen = false,
|
|
85
|
+
dismissWhenPressedOutside = false
|
|
84
86
|
},
|
|
85
87
|
ref
|
|
86
88
|
) => {
|
|
87
89
|
const viewport = useViewport()
|
|
88
90
|
const themeTokens = useThemeTokens('Modal', tokens, variant, { viewport, maxWidth: false })
|
|
91
|
+
|
|
92
|
+
const containerRef = React.useRef(ref || null)
|
|
93
|
+
useDetectOutsideClick(containerRef, onClose, dismissWhenPressedOutside)
|
|
94
|
+
|
|
89
95
|
const containerWidthHeight = {
|
|
90
96
|
minWidth: tokens.maxWidth ? maxWidthSize : minWidth,
|
|
91
97
|
minHeight: maxHeight ? maxHeightSize : minHeight,
|
|
@@ -101,7 +107,7 @@ const ModalOverlay = React.forwardRef(
|
|
|
101
107
|
return (
|
|
102
108
|
<Portal>
|
|
103
109
|
<View
|
|
104
|
-
ref={
|
|
110
|
+
ref={containerRef}
|
|
105
111
|
onLayout={onLayout}
|
|
106
112
|
style={[
|
|
107
113
|
overlaidPosition,
|
|
@@ -132,6 +138,7 @@ const ModalOverlay = React.forwardRef(
|
|
|
132
138
|
)
|
|
133
139
|
}
|
|
134
140
|
)
|
|
141
|
+
|
|
135
142
|
ModalOverlay.displayName = 'ModalOverlay'
|
|
136
143
|
|
|
137
144
|
ModalOverlay.propTypes = {
|
|
@@ -153,7 +160,12 @@ ModalOverlay.propTypes = {
|
|
|
153
160
|
tokens: getTokensPropType('Modal'),
|
|
154
161
|
copy: copyPropTypes,
|
|
155
162
|
onClose: PropTypes.func,
|
|
156
|
-
enableFullscreen: PropTypes.bool
|
|
163
|
+
enableFullscreen: PropTypes.bool,
|
|
164
|
+
/**
|
|
165
|
+
* If true, clicking outside the content will trigger the a close callback, dismissing the content.
|
|
166
|
+
* @deprecated This parameter will be removed in the next major release; detection will be always enabled by default.
|
|
167
|
+
*/
|
|
168
|
+
dismissWhenPressedOutside: PropTypes.bool
|
|
157
169
|
}
|
|
158
170
|
|
|
159
171
|
export default ModalOverlay
|
|
@@ -89,6 +89,7 @@ const MultiSelectFilter = React.forwardRef(
|
|
|
89
89
|
inactive = false,
|
|
90
90
|
rowLimit = 12,
|
|
91
91
|
dictionary = defaultDictionary,
|
|
92
|
+
dismissWhenPressedOutside = false,
|
|
92
93
|
...rest
|
|
93
94
|
},
|
|
94
95
|
ref
|
|
@@ -386,8 +387,9 @@ const MultiSelectFilter = React.forwardRef(
|
|
|
386
387
|
)}
|
|
387
388
|
{isOpen && viewport !== 'xs' && (
|
|
388
389
|
<ModalOverlay
|
|
389
|
-
|
|
390
|
+
dismissWhenPressedOutside={dismissWhenPressedOutside}
|
|
390
391
|
onClose={onClose}
|
|
392
|
+
overlaidPosition={overlaidPosition}
|
|
391
393
|
maxHeight={items.length > MAX_ITEMS_THRESHOLD ? true : maxHeight}
|
|
392
394
|
maxHeightSize={maxHeightSize}
|
|
393
395
|
maxWidthSize={maxWidthSize}
|
|
@@ -539,7 +541,12 @@ MultiSelectFilter.propTypes = {
|
|
|
539
541
|
* Sets the maximum number of items in one column. If number of items are more
|
|
540
542
|
* than the `rowLimit`, they will be rendered in 2 columns.
|
|
541
543
|
*/
|
|
542
|
-
rowLimit: PropTypes.number
|
|
544
|
+
rowLimit: PropTypes.number,
|
|
545
|
+
/**
|
|
546
|
+
* If true, clicking outside the content will trigger the a close callback, dismissing the content.
|
|
547
|
+
* @deprecated This parameter will be removed in the next major release; detection will be always enabled by default.
|
|
548
|
+
*/
|
|
549
|
+
dismissWhenPressedOutside: PropTypes.bool
|
|
543
550
|
}
|
|
544
551
|
|
|
545
552
|
export default MultiSelectFilter
|
package/src/utils/index.js
CHANGED
|
@@ -24,3 +24,4 @@ export { transformGradient } from './transformGradient'
|
|
|
24
24
|
export { default as convertFromMegaByteToByte } from './convertFromMegaByteToByte'
|
|
25
25
|
export { default as formatImageSource } from './formatImageSource'
|
|
26
26
|
export { default as getSpacingScale } from './getSpacingScale'
|
|
27
|
+
export { default as useVariants } from './useVariants'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Platform } from 'react-native'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to detect clicks outside of a ref, only on web.
|
|
6
|
+
*
|
|
7
|
+
* @param {React.RefObject<HTMLElement>} ref
|
|
8
|
+
* Reference to the element you want to “protect.”
|
|
9
|
+
* @param {() => void} onOutside
|
|
10
|
+
* Callback invoked when a click occurs outside that ref.
|
|
11
|
+
* @param {boolean} [enabled=true]
|
|
12
|
+
* Flag to enable or disable the outside-click detection at runtime.
|
|
13
|
+
* @deprecated Will be removed in next major release; detection will always be enabled.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function useDetectOutsideClick(ref, onOutside, enabled = true) {
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (!enabled || Platform.OS !== 'web') {
|
|
19
|
+
return undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handleClickOutside = (e) => {
|
|
23
|
+
if (ref.current && !ref.current.contains(e.target)) {
|
|
24
|
+
onOutside()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
29
|
+
return () => {
|
|
30
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
|
31
|
+
}
|
|
32
|
+
}, [ref, onOutside, enabled])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default useDetectOutsideClick
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
|
|
27
|
+
const { appearances } = getComponentTheme(theme, componentName)
|
|
28
|
+
if (!appearances) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Theme ${theme.metadata?.name} does not have any appearances set for ${componentName}`
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const variants = Object.entries(appearances).reduce(
|
|
35
|
+
(pairs, [key, { values, type } = {}]) =>
|
|
36
|
+
type === 'variant'
|
|
37
|
+
? [...pairs, ...values.map((value) => [key, value, getVariantLabel(key, value)])]
|
|
38
|
+
: pairs,
|
|
39
|
+
[[undefined, undefined, 'default style']]
|
|
40
|
+
)
|
|
41
|
+
return variants
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default useVariants
|