@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
@@ -8,8 +8,74 @@ import { useViewport } from '../ViewportProvider';
8
8
  import { applyOuterBorder, validateThemeTokens } from '../ThemeProvider';
9
9
  import { a11yProps, clickProps, focusHandlerProps, getTokensSetPropType, linkProps, resolvePressableState, resolvePressableTokens, selectSystemProps, selectTokens, variantProp, viewProps, withLinkRouter } from '../utils';
10
10
  import CardBase, { fullBleedContentPropTypes } from './CardBase';
11
- import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, focusHandlerProps, viewProps]);
13
+ const selectFocusOverlayContainerStyles = tokens => {
14
+ const {
15
+ flex,
16
+ minWidth,
17
+ marginTop,
18
+ marginBottom,
19
+ marginLeft,
20
+ marginRight
21
+ } = tokens;
22
+ return {
23
+ flex: flex || 1,
24
+ minWidth: minWidth || 0,
25
+ marginTop,
26
+ marginBottom,
27
+ marginLeft,
28
+ marginRight
29
+ };
30
+ };
31
+ const selectFocusBorderStyles = tokens => {
32
+ const {
33
+ borderWidth,
34
+ borderColor,
35
+ borderRadius
36
+ } = tokens;
37
+ return {
38
+ borderWidth,
39
+ borderColor,
40
+ borderRadius: borderRadius || 0
41
+ };
42
+ };
43
+ const FocusBorderOverlay = _ref => {
44
+ let {
45
+ tokens,
46
+ pressableState,
47
+ children
48
+ } = _ref;
49
+ const {
50
+ borderWidth = 0
51
+ } = tokens;
52
+ const showFocusBorder = pressableState.focused && borderWidth > 0;
53
+ return /*#__PURE__*/_jsxs(View, {
54
+ style: [staticStyles.focusOverlayContainer, selectFocusOverlayContainerStyles(tokens), Platform.OS === 'web' && staticStyles.webOutlineNone],
55
+ children: [children, showFocusBorder && /*#__PURE__*/_jsx(View, {
56
+ style: [staticStyles.focusBorder, selectFocusBorderStyles(tokens)],
57
+ accessible: false,
58
+ importantForAccessibility: "no"
59
+ })]
60
+ });
61
+ };
62
+ FocusBorderOverlay.propTypes = {
63
+ tokens: PropTypes.shape({
64
+ flex: PropTypes.number,
65
+ minWidth: PropTypes.number,
66
+ marginTop: PropTypes.number,
67
+ marginBottom: PropTypes.number,
68
+ marginLeft: PropTypes.number,
69
+ marginRight: PropTypes.number,
70
+ borderColor: PropTypes.string,
71
+ borderWidth: PropTypes.number,
72
+ borderRadius: PropTypes.number
73
+ }).isRequired,
74
+ pressableState: PropTypes.shape({
75
+ focused: PropTypes.bool
76
+ }).isRequired,
77
+ children: PropTypes.node
78
+ };
13
79
  const tokenKeys = ['flex', 'backgroundColor', 'borderColor', 'gradient', 'borderRadius', 'borderWidth', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'marginTop', 'marginBottom', 'marginLeft', 'marginRight', 'minWidth', 'shadow', 'contentAlignItems', 'contentJustifyContent', 'contentFlexGrow', 'contentFlexShrink',
14
80
  // Outer border tokens. TODO: centralise common token sets like these as part of
15
81
  // https://github.com/telus/universal-design-system/issues/782
@@ -21,7 +87,7 @@ export const selectPressableCardTokens = tokens => Object.fromEntries(tokenKeys.
21
87
  * based on these to an outer border and a base Card component. Not intended to be used in
22
88
  * apps or sites directly: build themed components on top of this.
23
89
  */
24
- const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
90
+ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
25
91
  let {
26
92
  children,
27
93
  tokens,
@@ -35,7 +101,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
35
101
  fullBleedContent,
36
102
  accessibilityRole = href ? 'link' : undefined,
37
103
  ...rawRest
38
- } = _ref;
104
+ } = _ref2;
39
105
  const {
40
106
  onPress,
41
107
  ...rest
@@ -52,11 +118,62 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
52
118
  partial: true,
53
119
  allowFunction: true
54
120
  }), 'PressableCard');
