@telus-uds/components-base 3.12.1 → 3.13.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 +29 -2
- package/lib/cjs/Button/ButtonDropdown.js +105 -12
- package/lib/cjs/Carousel/Carousel.js +26 -0
- package/lib/cjs/Carousel/CarouselContext.js +7 -4
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +13 -2
- package/lib/cjs/Carousel/Constants.js +2 -1
- package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +1 -1
- package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
- package/lib/cjs/FlexGrid/FlexGrid.js +54 -5
- package/lib/cjs/Icon/Icon.js +3 -1
- package/lib/cjs/InputLabel/InputLabel.js +1 -1
- package/lib/cjs/InputSupports/InputSupports.js +1 -1
- package/lib/cjs/Notification/Notification.js +27 -8
- package/lib/cjs/utils/props/inputSupportsProps.js +1 -1
- package/lib/esm/Button/ButtonDropdown.js +107 -14
- package/lib/esm/Carousel/Carousel.js +27 -1
- package/lib/esm/Carousel/CarouselContext.js +7 -4
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +13 -2
- package/lib/esm/Carousel/Constants.js +1 -0
- package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +2 -2
- package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
- package/lib/esm/FlexGrid/FlexGrid.js +55 -6
- package/lib/esm/Icon/Icon.js +3 -1
- package/lib/esm/InputLabel/InputLabel.js +1 -1
- package/lib/esm/InputSupports/InputSupports.js +1 -1
- package/lib/esm/Notification/Notification.js +27 -8
- package/lib/esm/utils/props/inputSupportsProps.js +1 -1
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/ButtonDropdown.jsx +109 -16
- package/src/Carousel/Carousel.jsx +30 -0
- package/src/Carousel/CarouselContext.jsx +17 -4
- package/src/Carousel/CarouselItem/CarouselItem.jsx +17 -2
- package/src/Carousel/Constants.js +1 -0
- package/src/ExpandCollapse/ExpandCollapse.jsx +5 -2
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +2 -2
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +39 -9
- package/src/FlexGrid/FlexGrid.jsx +62 -6
- package/src/Icon/Icon.jsx +3 -1
- package/src/InputLabel/InputLabel.jsx +1 -1
- package/src/InputSupports/InputSupports.jsx +1 -1
- package/src/Notification/Notification.jsx +58 -9
- package/src/utils/props/inputSupportsProps.js +1 -1
|
@@ -116,7 +116,7 @@ InputSupports.propTypes = {
|
|
|
116
116
|
* 1. `tooltip` as a string - The content of the tooltip.
|
|
117
117
|
* 2. `tooltip` as an object - Tooltip component props to be passed.
|
|
118
118
|
*/
|
|
119
|
-
tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
|
|
119
|
+
tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
|
|
120
120
|
/**
|
|
121
121
|
* Use to visually mark an input as valid or invalid.
|
|
122
122
|
*/
|
|
@@ -9,6 +9,7 @@ import useCopy from '../utils/useCopy';
|
|
|
9
9
|
import dictionary from './dictionary';
|
|
10
10
|
import { useViewport } from '../ViewportProvider';
|
|
11
11
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
const CONTENT_MAX_WIDTH = 'max';
|
|
12
13
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
13
14
|
const selectContainerStyles = tokens => ({
|
|
14
15
|
...tokens
|
|
@@ -63,13 +64,13 @@ const selectDismissButtonContainerStyles = _ref4 => {
|
|
|
63
64
|
placeContent: 'start'
|
|
64
65
|
};
|
|
65
66
|
};
|
|
66
|
-
const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport) => ({
|
|
67
|
-
maxWidth: system && viewport === 'xl' ? maxWidth : '100%',
|
|
67
|
+
const selectContentContainerStyle = (themeTokens, maxWidth, system, viewport, useContentMaxWidth) => ({
|
|
68
|
+
maxWidth: system && (useContentMaxWidth || viewport === 'xl') ? maxWidth : '100%',
|
|
68
69
|
width: '100%',
|
|
69
70
|
paddingRight: themeTokens?.containerPaddingRight,
|
|
70
71
|
paddingLeft: themeTokens?.containerPaddingLeft
|
|
71
72
|
});
|
|
72
|
-
const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, system) => {
|
|
73
|
+
const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, system, useContentMaxWidth) => {
|
|
73
74
|
const transformedSelectContainerStyles = Object.entries(themeTokens).reduce((acc, _ref5) => {
|
|
74
75
|
let [vp, viewportTokens] = _ref5;
|
|
75
76
|
acc[vp] = {
|
|
@@ -92,7 +93,7 @@ const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, d
|
|
|
92
93
|
const transformedSelectContentContainerStyles = Object.entries(themeTokens).reduce((acc, _ref6) => {
|
|
93
94
|
let [vp, viewportTokens] = _ref6;
|
|
94
95
|
acc[vp] = {
|
|
95
|
-
...selectContentContainerStyle(viewportTokens, maxWidth, system, vp),
|
|
96
|
+
...selectContentContainerStyle(viewportTokens, maxWidth, system, vp, useContentMaxWidth),
|
|
96
97
|
flexDirection: 'row',
|
|
97
98
|
flexShrink: 1,
|
|
98
99
|
justifyContent: 'space-between',
|
|
@@ -169,7 +170,7 @@ const getMediaQueryStyles = (themeTokens, themeOptions, maxWidth, mediaIdsRef, d
|
|
|
169
170
|
selectDismissIconPropsStyles
|
|
170
171
|
};
|
|
171
172
|
};
|
|
172
|
-
const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system) => ({
|
|
173
|
+
const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, viewport, system, useContentMaxWidth) => ({
|
|
173
174
|
containerStyles: {
|
|
174
175
|
container: {
|
|
175
176
|
flexDirection: 'column',
|
|
@@ -181,7 +182,7 @@ const getDefaultStyles = (themeTokens, themeOptions, maxWidth, dismissible, view
|
|
|
181
182
|
flexDirection: 'row',
|
|
182
183
|
flexShrink: 1,
|
|
183
184
|
justifyContent: 'space-between',
|
|
184
|
-
...selectContentContainerStyle(themeTokens, maxWidth, system, viewport),
|
|
185
|
+
...selectContentContainerStyle(themeTokens, maxWidth, system, viewport, useContentMaxWidth),
|
|
185
186
|
...(system && {
|
|
186
187
|
alignSelf: 'center'
|
|
187
188
|
})
|
|
@@ -280,6 +281,7 @@ const Notification = /*#__PURE__*/React.forwardRef((_ref7, ref) => {
|
|
|
280
281
|
tokens,
|
|
281
282
|
variant,
|
|
282
283
|
onDismiss,
|
|
284
|
+
contentMinWidth,
|
|
283
285
|
...rest
|
|
284
286
|
} = _ref7;
|
|
285
287
|
const [isDismissed, setIsDismissed] = React.useState(false);
|
|
@@ -302,6 +304,7 @@ const Notification = /*#__PURE__*/React.forwardRef((_ref7, ref) => {
|
|
|
302
304
|
system: isSystemEnabled,
|
|
303
305
|
viewport
|
|
304
306
|
});
|
|
307
|
+
const useContentMaxWidth = useResponsiveProp(contentMinWidth) === CONTENT_MAX_WIDTH;
|
|
305
308
|
const maxWidth = useResponsiveProp(themeOptions?.contentMaxWidth, viewports.map.get(viewports.xl));
|
|
306
309
|
const notificationComponentRef = React.useRef({
|
|
307
310
|
containerStyles: {},
|
|
@@ -324,9 +327,9 @@ const Notification = /*#__PURE__*/React.forwardRef((_ref7, ref) => {
|
|
|
324
327
|
selectDismissIconPropsIds: {}
|
|
325
328
|
});
|
|
326
329
|
if (enableMediaQueryStyleSheet) {
|
|
327
|
-
notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, isSystemEnabled);
|
|
330
|
+
notificationComponentRef.current = getMediaQueryStyles(themeTokens, themeOptions, maxWidth, mediaIdsRef, dismissible, viewport, isSystemEnabled, useContentMaxWidth);
|
|
328
331
|
} else {
|
|
329
|
-
notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible, viewport, isSystemEnabled);
|
|
332
|
+
notificationComponentRef.current = getDefaultStyles(themeTokens, themeOptions, maxWidth, dismissible, viewport, isSystemEnabled, useContentMaxWidth);
|
|
330
333
|
}
|
|
331
334
|
if (isDismissed) {
|
|
332
335
|
return null;
|
|
@@ -423,6 +426,22 @@ Notification.propTypes = {
|
|
|
423
426
|
* Callback function called when the dismiss button is clicked
|
|
424
427
|
*/
|
|
425
428
|
onDismiss: PropTypes.func,
|
|
429
|
+
/**
|
|
430
|
+
* The minimum width of the content in the Notification when using the system variant.
|
|
431
|
+
* This prop accepts responsive values for different viewports.
|
|
432
|
+
* - `xs`: 'max' | 'full'
|
|
433
|
+
* - `sm`: 'max' | 'full'
|
|
434
|
+
* - `md`: 'max' | 'full'
|
|
435
|
+
* - `lg`: 'max' | 'full'
|
|
436
|
+
* - `xl`: 'max' | 'full'
|
|
437
|
+
*/
|
|
438
|
+
contentMinWidth: PropTypes.shape({
|
|
439
|
+
xl: PropTypes.oneOf(['max', 'full']),
|
|
440
|
+
lg: PropTypes.oneOf(['max', 'full']),
|
|
441
|
+
md: PropTypes.oneOf(['max', 'full']),
|
|
442
|
+
sm: PropTypes.oneOf(['max', 'full']),
|
|
443
|
+
xs: PropTypes.oneOf(['max', 'full'])
|
|
444
|
+
}),
|
|
426
445
|
tokens: getTokensPropType('Notification'),
|
|
427
446
|
variant: variantProp.propType
|
|
428
447
|
};
|
|
@@ -37,7 +37,7 @@ export default {
|
|
|
37
37
|
* 1. `tooltip` as a string - The content of the tooltip.
|
|
38
38
|
* 2. `tooltip` as an object - Tooltip component props to be passed.
|
|
39
39
|
*/
|
|
40
|
-
tooltip: PropTypes.oneOfType([tooltipPropTypes, PropTypes.string]),
|
|
40
|
+
tooltip: PropTypes.oneOfType([PropTypes.shape(tooltipPropTypes), PropTypes.string]),
|
|
41
41
|
/**
|
|
42
42
|
* Use to visually mark an input as valid or invalid.
|
|
43
43
|
*/
|
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.12.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.
|
|
87
|
+
"version": "3.13.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.12.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.
|
|
87
|
+
"version": "3.13.0",
|
|
88
88
|
"types": "types/index.d.ts"
|
|
89
89
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { Platform, Text, View } from 'react-native'
|
|
3
|
+
import { Platform, StyleSheet, Text, View } from 'react-native'
|
|
4
4
|
import buttonPropTypes, { textAndA11yText } from './propTypes'
|
|
5
5
|
import ButtonBase from './ButtonBase'
|
|
6
|
-
import { useThemeTokensCallback } from '../ThemeProvider'
|
|
6
|
+
import { applyTextStyles, useThemeTokensCallback } from '../ThemeProvider'
|
|
7
7
|
import {
|
|
8
8
|
a11yProps,
|
|
9
9
|
getTokensPropType,
|
|
@@ -13,9 +13,10 @@ import {
|
|
|
13
13
|
useInputValue
|
|
14
14
|
} from '../utils'
|
|
15
15
|
import Icon from '../Icon'
|
|
16
|
-
import { getStackedContent } from '../StackView'
|
|
17
16
|
import { getPressHandlersWithArgs } from '../utils/pressability'
|
|
18
17
|
|
|
18
|
+
const FULL_WIDTH_STYLE = 'full'
|
|
19
|
+
|
|
19
20
|
const selectIconTokens = ({
|
|
20
21
|
icon,
|
|
21
22
|
iconPosition,
|
|
@@ -50,6 +51,44 @@ const selectIconTokens = ({
|
|
|
50
51
|
}
|
|
51
52
|
})
|
|
52
53
|
|
|
54
|
+
const selectDescriptionTextStyles = (tokens) => ({
|
|
55
|
+
...applyTextStyles({
|
|
56
|
+
fontName: tokens?.descriptionFontName,
|
|
57
|
+
fontSize: tokens?.descriptionFontSize,
|
|
58
|
+
fontWeight: tokens?.descriptionFontWeight,
|
|
59
|
+
fontColor: tokens?.color
|
|
60
|
+
}),
|
|
61
|
+
paddingBottom: tokens?.descriptionTextPaddingBottom
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const selectLeadIconTokens = (tokens) => ({
|
|
65
|
+
color: tokens?.leadIconColor,
|
|
66
|
+
backgroundColor: tokens?.leadIconBackgroundColor,
|
|
67
|
+
size: tokens?.leadIconSize,
|
|
68
|
+
borderRadius: tokens?.leadIconBorderRadius,
|
|
69
|
+
padding: tokens?.leadIconPadding
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const selectLeadIconContainerStyles = (tokens) => ({
|
|
73
|
+
paddingTop: tokens?.leadIconContainerPaddingTop,
|
|
74
|
+
paddingBottom: tokens?.leadIconContainerPaddingBottom,
|
|
75
|
+
paddingLeft: tokens?.leadIconContainerPaddingLeft,
|
|
76
|
+
paddingRight: tokens?.leadIconContainerPaddingRight
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const selectTextContainerStyles = (tokens) => ({
|
|
80
|
+
paddingLeft: tokens?.textPaddingLeft,
|
|
81
|
+
paddingRight: tokens?.textPaddingRight
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const selectStackedContentStyles = (tokens, iconPosition, isFullWidth) => ({
|
|
85
|
+
...staticStyles.stackedContent,
|
|
86
|
+
gap: tokens?.iconSpace,
|
|
87
|
+
flexDirection: iconPosition === 'left' ? 'row' : 'row-reverse',
|
|
88
|
+
...(Platform.OS === 'web' && { flex: 1 }),
|
|
89
|
+
...(isFullWidth && { justifyContent: 'space-between', flex: 1 })
|
|
90
|
+
})
|
|
91
|
+
|
|
53
92
|
const ButtonDropdown = React.forwardRef(
|
|
54
93
|
(
|
|
55
94
|
{
|
|
@@ -63,10 +102,14 @@ const ButtonDropdown = React.forwardRef(
|
|
|
63
102
|
readOnly = false,
|
|
64
103
|
children = null,
|
|
65
104
|
accessibilityRole = 'radio',
|
|
105
|
+
description,
|
|
106
|
+
singleOption,
|
|
66
107
|
...props
|
|
67
108
|
},
|
|
68
109
|
ref
|
|
69
110
|
) => {
|
|
111
|
+
const isFullWidth = variant?.width === FULL_WIDTH_STYLE
|
|
112
|
+
|
|
70
113
|
const { currentValue: isOpen, setValue: setIsOpen } = useInputValue(
|
|
71
114
|
{
|
|
72
115
|
value,
|
|
@@ -85,7 +128,11 @@ const ButtonDropdown = React.forwardRef(
|
|
|
85
128
|
|
|
86
129
|
const getTokens = useThemeTokensCallback('ButtonDropdown', tokens, extraState)
|
|
87
130
|
|
|
88
|
-
const getButtonTokens = (buttonState) =>
|
|
131
|
+
const getButtonTokens = (buttonState) => ({
|
|
132
|
+
...selectTokens('Button', getTokens(buttonState)),
|
|
133
|
+
iconSpace: props?.icon ? getTokens(buttonState)?.iconSpace : 0,
|
|
134
|
+
...(isFullWidth && { width: 'full' })
|
|
135
|
+
})
|
|
89
136
|
|
|
90
137
|
// Pass an object of relevant component state as first argument for any passed-in press handlers
|
|
91
138
|
const pressHandlers = getPressHandlersWithArgs(props, [{ label, open: isOpen }])
|
|
@@ -103,7 +150,7 @@ const ButtonDropdown = React.forwardRef(
|
|
|
103
150
|
{...pressHandlers}
|
|
104
151
|
onPress={handlePress}
|
|
105
152
|
tokens={getButtonTokens}
|
|
106
|
-
inactive={inactive}
|
|
153
|
+
inactive={singleOption || inactive}
|
|
107
154
|
icon={() => null}
|
|
108
155
|
accessibilityRole={accessibilityRole}
|
|
109
156
|
{...props}
|
|
@@ -116,31 +163,50 @@ const ButtonDropdown = React.forwardRef(
|
|
|
116
163
|
// - Token sets: https://github.com/telus/universal-design-system/issues/782
|
|
117
164
|
|
|
118
165
|
const itemTokens = getTokens(buttonState)
|
|
166
|
+
const leadIcon = itemTokens?.leadIcon
|
|
119
167
|
|
|
120
168
|
const {
|
|
121
169
|
iconTokens,
|
|
122
170
|
iconPosition,
|
|
123
|
-
iconSpace,
|
|
124
171
|
iconWrapperStyle,
|
|
125
172
|
icon: IconComponent
|
|
126
173
|
} = selectIconTokens(itemTokens)
|
|
127
174
|
|
|
128
|
-
const iconContent =
|
|
129
|
-
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
175
|
+
const iconContent =
|
|
176
|
+
IconComponent && !singleOption ? (
|
|
177
|
+
<View style={iconWrapperStyle}>
|
|
178
|
+
<Icon icon={IconComponent} tokens={iconTokens} />
|
|
179
|
+
</View>
|
|
180
|
+
) : null
|
|
133
181
|
|
|
134
182
|
const childrenContent = () =>
|
|
135
183
|
typeof children === 'function'
|
|
136
184
|
? children({ ...resolvePressableState(buttonState, extraState), textStyles })
|
|
137
185
|
: children
|
|
138
186
|
|
|
139
|
-
const content = children ?
|
|
187
|
+
const content = children ? (
|
|
188
|
+
childrenContent()
|
|
189
|
+
) : (
|
|
190
|
+
<View style={staticStyles.contentContainer}>
|
|
191
|
+
{leadIcon && (
|
|
192
|
+
<View style={selectLeadIconContainerStyles(itemTokens)}>
|
|
193
|
+
<Icon icon={leadIcon} tokens={selectLeadIconTokens(itemTokens)} />
|
|
194
|
+
</View>
|
|
195
|
+
)}
|
|
196
|
+
<View style={[staticStyles.textContainer, selectTextContainerStyles(itemTokens)]}>
|
|
197
|
+
<Text style={textStyles}>{label}</Text>
|
|
198
|
+
{description && (
|
|
199
|
+
<Text style={selectDescriptionTextStyles(itemTokens)}>{description}</Text>
|
|
200
|
+
)}
|
|
201
|
+
</View>
|
|
202
|
+
</View>
|
|
203
|
+
)
|
|
140
204
|
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
|
|
205
|
+
return (
|
|
206
|
+
<View style={selectStackedContentStyles(itemTokens, iconPosition, isFullWidth)}>
|
|
207
|
+
{iconContent}
|
|
208
|
+
{content}
|
|
209
|
+
</View>
|
|
144
210
|
)
|
|
145
211
|
}}
|
|
146
212
|
</ButtonBase>
|
|
@@ -176,7 +242,34 @@ ButtonDropdown.propTypes = {
|
|
|
176
242
|
/**
|
|
177
243
|
* By default, `ButtonDropdown` is treated by accessibility tools as a radio button.
|
|
178
244
|
*/
|
|
179
|
-
accessibilityRole: PropTypes.string
|
|
245
|
+
accessibilityRole: PropTypes.string,
|
|
246
|
+
/**
|
|
247
|
+
* The description of ButtonDropdown.
|
|
248
|
+
*/
|
|
249
|
+
description: PropTypes.string,
|
|
250
|
+
/**
|
|
251
|
+
* Use this prop to render the ButtonDropdown as display only without any interaction when there is only one option.
|
|
252
|
+
*/
|
|
253
|
+
singleOption: PropTypes.bool
|
|
180
254
|
}
|
|
181
255
|
|
|
256
|
+
const staticStyles = StyleSheet.create({
|
|
257
|
+
textContainer: {
|
|
258
|
+
alignItems: 'flex-start',
|
|
259
|
+
flexShrink: 1,
|
|
260
|
+
...(Platform.OS === 'web' && { flex: 1 })
|
|
261
|
+
},
|
|
262
|
+
contentContainer: {
|
|
263
|
+
flexDirection: 'row',
|
|
264
|
+
alignContent: 'center',
|
|
265
|
+
alignItems: 'center',
|
|
266
|
+
...(Platform.OS === 'web' && { flex: 1 }),
|
|
267
|
+
flexShrink: 1
|
|
268
|
+
},
|
|
269
|
+
stackedContent: {
|
|
270
|
+
flexDirection: 'row',
|
|
271
|
+
alignItems: 'center'
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
|
|
182
275
|
export default ButtonDropdown
|
|
@@ -27,6 +27,7 @@ import CarouselTabsPanelItem from './CarouselTabs/CarouselTabsPanelItem'
|
|
|
27
27
|
import dictionary from './dictionary'
|
|
28
28
|
import Box from '../Box'
|
|
29
29
|
import {
|
|
30
|
+
ITEMS_PER_VIEWPORT_XS_SM,
|
|
30
31
|
ITEMS_PER_VIEWPORT_MD,
|
|
31
32
|
ITEMS_PER_VIEWPORT_LG_XL,
|
|
32
33
|
DEFAULT_POSITION_OFFSET,
|
|
@@ -211,6 +212,31 @@ const getTotalItems = (enableDisplayMultipleItemsPerSlide, childrenArray, viewpo
|
|
|
211
212
|
return childrenArray.length
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Determines the maximum number of items that can be displayed per slide based on viewport
|
|
217
|
+
*
|
|
218
|
+
* @param {boolean} enableDisplayMultipleItemsPerSlide - Flag indicating whether multiple items per slide is enabled
|
|
219
|
+
* @param {string} viewport - The current viewport size ('xs', 'sm', 'md', 'lg', 'xl')
|
|
220
|
+
* @returns {number} The maximum number of items that can be displayed per slide
|
|
221
|
+
*/
|
|
222
|
+
const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) => {
|
|
223
|
+
if (enableDisplayMultipleItemsPerSlide) {
|
|
224
|
+
switch (viewport) {
|
|
225
|
+
case 'xs':
|
|
226
|
+
case 'sm':
|
|
227
|
+
return ITEMS_PER_VIEWPORT_XS_SM
|
|
228
|
+
case 'md':
|
|
229
|
+
return ITEMS_PER_VIEWPORT_MD
|
|
230
|
+
case 'lg':
|
|
231
|
+
case 'xl':
|
|
232
|
+
return ITEMS_PER_VIEWPORT_LG_XL
|
|
233
|
+
default:
|
|
234
|
+
return ITEMS_PER_VIEWPORT_XS_SM
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return ITEMS_PER_VIEWPORT_XS_SM
|
|
238
|
+
}
|
|
239
|
+
|
|
214
240
|
const selectRootContainerStyles = (enableHero, viewport) => {
|
|
215
241
|
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
216
242
|
return {
|
|
@@ -993,6 +1019,10 @@ const Carousel = React.forwardRef(
|
|
|
993
1019
|
firstFocusRef={firstFocusRef}
|
|
994
1020
|
refocus={refocus}
|
|
995
1021
|
width={containerLayout.width}
|
|
1022
|
+
maximumItemsForSlide={getMaximumItemsForSlide(
|
|
1023
|
+
enableDisplayMultipleItemsPerSlide,
|
|
1024
|
+
viewport
|
|
1025
|
+
)}
|
|
996
1026
|
>
|
|
997
1027
|
<View
|
|
998
1028
|
style={[
|
|
@@ -13,7 +13,8 @@ const CarouselProvider = ({
|
|
|
13
13
|
refocus = false,
|
|
14
14
|
themeTokens,
|
|
15
15
|
totalItems,
|
|
16
|
-
width
|
|
16
|
+
width,
|
|
17
|
+
maximumItemsForSlide
|
|
17
18
|
}) => {
|
|
18
19
|
const value = React.useMemo(
|
|
19
20
|
() => ({
|
|
@@ -24,9 +25,20 @@ const CarouselProvider = ({
|
|
|
24
25
|
refocus,
|
|
25
26
|
themeTokens,
|
|
26
27
|
totalItems,
|
|
27
|
-
width
|
|
28
|
+
width,
|
|
29
|
+
maximumItemsForSlide
|
|
28
30
|
}),
|
|
29
|
-
[
|
|
31
|
+
[
|
|
32
|
+
activeIndex,
|
|
33
|
+
goTo,
|
|
34
|
+
getCopyWithPlaceholders,
|
|
35
|
+
itemLabel,
|
|
36
|
+
refocus,
|
|
37
|
+
totalItems,
|
|
38
|
+
themeTokens,
|
|
39
|
+
width,
|
|
40
|
+
maximumItemsForSlide
|
|
41
|
+
]
|
|
30
42
|
)
|
|
31
43
|
return <CarouselContext.Provider value={value}>{children}</CarouselContext.Provider>
|
|
32
44
|
}
|
|
@@ -48,7 +60,8 @@ CarouselProvider.propTypes = {
|
|
|
48
60
|
refocus: PropTypes.bool,
|
|
49
61
|
themeTokens: getTokensPropType('Carousel'),
|
|
50
62
|
totalItems: PropTypes.number.isRequired,
|
|
51
|
-
width: PropTypes.number.isRequired
|
|
63
|
+
width: PropTypes.number.isRequired,
|
|
64
|
+
maximumItemsForSlide: PropTypes.number
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
export { CarouselProvider, useCarousel }
|
|
@@ -102,14 +102,28 @@ const CarouselItem = React.forwardRef(
|
|
|
102
102
|
},
|
|
103
103
|
ref
|
|
104
104
|
) => {
|
|
105
|
-
const { width, activeIndex } = useCarousel()
|
|
105
|
+
const { width, activeIndex, goTo, maximumItemsForSlide } = useCarousel()
|
|
106
106
|
|
|
107
107
|
const selectedProps = selectProps({
|
|
108
108
|
...rest,
|
|
109
109
|
...getA11yPropsFromHtmlTag(tag, rest.accessibilityRole)
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
const focusabilityProps =
|
|
112
|
+
const focusabilityProps =
|
|
113
|
+
activeIndex === elementIndex || enablePeeking ? {} : a11yProps.nonFocusableProps
|
|
114
|
+
|
|
115
|
+
const handleFocus = React.useCallback(
|
|
116
|
+
(event) => {
|
|
117
|
+
if (Platform.OS === 'web' && elementIndex >= maximumItemsForSlide * (activeIndex + 1)) {
|
|
118
|
+
goTo(activeIndex + 1)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (rest.onFocus) {
|
|
122
|
+
rest.onFocus(event)
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
[elementIndex, activeIndex, goTo, maximumItemsForSlide, rest]
|
|
126
|
+
)
|
|
113
127
|
|
|
114
128
|
return (
|
|
115
129
|
<View
|
|
@@ -125,6 +139,7 @@ const CarouselItem = React.forwardRef(
|
|
|
125
139
|
{...selectedProps}
|
|
126
140
|
{...focusabilityProps}
|
|
127
141
|
ref={ref}
|
|
142
|
+
onFocus={handleFocus}
|
|
128
143
|
>
|
|
129
144
|
{children}
|
|
130
145
|
</View>
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
useMultipleInputValues,
|
|
11
11
|
variantProp,
|
|
12
12
|
viewProps,
|
|
13
|
-
contentfulProps
|
|
13
|
+
contentfulProps,
|
|
14
|
+
useUniqueId
|
|
14
15
|
} from '../utils'
|
|
15
16
|
|
|
16
17
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
@@ -36,6 +37,8 @@ function selectBorderStyles(tokens) {
|
|
|
36
37
|
*/
|
|
37
38
|
const ExpandCollapse = React.forwardRef(
|
|
38
39
|
({ children, tokens, variant, maxOpen, open, initialOpen, onChange, dataSet, ...rest }, ref) => {
|
|
40
|
+
const instanceId = useUniqueId('ExpandCollapse')
|
|
41
|
+
|
|
39
42
|
const {
|
|
40
43
|
currentValues: openIds,
|
|
41
44
|
toggleOneValue: onToggle,
|
|
@@ -54,7 +57,7 @@ const ExpandCollapse = React.forwardRef(
|
|
|
54
57
|
<View style={staticStyles.container} ref={ref} {...selectProps(rest)} dataSet={dataSet}>
|
|
55
58
|
<View style={selectBorderStyles(themeTokens)}>
|
|
56
59
|
{typeof children === 'function'
|
|
57
|
-
? children({ openIds, onToggle, resetValues, setValues, unsetValues })
|
|
60
|
+
? children({ openIds, onToggle, resetValues, setValues, unsetValues, instanceId })
|
|
58
61
|
: children}
|
|
59
62
|
</View>
|
|
60
63
|
</View>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import ExpandCollapse from '../ExpandCollapse'
|
|
4
|
-
import { getTokensPropType, selectSystemProps, contentfulProps } from '../utils'
|
|
4
|
+
import { getTokensPropType, selectSystemProps, contentfulProps, useUniqueId } from '../utils'
|
|
5
5
|
import { variantProp } from '../utils/props'
|
|
6
6
|
import ExpandCollapseMiniControl from './ExpandCollapseMiniControl'
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ const ExpandCollapseMini = React.forwardRef(
|
|
|
12
12
|
{ children, onToggle = () => {}, tokens = {}, nativeID, initialOpen = false, dataSet, ...rest },
|
|
13
13
|
ref
|
|
14
14
|
) => {
|
|
15
|
-
const expandCollapeMiniPanelId = 'ExpandCollapseMiniPanel'
|
|
15
|
+
const expandCollapeMiniPanelId = useUniqueId('ExpandCollapseMiniPanel')
|
|
16
16
|
const handleChange = (openPanels, event) => {
|
|
17
17
|
if (typeof onToggle === 'function') {
|
|
18
18
|
const isOpen = openPanels.length > 0
|
|
@@ -42,10 +42,15 @@ const ExpandCollapseMiniControl = React.forwardRef(
|
|
|
42
42
|
pressed
|
|
43
43
|
}
|
|
44
44
|
)
|
|
45
|
-
const {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const { fontSize, lineHeight, iconSize, icon } = useThemeTokens(
|
|
46
|
+
'ExpandCollapseMiniControl',
|
|
47
|
+
tokens,
|
|
48
|
+
variant,
|
|
49
|
+
{
|
|
50
|
+
expanded,
|
|
51
|
+
focus
|
|
52
|
+
}
|
|
53
|
+
)
|
|
49
54
|
|
|
50
55
|
// Choose hover styles when any part of Control is hoverred
|
|
51
56
|
const appearance = { ...variant, hover }
|
|
@@ -54,16 +59,39 @@ const ExpandCollapseMiniControl = React.forwardRef(
|
|
|
54
59
|
const { hover: linkHover } = linkState || {}
|
|
55
60
|
const isHovered = hover || linkHover
|
|
56
61
|
|
|
62
|
+
const iconBaselineOffset = 0
|
|
63
|
+
const hoverTranslateY = 4
|
|
64
|
+
|
|
65
|
+
// Calculate baseline alignment to vertically center icon with text
|
|
66
|
+
// This combines font and icon metrics with adjustments for visual balance
|
|
67
|
+
const fontBaseline = fontSize / hoverTranslateY // Quarter of font size - adjusts for text's visual center point
|
|
68
|
+
const iconBaseline = iconSize / hoverTranslateY // Quarter of icon size - adjusts for icon's visual center point
|
|
69
|
+
const staticOffset = hoverTranslateY // Fixed downward adjustment to fine-tune vertical alignment
|
|
70
|
+
const sizeCompensation = -Math.abs(iconSize - fontSize) // Compensates when icon and text sizes differ significantly
|
|
71
|
+
|
|
72
|
+
const baselineAlignment = fontBaseline + iconBaseline - staticOffset + sizeCompensation
|
|
73
|
+
|
|
57
74
|
if (Platform.OS !== 'web') {
|
|
58
|
-
|
|
75
|
+
// For native platforms, use baseline alignment with optional offset
|
|
76
|
+
return { iconTranslateY: baselineAlignment + iconBaselineOffset }
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
if (isHovered) {
|
|
62
|
-
//
|
|
63
|
-
|
|
80
|
+
// Apply animation offset to the baseline-aligned position
|
|
81
|
+
// When expanded: move icon UP (1.3 the hover distance for clear movement)
|
|
82
|
+
// When collapsed: move icon DOWN (single hover distance)
|
|
83
|
+
const hoverMovementDistance = 1.3
|
|
84
|
+
const animationOffset = expanded
|
|
85
|
+
? -(hoverTranslateY * hoverMovementDistance)
|
|
86
|
+
: hoverTranslateY
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
iconTranslateY: baselineAlignment + iconBaselineOffset + animationOffset
|
|
90
|
+
}
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
|
|
93
|
+
// Default state uses baseline alignment with optional offset
|
|
94
|
+
return { iconTranslateY: baselineAlignment + iconBaselineOffset }
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
return (
|
|
@@ -73,7 +101,9 @@ const ExpandCollapseMiniControl = React.forwardRef(
|
|
|
73
101
|
iconPosition={iconPosition}
|
|
74
102
|
tokens={(linkState) => ({
|
|
75
103
|
...linkTokens,
|
|
76
|
-
...getTokens(linkState)
|
|
104
|
+
...getTokens(linkState),
|
|
105
|
+
iconSize,
|
|
106
|
+
blockLineHeight: lineHeight
|
|
77
107
|
})}
|
|
78
108
|
ref={ref}
|
|
79
109
|
{...presentationOnly}
|