@telus-uds/components-base 3.23.0 → 3.25.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 +25 -1
- package/lib/cjs/Button/ButtonGroup.js +9 -2
- package/lib/cjs/Card/CardBase.js +97 -17
- package/lib/cjs/Card/PressableCardBase.js +12 -8
- package/lib/cjs/Carousel/Carousel.js +35 -4
- package/lib/cjs/FlexGrid/FlexGrid.js +31 -35
- package/lib/cjs/HorizontalScroll/HorizontalScroll.js +5 -2
- package/lib/cjs/Icon/Icon.js +3 -0
- package/lib/cjs/IconButton/IconButton.js +15 -5
- package/lib/cjs/Listbox/GroupControl.js +12 -6
- package/lib/cjs/Listbox/Listbox.js +41 -7
- package/lib/cjs/Listbox/ListboxGroup.js +139 -8
- package/lib/cjs/Listbox/ListboxOverlay.js +10 -5
- package/lib/cjs/Listbox/SecondLevelHeader.js +201 -0
- package/lib/cjs/Listbox/dictionary.js +14 -0
- package/lib/cjs/Shortcuts/Shortcuts.js +169 -0
- package/lib/cjs/Shortcuts/ShortcutsItem.js +280 -0
- package/lib/cjs/Shortcuts/index.js +16 -0
- package/lib/cjs/TextInput/TextInputBase.js +2 -3
- package/lib/cjs/Tooltip/Tooltip.native.js +2 -0
- package/lib/cjs/index.js +15 -0
- package/lib/cjs/utils/index.js +9 -1
- package/lib/cjs/utils/resolveContentMaxWidth.js +30 -0
- package/lib/esm/Button/ButtonGroup.js +9 -2
- package/lib/esm/Card/CardBase.js +97 -17
- package/lib/esm/Card/PressableCardBase.js +10 -8
- package/lib/esm/Carousel/Carousel.js +37 -6
- package/lib/esm/FlexGrid/FlexGrid.js +31 -35
- package/lib/esm/HorizontalScroll/HorizontalScroll.js +6 -3
- package/lib/esm/Icon/Icon.js +3 -0
- package/lib/esm/IconButton/IconButton.js +15 -5
- package/lib/esm/Listbox/GroupControl.js +12 -6
- package/lib/esm/Listbox/Listbox.js +41 -7
- package/lib/esm/Listbox/ListboxGroup.js +141 -10
- package/lib/esm/Listbox/ListboxOverlay.js +10 -5
- package/lib/esm/Listbox/SecondLevelHeader.js +194 -0
- package/lib/esm/Listbox/dictionary.js +8 -0
- package/lib/esm/Shortcuts/Shortcuts.js +160 -0
- package/lib/esm/Shortcuts/ShortcutsItem.js +273 -0
- package/lib/esm/Shortcuts/index.js +3 -0
- package/lib/esm/TextInput/TextInputBase.js +2 -3
- package/lib/esm/Tooltip/Tooltip.native.js +2 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/utils/index.js +2 -1
- package/lib/esm/utils/resolveContentMaxWidth.js +24 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Button/ButtonGroup.jsx +20 -3
- package/src/Card/CardBase.jsx +113 -14
- package/src/Card/PressableCardBase.jsx +17 -5
- package/src/Carousel/Carousel.jsx +38 -6
- package/src/FlexGrid/FlexGrid.jsx +30 -39
- package/src/HorizontalScroll/HorizontalScroll.jsx +6 -3
- package/src/Icon/Icon.jsx +3 -0
- package/src/IconButton/IconButton.jsx +12 -5
- package/src/Listbox/GroupControl.jsx +41 -33
- package/src/Listbox/Listbox.jsx +41 -2
- package/src/Listbox/ListboxGroup.jsx +158 -26
- package/src/Listbox/ListboxOverlay.jsx +18 -5
- package/src/Listbox/SecondLevelHeader.jsx +182 -0
- package/src/Listbox/dictionary.js +8 -0
- package/src/Shortcuts/Shortcuts.jsx +174 -0
- package/src/Shortcuts/ShortcutsItem.jsx +297 -0
- package/src/Shortcuts/index.js +4 -0
- package/src/TextInput/TextInputBase.jsx +2 -2
- package/src/Tooltip/Tooltip.native.jsx +2 -1
- package/src/index.js +1 -0
- package/src/utils/index.js +1 -0
- package/src/utils/resolveContentMaxWidth.js +28 -0
- package/types/Listbox.d.ts +24 -0
- package/types/Shortcuts.d.ts +136 -0
- package/types/Status.d.ts +42 -0
- package/types/index.d.ts +15 -0
package/src/Card/CardBase.jsx
CHANGED
|
@@ -6,9 +6,18 @@ import { applyShadowToken } from '../ThemeProvider'
|
|
|
6
6
|
import { getTokensPropType, responsiveProps, useResponsiveProp, formatImageSource } from '../utils'
|
|
7
7
|
import { a11yProps, viewProps, selectSystemProps } from '../utils/props'
|
|
8
8
|
import backgroundImageStylesMap from './backgroundImageStylesMap'
|
|
9
|
+
import FlexGrid from '../FlexGrid/FlexGrid'
|
|
10
|
+
import FlexGridRow from '../FlexGrid/Row'
|
|
11
|
+
import FlexGridCol from '../FlexGrid/Col'
|
|
9
12
|
|
|
10
13
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
11
14
|
|
|
15
|
+
const GRID_COLUMNS = 12
|
|
16
|
+
|
|
17
|
+
const isOverlayColor = (color) => {
|
|
18
|
+
return color && typeof color === 'string' && color.startsWith('rgba(')
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
const setBackgroundImage = ({
|
|
13
22
|
src,
|
|
14
23
|
alt,
|
|
@@ -22,12 +31,10 @@ const setBackgroundImage = ({
|
|
|
22
31
|
const borderWidth = cardStyle?.borderWidth || 0
|
|
23
32
|
const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth)
|
|
24
33
|
|
|
25
|
-
// For contain mode with position and align, use CSS background properties for web
|
|
26
34
|
if (backgroundImageResizeMode === 'contain' && backgroundImagePosition && backgroundImageAlign) {
|
|
27
35
|
const positionKey = `${backgroundImagePosition}-${backgroundImageAlign}`
|
|
28
36
|
|
|
29
37
|
if (Platform.OS === 'web') {
|
|
30
|
-
// Create background position based on position and align
|
|
31
38
|
let backgroundPosition
|
|
32
39
|
|
|
33
40
|
switch (positionKey) {
|
|
@@ -77,7 +84,7 @@ const setBackgroundImage = ({
|
|
|
77
84
|
</View>
|
|
78
85
|
)
|
|
79
86
|
}
|
|
80
|
-
|
|
87
|
+
|
|
81
88
|
const positionStyles = backgroundImageStylesMap[positionKey] || {}
|
|
82
89
|
|
|
83
90
|
return (
|
|
@@ -95,7 +102,6 @@ const setBackgroundImage = ({
|
|
|
95
102
|
)
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
// Use ImageBackground for all other resize modes and React Native
|
|
99
105
|
return (
|
|
100
106
|
<ImageBackground
|
|
101
107
|
source={src}
|
|
@@ -121,6 +127,10 @@ export const selectStyles = ({
|
|
|
121
127
|
paddingLeft,
|
|
122
128
|
paddingRight,
|
|
123
129
|
paddingTop,
|
|
130
|
+
marginTop,
|
|
131
|
+
marginBottom,
|
|
132
|
+
marginLeft,
|
|
133
|
+
marginRight,
|
|
124
134
|
minWidth,
|
|
125
135
|
shadow,
|
|
126
136
|
backgroundGradient,
|
|
@@ -128,9 +138,27 @@ export const selectStyles = ({
|
|
|
128
138
|
maxHeight,
|
|
129
139
|
overflowY
|
|
130
140
|
}) => {
|
|
141
|
+
const hasGradient = (gradient || backgroundGradient) && Platform.OS === 'web'
|
|
142
|
+
|
|
143
|
+
let backgroundImageValue = null
|
|
144
|
+
|
|
145
|
+
if (hasGradient) {
|
|
146
|
+
const gradientObj = gradient || backgroundGradient
|
|
147
|
+
const gradientString = `linear-gradient(${gradientObj.angle}deg, ${gradientObj.stops[0].color}, ${gradientObj.stops[1].color})`
|
|
148
|
+
const shouldApplyOverlay =
|
|
149
|
+
(gradient || (backgroundGradient && backgroundColor && backgroundColor !== 'transparent')) &&
|
|
150
|
+
isOverlayColor(backgroundColor)
|
|
151
|
+
|
|
152
|
+
backgroundImageValue = shouldApplyOverlay
|
|
153
|
+
? `linear-gradient(${backgroundColor}, ${backgroundColor}), ${gradientString}`
|
|
154
|
+
: gradientString
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const boxShadowColor = isOverlayColor(backgroundColor) ? backgroundColor : 'white'
|
|
158
|
+
|
|
131
159
|
return {
|
|
132
160
|
flex,
|
|
133
|
-
backgroundColor,
|
|
161
|
+
backgroundColor: hasGradient ? 'transparent' : backgroundColor,
|
|
134
162
|
borderColor,
|
|
135
163
|
borderRadius,
|
|
136
164
|
borderWidth,
|
|
@@ -138,19 +166,23 @@ export const selectStyles = ({
|
|
|
138
166
|
paddingLeft,
|
|
139
167
|
paddingRight,
|
|
140
168
|
paddingTop,
|
|
169
|
+
marginTop,
|
|
170
|
+
marginBottom,
|
|
171
|
+
marginLeft,
|
|
172
|
+
marginRight,
|
|
141
173
|
minWidth,
|
|
142
174
|
...applyShadowToken(shadow),
|
|
143
175
|
...(gradient && Platform.OS === 'web'
|
|
144
176
|
? {
|
|
145
|
-
backgroundImage:
|
|
177
|
+
backgroundImage: backgroundImageValue,
|
|
146
178
|
backgroundOrigin: `border-box`,
|
|
147
|
-
boxShadow: `inset 0 1000px
|
|
179
|
+
boxShadow: `inset 0 1000px ${boxShadowColor}`,
|
|
148
180
|
border: `${borderWidth}px solid transparent`
|
|
149
181
|
}
|
|
150
182
|
: {}),
|
|
151
183
|
...(backgroundGradient && Platform.OS === 'web'
|
|
152
184
|
? {
|
|
153
|
-
backgroundImage:
|
|
185
|
+
backgroundImage: backgroundImageValue
|
|
154
186
|
}
|
|
155
187
|
: {}),
|
|
156
188
|
...(Platform.OS === 'web' ? { maxHeight, overflowY } : {})
|
|
@@ -162,8 +194,8 @@ export const selectStyles = ({
|
|
|
162
194
|
* intended to be used in apps or sites directly: build themed components on top of this.
|
|
163
195
|
*/
|
|
164
196
|
const CardBase = React.forwardRef(
|
|
165
|
-
({ children, tokens, dataSet, backgroundImage, ...rest }, ref) => {
|
|
166
|
-
const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens)
|
|
197
|
+
({ children, tokens, dataSet, backgroundImage, fullBleedContent, cardState, ...rest }, ref) => {
|
|
198
|
+
const cardStyle = selectStyles(typeof tokens === 'function' ? tokens(cardState) : tokens)
|
|
167
199
|
const props = selectProps(rest)
|
|
168
200
|
|
|
169
201
|
let content = children
|
|
@@ -174,12 +206,16 @@ const CardBase = React.forwardRef(
|
|
|
174
206
|
const backgroundImageAlign = useResponsiveProp(align)
|
|
175
207
|
const imageSourceViewport = formatImageSource(useResponsiveProp(src))
|
|
176
208
|
|
|
209
|
+
const {
|
|
210
|
+
content: fullBleedImageContent,
|
|
211
|
+
position: fullBleedContentPosition,
|
|
212
|
+
imgCol
|
|
213
|
+
} = fullBleedContent || {}
|
|
214
|
+
const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')
|
|
215
|
+
|
|
177
216
|
if (backgroundImage && src) {
|
|
178
|
-
// When there's a background image, separate the padding from the container style
|
|
179
|
-
// so the image can fill the entire container without padding interference
|
|
180
217
|
const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
|
|
181
218
|
|
|
182
|
-
// Only create padding wrapper if there's actually padding defined
|
|
183
219
|
const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
|
|
184
220
|
const paddedContent = hasPadding ? (
|
|
185
221
|
<View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
|
|
@@ -204,6 +240,53 @@ const CardBase = React.forwardRef(
|
|
|
204
240
|
)
|
|
205
241
|
}
|
|
206
242
|
|
|
243
|
+
if (fullBleedContent && fullBleedImageContent) {
|
|
244
|
+
const { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
|
|
245
|
+
const imageColumns = imgCol || { xs: GRID_COLUMNS }
|
|
246
|
+
|
|
247
|
+
const textColumns = {}
|
|
248
|
+
Object.keys(imageColumns).forEach((breakpoint) => {
|
|
249
|
+
textColumns[breakpoint] = GRID_COLUMNS - (imageColumns[breakpoint] || GRID_COLUMNS)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
const imageFirst = fullBleedPosition === 'top' || fullBleedPosition === 'left'
|
|
253
|
+
|
|
254
|
+
const imageColContent = (
|
|
255
|
+
<FlexGridCol {...imageColumns} style={staticStyles.fullBleedImageCol}>
|
|
256
|
+
{fullBleedImageContent}
|
|
257
|
+
</FlexGridCol>
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
const textColContent = (
|
|
261
|
+
<FlexGridCol
|
|
262
|
+
{...textColumns}
|
|
263
|
+
style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}
|
|
264
|
+
>
|
|
265
|
+
{children}
|
|
266
|
+
</FlexGridCol>
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<View style={containerStyle} dataSet={dataSet} ref={ref} {...props}>
|
|
271
|
+
<FlexGrid>
|
|
272
|
+
<FlexGridRow>
|
|
273
|
+
{imageFirst ? (
|
|
274
|
+
<>
|
|
275
|
+
{imageColContent}
|
|
276
|
+
{textColContent}
|
|
277
|
+
</>
|
|
278
|
+
) : (
|
|
279
|
+
<>
|
|
280
|
+
{textColContent}
|
|
281
|
+
{imageColContent}
|
|
282
|
+
</>
|
|
283
|
+
)}
|
|
284
|
+
</FlexGridRow>
|
|
285
|
+
</FlexGrid>
|
|
286
|
+
</View>
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
207
290
|
return (
|
|
208
291
|
<View style={cardStyle} dataSet={dataSet} ref={ref} {...props}>
|
|
209
292
|
{content}
|
|
@@ -213,6 +296,18 @@ const CardBase = React.forwardRef(
|
|
|
213
296
|
)
|
|
214
297
|
CardBase.displayName = 'CardBase'
|
|
215
298
|
|
|
299
|
+
export const fullBleedContentPropTypes = PropTypes.shape({
|
|
300
|
+
content: PropTypes.node.isRequired,
|
|
301
|
+
alt: PropTypes.string,
|
|
302
|
+
position: responsiveProps.getTypeOptionallyByViewport(
|
|
303
|
+
PropTypes.oneOf(['bottom', 'left', 'right', 'top'])
|
|
304
|
+
),
|
|
305
|
+
align: responsiveProps.getTypeOptionallyByViewport(
|
|
306
|
+
PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
|
|
307
|
+
),
|
|
308
|
+
imgCol: PropTypes.object
|
|
309
|
+
})
|
|
310
|
+
|
|
216
311
|
const staticStyles = StyleSheet.create({
|
|
217
312
|
imageBackground: { width: '100%', height: '100%' },
|
|
218
313
|
contentOverlay: {
|
|
@@ -231,6 +326,9 @@ const staticStyles = StyleSheet.create({
|
|
|
231
326
|
position: 'absolute',
|
|
232
327
|
width: '100%',
|
|
233
328
|
height: '100%'
|
|
329
|
+
},
|
|
330
|
+
fullBleedImageCol: {
|
|
331
|
+
padding: 0
|
|
234
332
|
}
|
|
235
333
|
})
|
|
236
334
|
|
|
@@ -255,7 +353,8 @@ CardBase.propTypes = {
|
|
|
255
353
|
align: responsiveProps.getTypeOptionallyByViewport(
|
|
256
354
|
PropTypes.oneOf(['start', 'end', 'center', 'stretch'])
|
|
257
355
|
)
|
|
258
|
-
})
|
|
356
|
+
}),
|
|
357
|
+
fullBleedContent: fullBleedContentPropTypes
|
|
259
358
|
}
|
|
260
359
|
|
|
261
360
|
export default CardBase
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
viewProps,
|
|
19
19
|
withLinkRouter
|
|
20
20
|
} from '../utils'
|
|
21
|
-
import CardBase from './CardBase'
|
|
21
|
+
import CardBase, { fullBleedContentPropTypes } from './CardBase'
|
|
22
22
|
|
|
23
23
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
24
24
|
a11yProps,
|
|
@@ -37,6 +37,10 @@ const tokenKeys = [
|
|
|
37
37
|
'paddingLeft',
|
|
38
38
|
'paddingRight',
|
|
39
39
|
'paddingTop',
|
|
40
|
+
'marginTop',
|
|
41
|
+
'marginBottom',
|
|
42
|
+
'marginLeft',
|
|
43
|
+
'marginRight',
|
|
40
44
|
'minWidth',
|
|
41
45
|
'shadow',
|
|
42
46
|
'contentAlignItems',
|
|
@@ -81,6 +85,7 @@ const PressableCardBase = React.forwardRef(
|
|
|
81
85
|
hrefAttrs,
|
|
82
86
|
dataSet,
|
|
83
87
|
backgroundImage,
|
|
88
|
+
fullBleedContent,
|
|
84
89
|
accessibilityRole = href ? 'link' : undefined,
|
|
85
90
|
...rawRest
|
|
86
91
|
},
|
|
@@ -159,13 +164,14 @@ const PressableCardBase = React.forwardRef(
|
|
|
159
164
|
setFocused(false)
|
|
160
165
|
setPressed(false)
|
|
161
166
|
}}
|
|
162
|
-
style={
|
|
167
|
+
style={staticStyles.linkContainer}
|
|
163
168
|
{...(hrefAttrs || {})}
|
|
164
169
|
role={accessibilityRole}
|
|
165
170
|
>
|
|
166
171
|
<CardBase
|
|
167
172
|
tokens={getCardTokens({ pressed, focused, hovered })}
|
|
168
173
|
backgroundImage={backgroundImage}
|
|
174
|
+
fullBleedContent={fullBleedContent}
|
|
169
175
|
>
|
|
170
176
|
{typeof children === 'function'
|
|
171
177
|
? children(getCardState({ pressed, focused, hovered }))
|
|
@@ -188,7 +194,11 @@ const PressableCardBase = React.forwardRef(
|
|
|
188
194
|
{...selectProps({ ...rest, accessibilityRole })}
|
|
189
195
|
>
|
|
190
196
|
{(pressableState) => (
|
|
191
|
-
<CardBase
|
|
197
|
+
<CardBase
|
|
198
|
+
tokens={getCardTokens(pressableState)}
|
|
199
|
+
backgroundImage={backgroundImage}
|
|
200
|
+
fullBleedContent={fullBleedContent}
|
|
201
|
+
>
|
|
192
202
|
{typeof children === 'function' ? children(getCardState(pressableState)) : children}
|
|
193
203
|
</CardBase>
|
|
194
204
|
)}
|
|
@@ -206,7 +216,8 @@ const staticStyles = StyleSheet.create({
|
|
|
206
216
|
flex: 1,
|
|
207
217
|
display: 'flex',
|
|
208
218
|
alignItems: 'stretch',
|
|
209
|
-
justifyContent: 'flex-start'
|
|
219
|
+
justifyContent: 'flex-start',
|
|
220
|
+
textDecorationLine: 'none'
|
|
210
221
|
}
|
|
211
222
|
})
|
|
212
223
|
|
|
@@ -234,7 +245,8 @@ PressableCardBase.propTypes = {
|
|
|
234
245
|
PropTypes.oneOf(['start', 'end', 'center', 'stretch']),
|
|
235
246
|
PropTypes.object
|
|
236
247
|
])
|
|
237
|
-
})
|
|
248
|
+
}),
|
|
249
|
+
fullBleedContent: fullBleedContentPropTypes
|
|
238
250
|
}
|
|
239
251
|
|
|
240
252
|
export default withLinkRouter(PressableCardBase)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { View, Animated, PanResponder, StyleSheet, Platform, Dimensions } from 'react-native'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
|
-
import { useThemeTokens } from '../ThemeProvider'
|
|
4
|
+
import { useThemeTokens, useTheme } from '../ThemeProvider'
|
|
5
5
|
import { useViewport } from '../ViewportProvider'
|
|
6
6
|
import {
|
|
7
7
|
getTokensPropType,
|
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
viewProps,
|
|
14
14
|
useCopy,
|
|
15
15
|
unpackFragment,
|
|
16
|
-
isTouchDevice
|
|
16
|
+
isTouchDevice,
|
|
17
|
+
useResponsiveProp,
|
|
18
|
+
resolveContentMaxWidth
|
|
17
19
|
} from '../utils'
|
|
18
20
|
import { useA11yInfo } from '../A11yInfoProvider'
|
|
19
21
|
import { CarouselProvider } from './CarouselContext'
|
|
@@ -252,11 +254,18 @@ const selectRootContainerStyles = (enableHero, viewport) => {
|
|
|
252
254
|
return {}
|
|
253
255
|
}
|
|
254
256
|
|
|
255
|
-
const selectMainContainerStyles = (enableHero, viewport) => {
|
|
257
|
+
const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
256
258
|
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
257
259
|
return {
|
|
258
260
|
width: '100%',
|
|
259
|
-
maxWidth: 1200
|
|
261
|
+
maxWidth: maxWidth || 1200
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (maxWidth !== null && maxWidth !== undefined) {
|
|
265
|
+
return {
|
|
266
|
+
maxWidth,
|
|
267
|
+
alignSelf: 'center',
|
|
268
|
+
width: '100%'
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
271
|
return {}
|
|
@@ -402,6 +411,7 @@ const Carousel = React.forwardRef(
|
|
|
402
411
|
loopDuration = transitionDuration,
|
|
403
412
|
autoPlay = false,
|
|
404
413
|
enablePeeking = false,
|
|
414
|
+
contentMaxWidth,
|
|
405
415
|
...rest
|
|
406
416
|
},
|
|
407
417
|
ref
|
|
@@ -409,6 +419,11 @@ const Carousel = React.forwardRef(
|
|
|
409
419
|
let childrenArray = unpackFragment(children)
|
|
410
420
|
const isTransitioningRef = React.useRef(false)
|
|
411
421
|
const viewport = useViewport()
|
|
422
|
+
const { themeOptions } = useTheme()
|
|
423
|
+
|
|
424
|
+
const contentMaxWidthValue = useResponsiveProp(contentMaxWidth)
|
|
425
|
+
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
426
|
+
const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth)
|
|
412
427
|
const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport)
|
|
413
428
|
const autoPlayFeatureEnabled =
|
|
414
429
|
autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1
|
|
@@ -1031,7 +1046,7 @@ const Carousel = React.forwardRef(
|
|
|
1031
1046
|
|
|
1032
1047
|
return (
|
|
1033
1048
|
<View style={selectRootContainerStyles(enableHero, viewport)}>
|
|
1034
|
-
<View style={selectMainContainerStyles(enableHero, viewport)}>
|
|
1049
|
+
<View style={selectMainContainerStyles(enableHero, viewport, maxWidth)}>
|
|
1035
1050
|
<CarouselProvider
|
|
1036
1051
|
activeIndex={activeIndex}
|
|
1037
1052
|
goTo={goTo}
|
|
@@ -1463,7 +1478,24 @@ Carousel.propTypes = {
|
|
|
1463
1478
|
* If set to `true`, the Carousel will show multiple slides at once
|
|
1464
1479
|
* - Default value is `false`
|
|
1465
1480
|
*/
|
|
1466
|
-
enableDisplayMultipleItemsPerSlide: PropTypes.bool
|
|
1481
|
+
enableDisplayMultipleItemsPerSlide: PropTypes.bool,
|
|
1482
|
+
/**
|
|
1483
|
+
* The maximum width of the content in the Carousel.
|
|
1484
|
+
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
1485
|
+
* it will be the max content width for the desired viewport.
|
|
1486
|
+
* - `xs`: 'max' | 'full' | <number>
|
|
1487
|
+
* - `sm`: 'max' | 'full' | <number>
|
|
1488
|
+
* - `md`: 'max' | 'full' | <number>
|
|
1489
|
+
* - `lg`: 'max' | 'full' | <number>
|
|
1490
|
+
* - `xl`: 'max' | 'full' | <number>
|
|
1491
|
+
*/
|
|
1492
|
+
contentMaxWidth: PropTypes.shape({
|
|
1493
|
+
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1494
|
+
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1495
|
+
md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1496
|
+
sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1497
|
+
xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
|
|
1498
|
+
})
|
|
1467
1499
|
}
|
|
1468
1500
|
|
|
1469
1501
|
Carousel.Item = CarouselItem
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
createMediaQueryStyles,
|
|
14
14
|
useResponsiveProp
|
|
15
15
|
} from '../utils'
|
|
16
|
+
import resolveContentMaxWidth from '../utils/resolveContentMaxWidth'
|
|
16
17
|
import Row from './Row'
|
|
17
18
|
import Col from './Col'
|
|
18
19
|
import GutterContext from './providers/GutterContext'
|
|
@@ -22,43 +23,16 @@ import { useViewport } from '../ViewportProvider'
|
|
|
22
23
|
|
|
23
24
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
|
|
24
25
|
|
|
25
|
-
const CONTENT_MAX_WIDTH = 'max'
|
|
26
|
-
const CONTENT_FULL_WIDTH = 'full'
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Resolves the maximum width for content based on the provided value and responsive width.
|
|
30
|
-
*
|
|
31
|
-
* @param {number|string|null|undefined} contentMinWidthValue - The minimum width value for the content.
|
|
32
|
-
* Can be a number, a special string constant (e.g., CONTENT_FULL_WIDTH, CONTENT_MAX_WIDTH), or null/undefined.
|
|
33
|
-
* @param {number} responsiveWidth - The responsive width to use when contentMinWidthValue is CONTENT_MAX_WIDTH.
|
|
34
|
-
* @returns {number|string|null} The resolved maximum width value, or null if full width is desired.
|
|
35
|
-
*/
|
|
36
|
-
const resolveContentMaxWidth = (contentMinWidthValue, responsiveWidth) => {
|
|
37
|
-
if (!contentMinWidthValue || contentMinWidthValue === CONTENT_FULL_WIDTH) {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (Number.isFinite(contentMinWidthValue)) {
|
|
42
|
-
return contentMinWidthValue
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (contentMinWidthValue === CONTENT_MAX_WIDTH) {
|
|
46
|
-
return responsiveWidth
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return contentMinWidthValue
|
|
50
|
-
}
|
|
51
|
-
|
|
52
26
|
/**
|
|
53
|
-
* Calculates the maximum width for a given viewport based on limitWidth and
|
|
27
|
+
* Calculates the maximum width for a given viewport based on limitWidth and contentMaxWidth settings.
|
|
54
28
|
*
|
|
55
29
|
* @param {string} viewportKey - The viewport key ('xs', 'sm', 'md', 'lg', 'xl')
|
|
56
30
|
* @param {boolean} limitWidth - Whether to limit the width to viewport breakpoints
|
|
57
|
-
* @param {any}
|
|
31
|
+
* @param {any} contentWidthProp - The contentMaxWidth (or contentMinWidth) prop value
|
|
58
32
|
* @param {number|string|null} maxWidth - The resolved max width value
|
|
59
33
|
* @returns {number|string|null} The calculated maximum width for the viewport
|
|
60
34
|
*/
|
|
61
|
-
const getMaxWidthForViewport = (viewportKey, limitWidth,
|
|
35
|
+
const getMaxWidthForViewport = (viewportKey, limitWidth, contentWidthProp, maxWidth) => {
|
|
62
36
|
if (limitWidth) {
|
|
63
37
|
if (viewportKey === 'xs') {
|
|
64
38
|
return null
|
|
@@ -66,7 +40,7 @@ const getMaxWidthForViewport = (viewportKey, limitWidth, contentMinWidth, maxWid
|
|
|
66
40
|
return viewports.map.get(viewportKey)
|
|
67
41
|
}
|
|
68
42
|
|
|
69
|
-
if (
|
|
43
|
+
if (contentWidthProp) {
|
|
70
44
|
return maxWidth
|
|
71
45
|
}
|
|
72
46
|
|
|
@@ -92,6 +66,7 @@ const FlexGrid = React.forwardRef(
|
|
|
92
66
|
accessibilityRole,
|
|
93
67
|
children,
|
|
94
68
|
dataSet,
|
|
69
|
+
contentMaxWidth,
|
|
95
70
|
contentMinWidth,
|
|
96
71
|
...rest
|
|
97
72
|
},
|
|
@@ -107,29 +82,31 @@ const FlexGrid = React.forwardRef(
|
|
|
107
82
|
let flexgridStyles
|
|
108
83
|
let mediaIds
|
|
109
84
|
|
|
110
|
-
|
|
85
|
+
// Support both contentMaxWidth and deprecated contentMinWidth for backwards compatibility
|
|
86
|
+
const contentWidthProp = contentMaxWidth || contentMinWidth
|
|
87
|
+
const contentWidthValue = useResponsiveProp(contentWidthProp)
|
|
111
88
|
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
112
|
-
const maxWidth = resolveContentMaxWidth(
|
|
89
|
+
const maxWidth = resolveContentMaxWidth(contentWidthValue, responsiveWidth)
|
|
113
90
|
|
|
114
91
|
const stylesByViewport = {
|
|
115
92
|
xs: {
|
|
116
|
-
maxWidth: getMaxWidthForViewport('xs', limitWidth,
|
|
93
|
+
maxWidth: getMaxWidthForViewport('xs', limitWidth, contentWidthProp, maxWidth),
|
|
117
94
|
flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
|
|
118
95
|
},
|
|
119
96
|
sm: {
|
|
120
|
-
maxWidth: getMaxWidthForViewport('sm', limitWidth,
|
|
97
|
+
maxWidth: getMaxWidthForViewport('sm', limitWidth, contentWidthProp, maxWidth),
|
|
121
98
|
flexDirection: reverseLevel[1] ? 'column-reverse' : 'column'
|
|
122
99
|
},
|
|
123
100
|
md: {
|
|
124
|
-
maxWidth: getMaxWidthForViewport('md', limitWidth,
|
|
101
|
+
maxWidth: getMaxWidthForViewport('md', limitWidth, contentWidthProp, maxWidth),
|
|
125
102
|
flexDirection: reverseLevel[2] ? 'column-reverse' : 'column'
|
|
126
103
|
},
|
|
127
104
|
lg: {
|
|
128
|
-
maxWidth: getMaxWidthForViewport('lg', limitWidth,
|
|
105
|
+
maxWidth: getMaxWidthForViewport('lg', limitWidth, contentWidthProp, maxWidth),
|
|
129
106
|
flexDirection: reverseLevel[3] ? 'column-reverse' : 'column'
|
|
130
107
|
},
|
|
131
108
|
xl: {
|
|
132
|
-
maxWidth: getMaxWidthForViewport('xl', limitWidth,
|
|
109
|
+
maxWidth: getMaxWidthForViewport('xl', limitWidth, contentWidthProp, maxWidth),
|
|
133
110
|
flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
|
|
134
111
|
}
|
|
135
112
|
}
|
|
@@ -223,7 +200,7 @@ FlexGrid.propTypes = {
|
|
|
223
200
|
*/
|
|
224
201
|
children: PropTypes.node.isRequired,
|
|
225
202
|
/**
|
|
226
|
-
* The
|
|
203
|
+
* The maximum width of the content in the FlexGrid.
|
|
227
204
|
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
228
205
|
* it will be the max content width for the desired viewport.
|
|
229
206
|
* - `xs`: 'max' | 'full' | <number>
|
|
@@ -232,6 +209,20 @@ FlexGrid.propTypes = {
|
|
|
232
209
|
* - `lg`: 'max' | 'full' | <number>
|
|
233
210
|
* - `xl`: 'max' | 'full' | <number>
|
|
234
211
|
*/
|
|
212
|
+
contentMaxWidth: PropTypes.shape({
|
|
213
|
+
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
214
|
+
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
215
|
+
md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
216
|
+
sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
217
|
+
xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
|
|
218
|
+
}),
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated Use `contentMaxWidth` instead. This prop will be removed in a future version.
|
|
221
|
+
*
|
|
222
|
+
* The minimum width of the content in the FlexGrid.
|
|
223
|
+
* This prop accepts responsive values for different viewports. If a number is provided,
|
|
224
|
+
* it will be the max content width for the desired viewport.
|
|
225
|
+
*/
|
|
235
226
|
contentMinWidth: PropTypes.shape({
|
|
236
227
|
xl: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
237
228
|
lg: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getTokensSetPropType,
|
|
9
9
|
selectSystemProps,
|
|
10
10
|
selectTokens,
|
|
11
|
+
variantProp,
|
|
11
12
|
viewProps
|
|
12
13
|
} from '../utils'
|
|
13
14
|
import ScrollViewEnd from './ScrollViewEnd'
|
|
@@ -38,7 +39,7 @@ const selectBorderStyles = (borderBottomWidth, borderBottomColor) => ({
|
|
|
38
39
|
* @TODO revisit `ScrollButton` after IconButton is stable.
|
|
39
40
|
*/
|
|
40
41
|
const HorizontalScroll = React.forwardRef(
|
|
41
|
-
({ ScrollButton, tokens, itemPositions, children, ...rest }, ref) => {
|
|
42
|
+
({ ScrollButton, tokens, itemPositions, variant, children, ...rest }, ref) => {
|
|
42
43
|
const {
|
|
43
44
|
nextIcon,
|
|
44
45
|
previousIcon,
|
|
@@ -74,8 +75,9 @@ const HorizontalScroll = React.forwardRef(
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const scrollMax = Math.max(0, contentWidth - containerWidth)
|
|
77
|
-
const
|
|
78
|
-
const
|
|
78
|
+
const hideNavigationButtons = variant?.hideNavigationButtons || false
|
|
79
|
+
const showNextButton = scrollOffset < scrollMax && !hideNavigationButtons
|
|
80
|
+
const showPreviousButton = scrollOffset > 0 && !hideNavigationButtons
|
|
79
81
|
|
|
80
82
|
const scrollRef = React.useRef(null)
|
|
81
83
|
const scrollTo = (targetX) => {
|
|
@@ -173,6 +175,7 @@ HorizontalScroll.propTypes = {
|
|
|
173
175
|
itemPositions: itemPositionsPropType,
|
|
174
176
|
ScrollButton: PropTypes.elementType,
|
|
175
177
|
tokens: getTokensSetPropType(tokenKeys, { allowFunction: true }),
|
|
178
|
+
variant: variantProp.propType,
|
|
176
179
|
children: PropTypes.node
|
|
177
180
|
}
|
|
178
181
|
|
package/src/Icon/Icon.jsx
CHANGED
|
@@ -14,6 +14,7 @@ const Icon = React.forwardRef(
|
|
|
14
14
|
tokens,
|
|
15
15
|
scalesWithText = false,
|
|
16
16
|
style = {},
|
|
17
|
+
testID,
|
|
17
18
|
dataSet
|
|
18
19
|
},
|
|
19
20
|
ref
|
|
@@ -45,6 +46,7 @@ const Icon = React.forwardRef(
|
|
|
45
46
|
const getIconContentForMobile = () => {
|
|
46
47
|
return (
|
|
47
48
|
<View
|
|
49
|
+
testID={testID}
|
|
48
50
|
style={{
|
|
49
51
|
backgroundColor: themeTokens.backgroundColor,
|
|
50
52
|
borderRadius: themeTokens.borderRadius,
|
|
@@ -58,6 +60,7 @@ const Icon = React.forwardRef(
|
|
|
58
60
|
|
|
59
61
|
return Platform.OS === 'web' ? (
|
|
60
62
|
<View
|
|
63
|
+
testID={testID}
|
|
61
64
|
ref={ref}
|
|
62
65
|
// eslint-disable-next-line react-native/no-inline-styles
|
|
63
66
|
style={{
|
|
@@ -136,6 +136,7 @@ const IconButton = React.forwardRef(
|
|
|
136
136
|
hrefAttrs,
|
|
137
137
|
testID,
|
|
138
138
|
accessibilityRole = href ? 'link' : 'button',
|
|
139
|
+
inactive = false,
|
|
139
140
|
...rawRest
|
|
140
141
|
},
|
|
141
142
|
ref
|
|
@@ -149,9 +150,10 @@ const IconButton = React.forwardRef(
|
|
|
149
150
|
linkProps.handleHref({ href, onPress })({ nativeEvent: { target: ref?.current?.id } })
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
const
|
|
153
|
+
const mergedVariant = inactive ? { ...variant, inactive: true } : variant
|
|
154
|
+
const getTokens = useThemeTokensCallback('IconButton', tokens, mergedVariant)
|
|
153
155
|
const getOuterStyle = (pressableState) =>
|
|
154
|
-
selectOuterStyle(getTokens(resolvePressableState(pressableState),
|
|
156
|
+
selectOuterStyle(getTokens(resolvePressableState(pressableState), mergedVariant.password))
|
|
155
157
|
|
|
156
158
|
return (
|
|
157
159
|
<Pressable
|
|
@@ -162,16 +164,17 @@ const IconButton = React.forwardRef(
|
|
|
162
164
|
style={getOuterStyle}
|
|
163
165
|
{...selectedProps}
|
|
164
166
|
testID={testID}
|
|
167
|
+
disabled={inactive}
|
|
165
168
|
>
|
|
166
169
|
{(pressableState) => {
|
|
167
170
|
const themeTokens = getTokens(resolvePressableState(pressableState))
|
|
168
171
|
return (
|
|
169
|
-
<View style={selectInnerStyle(themeTokens,
|
|
172
|
+
<View style={selectInnerStyle(themeTokens, mergedVariant.password)}>
|
|
170
173
|
<Icon
|
|
171
174
|
icon={IconComponent || themeTokens.icon}
|
|
172
175
|
title={selectedProps.accessibilityLabel}
|
|
173
176
|
tokens={selectTokens('Icon', themeTokens, 'icon')}
|
|
174
|
-
variant={
|
|
177
|
+
variant={mergedVariant}
|
|
175
178
|
/>
|
|
176
179
|
</View>
|
|
177
180
|
)
|
|
@@ -206,7 +209,11 @@ IconButton.propTypes = {
|
|
|
206
209
|
/**
|
|
207
210
|
* Function to execute when the `Iconbutton` is pressed
|
|
208
211
|
*/
|
|
209
|
-
onPress: PropTypes.func
|
|
212
|
+
onPress: PropTypes.func,
|
|
213
|
+
/**
|
|
214
|
+
* When true, applies the inactive variant styling
|
|
215
|
+
*/
|
|
216
|
+
inactive: PropTypes.bool
|
|
210
217
|
}
|
|
211
218
|
|
|
212
219
|
const staticStyles = StyleSheet.create({
|