55
- const getCardTokens = pressableState => selectTokens('Card', getTokens(pressableState));
121
+ const getCardTokens = pressableState => {
122
+ const allTokens = getTokens(pressableState);
123
+ const cardTokens = selectTokens('Card', allTokens);
124
+
125
+ // Handle focus border transparency to avoid double borders
126
+ if (pressableState.focused && allTokens.borderWidth > 0) {
127
+ const result = {
128
+ ...cardTokens,
129
+ borderColor: 'transparent'
130
+ };
131
+
132
+ // Also handle backgroundImage for interactive states
133
+ if (backgroundImage) {
134
+ const {
135
+ hovered,
136
+ pressed,
137
+ focused
138
+ } = pressableState || {};
139
+ const isInteractiveState = hovered || pressed || focused;
140
+ if (!isInteractiveState) {
141
+ const {
142
+ backgroundColor,
143
+ ...restTokens
144
+ } = result;
145
+ return restTokens;
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+
151
+ // Handle backgroundImage when not in focus state
152
+ if (backgroundImage) {
153
+ const {
154
+ hovered,
155
+ pressed,
156
+ focused
157
+ } = pressableState || {};
158
+ const isInteractiveState = hovered || pressed || focused;
159
+ if (!isInteractiveState) {
160
+ const {
161
+ backgroundColor,
162
+ ...restTokens
163
+ } = cardTokens;
164
+ return restTokens;
165
+ }
166
+ }
167
+ return cardTokens;
168
+ };
56
169
  const getOuterBorderStyle = pressableState => {
57
170
  const {
58
171
  flex,
59
172
  minWidth,
173
+ marginTop,
174
+ marginBottom,
175
+ marginLeft,
176
+ marginRight,
60
177
  outerBorderColor,
61
178
  outerBorderGap = 0,
62
179
  outerBorderWidth = 0,
@@ -65,6 +182,10 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
65
182
  return {
66
183
  flex,
67
184
  minWidth: minWidth + outerBorderGap + outerBorderWidth,
185
+ marginTop,
186
+ marginBottom,
187
+ marginLeft,
188
+ marginRight,
68
189
  ...applyOuterBorder({
69
190
  outerBorderColor,
70
191
  outerBorderGap,
@@ -158,11 +279,15 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
158
279
  ...rest,
159
280
  accessibilityRole
160
281
  }),
161
- children: pressableState => /*#__PURE__*/_jsx(CardBase, {
162
- tokens: getCardTokens(pressableState),
163
- backgroundImage: backgroundImage,
164
- fullBleedContent: fullBleedContent,
165
- children: typeof children === 'function' ? children(getCardState(pressableState)) : children
282
+ children: pressableState => /*#__PURE__*/_jsx(FocusBorderOverlay, {
283
+ tokens: getTokens(pressableState),
284
+ pressableState: pressableState,
285
+ children: /*#__PURE__*/_jsx(CardBase, {
286
+ tokens: getCardTokens(pressableState),
287
+ backgroundImage: backgroundImage,
288
+ fullBleedContent: fullBleedContent,
289
+ children: typeof children === 'function' ? children(getCardState(pressableState)) : children
290
+ })
166
291
  })
167
292
  });
168
293
  });
@@ -177,6 +302,20 @@ const staticStyles = StyleSheet.create({
177
302
  alignItems: 'stretch',
178
303
  justifyContent: 'flex-start',
179
304
  textDecorationLine: 'none'
305
+ },
306
+ focusOverlayContainer: {
307
+ position: 'relative'
308
+ },
309
+ webOutlineNone: {
310
+ outline: 'none'
311
+ },
312
+ focusBorder: {
313
+ position: 'absolute',
314
+ top: 0,
315
+ left: 0,
316
+ right: 0,
317
+ bottom: 0,
318
+ pointerEvents: 'none'
180
319
  }
181
320
  });
182
321
  PressableCardBase.displayName = 'PressableCardBase';
@@ -22,7 +22,7 @@ import CarouselTabsPanel from './CarouselTabs/CarouselTabsPanel';
22
22
  import CarouselTabsPanelItem from './CarouselTabs/CarouselTabsPanelItem';
23
23
  import dictionary from './dictionary';
24
24
  import Box from '../Box';
25
- import { ITEMS_PER_VIEWPORT_XS_SM, ITEMS_PER_VIEWPORT_MD, ITEMS_PER_VIEWPORT_LG_XL, DEFAULT_POSITION_OFFSET, LARGE_VIEWPORT_MARGIN, DEFAULT_VIEWPORT_MARGIN, PEEKING_MULTIPLIER, ACTIVE_INDEX_OFFSET_MULTIPLIER, NEGATIVE_MULTIPLIER, TRANSITION_MODES, SWIPE_RELEASE_STYLES, INSTANT_ANIMATION_DURATION, DEFAULT_SWIPE_RELEASE_DURATION } from './Constants';
25
+ import { ITEMS_PER_VIEWPORT_XS_SM, ITEMS_PER_VIEWPORT_MD, ITEMS_PER_VIEWPORT_LG_XL, DEFAULT_POSITION_OFFSET, LARGE_VIEWPORT_MARGIN, DEFAULT_VIEWPORT_MARGIN, PEEKING_MULTIPLIER, NEGATIVE_MULTIPLIER, TRANSITION_MODES, SWIPE_RELEASE_STYLES, INSTANT_ANIMATION_DURATION, DEFAULT_SWIPE_RELEASE_DURATION, POSITION_VARIANTS, POSITION_PROPERTIES } from './Constants';
26
26
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
27
  const staticStyles = StyleSheet.create({
28
28
  root: {
@@ -72,7 +72,7 @@ const selectHeroContainerStyles = (width, hidden) => ({
72
72
  width,
73
73
  visibility: hidden ? 'hidden' : 'visible'
74
74
  });
75
- const getDynamicPositionProperty = areStylesAppliedOnPreviousButton => areStylesAppliedOnPreviousButton ? 'left' : 'right';
75
+ const getDynamicPositionProperty = areStylesAppliedOnPreviousButton => areStylesAppliedOnPreviousButton ? POSITION_PROPERTIES.LEFT : POSITION_PROPERTIES.RIGHT;
76
76
  const selectControlButtonPositionStyles = _ref => {
77
77
  let {
78
78
  positionVariant,
@@ -82,23 +82,36 @@ const selectControlButtonPositionStyles = _ref => {
82
82
  enablePeeking,
83
83
  enableDisplayMultipleItemsPerSlide,
84
84
  isAutoPlayEnabled,
85
- viewport
85
+ viewport,
86
+ maxWidth,
87
+ viewportWidth
86
88
  } = _ref;
87
89
  const styles = {};
88
- if (positionVariant === 'edge') {
89
- styles[positionProperty] = -1 * (buttonWidth / 2);
90
- } else if (positionVariant === 'inside') {
91
- styles[positionProperty] = DEFAULT_POSITION_OFFSET;
92
- } else if (positionVariant === 'outside') {
90
+ let positionOffset = 0;
91
+ if (positionVariant === POSITION_VARIANTS.EDGE) {
92
+ positionOffset = -1 * (buttonWidth / 2);
93
+ } else if (positionVariant === POSITION_VARIANTS.INSIDE) {
94
+ positionOffset = DEFAULT_POSITION_OFFSET;
95
+ } else if (positionVariant === POSITION_VARIANTS.OUTSIDE) {
93
96
  if (enablePeeking || enableDisplayMultipleItemsPerSlide || isAutoPlayEnabled && viewport === 'xs') {
94
- styles[positionProperty] = 0;
97
+ positionOffset = 0;
95
98
  } else {
96
- styles[positionProperty] = -1 * (spaceBetweenSlideAndButton + buttonWidth);
99
+ positionOffset = -1 * (spaceBetweenSlideAndButton + buttonWidth);
100
+ }
101
+ }
102
+ if (enablePeeking) {
103
+ if (positionProperty === POSITION_PROPERTIES.RIGHT) {
104
+ const rightMargin = (viewportWidth - maxWidth) / 2;
105
+ positionOffset += rightMargin;
106
+ } else if (positionProperty === POSITION_PROPERTIES.LEFT) {
107
+ const leftMargin = (viewportWidth - maxWidth) / 2;
108
+ positionOffset += leftMargin;
97
109
  }
98
110
  }
111
+ styles[positionProperty] = positionOffset;
99
112
  return styles;
100
113
  };
101
- const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, areStylesAppliedOnPreviousButton, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport) => {
114
+ const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, areStylesAppliedOnPreviousButton, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, viewportWidth) => {
102
115
  const styles = {
103
116
  zIndex: 1,
104
117
  position: 'absolute'
@@ -120,7 +133,9 @@ const selectPreviousNextNavigationButtonStyles = (previousNextNavigationButtonWi
120
133
  enablePeeking,
121
134
  enableDisplayMultipleItemsPerSlide,
122
135
  isAutoPlayEnabled,
123
- viewport
136
+ viewport,
137
+ maxWidth,
138
+ viewportWidth
124
139
  })
125
140
  };
126
141
  };
@@ -186,7 +201,7 @@ const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) =
186
201
  }
187
202
  return ITEMS_PER_VIEWPORT_XS_SM;
188
203
  };
189
- const selectRootContainerStyles = (enableHero, viewport) => {
204
+ const selectRootContainerStyles = (enableHero, viewport, enablePeeking) => {
190
205
  if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
191
206
  return {
192
207
  alignItems: 'center'
@@ -197,15 +212,25 @@ const selectRootContainerStyles = (enableHero, viewport) => {
197
212
  paddingHorizontal: 16
198
213
  };
199
214
  }
215
+ if (enablePeeking) {
216
+ return {
217
+ width: '100%'
218
+ };
219
+ }
200
220
  return {};
201
221
  };
202
- const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
203
- if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
222
+ const selectMainContainerStyles = (enableHero, viewport, maxWidth, enablePeeking) => {
223
+ if (enableHero && viewport === 'xl' && Platform.OS === 'web' && !enablePeeking) {
204
224
  return {
205
225
  width: '100%',
206
226
  maxWidth: maxWidth || 1200
207
227
  };
208
228
  }
229
+ if (enablePeeking) {
230
+ return {
231
+ width: '100%'
232
+ };
233
+ }
209
234
  if (maxWidth !== null && maxWidth !== undefined) {
210
235
  return {
211
236
  maxWidth,
@@ -215,14 +240,20 @@ const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
215
240
  }
216
241
  return {};
217
242
  };
218
- const selectNavigationStyles = (tabs, enableHero, viewport) => {
243
+ const selectNavigationStyles = (tabs, enableHero, viewport, enablePeeking, maxWidth) => {
219
244
  let marginHorizontal = 0;
220
245
  if (enableHero && tabs) {
221
246
  marginHorizontal = viewport === 'xl' ? LARGE_VIEWPORT_MARGIN : DEFAULT_VIEWPORT_MARGIN;
222
247
  }
223
- return {
248
+ const styles = {
224
249
  marginHorizontal
225
250
  };
251
+ if (enablePeeking && maxWidth) {
252
+ styles.maxWidth = maxWidth;
253
+ styles.alignSelf = 'center';
254
+ styles.width = '100%';
255
+ }
256
+ return styles;
226
257
  };
227
258
 
228
259
  /**
@@ -231,23 +262,18 @@ const selectNavigationStyles = (tabs, enableHero, viewport) => {
231
262
  * @param {number} containerWidth - The width of the carousel container.
232
263
  * @param {boolean} enablePeeking - Flag indicating whether peeking is enabled.
233
264
  * @param {Object} viewport - The viewport configuration object used to determine peeking properties.
234
- * @param {React.MutableRefObject<number>} activeIndexRef - A ref object holding the current active index of the carousel.
265
+ * @param {number} maxWidth - The maximum width constraint for the carousel content.
235
266
  * @returns {number} The calculated final width of the carousel container.
236
267
  */
237
- const calculateFinalWidth = (containerWidth, enablePeeking, viewport, activeIndexRef) => {
268
+ const calculateFinalWidth = (containerWidth, enablePeeking, viewport, maxWidth) => {
238
269
  let finalWidth = containerWidth;
239
270
  if (enablePeeking) {
240
271
  const {
241
272
  peekingGap,
242
- peekingMiddleSpace,
243
- peekingMarginLeft
273
+ peekingMiddleSpace
244
274
  } = getPeekingProps(viewport);
245
- const slideWide = containerWidth - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER);
246
- if (activeIndexRef.current === 0) {
247
- finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace;
248
- } else {
249
- finalWidth = slideWide + peekingGap;
250
- }
275
+ const baseWidth = maxWidth || containerWidth;
276
+ finalWidth = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap;
251
277
  }
252
278
  return finalWidth;
253
279
  };
@@ -370,7 +396,10 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
370
396
  } = useTheme();
371
397
  const contentMaxWidthValue = useResponsiveProp(contentMaxWidth);
372
398
  const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth);
373
- const maxWidth = resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth);
399
+ let maxWidth = null;
400
+ if (enablePeeking || contentMaxWidth !== undefined) {
401
+ maxWidth = contentMaxWidthValue === undefined ? responsiveWidth : resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth);
402
+ }
374
403
  const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport);
375
404
  const autoPlayFeatureEnabled = autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1;
376
405
  // if `Carousel` only has one `Carousel.Item`, convert this to a single-item array
@@ -433,8 +462,16 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
433
462
  const [isCarouselPlaying, setisCarouselPlaying] = React.useState(autoPlayFeatureEnabled);
434
463
  const isSwiping = React.useRef(false);
435
464
  const autoPlayRef = React.useRef(null);
465
+ const [rootContainerLayout, setRootContainerLayout] = React.useState({
466
+ x: 0,
467
+ y: 0,
468
+ width: 0,
469
+ height: 0
470
+ });
471
+ const rootContainerLayoutRef = React.useRef(rootContainerLayout);
436
472
  const isFirstSlide = !activeIndex;
437
473
  const isLastSlide = activeIndex + 1 >= totalItems;
474
+ const currentViewportWidth = rootContainerLayout.width;
438
475
  const handleAnimationStart = React.useCallback(function () {
439
476
  if (typeof onAnimationStart === 'function') onAnimationStart(...arguments);
440
477
  setIsAnimating(true);
@@ -452,15 +489,15 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
452
489
  if (enablePeeking) {
453
490
  const {
454
491
  peekingGap,
455
- peekingMiddleSpace,
456
- peekingMarginLeft
492
+ peekingMiddleSpace
457
493
  } = getPeekingProps(viewport);
458
494
  let finalWidth;
459
- const slideWide = containerLayoutRef.current.width - (peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER);
495
+ const baseWidth = maxWidth || containerLayoutRef.current.width;
496
+ const slideWide = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER;
460
497
  if (activeIndexRef.current === 0) {
461
498
  finalWidth = 0;
462
499
  } else {
463
- finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace + (slideWide + peekingGap) * (activeIndexRef.current - ACTIVE_INDEX_OFFSET_MULTIPLIER);
500
+ finalWidth = (slideWide + peekingGap) * activeIndexRef.current;
464
501
  }
465
502
  animatedX.current = finalWidth * NEGATIVE_MULTIPLIER;
466
503
  } else {
@@ -487,7 +524,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
487
524
  y: 0
488
525
  });
489
526
  }
490
- }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking]);
527
+ }, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking, maxWidth]);
491
528
  const animate = React.useCallback(function (panToAnimate, toValue, toIndex) {
492
529
  let isSwipeRelease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
493
530
  const applicableTransitionDuration = isLastSlide && toIndex === 0 ? loopDuration : transitionDuration;
@@ -579,7 +616,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
579
616
  }
580
617
  stopAutoplay();
581
618
  setActiveIndex(index);
582
- const finalWidth = calculateFinalWidth(containerLayoutRef.current.width, enablePeeking, viewport, activeIndexRef);
619
+ const finalWidth = calculateFinalWidth(containerLayoutRef.current.width, enablePeeking, viewport, maxWidth);
583
620
  toValue.x = finalWidth * -1 * calcDelta;
584
621
  const heroToValue = {
585
622
  x: 0,
@@ -606,7 +643,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
606
643
  }
607
644
  if (onIndexChanged) onIndexChanged(calcDelta, index);
608
645
  return calcDelta;
609
- }, [handleAnimationStart, triggerRefocus, slideDuration, updateOffset, animate, totalItems, onIndexChanged, isCarouselPlaying, stopAutoplay, isAutoPlayEnabled, viewport, enablePeeking, pan, heroPan, enableHero]);
646
+ }, [handleAnimationStart, triggerRefocus, slideDuration, updateOffset, animate, totalItems, onIndexChanged, isCarouselPlaying, stopAutoplay, isAutoPlayEnabled, viewport, enablePeeking, pan, heroPan, enableHero, maxWidth]);
610
647
  const startAutoplay = React.useCallback(() => {
611
648
  stopAutoplay();
612
649
  if (isAutoPlayEnabled) {
@@ -641,6 +678,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
641
678
  React.useEffect(() => {
642
679
  heroContainerLayoutRef.current = heroContainerLayout;
643
680
  }, [heroContainerLayout]);
681
+ React.useEffect(() => {
682
+ rootContainerLayoutRef.current = rootContainerLayout;
683
+ }, [rootContainerLayout]);
644
684
  React.useEffect(() => {
645
685
  pan.x.addListener(_ref5 => {
646
686
  let {
@@ -749,6 +789,25 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
749
789
  } = _ref11;
750
790
  return setPreviousNextNavigationButtonWidth(width);
751
791
  };
792
+ const onRootContainerLayout = _ref12 => {
793
+ let {
794
+ nativeEvent: {
795
+ layout: {
796
+ x,
797
+ y,
798
+ width,
799
+ height
800
+ }
801
+ }
802
+ } = _ref12;
803
+ return setRootContainerLayout(prevState => ({
804
+ ...prevState,
805
+ x,
806
+ y,
807
+ width,
808
+ height
809
+ }));
810
+ };
752
811
  const isSwipeAllowed = React.useCallback(() => {
753
812
  if (totalItems === 1) {
754
813
  return false;
@@ -914,9 +973,10 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
914
973
  }
915
974
  }, [isTransitioningRef]);
916
975
  return /*#__PURE__*/_jsxs(View, {
917
- style: selectRootContainerStyles(enableHero, viewport),
976
+ style: selectRootContainerStyles(enableHero, viewport, enablePeeking),
977
+ onLayout: onRootContainerLayout,
918
978
  children: [/*#__PURE__*/_jsx(View, {
919
- style: selectMainContainerStyles(enableHero, viewport, maxWidth),
979
+ style: selectMainContainerStyles(enableHero, viewport, maxWidth, enablePeeking),
920
980
  children: /*#__PURE__*/_jsxs(CarouselProvider, {
921
981
  activeIndex: activeIndex,
922
982
  goTo: goTo,
@@ -928,6 +988,8 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
928
988
  refocus: refocus,
929
989
  width: containerLayout.width,
930
990
  maximumItemsForSlide: getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport),
991
+ maxWidth: maxWidth,
992
+ viewportWidth: currentViewportWidth,
931
993
  children: [/*#__PURE__*/_jsxs(View, {
932
994
  style: [staticStyles.root, {
933
995
  ...(Platform.OS === 'web' ? {
@@ -950,7 +1012,9 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
950
1012
  enablePeeking,
951
1013
  enableDisplayMultipleItemsPerSlide,
952
1014
  isAutoPlayEnabled,
953
- viewport
1015
+ viewport,
1016
+ maxWidth,
1017
+ viewportWidth: currentViewportWidth
954
1018
  })],
955
1019
  children: /*#__PURE__*/_jsx(IconButton, {
956
1020
  icon: isCarouselPlaying ? pauseIcon : playIcon,
@@ -959,7 +1023,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
959
1023
  onPress: onAnimationControlButtonPress
960
1024
  })
961
1025
  }) : null, showPreviousNextNavigation && totalItems > 1 ? /*#__PURE__*/_jsx(View, {
962
- style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, true, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport),
1026
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, true, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, currentViewportWidth),
963
1027
  testID: "previous-button-container",
964
1028
  children: /*#__PURE__*/_jsx(IconButton, {
965
1029
  onLayout: onPreviousNextNavigationButtonLayout,
@@ -997,16 +1061,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
997
1061
  children: childrenArray.map((element, index) => {
998
1062
  let hidden = !isAnimating && index !== activeIndex;
999
1063
  if (enablePeeking && !isAnimating) {
1000
- if (enableDisplayMultipleItemsPerSlide) {
1001
- const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
1002
- if (index >= activeIndex * maxItemsForSlide - 1 && index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1) {
1003
- hidden = false;
1004
- } else {
1005
- hidden = true;
1006
- }
1007
- } else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
1008
- hidden = false;
1009
- }
1064
+ hidden = false;
1010
1065
  } else if (!enablePeeking && enableDisplayMultipleItemsPerSlide && !isAnimating) {
1011
1066
  const maxItemsForSlide = getMaximumItemsForSlide(enableDisplayMultipleItemsPerSlide, viewport);
1012
1067
  if (index >= activeIndex * maxItemsForSlide && index < activeIndex * maxItemsForSlide + maxItemsForSlide) {
@@ -1028,7 +1083,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
1028
1083
  })
1029
1084
  })
1030
1085
  }), showPreviousNextNavigation && totalItems > 1 ? /*#__PURE__*/_jsx(View, {
1031
- style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, false, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport),
1086
+ style: selectPreviousNextNavigationButtonStyles(previousNextNavigationButtonWidth, previousNextNavigationPosition, spaceBetweenSlideAndPreviousNextNavigation, isFirstSlide, isLastSlide, false, enablePeeking, enableDisplayMultipleItemsPerSlide, isAutoPlayEnabled, viewport, maxWidth, currentViewportWidth),
1032
1087
  testID: "next-button-container",
1033
1088
  children: /*#__PURE__*/_jsx(IconButton, {
1034
1089
  onLayout: onPreviousNextNavigationButtonLayout,
@@ -1040,7 +1095,7 @@ const Carousel = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
1040
1095
  })
1041
1096
  }) : null]
1042
1097
  }), /*#__PURE__*/_jsx(View, {
1043
- style: selectNavigationStyles(tabs, enableHero, viewport),
1098
+ style: selectNavigationStyles(tabs, enableHero, viewport, enablePeeking, maxWidth),
1044
1099
  children: showPanelNavigation ? activePanelNavigation : null
1045
1100
  })]
1046
1101
  })
@@ -14,7 +14,9 @@ const CarouselProvider = _ref => {
14
14
  themeTokens,
15
15
  totalItems,
16
16
  width,
17
- maximumItemsForSlide
17
+ maximumItemsForSlide,
18
+ maxWidth,
19
+ viewportWidth
18
20
  } = _ref;
19
21
  const value = React.useMemo(() => ({
20
22
  activeIndex,
@@ -25,8 +27,10 @@ const CarouselProvider = _ref => {
25
27
  themeTokens,
26
28
  totalItems,
27
29
  width,
28
- maximumItemsForSlide
29
- }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width, maximumItemsForSlide]);
30
+ maximumItemsForSlide,
31
+ maxWidth,
32
+ viewportWidth
33
+ }), [activeIndex, goTo, getCopyWithPlaceholders, itemLabel, refocus, totalItems, themeTokens, width, maximumItemsForSlide, maxWidth, viewportWidth]);
30
34
  return /*#__PURE__*/_jsx(CarouselContext.Provider, {
31
35
  value: value,
32
36
  children: children
@@ -49,6 +53,8 @@ CarouselProvider.propTypes = {
49
53
  themeTokens: getTokensPropType('Carousel'),
50
54
  totalItems: PropTypes.number.isRequired,
51
55
  width: PropTypes.number.isRequired,
52
- maximumItemsForSlide: PropTypes.number
56
+ maximumItemsForSlide: PropTypes.number,
57
+ maxWidth: PropTypes.number,
58
+ viewportWidth: PropTypes.number
53
59
  };
54
60
  export { CarouselProvider, useCarousel };
@@ -12,26 +12,26 @@ const selectContainerStyle = _ref => {
12
12
  width,
13
13
  elementIndex,
14
14
  enablePeeking,
15
- peekingMarginLeft,
16
15
  peekingGap,
17
16
  hidden,
18
17
  enableDisplayMultipleItemsPerSlide,
19
18
  viewport,
20
- peekingMiddleSpace
19
+ peekingMiddleSpace,
20
+ maxWidth,
21
+ viewportWidth
21
22
  } = _ref;
22
23
  let adjustedWidth = width;
23
24
  let marginLeft = 0;
24
25
  if (enablePeeking) {
25
26
  const isFirst = elementIndex === 0;
26
- adjustedWidth = width - (peekingMiddleSpace * 2 + peekingGap * 2);
27
+ const baseWidth = maxWidth || width;
28
+ adjustedWidth = baseWidth - peekingMiddleSpace * 2;
27
29
  if (isFirst) {
28
- marginLeft = peekingMarginLeft;
30
+ marginLeft = peekingMiddleSpace + (viewportWidth - maxWidth) / 2;
29
31
  } else {
30
32
  marginLeft = peekingGap;
31
33
  }
32
34
  }
33
-
34
- // Adjust width and margins for multiple items per slide.
35
35
  if (enableDisplayMultipleItemsPerSlide) {
36
36
  switch (viewport) {
37
37
  case 'xs':
@@ -89,7 +89,9 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
89
89
  width,
90
90
  activeIndex,
91
91
  goTo,
92
- maximumItemsForSlide
92
+ maximumItemsForSlide,
93
+ maxWidth,
94
+ viewportWidth
93
95
  } = useCarousel();
94
96
  const selectedProps = selectProps({
95
97
  ...rest,
@@ -132,6 +134,8 @@ const CarouselItem = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
132
134
  enablePeeking,
133
135
  enableDisplayMultipleItemsPerSlide,
134
136
  viewport,
137
+ maxWidth,
138
+ viewportWidth,
135
139
  ...peekingProps
136
140
  }),
137
141
  ...selectedProps,
@@ -19,4 +19,13 @@ export const SWIPE_RELEASE_STYLES = {
19
19
  EASE_OUT: 'ease-out'
20
20
  };
21
21
  export const INSTANT_ANIMATION_DURATION = 1;
22
- export const DEFAULT_SWIPE_RELEASE_DURATION = 500;
22
+ export const DEFAULT_SWIPE_RELEASE_DURATION = 500;
23
+ export const POSITION_VARIANTS = {
24
+ EDGE: 'edge',
25
+ INSIDE: 'inside',
26
+ OUTSIDE: 'outside'
27
+ };
28
+ export const POSITION_PROPERTIES = {
29
+ LEFT: 'left',
30
+ RIGHT: 'right'
31
+ };