@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/lib/cjs/Card/Card.js +34 -13
  3. package/lib/cjs/Card/CardBase.js +78 -11
  4. package/lib/cjs/Card/PressableCardBase.js +147 -8
  5. package/lib/cjs/Carousel/Carousel.js +105 -50
  6. package/lib/cjs/Carousel/CarouselContext.js +10 -4
  7. package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
  8. package/lib/cjs/Carousel/Constants.js +11 -2
  9. package/lib/cjs/Checkbox/Checkbox.js +43 -13
  10. package/lib/cjs/List/List.js +24 -9
  11. package/lib/cjs/List/ListItem.js +18 -1
  12. package/lib/cjs/List/ListItemBase.js +27 -8
  13. package/lib/cjs/List/ListItemMark.js +33 -62
  14. package/lib/cjs/List/PressableListItemBase.js +1 -0
  15. package/lib/esm/Card/Card.js +34 -13
  16. package/lib/esm/Card/CardBase.js +78 -11
  17. package/lib/esm/Card/PressableCardBase.js +148 -9
  18. package/lib/esm/Carousel/Carousel.js +106 -51
  19. package/lib/esm/Carousel/CarouselContext.js +10 -4
  20. package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
  21. package/lib/esm/Carousel/Constants.js +10 -1
  22. package/lib/esm/Checkbox/Checkbox.js +43 -13
  23. package/lib/esm/List/List.js +24 -9
  24. package/lib/esm/List/ListItem.js +19 -2
  25. package/lib/esm/List/ListItemBase.js +27 -8
  26. package/lib/esm/List/ListItemMark.js +33 -62
  27. package/lib/esm/List/PressableListItemBase.js +1 -0
  28. package/lib/package.json +2 -2
  29. package/package.json +2 -2
  30. package/src/Card/Card.jsx +29 -7
  31. package/src/Card/CardBase.jsx +88 -8
  32. package/src/Card/PressableCardBase.jsx +135 -9
  33. package/src/Carousel/Carousel.jsx +119 -64
  34. package/src/Carousel/CarouselContext.jsx +12 -4
  35. package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
  36. package/src/Carousel/Constants.js +10 -0
  37. package/src/Checkbox/Checkbox.jsx +29 -7
  38. package/src/List/List.jsx +33 -9
  39. package/src/List/ListItem.jsx +33 -11
  40. package/src/List/ListItemBase.jsx +33 -9
  41. package/src/List/ListItemMark.jsx +32 -53
  42. package/src/List/PressableListItemBase.jsx +1 -0
@@ -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 { paddingTop, paddingBottom, paddingLeft, paddingRight, ...containerStyle } = cardStyle
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 paddedContent = hasPadding ? (
221
- <View style={{ paddingTop, paddingBottom, paddingLeft, paddingRight }}>{children}</View>
222
- ) : (
223
- children
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: paddedContent,
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: 1
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) => selectTokens('Card', getTokens(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
- <CardBase
198
- tokens={getCardTokens(pressableState)}
199
- backgroundImage={backgroundImage}
200
- fullBleedContent={fullBleedContent}
201
- >
202
- {typeof children === 'function' ? children(getCardState(pressableState)) : children}
203
- </CardBase>
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 ? 'left' : 'right'
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
- if (positionVariant === 'edge') {
129
- styles[positionProperty] = -1 * (buttonWidth / 2)
130
- } else if (positionVariant === 'inside') {
131
- styles[positionProperty] = DEFAULT_POSITION_OFFSET
132
- } else if (positionVariant === 'outside') {
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
- styles[positionProperty] = 0
144
+ positionOffset = 0
139
145
  } else {
140
- styles[positionProperty] = -1 * (spaceBetweenSlideAndButton + buttonWidth)
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
- return {
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 {React.MutableRefObject<number>} activeIndexRef - A ref object holding the current active index of the carousel.
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, activeIndexRef) => {
341
+ const calculateFinalWidth = (containerWidth, enablePeeking, viewport, maxWidth) => {
302
342
  let finalWidth = containerWidth
303
343
 
304
344
  if (enablePeeking) {
305
- const { peekingGap, peekingMiddleSpace, peekingMarginLeft } = getPeekingProps(viewport)
306
- const slideWide =
307
- containerWidth - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER)
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
- const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth)
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, peekingMarginLeft } = getPeekingProps(viewport)
572
+ const { peekingGap, peekingMiddleSpace } = getPeekingProps(viewport)
522
573
 
523
574
  let finalWidth
524
- const slideWide =
525
- containerLayoutRef.current.width -
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
- activeIndexRef
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 style={selectRootContainerStyles(enableHero, viewport)}>
1075
- <View style={selectMainContainerStyles(enableHero, viewport, maxWidth)}>
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
- if (enableDisplayMultipleItemsPerSlide) {
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 style={selectNavigationStyles(tabs, enableHero, viewport)}>
1324
+ <View
1325
+ style={selectNavigationStyles(tabs, enableHero, viewport, enablePeeking, maxWidth)}
1326
+ >
1272
1327
  {showPanelNavigation ? activePanelNavigation : null}
1273
1328
  </View>
1274
1329
  </CarouselProvider>