@telus-uds/components-base 3.25.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 +28 -1
- 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 +161 -77
- package/lib/cjs/Carousel/CarouselContext.js +10 -4
- package/lib/cjs/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/cjs/Carousel/Constants.js +22 -2
- package/lib/cjs/Checkbox/Checkbox.js +43 -13
- package/lib/cjs/InputSupports/InputSupports.js +2 -1
- 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/cjs/PriceLockup/PriceLockup.js +1 -1
- 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 +153 -69
- package/lib/esm/Carousel/CarouselContext.js +10 -4
- package/lib/esm/Carousel/CarouselItem/CarouselItem.js +11 -7
- package/lib/esm/Carousel/Constants.js +21 -1
- package/lib/esm/Checkbox/Checkbox.js +43 -13
- package/lib/esm/InputSupports/InputSupports.js +2 -1
- 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/esm/PriceLockup/PriceLockup.js +1 -1
- 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 +185 -88
- package/src/Carousel/CarouselContext.jsx +12 -4
- package/src/Carousel/CarouselItem/CarouselItem.jsx +10 -6
- package/src/Carousel/Constants.js +24 -0
- package/src/Checkbox/Checkbox.jsx +29 -7
- package/src/InputSupports/InputSupports.jsx +6 -1
- 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/PriceLockup/PriceLockup.jsx +1 -1
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Animated,
|
|
5
|
+
PanResponder,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Platform,
|
|
8
|
+
Dimensions,
|
|
9
|
+
Easing
|
|
10
|
+
} from 'react-native'
|
|
3
11
|
import PropTypes from 'prop-types'
|
|
4
12
|
import { useThemeTokens, useTheme } from '../ThemeProvider'
|
|
5
13
|
import { useViewport } from '../ViewportProvider'
|
|
@@ -37,16 +45,15 @@ import {
|
|
|
37
45
|
LARGE_VIEWPORT_MARGIN,
|
|
38
46
|
DEFAULT_VIEWPORT_MARGIN,
|
|
39
47
|
PEEKING_MULTIPLIER,
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
NEGATIVE_MULTIPLIER,
|
|
49
|
+
TRANSITION_MODES,
|
|
50
|
+
SWIPE_RELEASE_STYLES,
|
|
51
|
+
INSTANT_ANIMATION_DURATION,
|
|
52
|
+
DEFAULT_SWIPE_RELEASE_DURATION,
|
|
53
|
+
POSITION_VARIANTS,
|
|
54
|
+
POSITION_PROPERTIES
|
|
42
55
|
} from './Constants'
|
|
43
56
|
|
|
44
|
-
const TRANSITION_MODES = {
|
|
45
|
-
MANUAL: 'manual',
|
|
46
|
-
AUTOMATIC: 'automatic',
|
|
47
|
-
SWIPE: 'swipe'
|
|
48
|
-
}
|
|
49
|
-
|
|
50
57
|
const staticStyles = StyleSheet.create({
|
|
51
58
|
root: {
|
|
52
59
|
backgroundColor: 'transparent',
|
|
@@ -106,7 +113,7 @@ const selectHeroContainerStyles = (width, hidden) => ({
|
|
|
106
113
|
})
|
|
107
114
|
|
|
108
115
|
const getDynamicPositionProperty = (areStylesAppliedOnPreviousButton) =>
|
|
109
|
-
areStylesAppliedOnPreviousButton ?
|
|
116
|
+
areStylesAppliedOnPreviousButton ? POSITION_PROPERTIES.LEFT : POSITION_PROPERTIES.RIGHT
|
|
110
117
|
|
|
111
118
|
const selectControlButtonPositionStyles = ({
|
|
112
119
|
positionVariant,
|
|
@@ -116,24 +123,41 @@ const selectControlButtonPositionStyles = ({
|
|
|
116
123
|
enablePeeking,
|
|
117
124
|
enableDisplayMultipleItemsPerSlide,
|
|
118
125
|
isAutoPlayEnabled,
|
|
119
|
-
viewport
|
|
126
|
+
viewport,
|
|
127
|
+
maxWidth,
|
|
128
|
+
viewportWidth
|
|
120
129
|
}) => {
|
|
121
130
|
const styles = {}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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) {
|
|
127
139
|
if (
|
|
128
140
|
enablePeeking ||
|
|
129
141
|
enableDisplayMultipleItemsPerSlide ||
|
|
130
142
|
(isAutoPlayEnabled && viewport === 'xs')
|
|
131
143
|
) {
|
|
132
|
-
|
|
144
|
+
positionOffset = 0
|
|
133
145
|
} else {
|
|
134
|
-
|
|
146
|
+
positionOffset = -1 * (spaceBetweenSlideAndButton + buttonWidth)
|
|
135
147
|
}
|
|
136
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
|
|
137
161
|
return styles
|
|
138
162
|
}
|
|
139
163
|
|
|
@@ -147,7 +171,9 @@ const selectPreviousNextNavigationButtonStyles = (
|
|
|
147
171
|
enablePeeking,
|
|
148
172
|
enableDisplayMultipleItemsPerSlide,
|
|
149
173
|
isAutoPlayEnabled,
|
|
150
|
-
viewport
|
|
174
|
+
viewport,
|
|
175
|
+
maxWidth,
|
|
176
|
+
viewportWidth
|
|
151
177
|
) => {
|
|
152
178
|
const styles = {
|
|
153
179
|
zIndex: 1,
|
|
@@ -171,7 +197,9 @@ const selectPreviousNextNavigationButtonStyles = (
|
|
|
171
197
|
enablePeeking,
|
|
172
198
|
enableDisplayMultipleItemsPerSlide,
|
|
173
199
|
isAutoPlayEnabled,
|
|
174
|
-
viewport
|
|
200
|
+
viewport,
|
|
201
|
+
maxWidth,
|
|
202
|
+
viewportWidth
|
|
175
203
|
})
|
|
176
204
|
}
|
|
177
205
|
}
|
|
@@ -240,7 +268,7 @@ const getMaximumItemsForSlide = (enableDisplayMultipleItemsPerSlide, viewport) =
|
|
|
240
268
|
return ITEMS_PER_VIEWPORT_XS_SM
|
|
241
269
|
}
|
|
242
270
|
|
|
243
|
-
const selectRootContainerStyles = (enableHero, viewport) => {
|
|
271
|
+
const selectRootContainerStyles = (enableHero, viewport, enablePeeking) => {
|
|
244
272
|
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
245
273
|
return {
|
|
246
274
|
alignItems: 'center'
|
|
@@ -251,16 +279,26 @@ const selectRootContainerStyles = (enableHero, viewport) => {
|
|
|
251
279
|
paddingHorizontal: 16
|
|
252
280
|
}
|
|
253
281
|
}
|
|
282
|
+
if (enablePeeking) {
|
|
283
|
+
return {
|
|
284
|
+
width: '100%'
|
|
285
|
+
}
|
|
286
|
+
}
|
|
254
287
|
return {}
|
|
255
288
|
}
|
|
256
289
|
|
|
257
|
-
const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
258
|
-
if (enableHero && viewport === 'xl' && Platform.OS === 'web') {
|
|
290
|
+
const selectMainContainerStyles = (enableHero, viewport, maxWidth, enablePeeking) => {
|
|
291
|
+
if (enableHero && viewport === 'xl' && Platform.OS === 'web' && !enablePeeking) {
|
|
259
292
|
return {
|
|
260
293
|
width: '100%',
|
|
261
294
|
maxWidth: maxWidth || 1200
|
|
262
295
|
}
|
|
263
296
|
}
|
|
297
|
+
if (enablePeeking) {
|
|
298
|
+
return {
|
|
299
|
+
width: '100%'
|
|
300
|
+
}
|
|
301
|
+
}
|
|
264
302
|
if (maxWidth !== null && maxWidth !== undefined) {
|
|
265
303
|
return {
|
|
266
304
|
maxWidth,
|
|
@@ -271,16 +309,24 @@ const selectMainContainerStyles = (enableHero, viewport, maxWidth) => {
|
|
|
271
309
|
return {}
|
|
272
310
|
}
|
|
273
311
|
|
|
274
|
-
const selectNavigationStyles = (tabs, enableHero, viewport) => {
|
|
312
|
+
const selectNavigationStyles = (tabs, enableHero, viewport, enablePeeking, maxWidth) => {
|
|
275
313
|
let marginHorizontal = 0
|
|
276
314
|
|
|
277
315
|
if (enableHero && tabs) {
|
|
278
316
|
marginHorizontal = viewport === 'xl' ? LARGE_VIEWPORT_MARGIN : DEFAULT_VIEWPORT_MARGIN
|
|
279
317
|
}
|
|
280
318
|
|
|
281
|
-
|
|
319
|
+
const styles = {
|
|
282
320
|
marginHorizontal
|
|
283
321
|
}
|
|
322
|
+
|
|
323
|
+
if (enablePeeking && maxWidth) {
|
|
324
|
+
styles.maxWidth = maxWidth
|
|
325
|
+
styles.alignSelf = 'center'
|
|
326
|
+
styles.width = '100%'
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return styles
|
|
284
330
|
}
|
|
285
331
|
|
|
286
332
|
/**
|
|
@@ -289,22 +335,16 @@ const selectNavigationStyles = (tabs, enableHero, viewport) => {
|
|
|
289
335
|
* @param {number} containerWidth - The width of the carousel container.
|
|
290
336
|
* @param {boolean} enablePeeking - Flag indicating whether peeking is enabled.
|
|
291
337
|
* @param {Object} viewport - The viewport configuration object used to determine peeking properties.
|
|
292
|
-
* @param {
|
|
338
|
+
* @param {number} maxWidth - The maximum width constraint for the carousel content.
|
|
293
339
|
* @returns {number} The calculated final width of the carousel container.
|
|
294
340
|
*/
|
|
295
|
-
const calculateFinalWidth = (containerWidth, enablePeeking, viewport,
|
|
341
|
+
const calculateFinalWidth = (containerWidth, enablePeeking, viewport, maxWidth) => {
|
|
296
342
|
let finalWidth = containerWidth
|
|
297
343
|
|
|
298
344
|
if (enablePeeking) {
|
|
299
|
-
const { peekingGap, peekingMiddleSpace
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (activeIndexRef.current === 0) {
|
|
304
|
-
finalWidth = slideWide + peekingMarginLeft - peekingMiddleSpace
|
|
305
|
-
} else {
|
|
306
|
-
finalWidth = slideWide + peekingGap
|
|
307
|
-
}
|
|
345
|
+
const { peekingGap, peekingMiddleSpace } = getPeekingProps(viewport)
|
|
346
|
+
const baseWidth = maxWidth || containerWidth
|
|
347
|
+
finalWidth = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap
|
|
308
348
|
}
|
|
309
349
|
|
|
310
350
|
return finalWidth
|
|
@@ -412,6 +452,8 @@ const Carousel = React.forwardRef(
|
|
|
412
452
|
autoPlay = false,
|
|
413
453
|
enablePeeking = false,
|
|
414
454
|
contentMaxWidth,
|
|
455
|
+
swipeReleaseStyle = SWIPE_RELEASE_STYLES.INSTANT,
|
|
456
|
+
swipeReleaseDuration = DEFAULT_SWIPE_RELEASE_DURATION,
|
|
415
457
|
...rest
|
|
416
458
|
},
|
|
417
459
|
ref
|
|
@@ -423,7 +465,15 @@ const Carousel = React.forwardRef(
|
|
|
423
465
|
|
|
424
466
|
const contentMaxWidthValue = useResponsiveProp(contentMaxWidth)
|
|
425
467
|
const responsiveWidth = useResponsiveProp(themeOptions?.contentMaxWidth)
|
|
426
|
-
|
|
468
|
+
|
|
469
|
+
let maxWidth = null
|
|
470
|
+
if (enablePeeking || contentMaxWidth !== undefined) {
|
|
471
|
+
maxWidth =
|
|
472
|
+
contentMaxWidthValue === undefined
|
|
473
|
+
? responsiveWidth
|
|
474
|
+
: resolveContentMaxWidth(contentMaxWidthValue, responsiveWidth)
|
|
475
|
+
}
|
|
476
|
+
|
|
427
477
|
const totalItems = getTotalItems(enableDisplayMultipleItemsPerSlide, childrenArray, viewport)
|
|
428
478
|
const autoPlayFeatureEnabled =
|
|
429
479
|
autoPlay && slideDuration > 0 && transitionDuration > 0 && totalItems > 1
|
|
@@ -486,8 +536,17 @@ const Carousel = React.forwardRef(
|
|
|
486
536
|
const isSwiping = React.useRef(false)
|
|
487
537
|
const autoPlayRef = React.useRef(null)
|
|
488
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
|
+
|
|
489
547
|
const isFirstSlide = !activeIndex
|
|
490
548
|
const isLastSlide = activeIndex + 1 >= totalItems
|
|
549
|
+
const currentViewportWidth = rootContainerLayout.width
|
|
491
550
|
|
|
492
551
|
const handleAnimationStart = React.useCallback(
|
|
493
552
|
(...args) => {
|
|
@@ -510,21 +569,16 @@ const Carousel = React.forwardRef(
|
|
|
510
569
|
|
|
511
570
|
const updateOffset = React.useCallback(() => {
|
|
512
571
|
if (enablePeeking) {
|
|
513
|
-
const { peekingGap, peekingMiddleSpace
|
|
572
|
+
const { peekingGap, peekingMiddleSpace } = getPeekingProps(viewport)
|
|
514
573
|
|
|
515
574
|
let finalWidth
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
(peekingMiddleSpace * PEEKING_MULTIPLIER + peekingGap * PEEKING_MULTIPLIER)
|
|
575
|
+
const baseWidth = maxWidth || containerLayoutRef.current.width
|
|
576
|
+
const slideWide = baseWidth - peekingMiddleSpace * PEEKING_MULTIPLIER
|
|
519
577
|
|
|
520
578
|
if (activeIndexRef.current === 0) {
|
|
521
579
|
finalWidth = 0
|
|
522
580
|
} else {
|
|
523
|
-
finalWidth =
|
|
524
|
-
slideWide +
|
|
525
|
-
peekingMarginLeft -
|
|
526
|
-
peekingMiddleSpace +
|
|
527
|
-
(slideWide + peekingGap) * (activeIndexRef.current - ACTIVE_INDEX_OFFSET_MULTIPLIER)
|
|
581
|
+
finalWidth = (slideWide + peekingGap) * activeIndexRef.current
|
|
528
582
|
}
|
|
529
583
|
|
|
530
584
|
animatedX.current = finalWidth * NEGATIVE_MULTIPLIER
|
|
@@ -550,17 +604,32 @@ const Carousel = React.forwardRef(
|
|
|
550
604
|
})
|
|
551
605
|
heroPan.setValue({ x: 0, y: 0 })
|
|
552
606
|
}
|
|
553
|
-
}, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking])
|
|
607
|
+
}, [pan, animatedX, heroPan, heroAnimatedX, enableHero, viewport, enablePeeking, maxWidth])
|
|
554
608
|
|
|
555
609
|
const animate = React.useCallback(
|
|
556
|
-
(panToAnimate, toValue, toIndex) => {
|
|
610
|
+
(panToAnimate, toValue, toIndex, isSwipeRelease = false) => {
|
|
557
611
|
const applicableTransitionDuration =
|
|
558
612
|
isLastSlide && toIndex === 0 ? loopDuration : transitionDuration
|
|
559
613
|
const handleAnimationEndToIndex = (...args) => handleAnimationEnd(toIndex, ...args)
|
|
560
|
-
if (reduceMotionRef.current
|
|
561
|
-
Animated.timing(panToAnimate, {
|
|
562
|
-
|
|
563
|
-
|
|
614
|
+
if (reduceMotionRef.current) {
|
|
615
|
+
Animated.timing(panToAnimate, {
|
|
616
|
+
toValue,
|
|
617
|
+
duration: INSTANT_ANIMATION_DURATION,
|
|
618
|
+
useNativeDriver: false
|
|
619
|
+
}).start(handleAnimationEndToIndex)
|
|
620
|
+
} else if (isSwipeRelease && swipeReleaseStyle === SWIPE_RELEASE_STYLES.EASE_OUT) {
|
|
621
|
+
Animated.timing(panToAnimate, {
|
|
622
|
+
toValue,
|
|
623
|
+
duration: swipeReleaseDuration,
|
|
624
|
+
easing: Easing.out(Easing.cubic),
|
|
625
|
+
useNativeDriver: false
|
|
626
|
+
}).start(handleAnimationEndToIndex)
|
|
627
|
+
} else if (isSwiping.current || isSwipeRelease) {
|
|
628
|
+
Animated.timing(panToAnimate, {
|
|
629
|
+
toValue,
|
|
630
|
+
duration: INSTANT_ANIMATION_DURATION,
|
|
631
|
+
useNativeDriver: false
|
|
632
|
+
}).start(handleAnimationEndToIndex)
|
|
564
633
|
} else if (isAutoPlayEnabled) {
|
|
565
634
|
Animated.timing(panToAnimate, {
|
|
566
635
|
...springConfig,
|
|
@@ -591,7 +660,9 @@ const Carousel = React.forwardRef(
|
|
|
591
660
|
isLastSlide,
|
|
592
661
|
isAutoPlayEnabled,
|
|
593
662
|
enablePeeking,
|
|
594
|
-
enableDisplayMultipleItemsPerSlide
|
|
663
|
+
enableDisplayMultipleItemsPerSlide,
|
|
664
|
+
swipeReleaseStyle,
|
|
665
|
+
swipeReleaseDuration
|
|
595
666
|
]
|
|
596
667
|
)
|
|
597
668
|
|
|
@@ -612,6 +683,7 @@ const Carousel = React.forwardRef(
|
|
|
612
683
|
const toValue = { x: 0, y: 0 }
|
|
613
684
|
let skipChanges = !delta
|
|
614
685
|
let calcDelta = delta
|
|
686
|
+
const isSwipeRelease = transitionMode === TRANSITION_MODES.SWIPE
|
|
615
687
|
if (activeIndexRef.current <= 0 && delta < 0) {
|
|
616
688
|
skipChanges = transitionMode !== TRANSITION_MODES.AUTOMATIC
|
|
617
689
|
calcDelta = totalItems + delta
|
|
@@ -623,10 +695,10 @@ const Carousel = React.forwardRef(
|
|
|
623
695
|
const index = activeIndexRef.current + calcDelta
|
|
624
696
|
if (skipChanges) {
|
|
625
697
|
isTransitioningRef.current = true
|
|
626
|
-
animate(pan, toValue, index)
|
|
698
|
+
animate(pan, toValue, index, isSwipeRelease)
|
|
627
699
|
|
|
628
700
|
if (enableHero) {
|
|
629
|
-
animate(heroPan, toValue, index)
|
|
701
|
+
animate(heroPan, toValue, index, isSwipeRelease)
|
|
630
702
|
}
|
|
631
703
|
return calcDelta
|
|
632
704
|
}
|
|
@@ -638,16 +710,16 @@ const Carousel = React.forwardRef(
|
|
|
638
710
|
containerLayoutRef.current.width,
|
|
639
711
|
enablePeeking,
|
|
640
712
|
viewport,
|
|
641
|
-
|
|
713
|
+
maxWidth
|
|
642
714
|
)
|
|
643
715
|
|
|
644
716
|
toValue.x = finalWidth * -1 * calcDelta
|
|
645
717
|
const heroToValue = { x: 0, y: 0 }
|
|
646
718
|
heroToValue.x = heroContainerLayoutRef.current.width * -1 * calcDelta
|
|
647
719
|
isTransitioningRef.current = true
|
|
648
|
-
animate(pan, toValue, index)
|
|
720
|
+
animate(pan, toValue, index, isSwipeRelease)
|
|
649
721
|
if (enableHero) {
|
|
650
|
-
animate(heroPan, heroToValue, index)
|
|
722
|
+
animate(heroPan, heroToValue, index, isSwipeRelease)
|
|
651
723
|
}
|
|
652
724
|
if (isCarouselPlaying) {
|
|
653
725
|
stopAutoplay()
|
|
@@ -684,7 +756,8 @@ const Carousel = React.forwardRef(
|
|
|
684
756
|
enablePeeking,
|
|
685
757
|
pan,
|
|
686
758
|
heroPan,
|
|
687
|
-
enableHero
|
|
759
|
+
enableHero,
|
|
760
|
+
maxWidth
|
|
688
761
|
]
|
|
689
762
|
)
|
|
690
763
|
|
|
@@ -741,6 +814,10 @@ const Carousel = React.forwardRef(
|
|
|
741
814
|
heroContainerLayoutRef.current = heroContainerLayout
|
|
742
815
|
}, [heroContainerLayout])
|
|
743
816
|
|
|
817
|
+
React.useEffect(() => {
|
|
818
|
+
rootContainerLayoutRef.current = rootContainerLayout
|
|
819
|
+
}, [rootContainerLayout])
|
|
820
|
+
|
|
744
821
|
React.useEffect(() => {
|
|
745
822
|
pan.x.addListener(({ value }) => {
|
|
746
823
|
animatedX.current = value
|
|
@@ -823,6 +900,12 @@ const Carousel = React.forwardRef(
|
|
|
823
900
|
}
|
|
824
901
|
}) => setPreviousNextNavigationButtonWidth(width)
|
|
825
902
|
|
|
903
|
+
const onRootContainerLayout = ({
|
|
904
|
+
nativeEvent: {
|
|
905
|
+
layout: { x, y, width, height }
|
|
906
|
+
}
|
|
907
|
+
}) => setRootContainerLayout((prevState) => ({ ...prevState, x, y, width, height }))
|
|
908
|
+
|
|
826
909
|
const isSwipeAllowed = React.useCallback(() => {
|
|
827
910
|
if (totalItems === 1) {
|
|
828
911
|
return false
|
|
@@ -866,14 +949,14 @@ const Carousel = React.forwardRef(
|
|
|
866
949
|
}
|
|
867
950
|
const correction = gesture.moveX - gesture.x0
|
|
868
951
|
|
|
952
|
+
isSwiping.current = false
|
|
953
|
+
|
|
869
954
|
if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
|
|
870
|
-
animate(pan, { x: 0, y: 0 },
|
|
955
|
+
animate(pan, { x: 0, y: 0 }, activeIndexRef.current, true)
|
|
871
956
|
} else {
|
|
872
957
|
const delta = correction > 0 ? -1 : 1
|
|
873
958
|
updateIndex(delta, TRANSITION_MODES.SWIPE)
|
|
874
959
|
}
|
|
875
|
-
|
|
876
|
-
isSwiping.current = false
|
|
877
960
|
}
|
|
878
961
|
}),
|
|
879
962
|
[
|
|
@@ -924,14 +1007,14 @@ const Carousel = React.forwardRef(
|
|
|
924
1007
|
}
|
|
925
1008
|
const correction = gesture.moveX - gesture.x0
|
|
926
1009
|
|
|
1010
|
+
isSwiping.current = false
|
|
1011
|
+
|
|
927
1012
|
if (Math.abs(correction) < containerLayoutRef.current.width * minDistanceForAction) {
|
|
928
|
-
animate(heroPan, { x: 0, y: 0 },
|
|
1013
|
+
animate(heroPan, { x: 0, y: 0 }, activeIndexRef.current, true)
|
|
929
1014
|
} else {
|
|
930
1015
|
const delta = correction > 0 ? -1 : 1
|
|
931
1016
|
updateIndex(delta, TRANSITION_MODES.SWIPE)
|
|
932
1017
|
}
|
|
933
|
-
|
|
934
|
-
isSwiping.current = false
|
|
935
1018
|
}
|
|
936
1019
|
}),
|
|
937
1020
|
[
|
|
@@ -1045,8 +1128,11 @@ const Carousel = React.forwardRef(
|
|
|
1045
1128
|
)
|
|
1046
1129
|
|
|
1047
1130
|
return (
|
|
1048
|
-
<View
|
|
1049
|
-
|
|
1131
|
+
<View
|
|
1132
|
+
style={selectRootContainerStyles(enableHero, viewport, enablePeeking)}
|
|
1133
|
+
onLayout={onRootContainerLayout}
|
|
1134
|
+
>
|
|
1135
|
+
<View style={selectMainContainerStyles(enableHero, viewport, maxWidth, enablePeeking)}>
|
|
1050
1136
|
<CarouselProvider
|
|
1051
1137
|
activeIndex={activeIndex}
|
|
1052
1138
|
goTo={goTo}
|
|
@@ -1061,6 +1147,8 @@ const Carousel = React.forwardRef(
|
|
|
1061
1147
|
enableDisplayMultipleItemsPerSlide,
|
|
1062
1148
|
viewport
|
|
1063
1149
|
)}
|
|
1150
|
+
maxWidth={maxWidth}
|
|
1151
|
+
viewportWidth={currentViewportWidth}
|
|
1064
1152
|
>
|
|
1065
1153
|
<View
|
|
1066
1154
|
style={[
|
|
@@ -1087,7 +1175,9 @@ const Carousel = React.forwardRef(
|
|
|
1087
1175
|
enablePeeking,
|
|
1088
1176
|
enableDisplayMultipleItemsPerSlide,
|
|
1089
1177
|
isAutoPlayEnabled,
|
|
1090
|
-
viewport
|
|
1178
|
+
viewport,
|
|
1179
|
+
maxWidth,
|
|
1180
|
+
viewportWidth: currentViewportWidth
|
|
1091
1181
|
})
|
|
1092
1182
|
]}
|
|
1093
1183
|
>
|
|
@@ -1113,7 +1203,9 @@ const Carousel = React.forwardRef(
|
|
|
1113
1203
|
enablePeeking,
|
|
1114
1204
|
enableDisplayMultipleItemsPerSlide,
|
|
1115
1205
|
isAutoPlayEnabled,
|
|
1116
|
-
viewport
|
|
1206
|
+
viewport,
|
|
1207
|
+
maxWidth,
|
|
1208
|
+
currentViewportWidth
|
|
1117
1209
|
)}
|
|
1118
1210
|
testID="previous-button-container"
|
|
1119
1211
|
>
|
|
@@ -1166,22 +1258,7 @@ const Carousel = React.forwardRef(
|
|
|
1166
1258
|
let hidden = !isAnimating && index !== activeIndex
|
|
1167
1259
|
|
|
1168
1260
|
if (enablePeeking && !isAnimating) {
|
|
1169
|
-
|
|
1170
|
-
const maxItemsForSlide = getMaximumItemsForSlide(
|
|
1171
|
-
enableDisplayMultipleItemsPerSlide,
|
|
1172
|
-
viewport
|
|
1173
|
-
)
|
|
1174
|
-
if (
|
|
1175
|
-
index >= activeIndex * maxItemsForSlide - 1 &&
|
|
1176
|
-
index < activeIndex * maxItemsForSlide + maxItemsForSlide + 1
|
|
1177
|
-
) {
|
|
1178
|
-
hidden = false
|
|
1179
|
-
} else {
|
|
1180
|
-
hidden = true
|
|
1181
|
-
}
|
|
1182
|
-
} else if (index >= activeIndex - 1 && index <= activeIndex + 1) {
|
|
1183
|
-
hidden = false
|
|
1184
|
-
}
|
|
1261
|
+
hidden = false
|
|
1185
1262
|
} else if (
|
|
1186
1263
|
!enablePeeking &&
|
|
1187
1264
|
enableDisplayMultipleItemsPerSlide &&
|
|
@@ -1224,7 +1301,9 @@ const Carousel = React.forwardRef(
|
|
|
1224
1301
|
enablePeeking,
|
|
1225
1302
|
enableDisplayMultipleItemsPerSlide,
|
|
1226
1303
|
isAutoPlayEnabled,
|
|
1227
|
-
viewport
|
|
1304
|
+
viewport,
|
|
1305
|
+
maxWidth,
|
|
1306
|
+
currentViewportWidth
|
|
1228
1307
|
)}
|
|
1229
1308
|
testID="next-button-container"
|
|
1230
1309
|
>
|
|
@@ -1242,7 +1321,9 @@ const Carousel = React.forwardRef(
|
|
|
1242
1321
|
</View>
|
|
1243
1322
|
) : null}
|
|
1244
1323
|
</View>
|
|
1245
|
-
<View
|
|
1324
|
+
<View
|
|
1325
|
+
style={selectNavigationStyles(tabs, enableHero, viewport, enablePeeking, maxWidth)}
|
|
1326
|
+
>
|
|
1246
1327
|
{showPanelNavigation ? activePanelNavigation : null}
|
|
1247
1328
|
</View>
|
|
1248
1329
|
</CarouselProvider>
|
|
@@ -1495,7 +1576,23 @@ Carousel.propTypes = {
|
|
|
1495
1576
|
md: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1496
1577
|
sm: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number]),
|
|
1497
1578
|
xs: PropTypes.oneOfType([PropTypes.oneOf(['max', 'full']), PropTypes.number])
|
|
1498
|
-
})
|
|
1579
|
+
}),
|
|
1580
|
+
/**
|
|
1581
|
+
* Animation style for swipe release transitions.
|
|
1582
|
+
* - `'instant'`: Immediate snap to position (current default)
|
|
1583
|
+
* - `'ease-out'`: Smooth deceleration animation
|
|
1584
|
+
* - Default value is `'instant'`
|
|
1585
|
+
* - Use `swipeReleaseDuration` to customize the animation duration when using `'ease-out'`
|
|
1586
|
+
*
|
|
1587
|
+
* @deprecated The default will change to `'ease-out'` in Q2 2026 (introduced Jan 2026).
|
|
1588
|
+
*/
|
|
1589
|
+
swipeReleaseStyle: PropTypes.oneOf(['instant', 'ease-out']),
|
|
1590
|
+
/**
|
|
1591
|
+
* Duration in milliseconds of the ease-out animation when releasing a swipe gesture.
|
|
1592
|
+
* Only applies when `swipeReleaseStyle` is set to `'ease-out'`.
|
|
1593
|
+
* - Default value is `500` (500ms)
|
|
1594
|
+
*/
|
|
1595
|
+
swipeReleaseDuration: PropTypes.number
|
|
1499
1596
|
}
|
|
1500
1597
|
|
|
1501
1598
|
Carousel.Item = CarouselItem
|
|
@@ -14,7 +14,9 @@ const CarouselProvider = ({
|
|
|
14
14
|
themeTokens,
|
|
15
15
|
totalItems,
|
|
16
16
|
width,
|
|
17
|
-
maximumItemsForSlide
|
|
17
|
+
maximumItemsForSlide,
|
|
18
|
+
maxWidth,
|
|
19
|
+
viewportWidth
|
|
18
20
|
}) => {
|
|
19
21
|
const value = React.useMemo(
|
|
20
22
|
() => ({
|
|
@@ -26,7 +28,9 @@ const CarouselProvider = ({
|
|
|
26
28
|
themeTokens,
|
|
27
29
|
totalItems,
|
|
28
30
|
width,
|
|
29
|
-
maximumItemsForSlide
|
|
31
|
+
maximumItemsForSlide,
|
|
32
|
+
maxWidth,
|
|
33
|
+
viewportWidth
|
|
30
34
|
}),
|
|
31
35
|
[
|
|
32
36
|
activeIndex,
|
|
@@ -37,7 +41,9 @@ const CarouselProvider = ({
|
|
|
37
41
|
totalItems,
|
|
38
42
|
themeTokens,
|
|
39
43
|
width,
|
|
40
|
-
maximumItemsForSlide
|
|
44
|
+
maximumItemsForSlide,
|
|
45
|
+
maxWidth,
|
|
46
|
+
viewportWidth
|
|
41
47
|
]
|
|
42
48
|
)
|
|
43
49
|
return <CarouselContext.Provider value={value}>{children}</CarouselContext.Provider>
|
|
@@ -61,7 +67,9 @@ CarouselProvider.propTypes = {
|
|
|
61
67
|
themeTokens: getTokensPropType('Carousel'),
|
|
62
68
|
totalItems: PropTypes.number.isRequired,
|
|
63
69
|
width: PropTypes.number.isRequired,
|
|
64
|
-
maximumItemsForSlide: PropTypes.number
|
|
70
|
+
maximumItemsForSlide: PropTypes.number,
|
|
71
|
+
maxWidth: PropTypes.number,
|
|
72
|
+
viewportWidth: PropTypes.number
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
export { CarouselProvider, useCarousel }
|
|
@@ -18,27 +18,28 @@ const selectContainerStyle = ({
|
|
|
18
18
|
width,
|
|
19
19
|
elementIndex,
|
|
20
20
|
enablePeeking,
|
|
21
|
-
peekingMarginLeft,
|
|
22
21
|
peekingGap,
|
|
23
22
|
hidden,
|
|
24
23
|
enableDisplayMultipleItemsPerSlide,
|
|
25
24
|
viewport,
|
|
26
|
-
peekingMiddleSpace
|
|
25
|
+
peekingMiddleSpace,
|
|
26
|
+
maxWidth,
|
|
27
|
+
viewportWidth
|
|
27
28
|
}) => {
|
|
28
29
|
let adjustedWidth = width
|
|
29
30
|
let marginLeft = 0
|
|
30
31
|
|
|
31
32
|
if (enablePeeking) {
|
|
32
33
|
const isFirst = elementIndex === 0
|
|
33
|
-
|
|
34
|
+
const baseWidth = maxWidth || width
|
|
35
|
+
adjustedWidth = baseWidth - peekingMiddleSpace * 2
|
|
34
36
|
if (isFirst) {
|
|
35
|
-
marginLeft =
|
|
37
|
+
marginLeft = peekingMiddleSpace + (viewportWidth - maxWidth) / 2
|
|
36
38
|
} else {
|
|
37
39
|
marginLeft = peekingGap
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
// Adjust width and margins for multiple items per slide.
|
|
42
43
|
if (enableDisplayMultipleItemsPerSlide) {
|
|
43
44
|
switch (viewport) {
|
|
44
45
|
case 'xs':
|
|
@@ -102,7 +103,8 @@ const CarouselItem = React.forwardRef(
|
|
|
102
103
|
},
|
|
103
104
|
ref
|
|
104
105
|
) => {
|
|
105
|
-
const { width, activeIndex, goTo, maximumItemsForSlide } =
|
|
106
|
+
const { width, activeIndex, goTo, maximumItemsForSlide, maxWidth, viewportWidth } =
|
|
107
|
+
useCarousel()
|
|
106
108
|
|
|
107
109
|
const selectedProps = selectProps({
|
|
108
110
|
...rest,
|
|
@@ -162,6 +164,8 @@ const CarouselItem = React.forwardRef(
|
|
|
162
164
|
enablePeeking,
|
|
163
165
|
enableDisplayMultipleItemsPerSlide,
|
|
164
166
|
viewport,
|
|
167
|
+
maxWidth,
|
|
168
|
+
viewportWidth,
|
|
165
169
|
...peekingProps
|
|
166
170
|
})}
|
|
167
171
|
{...selectedProps}
|
|
@@ -9,3 +9,27 @@ export const DEFAULT_VIEWPORT_MARGIN = 10
|
|
|
9
9
|
export const PEEKING_MULTIPLIER = 2
|
|
10
10
|
export const ACTIVE_INDEX_OFFSET_MULTIPLIER = 1
|
|
11
11
|
export const NEGATIVE_MULTIPLIER = -1
|
|
12
|
+
|
|
13
|
+
export const TRANSITION_MODES = {
|
|
14
|
+
MANUAL: 'manual',
|
|
15
|
+
AUTOMATIC: 'automatic',
|
|
16
|
+
SWIPE: 'swipe'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const SWIPE_RELEASE_STYLES = {
|
|
20
|
+
INSTANT: 'instant',
|
|
21
|
+
EASE_OUT: 'ease-out'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const INSTANT_ANIMATION_DURATION = 1
|
|
25
|
+
export const DEFAULT_SWIPE_RELEASE_DURATION = 500
|
|
26
|
+
export const POSITION_VARIANTS = {
|
|
27
|
+
EDGE: 'edge',
|
|
28
|
+
INSIDE: 'inside',
|
|
29
|
+
OUTSIDE: 'outside'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const POSITION_PROPERTIES = {
|
|
33
|
+
LEFT: 'left',
|
|
34
|
+
RIGHT: 'right'
|
|
35
|
+
}
|