@telus-uds/components-base 3.26.0 → 3.27.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 +19 -2
- package/lib/cjs/Card/Card.js +34 -13
- package/lib/cjs/Card/CardBase.js +78 -11
- package/lib/cjs/Card/PressableCardBase.js +147 -8
- package/lib/cjs/Carousel/Carousel.js +105 -50
- package/lib/cjs/Carousel/CarouselContext.js +10 -4
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/cjs/Carousel/Constants.js +11 -2
- package/lib/cjs/Checkbox/Checkbox.js +43 -13
- package/lib/cjs/List/List.js +24 -9
- package/lib/cjs/List/ListItem.js +18 -1
- package/lib/cjs/List/ListItemBase.js +27 -8
- package/lib/cjs/List/ListItemMark.js +33 -62
- package/lib/cjs/List/PressableListItemBase.js +1 -0
- package/lib/esm/Card/Card.js +34 -13
- package/lib/esm/Card/CardBase.js +78 -11
- package/lib/esm/Card/PressableCardBase.js +148 -9
- package/lib/esm/Carousel/Carousel.js +106 -51
- package/lib/esm/Carousel/CarouselContext.js +10 -4
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/esm/Carousel/Constants.js +10 -1
- package/lib/esm/Checkbox/Checkbox.js +43 -13
- package/lib/esm/List/List.js +24 -9
- package/lib/esm/List/ListItem.js +19 -2
- package/lib/esm/List/ListItemBase.js +27 -8
- package/lib/esm/List/ListItemMark.js +33 -62
- package/lib/esm/List/PressableListItemBase.js +1 -0
- package/lib/package.json +2 -2
- package/package.json +2 -2
- package/src/Card/Card.jsx +29 -7
- package/src/Card/CardBase.jsx +88 -8
- package/src/Card/PressableCardBase.jsx +135 -9
- package/src/Carousel/Carousel.jsx +119 -64
- package/src/Carousel/CarouselContext.jsx +12 -4
- package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
- package/src/Carousel/Constants.js +10 -0
- package/src/Checkbox/Checkbox.jsx +29 -7
- package/src/List/List.jsx +33 -9
- package/src/List/ListItem.jsx +33 -11
- package/src/List/ListItemBase.jsx +33 -9
- package/src/List/ListItemMark.jsx +32 -53
- package/src/List/PressableListItemBase.jsx +1 -0
package/src/Card/CardBase.jsx
CHANGED
|
@@ -116,6 +116,44 @@ const setBackgroundImage = ({
|
|
|
116
116
|
)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
const selectPaddedContentStyles = ({
|
|
120
|
+
paddingTop,
|
|
121
|
+
paddingBottom,
|
|
122
|
+
paddingLeft,
|
|
123
|
+
paddingRight,
|
|
124
|
+
borderWidth,
|
|
125
|
+
borderColor,
|
|
126
|
+
borderRadius,
|
|
127
|
+
hasInteractiveBorder
|
|
128
|
+
}) => ({
|
|
129
|
+
paddingTop,
|
|
130
|
+
paddingBottom,
|
|
131
|
+
paddingLeft,
|
|
132
|
+
paddingRight,
|
|
133
|
+
...(hasInteractiveBorder
|
|
134
|
+
? {
|
|
135
|
+
borderWidth,
|
|
136
|
+
borderColor,
|
|
137
|
+
borderRadius
|
|
138
|
+
}
|
|
139
|
+
: {})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const selectInteractiveOverlayStyles = ({ backgroundColor, borderRadius, borderWidth }) => {
|
|
143
|
+
const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth)
|
|
144
|
+
return {
|
|
145
|
+
position: 'absolute',
|
|
146
|
+
top: 0,
|
|
147
|
+
left: 0,
|
|
148
|
+
right: 0,
|
|
149
|
+
bottom: 0,
|
|
150
|
+
backgroundColor,
|
|
151
|
+
borderRadius: adjustedBorderRadius,
|
|
152
|
+
pointerEvents: 'none',
|
|
153
|
+
zIndex: 1
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
119
157
|
// Ensure explicit selection of tokens
|
|
120
158
|
export const selectStyles = ({
|
|
121
159
|
flex,
|
|
@@ -214,13 +252,55 @@ const CardBase = React.forwardRef(
|
|
|
214
252
|
const fullBleedPosition = useResponsiveProp(fullBleedContentPosition, 'bottom')
|
|
215
253
|
|
|
216
254
|
if (backgroundImage && src) {
|
|
217
|
-
const {
|
|
255
|
+
const {
|
|
256
|
+
paddingTop,
|
|
257
|
+
paddingBottom,
|
|
258
|
+
paddingLeft,
|
|
259
|
+
paddingRight,
|
|
260
|
+
borderWidth,
|
|
261
|
+
borderColor,
|
|
262
|
+
borderRadius,
|
|
263
|
+
backgroundColor,
|
|
264
|
+
...containerStyle
|
|
265
|
+
} = cardStyle
|
|
218
266
|
|
|
219
267
|
const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
268
|
+
const hasInteractiveBorder = borderWidth && borderWidth > 0
|
|
269
|
+
const hasInteractiveOverlay = isOverlayColor(backgroundColor)
|
|
270
|
+
|
|
271
|
+
const paddedContent =
|
|
272
|
+
hasPadding || hasInteractiveBorder ? (
|
|
273
|
+
<View
|
|
274
|
+
style={selectPaddedContentStyles({
|
|
275
|
+
paddingTop,
|
|
276
|
+
paddingBottom,
|
|
277
|
+
paddingLeft,
|
|
278
|
+
paddingRight,
|
|
279
|
+
borderWidth,
|
|
280
|
+
borderColor,
|
|
281
|
+
borderRadius,
|
|
282
|
+
hasInteractiveBorder
|
|
283
|
+
})}
|
|
284
|
+
>
|
|
285
|
+
{children}
|
|
286
|
+
</View>
|
|
287
|
+
) : (
|
|
288
|
+
children
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
const contentWithOverlay = (
|
|
292
|
+
<>
|
|
293
|
+
{hasInteractiveOverlay && Platform.OS === 'web' && (
|
|
294
|
+
<View
|
|
295
|
+
style={selectInteractiveOverlayStyles({
|
|
296
|
+
backgroundColor,
|
|
297
|
+
borderRadius,
|
|
298
|
+
borderWidth
|
|
299
|
+
})}
|
|
300
|
+
/>
|
|
301
|
+
)}
|
|
302
|
+
<View style={staticStyles.contentOverlay}>{paddedContent}</View>
|
|
303
|
+
</>
|
|
224
304
|
)
|
|
225
305
|
|
|
226
306
|
content = setBackgroundImage({
|
|
@@ -229,8 +309,8 @@ const CardBase = React.forwardRef(
|
|
|
229
309
|
backgroundImageResizeMode,
|
|
230
310
|
backgroundImagePosition,
|
|
231
311
|
backgroundImageAlign,
|
|
232
|
-
content:
|
|
233
|
-
cardStyle: containerStyle
|
|
312
|
+
content: contentWithOverlay,
|
|
313
|
+
cardStyle: { ...containerStyle, borderRadius }
|
|
234
314
|
})
|
|
235
315
|
|
|
236
316
|
return (
|
|
@@ -314,7 +394,7 @@ const staticStyles = StyleSheet.create({
|
|
|
314
394
|
position: 'relative',
|
|
315
395
|
width: '100%',
|
|
316
396
|
height: '100%',
|
|
317
|
-
zIndex:
|
|
397
|
+
zIndex: 2
|
|
318
398
|
},
|
|
319
399
|
containContainer: {
|
|
320
400
|
width: '100%',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import { Pressable, Platform, View, StyleSheet } from 'react-native'
|
|
4
|
-
|
|
5
4
|
import { useViewport } from '../ViewportProvider'
|
|
6
5
|
import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider'
|
|
7
6
|
import {
|
|
@@ -26,6 +25,71 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
|
|
|
26
25
|
viewProps
|
|
27
26
|
])
|
|
28
27
|
|
|
28
|
+
const selectFocusOverlayContainerStyles = (tokens) => {
|
|
29
|
+
const { flex, minWidth, marginTop, marginBottom, marginLeft, marginRight } = tokens
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
flex: flex || 1,
|
|
33
|
+
minWidth: minWidth || 0,
|
|
34
|
+
marginTop,
|
|
35
|
+
marginBottom,
|
|
36
|
+
marginLeft,
|
|
37
|
+
marginRight
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const selectFocusBorderStyles = (tokens) => {
|
|
42
|
+
const { borderWidth, borderColor, borderRadius } = tokens
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
borderWidth,
|
|
46
|
+
borderColor,
|
|
47
|
+
borderRadius: borderRadius || 0
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const FocusBorderOverlay = ({ tokens, pressableState, children }) => {
|
|
52
|
+
const { borderWidth = 0 } = tokens
|
|
53
|
+
const showFocusBorder = pressableState.focused && borderWidth > 0
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View
|
|
57
|
+
style={[
|
|
58
|
+
staticStyles.focusOverlayContainer,
|
|
59
|
+
selectFocusOverlayContainerStyles(tokens),
|
|
60
|
+
Platform.OS === 'web' && staticStyles.webOutlineNone
|
|
61
|
+
]}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
{showFocusBorder && (
|
|
65
|
+
<View
|
|
66
|
+
style={[staticStyles.focusBorder, selectFocusBorderStyles(tokens)]}
|
|
67
|
+
accessible={false}
|
|
68
|
+
importantForAccessibility="no"
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
FocusBorderOverlay.propTypes = {
|
|
76
|
+
tokens: PropTypes.shape({
|
|
77
|
+
flex: PropTypes.number,
|
|
78
|
+
minWidth: PropTypes.number,
|
|
79
|
+
marginTop: PropTypes.number,
|
|
80
|
+
marginBottom: PropTypes.number,
|
|
81
|
+
marginLeft: PropTypes.number,
|
|
82
|
+
marginRight: PropTypes.number,
|
|
83
|
+
borderColor: PropTypes.string,
|
|
84
|
+
borderWidth: PropTypes.number,
|
|
85
|
+
borderRadius: PropTypes.number
|
|
86
|
+
}).isRequired,
|
|
87
|
+
pressableState: PropTypes.shape({
|
|
88
|
+
focused: PropTypes.bool
|
|
89
|
+
}).isRequired,
|
|
90
|
+
children: PropTypes.node
|
|
91
|
+
}
|
|
92
|
+
|
|
29
93
|
const tokenKeys = [
|
|
30
94
|
'flex',
|
|
31
95
|
'backgroundColor',
|
|
@@ -103,11 +167,53 @@ const PressableCardBase = React.forwardRef(
|
|
|
103
167
|
'PressableCard'
|
|
104
168
|
)
|
|
105
169
|
|
|
106
|
-
const getCardTokens = (pressableState) =>
|
|
170
|
+
const getCardTokens = (pressableState) => {
|
|
171
|
+
const allTokens = getTokens(pressableState)
|
|
172
|
+
const cardTokens = selectTokens('Card', allTokens)
|
|
173
|
+
|
|
174
|
+
// Handle focus border transparency to avoid double borders
|
|
175
|
+
if (pressableState.focused && allTokens.borderWidth > 0) {
|
|
176
|
+
const result = {
|
|
177
|
+
...cardTokens,
|
|
178
|
+
borderColor: 'transparent'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Also handle backgroundImage for interactive states
|
|
182
|
+
if (backgroundImage) {
|
|
183
|
+
const { hovered, pressed, focused } = pressableState || {}
|
|
184
|
+
const isInteractiveState = hovered || pressed || focused
|
|
185
|
+
|
|
186
|
+
if (!isInteractiveState) {
|
|
187
|
+
const { backgroundColor, ...restTokens } = result
|
|
188
|
+
return restTokens
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return result
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Handle backgroundImage when not in focus state
|
|
196
|
+
if (backgroundImage) {
|
|
197
|
+
const { hovered, pressed, focused } = pressableState || {}
|
|
198
|
+
const isInteractiveState = hovered || pressed || focused
|
|
199
|
+
|
|
200
|
+
if (!isInteractiveState) {
|
|
201
|
+
const { backgroundColor, ...restTokens } = cardTokens
|
|
202
|
+
return restTokens
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return cardTokens
|
|
207
|
+
}
|
|
208
|
+
|
|
107
209
|
const getOuterBorderStyle = (pressableState) => {
|
|
108
210
|
const {
|
|
109
211
|
flex,
|
|
110
212
|
minWidth,
|
|
213
|
+
marginTop,
|
|
214
|
+
marginBottom,
|
|
215
|
+
marginLeft,
|
|
216
|
+
marginRight,
|
|
111
217
|
outerBorderColor,
|
|
112
218
|
outerBorderGap = 0,
|
|
113
219
|
outerBorderWidth = 0,
|
|
@@ -116,6 +222,10 @@ const PressableCardBase = React.forwardRef(
|
|
|
116
222
|
return {
|
|
117
223
|
flex,
|
|
118
224
|
minWidth: minWidth + outerBorderGap + outerBorderWidth,
|
|
225
|
+
marginTop,
|
|
226
|
+
marginBottom,
|
|
227
|
+
marginLeft,
|
|
228
|
+
marginRight,
|
|
119
229
|
...applyOuterBorder({ outerBorderColor, outerBorderGap, outerBorderWidth, borderRadius }),
|
|
120
230
|
...Platform.select({ web: { outline: 'none' } })
|
|
121
231
|
}
|
|
@@ -194,13 +304,15 @@ const PressableCardBase = React.forwardRef(
|
|
|
194
304
|
{...selectProps({ ...rest, accessibilityRole })}
|
|
195
305
|
>
|
|
196
306
|
{(pressableState) => (
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
307
|
+
<FocusBorderOverlay tokens={getTokens(pressableState)} pressableState={pressableState}>
|
|
308
|
+
<CardBase
|
|
309
|
+
tokens={getCardTokens(pressableState)}
|
|
310
|
+
backgroundImage={backgroundImage}
|
|
311
|
+
fullBleedContent={fullBleedContent}
|
|
312
|
+
>
|
|
313
|
+
{typeof children === 'function' ? children(getCardState(pressableState)) : children}
|
|
314
|
+
</CardBase>
|
|
315
|
+
</FocusBorderOverlay>
|
|
204
316
|
)}
|
|
205
317
|
</Pressable>
|
|
206
318
|
)
|
|
@@ -218,6 +330,20 @@ const staticStyles = StyleSheet.create({
|
|
|
218
330
|
alignItems: 'stretch',
|
|
219
331
|
justifyContent: 'flex-start',
|
|
220
332
|
textDecorationLine: 'none'
|
|
333
|
+
},
|
|
334
|
+
focusOverlayContainer: {
|
|
335
|
+
position: 'relative'
|
|
336
|
+
},
|
|
337
|
+
webOutlineNone: {
|
|
338
|
+
outline: 'none'
|
|
339
|
+
},
|
|
340
|
+
focusBorder: {
|
|
341
|
+
position: 'absolute',
|
|
342
|
+
top: 0,
|
|
343
|
+
left: 0,
|
|
344
|
+
right: 0,
|
|
345
|
+
bottom: 0,
|
|
346
|
+
pointerEvents: 'none'
|
|
221
347
|
}
|
|
222
348
|
})
|
|
223
349
|
|
|
@@ -45,12 +45,13 @@ import {
|
|
|
45
45
|
LARGE_VIEWPORT_MARGIN,
|
|
46
46
|
DEFAULT_VIEWPORT_MARGIN,
|
|
47
47
|
PEEKING_MULTIPLIER,
|
|
48
|
-
ACTIVE_INDEX_OFFSET_MULTIPLIER,
|
|
49
48
|
NEGATIVE_MULTIPLIER,
|
|
50
49
|
TRANSITION_MODES,
|
|
51
50
|
SWIPE_RELEASE_STYLES,
|
|
52
51
|
INSTANT_ANIMATION_DURATION,
|
|
53
|
-
DEFAULT_SWIPE_RELEASE_DURATION
|
|
52
|
+
DEFAULT_SWIPE_RELEASE_DURATION,
|
|
53
|
+
POSITION_VARIANTS,
|
|
54
|
+
POSITION_PROPERTIES
|
|
54
55
|
} from './Constants'
|
|
55
56
|
|
|
56
57
|
const staticStyles = StyleSheet.create({
|
|
@@ -112,7 +113,7 @@ const selectHeroContainerStyles = (width, hidden) => ({
|
|
|
112
113
|
})
|
|
113
114
|
|
|
114
115
|
const getDynamicPositionProperty = (areStylesAppliedOnPreviousButton) =>
|
|
115
|
-
areStylesAppliedOnPreviousButton ?
|
|
116
|
+
areStylesAppliedOnPreviousButton ? POSITION_PROPERTIES.LEFT : POSITION_PROPERTIES.RIGHT
|
|
116
117
|
|
|
117
118
|
const selectControlButtonPositionStyles = ({
|
|
118
119
|
positionVariant,
|
|
@@ -122,24 +123,41 @@ const selectControlButtonPositionStyles = ({
|
|
|
122
123
|
enablePeeking,
|
|
123
124
|
enableDisplayMultipleItemsPerSlide,
|
|
124
125
|
isAutoPlayEnabled,
|
|
125
|
-
viewport
|
|
126
|
+
viewport,
|
|
127
|
+
maxWidth,
|
|
128
|
+
viewportWidth
|
|
126
129
|
}) => {
|
|
127
130
|
const styles = {}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
|
|
132
|
+
let positionOffset = 0
|
|
133
|
+
|
|
134
|
+
if (positionVariant === POSITION_VARIANTS.EDGE) {
|
|
135
|
+
positionOffset = -1 * (buttonWidth / 2)
|
|
136
|
+
} else if (positionVariant === POSITION_VARIANTS.INSIDE) {
|
|
137
|
+
positionOffset = DEFAULT_POSITION_OFFSET
|
|
138
|
+
} else if (positionVariant === POSITION_VARIANTS.OUTSIDE) {
|
|
133
139
|
if (
|
|
134
140
|
enablePeeking ||
|
|
135
141
|
enableDisplayMultipleItemsPerSlide ||
|
|
136
142
|
(isAutoPlayEnabled && viewport === 'xs')
|
|
137
143
|
) {
|
|
138
|
-
|
|
144
|
+
positionOffset = 0
|
|
139
145
|
} else {
|
|
140
|
-
|
|
146
|
+
positionOffset = -1 * (spaceBetweenSlideAndButton + buttonWidth)
|
|
141
147
|
}
|
|
142
148
|
}
|
|
149
|
+
|
|
150
|
+
if (enablePeeking) {
|
|
151
|
+
if (positionProperty === POSITION_PROPERTIES.RIGHT) {
|
|
152
|
+
const rightMargin = (viewportWidth - maxWidth) / 2
|
|
153
|
+
positionOffset += rightMargin
|
|
154
|
+
} else if (positionProperty === POSITION_PROPERTIES.LEFT) {
|
|
155
|
+
const leftMargin = (viewportWidth - maxWidth) / 2
|
|
156
|
+
positionOffset += leftMargin
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
styles[positionProperty] = positionOffset
|
|
143
161
|
return styles
|
|
144
162
|
}
|
|
145
163
|
|
|
@@ -153,7 +171,9 @@ const selectPreviousNextNavigationButtonStyles = (
|
|
|
153
171
|
enablePeeking,
|
|
154
172
|
enableDisplayMultipleItemsPerSlide,
|
|
155
173
|
isAutoPlayEnabled,
|
|
156
|
-
viewport
|
|
174
|
+
viewport,
|
|
175
|
+
maxWidth,
|
|
176
|
+
viewportWidth
|
|
157
177
|
) => {
|
|
158
178
|
const styles = {
|
|
159
179
|
zIndex: 1,
|
|
@@ -177,7 +197,9 @@ const selectPreviousNextNavigationButtonStyles = (
|
|
|
177
197
|
enablePeeking,
|
|
178
198
|
enableDisplayMultipleItemsPerSlide,
|
|
179
199
|
isAutoPlayEnabled,
|
|
180
|
-
viewport
|
|
200
|
+
viewport,
|
|
201
|
+
maxWidth,
|
|
202
|
+
viewportWidth
|
|
181
203
|
})
|
|
182
204
|
}
|
|
183
205
|
}
|
|
@@ -246,7 +268,7 @@ const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) =
|
|
|
246
268
|
return ITEMS_PER_VIEWPORT_XS_SM
|
|
247
269
|
}
|
|
248
270
|
|
|
249
|
-
const selectRootContainerStyles = (enableHero, viewport) => {
|
|
271
|
+
const selectRootContainerStyles = (enableHero, viewport, enablePeeking) => {
|
|
250
272
|
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
251
273
|
return {
|
|
252
274
|
alignItems: 'center'
|
|
@@ -257,16 +279,26 @@ const selectRootContainerStyles = (enableHero, viewport) => {
|
|
|
257
279
|
paddingHorizontal: 16
|
|
258
280
|
}
|
|
259
281
|
}
|
|
282
|
+
if (enablePeeking) {
|
|
283
|
+
return {
|
|
284
|
+
width: '100%'
|
|
285
|
+
}
|
|
286
|
+
}
|
|
260
287
|
return {}
|
|
261
288
|
}
|
|
262
289
|
|
|
263
|
-
const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
264
|
-
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
290
|
+
const selectMainContainerStyles = (enableHero, viewport, maxWidth, enablePeeking) => {
|
|
291
|
+
if (enableHero && viewport === 'xl' && Platform.OS === 'web' && !enablePeeking) {
|
|
265
292
|
return {
|
|
266
293
|
width: '100%',
|
|
267
294
|
maxWidth: maxWidth || 1200
|
|
268
295
|
}
|
|
269
296
|
}
|
|
297
|
+
if (enablePeeking) {
|
|
298
|
+
return {
|
|
299
|
+
width: '100%'
|
|
300
|
+
}
|
|
301
|
+
}
|
|
270
302
|
if (maxWidth !== null && maxWidth !== undefined) {
|
|
271
303
|
return {
|
|
272
304
|
maxWidth,
|
|
@@ -277,16 +309,24 @@ const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
|
277
309
|
return {}
|
|
278
310
|
}
|
|
279
311
|
|
|
280
|
-
const selectNavigationStyles = (tabs, enableHero, viewport) => {
|
|
312
|
+
const selectNavigationStyles = (tabs, enableHero, viewport, enablePeeking, maxWidth) => {
|
|
281
313
|
let marginHorizontal = 0
|
|
282
314
|
|
|
283
315
|
if (enableHero && tabs) {
|
|
284
316
|
marginHorizontal = viewport === 'xl' ? LARGE_VIEWPORT_MARGIN : DEFAULT_VIEWPORT_MARGIN
|
|
285
317
|
}
|
|
286
318
|
|
|
287
|
-
|
|
319
|
+
const styles = {
|
|
288
320
|
marginHorizontal
|
|
289
321
|
}
|
|
322
|
+
|
|
323
|
+
if (enablePeeking && maxWidth) {
|
|
324
|
+
styles.maxWidth = maxWidth
|
|
325
|
+
styles.alignSelf = 'center'
|
|
326
|
+
styles.width = '100%'
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return styles
|
|
290
330
|
}
|
|
291
331
|
|
|
292
332
|
/**
|
|
@@ -295,22 +335,16 @@ const selectNavigationStyles = (tabs, enableHero, viewport) => {
|
|
|
295
335
|
* @param {number} containerWidth - The width of the carousel container.
|
|
296
336
|
* @param {boolean} enablePeeking - Flag indicating whether peeking is enabled.
|
|
297
337
|
* @param {Object} viewport - The viewport configuration object used to determine peeking properties.
|
|
298
|
-
* @param {
|
|
338
|
+
* @param {number} maxWidth - The maximum width constraint for the carousel content.
|
|
299
339
|
* @returns {number} The calculated final width of the carousel container.
|
|
300
340
|
*/
|
|
301
|
-
const calculateFinalWidth = (containerWidth, enablePeeking, viewport,
|
|
341
|
+
const calculateFinalWidth = (containerWidth, enablePeeking, viewport, maxWidth) => {
|
|
302
342
|
let finalWidth = containerWidth
|
|
303
343
|
|
|
304
344
|
if (enablePeeking) {
|
|
305
|
-
const { peekingGap, peekingMiddleSpace
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (activeIndexRef.current === 0) {
|
|
310
|
-
finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace
|
|
311
|
-
} else {
|
|
312
|
-
finalWidth = slideWide + peekingGap
|
|
313
|
-
}
|
|
345
|
+
const { peekingGap, peekingMiddleSpace } = getPeekingProps(viewport)
|
|
346
|
+
const baseWidth = maxWidth || containerWidth
|
|
347
|
+
finalWidth = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap
|
|
314
348
|
}
|
|
315
349
|
|
|
316
350
|
return finalWidth
|
|
@@ -431,7 +465,15 @@ const Carousel = React.forwardRef(
|
|
|
431
465
|
|
|
432
466
|
const contentMaxWidthValue = useResponsiveProp(contentMaxWidth)
|
|
433
467
|
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
434
|
-
|
|
468
|
+
|
|
469
|
+
let maxWidth = null
|
|
470
|
+
if (enablePeeking || contentMaxWidth !== undefined) {
|
|
471
|
+
maxWidth =
|
|
472
|
+
contentMaxWidthValue === undefined
|
|
473
|
+
? responsiveWidth
|
|
474
|
+
: resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth)
|
|
475
|
+
}
|
|
476
|
+
|
|
435
477
|
const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport)
|
|
436
478
|
const autoPlayFeatureEnabled =
|
|
437
479
|
autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1
|
|
@@ -494,8 +536,17 @@ const Carousel = React.forwardRef(
|
|
|
494
536
|
const isSwiping = React.useRef(false)
|
|
495
537
|
const autoPlayRef = React.useRef(null)
|
|
496
538
|
|
|
539
|
+
const [rootContainerLayout, setRootContainerLayout] = React.useState({
|
|
540
|
+
x: 0,
|
|
541
|
+
y: 0,
|
|
542
|
+
width: 0,
|
|
543
|
+
height: 0
|
|
544
|
+
})
|
|
545
|
+
const rootContainerLayoutRef = React.useRef(rootContainerLayout)
|
|
546
|
+
|
|
497
547
|
const isFirstSlide = !activeIndex
|
|
498
548
|
const isLastSlide = activeIndex + 1 >= totalItems
|
|
549
|
+
const currentViewportWidth = rootContainerLayout.width
|
|
499
550
|
|
|
500
551
|
const handleAnimationStart = React.useCallback(
|
|
501
552
|
(...args) => {
|
|
@@ -518,21 +569,16 @@ const Carousel = React.forwardRef(
|
|
|
518
569
|
|
|
519
570
|
const updateOffset = React.useCallback(() => {
|
|
520
571
|
if (enablePeeking) {
|
|
521
|
-
const { peekingGap, peekingMiddleSpace
|
|
572
|
+
const { peekingGap, peekingMiddleSpace } = getPeekingProps(viewport)
|
|
522
573
|
|
|
523
574
|
let finalWidth
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
(peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER)
|
|
575
|
+
const baseWidth = maxWidth || containerLayoutRef.current.width
|
|
576
|
+
const slideWide = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER
|
|
527
577
|
|
|
528
578
|
if (activeIndexRef.current === 0) {
|
|
529
579
|
finalWidth = 0
|
|
530
580
|
} else {
|
|
531
|
-
finalWidth =
|
|
532
|
-
slideWide +
|
|
533
|
-
peekingMarginLeft -
|
|
534
|
-
peekingMiddleSpace +
|
|
535
|
-
(slideWide + peekingGap) * (activeIndexRef.current - ACTIVE_INDEX_OFFSET_MULTIPLIER)
|
|
581
|
+
finalWidth = (slideWide + peekingGap) * activeIndexRef.current
|
|
536
582
|
}
|
|
537
583
|
|
|
538
584
|
animatedX.current = finalWidth * NEGATIVE_MULTIPLIER
|
|
@@ -558,7 +604,7 @@ const Carousel = React.forwardRef(
|
|
|
558
604
|
})
|
|
559
605
|
heroPan.setValue({ x: 0, y: 0 })
|
|
560
606
|
}
|
|
561
|
-
}, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking])
|
|
607
|
+
}, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking, maxWidth])
|
|
562
608
|
|
|
563
609
|
const animate = React.useCallback(
|
|
564
610
|
(panToAnimate, toValue, toIndex, isSwipeRelease = false) => {
|
|
@@ -664,7 +710,7 @@ const Carousel = React.forwardRef(
|
|
|
664
710
|
containerLayoutRef.current.width,
|
|
665
711
|
enablePeeking,
|
|
666
712
|
viewport,
|
|
667
|
-
|
|
713
|
+
maxWidth
|
|
668
714
|
)
|
|
669
715
|
|
|
670
716
|
toValue.x = finalWidth * -1 * calcDelta
|
|
@@ -710,7 +756,8 @@ const Carousel = React.forwardRef(
|
|
|
710
756
|
enablePeeking,
|
|
711
757
|
pan,
|
|
712
758
|
heroPan,
|
|
713
|
-
enableHero
|
|
759
|
+
enableHero,
|
|
760
|
+
maxWidth
|
|
714
761
|
]
|
|
715
762
|
)
|
|
716
763
|
|
|
@@ -767,6 +814,10 @@ const Carousel = React.forwardRef(
|
|
|
767
814
|
heroContainerLayoutRef.current = heroContainerLayout
|
|
768
815
|
}, [heroContainerLayout])
|
|
769
816
|
|
|
817
|
+
React.useEffect(() => {
|
|
818
|
+
rootContainerLayoutRef.current = rootContainerLayout
|
|
819
|
+
}, [rootContainerLayout])
|
|
820
|
+
|
|
770
821
|
React.useEffect(() => {
|
|
771
822
|
pan.x.addListener(({ value }) => {
|
|
772
823
|
animatedX.current = value
|
|
@@ -849,6 +900,12 @@ const Carousel = React.forwardRef(
|
|
|
849
900
|
}
|
|
850
901
|
}) => setPreviousNextNavigationButtonWidth(width)
|
|
851
902
|
|
|
903
|
+
const onRootContainerLayout = ({
|
|
904
|
+
nativeEvent: {
|
|
905
|
+
layout: { x, y, width, height }
|
|
906
|
+
}
|
|
907
|
+
}) => setRootContainerLayout((prevState) => ({ ...prevState, x, y, width, height }))
|
|
908
|
+
|
|
852
909
|
const isSwipeAllowed = React.useCallback(() => {
|
|
853
910
|
if (totalItems === 1) {
|
|
854
911
|
return false
|
|
@@ -1071,8 +1128,11 @@ const Carousel = React.forwardRef(
|
|
|
1071
1128
|
)
|
|
1072
1129
|
|
|
1073
1130
|
return (
|
|
1074
|
-
<View
|
|
1075
|
-
|
|
1131
|
+
<View
|
|
1132
|
+
style={selectRootContainerStyles(enableHero, viewport, enablePeeking)}
|
|
1133
|
+
onLayout={onRootContainerLayout}
|
|
1134
|
+
>
|
|
1135
|
+
<View style={selectMainContainerStyles(enableHero, viewport, maxWidth, enablePeeking)}>
|
|
1076
1136
|
<CarouselProvider
|
|
1077
1137
|
activeIndex={activeIndex}
|
|
1078
1138
|
goTo={goTo}
|
|
@@ -1087,6 +1147,8 @@ const Carousel = React.forwardRef(
|
|
|
1087
1147
|
enableDisplayMultipleItemsPerSlide,
|
|
1088
1148
|
viewport
|
|
1089
1149
|
)}
|
|
1150
|
+
maxWidth={maxWidth}
|
|
1151
|
+
viewportWidth={currentViewportWidth}
|
|
1090
1152
|
>
|
|
1091
1153
|
<View
|
|
1092
1154
|
style={[
|
|
@@ -1113,7 +1175,9 @@ const Carousel = React.forwardRef(
|
|
|
1113
1175
|
enablePeeking,
|
|
1114
1176
|
enableDisplayMultipleItemsPerSlide,
|
|
1115
1177
|
isAutoPlayEnabled,
|
|
1116
|
-
viewport
|
|
1178
|
+
viewport,
|
|
1179
|
+
maxWidth,
|
|
1180
|
+
viewportWidth: currentViewportWidth
|
|
1117
1181
|
})
|
|
1118
1182
|
]}
|
|
1119
1183
|
>
|
|
@@ -1139,7 +1203,9 @@ const Carousel = React.forwardRef(
|
|
|
1139
1203
|
enablePeeking,
|
|
1140
1204
|
enableDisplayMultipleItemsPerSlide,
|
|
1141
1205
|
isAutoPlayEnabled,
|
|
1142
|
-
viewport
|
|
1206
|
+
viewport,
|
|
1207
|
+
maxWidth,
|
|
1208
|
+
currentViewportWidth
|
|
1143
1209
|
)}
|
|
1144
1210
|
testID="previous-button-container"
|
|
1145
1211
|
>
|
|
@@ -1192,22 +1258,7 @@ const Carousel = React.forwardRef(
|
|
|
1192
1258
|
let hidden = !isAnimating && index !== activeIndex
|
|
1193
1259
|
|
|
1194
1260
|
if (enablePeeking && !isAnimating) {
|
|
1195
|
-
|
|
1196
|
-
const maxItemsForSlide = getMaximumItemsForSlide(
|
|
1197
|
-
enableDisplayMultipleItemsPerSlide,
|
|
1198
|
-
viewport
|
|
1199
|
-
)
|
|
1200
|
-
if (
|
|
1201
|
-
index >= activeIndex * maxItemsForSlide - 1 &&
|
|
1202
|
-
index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1
|
|
1203
|
-
) {
|
|
1204
|
-
hidden = false
|
|
1205
|
-
} else {
|
|
1206
|
-
hidden = true
|
|
1207
|
-
}
|
|
1208
|
-
} else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
|
|
1209
|
-
hidden = false
|
|
1210
|
-
}
|
|
1261
|
+
hidden = false
|
|
1211
1262
|
} else if (
|
|
1212
1263
|
!enablePeeking &&
|
|
1213
1264
|
enableDisplayMultipleItemsPerSlide &&
|
|
@@ -1250,7 +1301,9 @@ const Carousel = React.forwardRef(
|
|
|
1250
1301
|
enablePeeking,
|
|
1251
1302
|
enableDisplayMultipleItemsPerSlide,
|
|
1252
1303
|
isAutoPlayEnabled,
|
|
1253
|
-
viewport
|
|
1304
|
+
viewport,
|
|
1305
|
+
maxWidth,
|
|
1306
|
+
currentViewportWidth
|
|
1254
1307
|
)}
|
|
1255
1308
|
testID="next-button-container"
|
|
1256
1309
|
>
|
|
@@ -1268,7 +1321,9 @@ const Carousel = React.forwardRef(
|
|
|
1268
1321
|
</View>
|
|
1269
1322
|
) : null}
|
|
1270
1323
|
</View>
|
|
1271
|
-
<View
|
|
1324
|
+
<View
|
|
1325
|
+
style={selectNavigationStyles(tabs, enableHero, viewport, enablePeeking, maxWidth)}
|
|
1326
|
+
>
|
|
1272
1327
|
{showPanelNavigation ? activePanelNavigation : null}
|
|
1273
1328
|
</View>
|
|
1274
1329
|
</CarouselProvider>
|