@momo-kits/carousel 0.150.2-phuc.15 → 0.150.3-beta.20
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/animation.ts +148 -17
- package/index.tsx +1330 -700
- package/package.json +20 -20
- package/types.ts +64 -18
package/index.tsx
CHANGED
|
@@ -1,840 +1,1470 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
2
9
|
import {
|
|
3
10
|
Animated,
|
|
4
|
-
|
|
5
|
-
GestureResponderEvent,
|
|
11
|
+
I18nManager,
|
|
6
12
|
LayoutChangeEvent,
|
|
7
|
-
NativeScrollEvent,
|
|
8
13
|
NativeSyntheticEvent,
|
|
14
|
+
NativeScrollEvent,
|
|
9
15
|
Platform,
|
|
10
16
|
View,
|
|
11
|
-
ViewStyle,
|
|
12
17
|
} from 'react-native';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
import {
|
|
19
|
+
defaultAnimatedStyles,
|
|
20
|
+
defaultScrollInterpolator,
|
|
21
|
+
shiftAnimatedStyles,
|
|
22
|
+
stackAnimatedStyles,
|
|
23
|
+
stackScrollInterpolator,
|
|
24
|
+
tinderAnimatedStyles,
|
|
25
|
+
tinderScrollInterpolator,
|
|
26
|
+
} from './animation';
|
|
27
|
+
import { CarouselProps, CarouselRef, Position } from './types';
|
|
16
28
|
|
|
17
29
|
const IS_ANDROID = Platform.OS === 'android';
|
|
18
30
|
const IS_IOS = Platform.OS === 'ios';
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
activeSlideAlignment
|
|
24
|
-
activeSlideOffset
|
|
25
|
-
apparitionDelay
|
|
26
|
-
autoplay
|
|
27
|
-
autoplayDelay
|
|
28
|
-
autoplayInterval
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
useExperimentalSnap
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
31
|
+
const IS_RTL = I18nManager.isRTL;
|
|
32
|
+
|
|
33
|
+
const Carousel = forwardRef<CarouselRef, CarouselProps>((props, ref) => {
|
|
34
|
+
const {
|
|
35
|
+
activeSlideAlignment = 'center',
|
|
36
|
+
activeSlideOffset = 25,
|
|
37
|
+
apparitionDelay = 0,
|
|
38
|
+
autoplay = false,
|
|
39
|
+
autoplayDelay = 1000,
|
|
40
|
+
autoplayInterval = 3000,
|
|
41
|
+
data = [],
|
|
42
|
+
enableSnap = true,
|
|
43
|
+
firstItem = 0,
|
|
44
|
+
inactiveSlideOpacity = 1,
|
|
45
|
+
inactiveSlideScale = 0.9,
|
|
46
|
+
inactiveSlideShift = 0,
|
|
47
|
+
snapToInterval,
|
|
48
|
+
layout = 'default',
|
|
49
|
+
layoutCardOffset,
|
|
50
|
+
loop = false,
|
|
51
|
+
loopClonesPerSide = 3,
|
|
52
|
+
scrollEnabled: scrollEnabledProp = true,
|
|
53
|
+
useScrollView = false,
|
|
54
|
+
vertical = false,
|
|
55
|
+
useExperimentalSnap = false,
|
|
56
|
+
disableIntervalMomentum = false,
|
|
57
|
+
itemWidth,
|
|
58
|
+
itemHeight,
|
|
59
|
+
sliderWidth,
|
|
60
|
+
sliderHeight,
|
|
61
|
+
renderItem,
|
|
62
|
+
scrollInterpolator,
|
|
63
|
+
slideInterpolatedStyle,
|
|
64
|
+
slideStyle,
|
|
65
|
+
containerCustomStyle,
|
|
66
|
+
contentContainerCustomStyle,
|
|
67
|
+
style,
|
|
68
|
+
keyExtractor,
|
|
69
|
+
getItemLayout: getItemLayoutProp,
|
|
70
|
+
CellRendererComponent,
|
|
71
|
+
onScroll,
|
|
72
|
+
onScrollIndexChanged,
|
|
73
|
+
onSnapToItem,
|
|
74
|
+
onMomentumScrollEnd,
|
|
75
|
+
onLayout,
|
|
76
|
+
onTouchStart,
|
|
77
|
+
onTouchEnd,
|
|
78
|
+
} = props;
|
|
79
|
+
|
|
80
|
+
// State
|
|
81
|
+
const [hideCarousel, setHideCarousel] = useState(!!apparitionDelay);
|
|
82
|
+
const [interpolators, setInterpolators] = useState<
|
|
83
|
+
Animated.AnimatedInterpolation<number>[]
|
|
84
|
+
>([]);
|
|
85
|
+
|
|
86
|
+
// Refs for instance variables
|
|
87
|
+
const mountedRef = useRef(false);
|
|
88
|
+
const carouselRef = useRef<any>(null);
|
|
89
|
+
const scrollPosRef = useRef(new Animated.Value(0));
|
|
90
|
+
const onScrollHandlerRef = useRef<any>(null);
|
|
91
|
+
const positionsRef = useRef<Position[]>([]);
|
|
92
|
+
const currentScrollOffsetRef = useRef(0);
|
|
93
|
+
const scrollEnabledRef = useRef(scrollEnabledProp !== false);
|
|
94
|
+
const activeItemRef = useRef(0);
|
|
95
|
+
const onScrollActiveItemRef = useRef(0);
|
|
96
|
+
const previousFirstItemRef = useRef(0);
|
|
97
|
+
const previousItemsLengthRef = useRef(0);
|
|
98
|
+
const onLayoutInitDoneRef = useRef(false);
|
|
99
|
+
|
|
100
|
+
// Autoplay refs
|
|
101
|
+
const autoplayRef = useRef(false);
|
|
102
|
+
const autoplayingRef = useRef(false);
|
|
103
|
+
const autoplayTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
104
|
+
const autoplayIntervalRef = useRef<ReturnType<typeof setInterval> | null>(
|
|
105
|
+
null,
|
|
106
|
+
);
|
|
107
|
+
const enableAutoplayTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
|
|
108
|
+
null,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Other timeouts
|
|
112
|
+
const initTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
113
|
+
const apparitionTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
|
|
114
|
+
null,
|
|
115
|
+
);
|
|
116
|
+
const hackSlideAnimationTimeoutRef = useRef<ReturnType<
|
|
117
|
+
typeof setTimeout
|
|
118
|
+
> | null>(null);
|
|
119
|
+
const snapNoMomentumTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
|
|
120
|
+
null,
|
|
121
|
+
);
|
|
122
|
+
const androidRepositioningTimeoutRef = useRef<ReturnType<
|
|
123
|
+
typeof setTimeout
|
|
124
|
+
> | null>(null);
|
|
125
|
+
|
|
126
|
+
// Helper functions
|
|
127
|
+
const needsScrollView = useCallback(() => {
|
|
128
|
+
return IS_ANDROID
|
|
129
|
+
? useScrollView ||
|
|
130
|
+
!Animated.FlatList ||
|
|
131
|
+
shouldUseStackLayout() ||
|
|
132
|
+
shouldUseTinderLayout()
|
|
133
|
+
: useScrollView || !Animated.FlatList;
|
|
134
|
+
}, [useScrollView, layout]);
|
|
135
|
+
|
|
136
|
+
const needsRTLAdaptations = useCallback(() => {
|
|
137
|
+
return IS_RTL && IS_ANDROID && !vertical;
|
|
138
|
+
}, [vertical]);
|
|
139
|
+
|
|
140
|
+
const enableLoop = useCallback(() => {
|
|
141
|
+
return enableSnap && loop && data && data.length && data.length > 1;
|
|
142
|
+
}, [enableSnap, loop, data]);
|
|
47
143
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
144
|
+
const shouldAnimateSlides = useCallback(() => {
|
|
145
|
+
return (
|
|
146
|
+
inactiveSlideOpacity < 1 ||
|
|
147
|
+
inactiveSlideScale < 1 ||
|
|
148
|
+
!!scrollInterpolator ||
|
|
149
|
+
!!slideInterpolatedStyle ||
|
|
150
|
+
shouldUseShiftLayout() ||
|
|
151
|
+
shouldUseStackLayout() ||
|
|
152
|
+
shouldUseTinderLayout()
|
|
153
|
+
);
|
|
154
|
+
}, [
|
|
155
|
+
inactiveSlideOpacity,
|
|
156
|
+
inactiveSlideScale,
|
|
157
|
+
scrollInterpolator,
|
|
158
|
+
slideInterpolatedStyle,
|
|
159
|
+
layout,
|
|
160
|
+
inactiveSlideShift,
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
const shouldUseShiftLayout = useCallback(() => {
|
|
164
|
+
return layout === 'default' && inactiveSlideShift !== 0;
|
|
165
|
+
}, [layout, inactiveSlideShift]);
|
|
166
|
+
|
|
167
|
+
const shouldUseStackLayout = useCallback(() => {
|
|
168
|
+
return layout === 'stack';
|
|
169
|
+
}, [layout]);
|
|
170
|
+
|
|
171
|
+
const shouldUseTinderLayout = useCallback(() => {
|
|
172
|
+
return layout === 'tinder';
|
|
173
|
+
}, [layout]);
|
|
174
|
+
|
|
175
|
+
const shouldRepositionScroll = useCallback(
|
|
176
|
+
(index: number) => {
|
|
177
|
+
const dataLength = data && data.length;
|
|
178
|
+
if (
|
|
179
|
+
!enableSnap ||
|
|
180
|
+
!dataLength ||
|
|
181
|
+
!enableLoop() ||
|
|
182
|
+
(index >= loopClonesPerSide && index < dataLength + loopClonesPerSide)
|
|
183
|
+
) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
},
|
|
188
|
+
[data, enableSnap, loopClonesPerSide, enableLoop],
|
|
189
|
+
);
|
|
79
190
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this._onMomentumScrollEnd = this._onMomentumScrollEnd.bind(this);
|
|
96
|
-
this._onTouchStart = this._onTouchStart.bind(this);
|
|
97
|
-
this._onTouchEnd = this._onTouchEnd.bind(this);
|
|
98
|
-
this._renderItem = this._renderItem.bind(this);
|
|
99
|
-
this._setScrollHandler(props);
|
|
100
|
-
}
|
|
191
|
+
const isMultiple = useCallback((x: number, y: number) => {
|
|
192
|
+
return Math.round(Math.round(x / y) / (1 / y)) === Math.round(x);
|
|
193
|
+
}, []);
|
|
194
|
+
|
|
195
|
+
const getCustomData = useCallback(
|
|
196
|
+
(propsOverride = props) => {
|
|
197
|
+
const {
|
|
198
|
+
data: propsData = [],
|
|
199
|
+
loopClonesPerSide: loopClones = loopClonesPerSide,
|
|
200
|
+
} = propsOverride;
|
|
201
|
+
const dataLength = propsData && propsData.length;
|
|
202
|
+
|
|
203
|
+
if (!dataLength) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
101
206
|
|
|
102
|
-
|
|
103
|
-
|
|
207
|
+
if (!enableLoop()) {
|
|
208
|
+
return propsData;
|
|
209
|
+
}
|
|
104
210
|
|
|
105
|
-
|
|
106
|
-
|
|
211
|
+
let previousItems: any[] = [];
|
|
212
|
+
let nextItems: any[] = [];
|
|
107
213
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
214
|
+
if (loopClones > dataLength) {
|
|
215
|
+
const dataMultiplier = Math.floor(loopClones / dataLength);
|
|
216
|
+
const remainder = loopClones % dataLength;
|
|
113
217
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
if (autoplay) {
|
|
119
|
-
this.startAutoplay();
|
|
218
|
+
for (let i = 0; i < dataMultiplier; i++) {
|
|
219
|
+
previousItems.push(...propsData);
|
|
220
|
+
nextItems.push(...propsData);
|
|
120
221
|
}
|
|
121
|
-
};
|
|
122
222
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
apparitionCallback();
|
|
126
|
-
}, apparitionDelay);
|
|
223
|
+
previousItems.unshift(...propsData.slice(-remainder));
|
|
224
|
+
nextItems.push(...propsData.slice(0, remainder));
|
|
127
225
|
} else {
|
|
128
|
-
|
|
226
|
+
previousItems = propsData.slice(-loopClones);
|
|
227
|
+
nextItems = propsData.slice(0, loopClones);
|
|
129
228
|
}
|
|
130
|
-
}, 1);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
componentDidUpdate(prevProps: CarouselProps) {
|
|
134
|
-
const { interpolators } = this.state;
|
|
135
|
-
const { firstItem = 0, scrollEnabled } = this.props;
|
|
136
|
-
const itemsLength = this._getCustomDataLength(this.props);
|
|
137
229
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
230
|
+
return previousItems.concat(propsData, nextItems);
|
|
231
|
+
},
|
|
232
|
+
[data, loopClonesPerSide, enableLoop],
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const getCustomDataLength = useCallback(
|
|
236
|
+
(propsOverride = props) => {
|
|
237
|
+
const {
|
|
238
|
+
data: propsData = [],
|
|
239
|
+
loopClonesPerSide: loopClones = loopClonesPerSide,
|
|
240
|
+
} = propsOverride;
|
|
241
|
+
const dataLength = propsData && propsData.length;
|
|
242
|
+
|
|
243
|
+
if (!dataLength) {
|
|
244
|
+
return 0;
|
|
245
|
+
}
|
|
141
246
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
: nextFirstItem;
|
|
247
|
+
return enableLoop() ? dataLength + 2 * loopClones : dataLength;
|
|
248
|
+
},
|
|
249
|
+
[data, loopClonesPerSide, enableLoop],
|
|
250
|
+
);
|
|
147
251
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
252
|
+
const getCustomIndex = useCallback(
|
|
253
|
+
(index: number, propsOverride = props) => {
|
|
254
|
+
const itemsLength = getCustomDataLength(propsOverride);
|
|
151
255
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
256
|
+
if (!itemsLength || typeof index === 'undefined') {
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
155
259
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
260
|
+
return needsRTLAdaptations() ? itemsLength - index - 1 : index;
|
|
261
|
+
},
|
|
262
|
+
[getCustomDataLength, needsRTLAdaptations],
|
|
263
|
+
);
|
|
159
264
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this._previousFirstItem = nextFirstItem;
|
|
167
|
-
this._snapToItem(nextFirstItem, false, true, true);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (this.props.onScroll !== prevProps.onScroll) {
|
|
171
|
-
this._setScrollHandler(this.props);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
265
|
+
const getDataIndex = useCallback(
|
|
266
|
+
(index: number) => {
|
|
267
|
+
const dataLength = data && data.length;
|
|
268
|
+
if (!enableLoop() || !dataLength) {
|
|
269
|
+
return index;
|
|
270
|
+
}
|
|
174
271
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
272
|
+
if (index >= dataLength + loopClonesPerSide) {
|
|
273
|
+
return loopClonesPerSide > dataLength
|
|
274
|
+
? (index - loopClonesPerSide) % dataLength
|
|
275
|
+
: index - dataLength - loopClonesPerSide;
|
|
276
|
+
} else if (index < loopClonesPerSide) {
|
|
277
|
+
if (loopClonesPerSide > dataLength) {
|
|
278
|
+
const baseDataIndexes: number[] = [];
|
|
279
|
+
const dataIndexes: number[] = [];
|
|
280
|
+
const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
|
|
281
|
+
const remainder = loopClonesPerSide % dataLength;
|
|
282
|
+
|
|
283
|
+
for (let i = 0; i < dataLength; i++) {
|
|
284
|
+
baseDataIndexes.push(i);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
for (let j = 0; j < dataMultiplier; j++) {
|
|
288
|
+
dataIndexes.push(...baseDataIndexes);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
dataIndexes.unshift(...baseDataIndexes.slice(-remainder));
|
|
292
|
+
return dataIndexes[index];
|
|
293
|
+
} else {
|
|
294
|
+
return index + dataLength - loopClonesPerSide;
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
return index - loopClonesPerSide;
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
[data, loopClonesPerSide, enableLoop],
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const getPositionIndex = useCallback(
|
|
304
|
+
(index: number) => {
|
|
305
|
+
return loop ? index + loopClonesPerSide : index;
|
|
306
|
+
},
|
|
307
|
+
[loop, loopClonesPerSide],
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const getSnapOffsets = useCallback(() => {
|
|
311
|
+
const offset = getItemMainDimension();
|
|
312
|
+
return [...Array(getCustomDataLength())].map((_, i) => {
|
|
313
|
+
return i * offset;
|
|
314
|
+
});
|
|
315
|
+
}, [getCustomDataLength, itemWidth, itemHeight, vertical]);
|
|
188
316
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this._scrollPos = new Animated.Value(0);
|
|
195
|
-
const argMapping = [
|
|
196
|
-
{ nativeEvent: { contentOffset: { x: this._scrollPos } } },
|
|
197
|
-
];
|
|
317
|
+
const getFirstItem = useCallback(
|
|
318
|
+
(index: number, propsOverride = props) => {
|
|
319
|
+
const { loopClonesPerSide: loopClones = loopClonesPerSide } =
|
|
320
|
+
propsOverride;
|
|
321
|
+
const itemsLength = getCustomDataLength(propsOverride);
|
|
198
322
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const [argMap] = props.onScroll._argMapping;
|
|
202
|
-
if (argMap && argMap.nativeEvent && argMap.nativeEvent.contentOffset) {
|
|
203
|
-
this._scrollPos =
|
|
204
|
-
argMap.nativeEvent.contentOffset.x ||
|
|
205
|
-
argMap.nativeEvent.contentOffset.y ||
|
|
206
|
-
this._scrollPos;
|
|
323
|
+
if (!itemsLength || index > itemsLength - 1 || index < 0) {
|
|
324
|
+
return 0;
|
|
207
325
|
}
|
|
208
|
-
argMapping.push(...props.onScroll._argMapping);
|
|
209
|
-
}
|
|
210
|
-
this._onScrollHandler = Animated.event(argMapping, scrollEventConfig);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
_enableLoop() {
|
|
214
|
-
const { data, enableSnap, loop } = this.props;
|
|
215
|
-
return enableSnap && loop && data && data.length && data.length > 1;
|
|
216
|
-
}
|
|
217
326
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
327
|
+
return enableLoop() ? index + loopClones : index;
|
|
328
|
+
},
|
|
329
|
+
[getCustomDataLength, enableLoop, loopClonesPerSide],
|
|
330
|
+
);
|
|
222
331
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
332
|
+
const getWrappedRef = useCallback(() => {
|
|
333
|
+
if (
|
|
334
|
+
carouselRef.current &&
|
|
335
|
+
((needsScrollView() && carouselRef.current.scrollTo) ||
|
|
336
|
+
(!needsScrollView() && carouselRef.current.scrollToOffset))
|
|
337
|
+
) {
|
|
338
|
+
return carouselRef.current;
|
|
339
|
+
}
|
|
340
|
+
return (
|
|
341
|
+
carouselRef.current &&
|
|
342
|
+
carouselRef.current.getNode &&
|
|
343
|
+
carouselRef.current.getNode()
|
|
231
344
|
);
|
|
232
|
-
}
|
|
345
|
+
}, [needsScrollView]);
|
|
233
346
|
|
|
234
|
-
|
|
235
|
-
return
|
|
236
|
-
}
|
|
347
|
+
const getScrollEnabled = useCallback(() => {
|
|
348
|
+
return scrollEnabledRef.current;
|
|
349
|
+
}, []);
|
|
237
350
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (!dataLength) {
|
|
243
|
-
return [];
|
|
244
|
-
}
|
|
351
|
+
const setScrollEnabled = useCallback(
|
|
352
|
+
(enabled = true) => {
|
|
353
|
+
const wrappedRef = getWrappedRef();
|
|
245
354
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
355
|
+
if (!wrappedRef || !wrappedRef.setNativeProps) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
249
358
|
|
|
250
|
-
|
|
251
|
-
|
|
359
|
+
wrappedRef.setNativeProps({ scrollEnabled: enabled });
|
|
360
|
+
scrollEnabledRef.current = enabled;
|
|
361
|
+
},
|
|
362
|
+
[getWrappedRef],
|
|
363
|
+
);
|
|
252
364
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
365
|
+
const getItemMainDimension = useCallback(() => {
|
|
366
|
+
return vertical ? itemHeight : itemWidth;
|
|
367
|
+
}, [vertical, itemHeight, itemWidth]);
|
|
256
368
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
369
|
+
const getItemScrollOffset = useCallback((index: number) => {
|
|
370
|
+
return (
|
|
371
|
+
positionsRef.current &&
|
|
372
|
+
positionsRef.current[index] &&
|
|
373
|
+
positionsRef.current[index].start
|
|
374
|
+
);
|
|
375
|
+
}, []);
|
|
376
|
+
|
|
377
|
+
const getItemLayout = useCallback(
|
|
378
|
+
(_: any, index: number) => {
|
|
379
|
+
const itemMainDimension = getItemMainDimension();
|
|
380
|
+
return {
|
|
381
|
+
index,
|
|
382
|
+
length: itemMainDimension,
|
|
383
|
+
offset: itemMainDimension * index,
|
|
384
|
+
};
|
|
385
|
+
},
|
|
386
|
+
[getItemMainDimension],
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const getCellRendererComponent = useCallback(
|
|
390
|
+
({ children, index, style: cellStyle, ...cellProps }: any) => {
|
|
391
|
+
const customStyle = [
|
|
392
|
+
cellStyle,
|
|
393
|
+
!IS_ANDROID ? { zIndex: getCustomDataLength() - index } : {},
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<View style={customStyle} key={index} {...cellProps}>
|
|
398
|
+
{children}
|
|
399
|
+
</View>
|
|
400
|
+
);
|
|
401
|
+
},
|
|
402
|
+
[getCustomDataLength],
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
const getKeyExtractor = useCallback(
|
|
406
|
+
(_: any, index: number) => {
|
|
407
|
+
return needsScrollView()
|
|
408
|
+
? `scrollview-item-${index}`
|
|
409
|
+
: `flatlist-item-${index}`;
|
|
410
|
+
},
|
|
411
|
+
[needsScrollView],
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const getScrollOffset = useCallback(
|
|
415
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
416
|
+
return (
|
|
417
|
+
(event &&
|
|
418
|
+
event.nativeEvent &&
|
|
419
|
+
event.nativeEvent.contentOffset &&
|
|
420
|
+
event.nativeEvent.contentOffset[vertical ? 'y' : 'x']) ||
|
|
421
|
+
0
|
|
422
|
+
);
|
|
423
|
+
},
|
|
424
|
+
[vertical],
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
const getContainerInnerMargin = useCallback(
|
|
428
|
+
(opposite = false) => {
|
|
429
|
+
if (
|
|
430
|
+
(activeSlideAlignment === 'start' && !opposite) ||
|
|
431
|
+
(activeSlideAlignment === 'end' && opposite)
|
|
432
|
+
) {
|
|
433
|
+
return 0;
|
|
434
|
+
} else if (
|
|
435
|
+
(activeSlideAlignment === 'end' && !opposite) ||
|
|
436
|
+
(activeSlideAlignment === 'start' && opposite)
|
|
437
|
+
) {
|
|
438
|
+
return vertical ? sliderHeight - itemHeight : sliderWidth - itemWidth;
|
|
439
|
+
} else {
|
|
440
|
+
return vertical
|
|
441
|
+
? (sliderHeight - itemHeight) / 2
|
|
442
|
+
: (sliderWidth - itemWidth) / 2;
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
[
|
|
446
|
+
activeSlideAlignment,
|
|
447
|
+
vertical,
|
|
448
|
+
sliderHeight,
|
|
449
|
+
itemHeight,
|
|
450
|
+
sliderWidth,
|
|
451
|
+
itemWidth,
|
|
452
|
+
],
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
const getActiveSlideOffset = useCallback(() => {
|
|
456
|
+
const itemMainDimension = getItemMainDimension();
|
|
457
|
+
const minOffset = 10;
|
|
458
|
+
return itemMainDimension / 2 - activeSlideOffset >= minOffset
|
|
459
|
+
? activeSlideOffset
|
|
460
|
+
: minOffset;
|
|
461
|
+
}, [getItemMainDimension, activeSlideOffset]);
|
|
462
|
+
|
|
463
|
+
const getActiveItem = useCallback(
|
|
464
|
+
(offset: number) => {
|
|
465
|
+
const itemMainDimension = getItemMainDimension();
|
|
466
|
+
const center = offset + itemMainDimension / 2;
|
|
467
|
+
const activeOffset = getActiveSlideOffset();
|
|
468
|
+
const lastIndex = positionsRef.current.length - 1;
|
|
469
|
+
let itemIndex;
|
|
470
|
+
|
|
471
|
+
if (offset <= 0) {
|
|
472
|
+
return 0;
|
|
260
473
|
}
|
|
261
474
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return previousItems.concat(data, nextItems);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
_getCustomDataLength(props = this.props) {
|
|
273
|
-
const { data, loopClonesPerSide = 3 } = props;
|
|
274
|
-
const dataLength = data && data.length;
|
|
475
|
+
if (
|
|
476
|
+
positionsRef.current[lastIndex] &&
|
|
477
|
+
offset >= positionsRef.current[lastIndex].start
|
|
478
|
+
) {
|
|
479
|
+
return lastIndex;
|
|
480
|
+
}
|
|
275
481
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
482
|
+
for (let i = 0; i < positionsRef.current.length; i++) {
|
|
483
|
+
const { start, end } = positionsRef.current[i];
|
|
484
|
+
if (center + activeOffset >= start && center - activeOffset <= end) {
|
|
485
|
+
itemIndex = i;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
279
489
|
|
|
280
|
-
|
|
281
|
-
|
|
490
|
+
return itemIndex || 0;
|
|
491
|
+
},
|
|
492
|
+
[getItemMainDimension, getActiveSlideOffset],
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const getSlideInterpolatedStyle = useCallback(
|
|
496
|
+
(index: number, animatedValue: Animated.AnimatedInterpolation<number>) => {
|
|
497
|
+
if (slideInterpolatedStyle) {
|
|
498
|
+
return slideInterpolatedStyle(index, animatedValue, props);
|
|
499
|
+
} else if (shouldUseTinderLayout()) {
|
|
500
|
+
return tinderAnimatedStyles(
|
|
501
|
+
index,
|
|
502
|
+
animatedValue,
|
|
503
|
+
props,
|
|
504
|
+
layoutCardOffset,
|
|
505
|
+
);
|
|
506
|
+
} else if (shouldUseStackLayout()) {
|
|
507
|
+
return stackAnimatedStyles(
|
|
508
|
+
index,
|
|
509
|
+
animatedValue,
|
|
510
|
+
props,
|
|
511
|
+
layoutCardOffset,
|
|
512
|
+
);
|
|
513
|
+
} else if (shouldUseShiftLayout()) {
|
|
514
|
+
return shiftAnimatedStyles(index, animatedValue, props);
|
|
515
|
+
} else {
|
|
516
|
+
return defaultAnimatedStyles(index, animatedValue, props);
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
[
|
|
520
|
+
slideInterpolatedStyle,
|
|
521
|
+
shouldUseTinderLayout,
|
|
522
|
+
shouldUseStackLayout,
|
|
523
|
+
shouldUseShiftLayout,
|
|
524
|
+
layoutCardOffset,
|
|
525
|
+
props,
|
|
526
|
+
],
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
const initPositionsAndInterpolators = useCallback(
|
|
530
|
+
(propsOverride = props) => {
|
|
531
|
+
const { data: propsData = [], scrollInterpolator: scrollInterp } =
|
|
532
|
+
propsOverride;
|
|
533
|
+
const itemMainDimension = getItemMainDimension();
|
|
534
|
+
|
|
535
|
+
if (!propsData || !propsData.length) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
282
538
|
|
|
283
|
-
|
|
284
|
-
|
|
539
|
+
// Guard against invalid dimensions that would cause NaN in interpolators
|
|
540
|
+
if (
|
|
541
|
+
!itemMainDimension ||
|
|
542
|
+
itemMainDimension <= 0 ||
|
|
543
|
+
!isFinite(itemMainDimension)
|
|
544
|
+
) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
285
547
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
548
|
+
const newInterpolators: Animated.AnimatedInterpolation<number>[] = [];
|
|
549
|
+
positionsRef.current = [];
|
|
289
550
|
|
|
290
|
-
|
|
291
|
-
|
|
551
|
+
getCustomData(propsOverride).forEach((_itemData: any, index: number) => {
|
|
552
|
+
const _index = getCustomIndex(index, propsOverride);
|
|
553
|
+
let animatedValue: Animated.AnimatedInterpolation<number>;
|
|
292
554
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return index;
|
|
298
|
-
}
|
|
555
|
+
positionsRef.current[index] = {
|
|
556
|
+
start: index * itemMainDimension,
|
|
557
|
+
end: index * itemMainDimension + itemMainDimension,
|
|
558
|
+
};
|
|
299
559
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
560
|
+
if (!shouldAnimateSlides() || !scrollPosRef.current) {
|
|
561
|
+
animatedValue = new Animated.Value(1) as any;
|
|
562
|
+
} else {
|
|
563
|
+
let interpolator;
|
|
564
|
+
|
|
565
|
+
if (scrollInterp) {
|
|
566
|
+
interpolator = scrollInterp(_index, propsOverride);
|
|
567
|
+
} else if (shouldUseStackLayout()) {
|
|
568
|
+
interpolator = stackScrollInterpolator(_index, propsOverride);
|
|
569
|
+
} else if (shouldUseTinderLayout()) {
|
|
570
|
+
interpolator = tinderScrollInterpolator(_index, propsOverride);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (
|
|
574
|
+
!interpolator ||
|
|
575
|
+
!interpolator.inputRange ||
|
|
576
|
+
!interpolator.outputRange
|
|
577
|
+
) {
|
|
578
|
+
interpolator = defaultScrollInterpolator(_index, propsOverride);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Validate interpolator ranges to prevent NaN errors
|
|
582
|
+
const hasValidRange =
|
|
583
|
+
interpolator.inputRange &&
|
|
584
|
+
interpolator.inputRange.every((val: number) => isFinite(val)) &&
|
|
585
|
+
interpolator.outputRange &&
|
|
586
|
+
interpolator.outputRange.every((val: number) => isFinite(val));
|
|
587
|
+
|
|
588
|
+
if (!hasValidRange) {
|
|
589
|
+
animatedValue = new Animated.Value(1) as any;
|
|
590
|
+
} else {
|
|
591
|
+
animatedValue = scrollPosRef.current.interpolate({
|
|
592
|
+
...interpolator,
|
|
593
|
+
extrapolate: 'clamp',
|
|
594
|
+
});
|
|
595
|
+
}
|
|
313
596
|
}
|
|
314
597
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
598
|
+
newInterpolators.push(animatedValue);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
setInterpolators(newInterpolators);
|
|
602
|
+
},
|
|
603
|
+
[
|
|
604
|
+
data,
|
|
605
|
+
getItemMainDimension,
|
|
606
|
+
getCustomData,
|
|
607
|
+
getCustomIndex,
|
|
608
|
+
shouldAnimateSlides,
|
|
609
|
+
shouldUseStackLayout,
|
|
610
|
+
shouldUseTinderLayout,
|
|
611
|
+
scrollInterpolator,
|
|
612
|
+
],
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
const hackActiveSlideAnimation = useCallback(
|
|
616
|
+
(index: number, scrollValue = 1) => {
|
|
617
|
+
const offset = getItemScrollOffset(index);
|
|
318
618
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
619
|
+
if (
|
|
620
|
+
!mountedRef.current ||
|
|
621
|
+
!carouselRef.current ||
|
|
622
|
+
typeof offset === 'undefined'
|
|
623
|
+
) {
|
|
624
|
+
return;
|
|
323
625
|
}
|
|
324
|
-
} else {
|
|
325
|
-
return index - loopClonesPerSide;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
_getFirstItem(index: number, props = this.props) {
|
|
330
|
-
const { loopClonesPerSide = 3 } = props;
|
|
331
|
-
const itemsLength = this._getCustomDataLength(props);
|
|
332
626
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
627
|
+
const multiplier = currentScrollOffsetRef.current === 0 ? 1 : -1;
|
|
628
|
+
const scrollDelta = scrollValue * multiplier;
|
|
336
629
|
|
|
337
|
-
|
|
338
|
-
}
|
|
630
|
+
scrollTo({ offset: offset + scrollDelta, animated: false });
|
|
339
631
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
632
|
+
if (hackSlideAnimationTimeoutRef.current) {
|
|
633
|
+
clearTimeout(hackSlideAnimationTimeoutRef.current);
|
|
634
|
+
}
|
|
635
|
+
hackSlideAnimationTimeoutRef.current = setTimeout(() => {
|
|
636
|
+
scrollTo({ offset, animated: false });
|
|
637
|
+
}, 1);
|
|
638
|
+
},
|
|
639
|
+
[getItemScrollOffset],
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
const repositionScroll = useCallback(
|
|
643
|
+
(index: number, animated = false) => {
|
|
644
|
+
const dataLength = data && data.length;
|
|
645
|
+
|
|
646
|
+
if (typeof index === 'undefined' || !shouldRepositionScroll(index)) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
347
649
|
|
|
348
|
-
|
|
349
|
-
this._scrollEnabled = scrollEnabled;
|
|
350
|
-
}
|
|
650
|
+
let repositionTo = index;
|
|
351
651
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
652
|
+
if (index >= dataLength + loopClonesPerSide) {
|
|
653
|
+
repositionTo = index - dataLength;
|
|
654
|
+
} else if (index < loopClonesPerSide) {
|
|
655
|
+
repositionTo = index + dataLength;
|
|
656
|
+
}
|
|
357
657
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
658
|
+
snapToItem(repositionTo, animated, false);
|
|
659
|
+
},
|
|
660
|
+
[data, loopClonesPerSide, shouldRepositionScroll],
|
|
661
|
+
);
|
|
363
662
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
663
|
+
const scrollTo = useCallback(
|
|
664
|
+
({
|
|
665
|
+
offset,
|
|
367
666
|
index,
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
667
|
+
animated = true,
|
|
668
|
+
}: {
|
|
669
|
+
offset?: number;
|
|
670
|
+
index?: number;
|
|
671
|
+
animated?: boolean;
|
|
672
|
+
}) => {
|
|
673
|
+
const wrappedRef = getWrappedRef();
|
|
674
|
+
if (
|
|
675
|
+
!mountedRef.current ||
|
|
676
|
+
!wrappedRef ||
|
|
677
|
+
(typeof offset === 'undefined' && typeof index === 'undefined')
|
|
678
|
+
) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
372
681
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
682
|
+
let scrollToOffset;
|
|
683
|
+
if (typeof index !== 'undefined') {
|
|
684
|
+
scrollToOffset = getItemScrollOffset(index);
|
|
685
|
+
} else {
|
|
686
|
+
scrollToOffset = offset;
|
|
687
|
+
}
|
|
376
688
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
689
|
+
if (typeof scrollToOffset === 'undefined') {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
380
692
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
693
|
+
const options = needsScrollView()
|
|
694
|
+
? {
|
|
695
|
+
x: vertical ? 0 : scrollToOffset,
|
|
696
|
+
y: vertical ? scrollToOffset : 0,
|
|
697
|
+
animated,
|
|
698
|
+
}
|
|
699
|
+
: {
|
|
700
|
+
offset: scrollToOffset,
|
|
701
|
+
animated,
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
if (needsScrollView()) {
|
|
705
|
+
wrappedRef.scrollTo(options);
|
|
706
|
+
} else {
|
|
707
|
+
wrappedRef.scrollToOffset(options);
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
[getWrappedRef, getItemScrollOffset, needsScrollView, vertical],
|
|
711
|
+
);
|
|
712
|
+
|
|
713
|
+
const handleTouchStart = useCallback(
|
|
714
|
+
(event: any) => {
|
|
715
|
+
if (getScrollEnabled() !== false && autoplayingRef.current) {
|
|
716
|
+
pauseAutoPlay();
|
|
717
|
+
}
|
|
389
718
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const lastIndex = this._positions.length - 1;
|
|
395
|
-
let itemIndex;
|
|
719
|
+
onTouchStart && onTouchStart(event);
|
|
720
|
+
},
|
|
721
|
+
[getScrollEnabled, onTouchStart],
|
|
722
|
+
);
|
|
396
723
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
724
|
+
const handleTouchEnd = useCallback(
|
|
725
|
+
(event: any) => {
|
|
726
|
+
if (
|
|
727
|
+
getScrollEnabled() &&
|
|
728
|
+
autoplayRef.current &&
|
|
729
|
+
!autoplayingRef.current
|
|
730
|
+
) {
|
|
731
|
+
startAutoplay();
|
|
732
|
+
}
|
|
400
733
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
734
|
+
onTouchEnd && onTouchEnd(event);
|
|
735
|
+
},
|
|
736
|
+
[getScrollEnabled, onTouchEnd],
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
const handleScroll = useCallback(
|
|
740
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
741
|
+
const scrollOffset = event
|
|
742
|
+
? getScrollOffset(event)
|
|
743
|
+
: currentScrollOffsetRef.current;
|
|
744
|
+
const nextActiveItem = getActiveItem(scrollOffset);
|
|
745
|
+
const dataLength = getCustomDataLength();
|
|
746
|
+
const lastItemScrollOffset = getItemScrollOffset(dataLength - 1);
|
|
747
|
+
|
|
748
|
+
currentScrollOffsetRef.current = scrollOffset;
|
|
749
|
+
|
|
750
|
+
if (nextActiveItem !== onScrollActiveItemRef.current) {
|
|
751
|
+
onScrollActiveItemRef.current = nextActiveItem;
|
|
752
|
+
onScrollIndexChanged &&
|
|
753
|
+
onScrollIndexChanged(getDataIndex(nextActiveItem));
|
|
754
|
+
}
|
|
407
755
|
|
|
408
|
-
for (let i = 0; i < this._positions.length; i++) {
|
|
409
|
-
const { start, end } = this._positions[i];
|
|
410
756
|
if (
|
|
411
|
-
|
|
412
|
-
|
|
757
|
+
(IS_IOS && scrollOffset >= lastItemScrollOffset) ||
|
|
758
|
+
(IS_ANDROID &&
|
|
759
|
+
Math.floor(scrollOffset) >= Math.floor(lastItemScrollOffset))
|
|
413
760
|
) {
|
|
414
|
-
|
|
415
|
-
|
|
761
|
+
activeItemRef.current = nextActiveItem;
|
|
762
|
+
repositionScroll(nextActiveItem);
|
|
416
763
|
}
|
|
417
|
-
}
|
|
418
764
|
|
|
419
|
-
|
|
420
|
-
|
|
765
|
+
if (typeof onScroll === 'function' && event) {
|
|
766
|
+
onScroll(event);
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
[
|
|
770
|
+
getScrollOffset,
|
|
771
|
+
getActiveItem,
|
|
772
|
+
getCustomDataLength,
|
|
773
|
+
getItemScrollOffset,
|
|
774
|
+
getDataIndex,
|
|
775
|
+
onScrollIndexChanged,
|
|
776
|
+
onScroll,
|
|
777
|
+
repositionScroll,
|
|
778
|
+
],
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
const handleMomentumScrollEnd = useCallback(
|
|
782
|
+
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
783
|
+
const scrollOffset = event
|
|
784
|
+
? getScrollOffset(event)
|
|
785
|
+
: currentScrollOffsetRef.current;
|
|
786
|
+
const nextActiveItem = getActiveItem(scrollOffset);
|
|
787
|
+
const hasSnapped = isMultiple(
|
|
788
|
+
scrollOffset,
|
|
789
|
+
vertical ? itemHeight : itemWidth,
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
if (nextActiveItem !== activeItemRef.current) {
|
|
793
|
+
activeItemRef.current = nextActiveItem;
|
|
794
|
+
onSnapToItem && onSnapToItem(getDataIndex(nextActiveItem));
|
|
795
|
+
|
|
796
|
+
if (hasSnapped && IS_ANDROID) {
|
|
797
|
+
repositionScroll(nextActiveItem);
|
|
798
|
+
} else if (IS_IOS) {
|
|
799
|
+
repositionScroll(nextActiveItem);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
421
802
|
|
|
422
|
-
|
|
423
|
-
const { data } = props;
|
|
424
|
-
const itemMainDimension = this._getItemMainDimension();
|
|
803
|
+
onMomentumScrollEnd && onMomentumScrollEnd(event);
|
|
425
804
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
805
|
+
if (IS_ANDROID && autoplayRef.current && !autoplayingRef.current) {
|
|
806
|
+
if (enableAutoplayTimeoutRef.current) {
|
|
807
|
+
clearTimeout(enableAutoplayTimeoutRef.current);
|
|
808
|
+
}
|
|
809
|
+
enableAutoplayTimeoutRef.current = setTimeout(() => {
|
|
810
|
+
startAutoplay();
|
|
811
|
+
}, autoplayDelay);
|
|
812
|
+
}
|
|
813
|
+
},
|
|
814
|
+
[
|
|
815
|
+
getScrollOffset,
|
|
816
|
+
getActiveItem,
|
|
817
|
+
isMultiple,
|
|
818
|
+
vertical,
|
|
819
|
+
itemHeight,
|
|
820
|
+
itemWidth,
|
|
821
|
+
onSnapToItem,
|
|
822
|
+
getDataIndex,
|
|
823
|
+
repositionScroll,
|
|
824
|
+
onMomentumScrollEnd,
|
|
825
|
+
autoplayDelay,
|
|
826
|
+
],
|
|
827
|
+
);
|
|
828
|
+
|
|
829
|
+
const handleLayout = useCallback(
|
|
830
|
+
(event: LayoutChangeEvent) => {
|
|
831
|
+
if (onLayoutInitDoneRef.current) {
|
|
832
|
+
initPositionsAndInterpolators();
|
|
833
|
+
snapToItem(activeItemRef.current, false, false, true);
|
|
834
|
+
} else {
|
|
835
|
+
onLayoutInitDoneRef.current = true;
|
|
836
|
+
}
|
|
429
837
|
|
|
430
|
-
|
|
431
|
-
|
|
838
|
+
onLayout && onLayout(event);
|
|
839
|
+
},
|
|
840
|
+
[initPositionsAndInterpolators, onLayout],
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
const snapToItem = useCallback(
|
|
844
|
+
(
|
|
845
|
+
index: number,
|
|
846
|
+
animated = true,
|
|
847
|
+
fireCallback = true,
|
|
848
|
+
forceScrollTo = false,
|
|
849
|
+
) => {
|
|
850
|
+
const itemsLength = getCustomDataLength();
|
|
851
|
+
const wrappedRef = getWrappedRef();
|
|
852
|
+
if (!itemsLength || !wrappedRef) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
432
855
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
856
|
+
if (!index || index < 0) {
|
|
857
|
+
index = 0;
|
|
858
|
+
} else if (itemsLength > 0 && index >= itemsLength) {
|
|
859
|
+
index = itemsLength - 1;
|
|
860
|
+
}
|
|
436
861
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
};
|
|
862
|
+
if (index === activeItemRef.current && !forceScrollTo) {
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
441
865
|
|
|
442
|
-
|
|
443
|
-
animatedValue = new Animated.Value(1);
|
|
444
|
-
} else {
|
|
445
|
-
let interpolator = defaultScrollInterpolator(
|
|
446
|
-
_index,
|
|
447
|
-
this.state.itemWidth,
|
|
448
|
-
);
|
|
866
|
+
const offset = getItemScrollOffset(index);
|
|
449
867
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
extrapolate: 'clamp',
|
|
453
|
-
});
|
|
868
|
+
if (offset === undefined) {
|
|
869
|
+
return;
|
|
454
870
|
}
|
|
455
871
|
|
|
456
|
-
|
|
457
|
-
});
|
|
872
|
+
scrollTo({ offset, animated });
|
|
458
873
|
|
|
459
|
-
|
|
460
|
-
|
|
874
|
+
const requiresManualTrigger = !animated || IS_ANDROID;
|
|
875
|
+
if (requiresManualTrigger) {
|
|
876
|
+
activeItemRef.current = index;
|
|
461
877
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
878
|
+
if (fireCallback) {
|
|
879
|
+
onSnapToItem && onSnapToItem(getDataIndex(index));
|
|
880
|
+
}
|
|
465
881
|
|
|
466
|
-
|
|
882
|
+
if (IS_ANDROID && shouldRepositionScroll(index)) {
|
|
883
|
+
if (animated) {
|
|
884
|
+
if (androidRepositioningTimeoutRef.current) {
|
|
885
|
+
clearTimeout(androidRepositioningTimeoutRef.current);
|
|
886
|
+
}
|
|
887
|
+
androidRepositioningTimeoutRef.current = setTimeout(() => {
|
|
888
|
+
repositionScroll(index, false);
|
|
889
|
+
}, 400);
|
|
890
|
+
} else {
|
|
891
|
+
repositionScroll(index);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
[
|
|
897
|
+
getCustomDataLength,
|
|
898
|
+
getWrappedRef,
|
|
899
|
+
getItemScrollOffset,
|
|
900
|
+
scrollTo,
|
|
901
|
+
onSnapToItem,
|
|
902
|
+
getDataIndex,
|
|
903
|
+
shouldRepositionScroll,
|
|
904
|
+
repositionScroll,
|
|
905
|
+
],
|
|
906
|
+
);
|
|
907
|
+
|
|
908
|
+
const startAutoplay = useCallback(() => {
|
|
909
|
+
autoplayRef.current = true;
|
|
910
|
+
|
|
911
|
+
if (autoplayingRef.current) {
|
|
467
912
|
return;
|
|
468
913
|
}
|
|
469
914
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (index >= dataLength + loopClonesPerSide) {
|
|
473
|
-
repositionTo = index - dataLength;
|
|
474
|
-
} else if (index < loopClonesPerSide) {
|
|
475
|
-
repositionTo = index + dataLength;
|
|
915
|
+
if (autoplayTimeoutRef.current) {
|
|
916
|
+
clearTimeout(autoplayTimeoutRef.current);
|
|
476
917
|
}
|
|
918
|
+
autoplayTimeoutRef.current = setTimeout(() => {
|
|
919
|
+
autoplayingRef.current = true;
|
|
920
|
+
autoplayIntervalRef.current = setInterval(() => {
|
|
921
|
+
if (autoplayingRef.current) {
|
|
922
|
+
snapToNext();
|
|
923
|
+
}
|
|
924
|
+
}, autoplayInterval);
|
|
925
|
+
}, autoplayDelay);
|
|
926
|
+
}, [autoplayInterval, autoplayDelay]);
|
|
477
927
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const { onTouchStart } = this.props;
|
|
483
|
-
|
|
484
|
-
if (this._getScrollEnabled() !== false && this._autoplaying) {
|
|
485
|
-
this.pauseAutoPlay();
|
|
928
|
+
const pauseAutoPlay = useCallback(() => {
|
|
929
|
+
autoplayingRef.current = false;
|
|
930
|
+
if (autoplayTimeoutRef.current) {
|
|
931
|
+
clearTimeout(autoplayTimeoutRef.current);
|
|
486
932
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const { onTouchEnd } = this.props;
|
|
493
|
-
|
|
494
|
-
if (
|
|
495
|
-
this._getScrollEnabled() !== false &&
|
|
496
|
-
this._autoplay &&
|
|
497
|
-
!this._autoplaying
|
|
498
|
-
) {
|
|
499
|
-
this.startAutoplay();
|
|
933
|
+
if (enableAutoplayTimeoutRef.current) {
|
|
934
|
+
clearTimeout(enableAutoplayTimeoutRef.current);
|
|
935
|
+
}
|
|
936
|
+
if (autoplayIntervalRef.current) {
|
|
937
|
+
clearInterval(autoplayIntervalRef.current);
|
|
500
938
|
}
|
|
939
|
+
}, []);
|
|
501
940
|
|
|
502
|
-
|
|
503
|
-
|
|
941
|
+
const stopAutoplay = useCallback(() => {
|
|
942
|
+
autoplayRef.current = false;
|
|
943
|
+
pauseAutoPlay();
|
|
944
|
+
}, [pauseAutoPlay]);
|
|
945
|
+
|
|
946
|
+
const snapToItemPublic = useCallback(
|
|
947
|
+
(index: number, animated = true, fireCallback = true) => {
|
|
948
|
+
if (!index || index < 0) {
|
|
949
|
+
index = 0;
|
|
950
|
+
}
|
|
504
951
|
|
|
505
|
-
|
|
506
|
-
const { onScroll, onScrollIndexChanged, onSnapToItem } = this.props;
|
|
507
|
-
const scrollOffset = event
|
|
508
|
-
? this._getScrollOffset(event)
|
|
509
|
-
: this._currentScrollOffset;
|
|
510
|
-
const nextActiveItem = this._getActiveItem(scrollOffset);
|
|
511
|
-
const dataLength = this._getCustomDataLength();
|
|
512
|
-
const lastItemScrollOffset = this._getItemScrollOffset(dataLength - 1);
|
|
952
|
+
const positionIndex = getPositionIndex(index);
|
|
513
953
|
|
|
514
|
-
|
|
954
|
+
if (positionIndex === activeItemRef.current) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
515
957
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
958
|
+
snapToItem(positionIndex, animated, fireCallback);
|
|
959
|
+
},
|
|
960
|
+
[getPositionIndex, snapToItem],
|
|
961
|
+
);
|
|
520
962
|
|
|
521
|
-
|
|
522
|
-
|
|
963
|
+
const snapToNext = useCallback(
|
|
964
|
+
(animated = true, fireCallback = true) => {
|
|
965
|
+
const itemsLength = getCustomDataLength();
|
|
523
966
|
|
|
524
|
-
|
|
525
|
-
(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
967
|
+
let newIndex = activeItemRef.current + 1;
|
|
968
|
+
if (newIndex > itemsLength - 1) {
|
|
969
|
+
newIndex = 0;
|
|
970
|
+
}
|
|
971
|
+
snapToItem(newIndex, animated, fireCallback);
|
|
972
|
+
},
|
|
973
|
+
[getCustomDataLength, snapToItem],
|
|
974
|
+
);
|
|
975
|
+
|
|
976
|
+
const snapToPrev = useCallback(
|
|
977
|
+
(animated = true, fireCallback = true) => {
|
|
978
|
+
const itemsLength = getCustomDataLength();
|
|
979
|
+
|
|
980
|
+
let newIndex = activeItemRef.current - 1;
|
|
981
|
+
if (newIndex < 0) {
|
|
982
|
+
newIndex = itemsLength - 1;
|
|
983
|
+
}
|
|
984
|
+
snapToItem(newIndex, animated, fireCallback);
|
|
985
|
+
},
|
|
986
|
+
[getCustomDataLength, snapToItem],
|
|
987
|
+
);
|
|
988
|
+
|
|
989
|
+
const triggerRenderingHack = useCallback(
|
|
990
|
+
(offset = 1) => {
|
|
991
|
+
hackActiveSlideAnimation(activeItemRef.current, offset);
|
|
992
|
+
},
|
|
993
|
+
[hackActiveSlideAnimation],
|
|
994
|
+
);
|
|
995
|
+
|
|
996
|
+
// Display warnings
|
|
997
|
+
const displayWarnings = useCallback(() => {
|
|
998
|
+
const pluginName = 'react-native-snap-carousel';
|
|
999
|
+
|
|
1000
|
+
if (!vertical && (!sliderWidth || !itemWidth)) {
|
|
1001
|
+
console.error(
|
|
1002
|
+
`${pluginName}: You need to specify both 'sliderWidth' and 'itemWidth' for horizontal carousels`,
|
|
1003
|
+
);
|
|
531
1004
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
1005
|
+
if (vertical && (!sliderHeight || !itemHeight)) {
|
|
1006
|
+
console.error(
|
|
1007
|
+
`${pluginName}: You need to specify both 'sliderHeight' and 'itemHeight' for vertical carousels`,
|
|
1008
|
+
);
|
|
535
1009
|
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
_onMomentumScrollEnd(event: NativeSyntheticEvent<NativeScrollEvent>) {
|
|
539
|
-
const { autoplayDelay, onMomentumScrollEnd, onSnapToItem } = this.props;
|
|
540
|
-
const { itemWidth } = this.state;
|
|
541
|
-
const scrollOffset = event
|
|
542
|
-
? this._getScrollOffset(event)
|
|
543
|
-
: this._currentScrollOffset;
|
|
544
|
-
const nextActiveItem = this._getActiveItem(scrollOffset);
|
|
545
|
-
const hasSnapped = this._isMultiple(scrollOffset, itemWidth);
|
|
1010
|
+
}, [vertical, sliderWidth, itemWidth, sliderHeight, itemHeight]);
|
|
546
1011
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
1012
|
+
// Set scroll handler
|
|
1013
|
+
const setScrollHandler = useCallback(() => {
|
|
1014
|
+
const scrollEventConfig = {
|
|
1015
|
+
listener: handleScroll,
|
|
1016
|
+
useNativeDriver: true,
|
|
1017
|
+
};
|
|
1018
|
+
scrollPosRef.current = new Animated.Value(0);
|
|
1019
|
+
const argMapping = vertical
|
|
1020
|
+
? [{ nativeEvent: { contentOffset: { y: scrollPosRef.current } } }]
|
|
1021
|
+
: [{ nativeEvent: { contentOffset: { x: scrollPosRef.current } } }];
|
|
550
1022
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
1023
|
+
if (onScroll && Array.isArray((onScroll as any)._argMapping)) {
|
|
1024
|
+
argMapping.pop();
|
|
1025
|
+
const [argMap] = (onScroll as any)._argMapping;
|
|
1026
|
+
if (argMap && argMap.nativeEvent && argMap.nativeEvent.contentOffset) {
|
|
1027
|
+
scrollPosRef.current =
|
|
1028
|
+
argMap.nativeEvent.contentOffset.x ||
|
|
1029
|
+
argMap.nativeEvent.contentOffset.y ||
|
|
1030
|
+
scrollPosRef.current;
|
|
555
1031
|
}
|
|
1032
|
+
argMapping.push(...(onScroll as any)._argMapping);
|
|
556
1033
|
}
|
|
1034
|
+
onScrollHandlerRef.current = Animated.event(argMapping, scrollEventConfig);
|
|
1035
|
+
}, [handleScroll, vertical, onScroll]);
|
|
1036
|
+
|
|
1037
|
+
// Expose public methods via ref
|
|
1038
|
+
useImperativeHandle(
|
|
1039
|
+
ref,
|
|
1040
|
+
() => ({
|
|
1041
|
+
snapToItem: snapToItemPublic,
|
|
1042
|
+
snapToNext,
|
|
1043
|
+
snapToPrev,
|
|
1044
|
+
startAutoplay,
|
|
1045
|
+
pauseAutoPlay,
|
|
1046
|
+
stopAutoplay,
|
|
1047
|
+
triggerRenderingHack,
|
|
1048
|
+
get realIndex() {
|
|
1049
|
+
return activeItemRef.current;
|
|
1050
|
+
},
|
|
1051
|
+
get currentIndex() {
|
|
1052
|
+
return getDataIndex(activeItemRef.current);
|
|
1053
|
+
},
|
|
1054
|
+
get currentScrollPosition() {
|
|
1055
|
+
return currentScrollOffsetRef.current;
|
|
1056
|
+
},
|
|
1057
|
+
}),
|
|
1058
|
+
[
|
|
1059
|
+
snapToItemPublic,
|
|
1060
|
+
snapToNext,
|
|
1061
|
+
snapToPrev,
|
|
1062
|
+
startAutoplay,
|
|
1063
|
+
pauseAutoPlay,
|
|
1064
|
+
stopAutoplay,
|
|
1065
|
+
triggerRenderingHack,
|
|
1066
|
+
getDataIndex,
|
|
1067
|
+
],
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
// componentDidMount
|
|
1071
|
+
useEffect(() => {
|
|
1072
|
+
mountedRef.current = true;
|
|
1073
|
+
initPositionsAndInterpolators();
|
|
1074
|
+
displayWarnings();
|
|
1075
|
+
setScrollHandler();
|
|
1076
|
+
|
|
1077
|
+
initTimeoutRef.current = setTimeout(() => {
|
|
1078
|
+
if (!mountedRef.current) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
557
1081
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
1082
|
+
const apparitionCallback = () => {
|
|
1083
|
+
if (apparitionDelay) {
|
|
1084
|
+
setHideCarousel(false);
|
|
1085
|
+
}
|
|
1086
|
+
if (autoplay) {
|
|
1087
|
+
startAutoplay();
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
568
1090
|
|
|
569
|
-
|
|
570
|
-
|
|
1091
|
+
if (needsScrollView()) {
|
|
1092
|
+
const _firstItem = getFirstItem(firstItem);
|
|
1093
|
+
snapToItem(_firstItem, false, false, true);
|
|
1094
|
+
}
|
|
571
1095
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
this.props.visibleItem === 1
|
|
581
|
-
? screenWidth - Spacing.M * 2
|
|
582
|
-
: Math.ceil(
|
|
583
|
-
(containerWidth * 0.9 - visibleItem * Spacing.S) / visibleItem,
|
|
584
|
-
);
|
|
585
|
-
if (this.props.itemWidth) {
|
|
586
|
-
itemWidth = this.props.itemWidth;
|
|
587
|
-
}
|
|
588
|
-
if (this.props.full) {
|
|
589
|
-
itemWidth = containerWidth;
|
|
590
|
-
}
|
|
1096
|
+
if (apparitionDelay) {
|
|
1097
|
+
apparitionTimeoutRef.current = setTimeout(() => {
|
|
1098
|
+
apparitionCallback();
|
|
1099
|
+
}, apparitionDelay);
|
|
1100
|
+
} else {
|
|
1101
|
+
apparitionCallback();
|
|
1102
|
+
}
|
|
1103
|
+
}, 1);
|
|
591
1104
|
|
|
592
|
-
|
|
1105
|
+
return () => {
|
|
1106
|
+
mountedRef.current = false;
|
|
1107
|
+
stopAutoplay();
|
|
1108
|
+
if (initTimeoutRef.current) clearTimeout(initTimeoutRef.current);
|
|
1109
|
+
if (apparitionTimeoutRef.current)
|
|
1110
|
+
clearTimeout(apparitionTimeoutRef.current);
|
|
1111
|
+
if (hackSlideAnimationTimeoutRef.current)
|
|
1112
|
+
clearTimeout(hackSlideAnimationTimeoutRef.current);
|
|
1113
|
+
if (enableAutoplayTimeoutRef.current)
|
|
1114
|
+
clearTimeout(enableAutoplayTimeoutRef.current);
|
|
1115
|
+
if (autoplayTimeoutRef.current) clearTimeout(autoplayTimeoutRef.current);
|
|
1116
|
+
if (snapNoMomentumTimeoutRef.current)
|
|
1117
|
+
clearTimeout(snapNoMomentumTimeoutRef.current);
|
|
1118
|
+
if (androidRepositioningTimeoutRef.current)
|
|
1119
|
+
clearTimeout(androidRepositioningTimeoutRef.current);
|
|
1120
|
+
};
|
|
1121
|
+
}, []);
|
|
593
1122
|
|
|
594
|
-
|
|
595
|
-
|
|
1123
|
+
// componentDidUpdate - handle scrollEnabled changes
|
|
1124
|
+
useEffect(() => {
|
|
1125
|
+
if (scrollEnabledProp !== scrollEnabledRef.current) {
|
|
1126
|
+
setScrollEnabled(scrollEnabledProp);
|
|
1127
|
+
}
|
|
1128
|
+
}, [scrollEnabledProp, setScrollEnabled]);
|
|
596
1129
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
1130
|
+
// componentDidUpdate - handle data/size changes
|
|
1131
|
+
useEffect(() => {
|
|
1132
|
+
const itemsLength = getCustomDataLength();
|
|
601
1133
|
|
|
602
|
-
|
|
603
|
-
index: number,
|
|
604
|
-
animated = true,
|
|
605
|
-
fireCallback = true,
|
|
606
|
-
forceScrollTo = false,
|
|
607
|
-
) {
|
|
608
|
-
const { onSnapToItem } = this.props;
|
|
609
|
-
const itemsLength = this._getCustomDataLength();
|
|
610
|
-
const wrappedRef = this._getWrappedRef();
|
|
611
|
-
if (!itemsLength || !wrappedRef) {
|
|
1134
|
+
if (!itemsLength) {
|
|
612
1135
|
return;
|
|
613
1136
|
}
|
|
614
1137
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1138
|
+
const nextFirstItem = getFirstItem(firstItem);
|
|
1139
|
+
let nextActiveItem =
|
|
1140
|
+
typeof activeItemRef.current !== 'undefined'
|
|
1141
|
+
? activeItemRef.current
|
|
1142
|
+
: nextFirstItem;
|
|
620
1143
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
1144
|
+
const hasNewSize =
|
|
1145
|
+
(vertical &&
|
|
1146
|
+
(previousItemsLengthRef.current !== itemsLength ||
|
|
1147
|
+
previousItemsLengthRef.current === 0)) ||
|
|
1148
|
+
(!vertical &&
|
|
1149
|
+
(previousItemsLengthRef.current !== itemsLength ||
|
|
1150
|
+
previousItemsLengthRef.current === 0));
|
|
624
1151
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
});
|
|
1152
|
+
if (nextActiveItem > itemsLength - 1) {
|
|
1153
|
+
nextActiveItem = itemsLength - 1;
|
|
1154
|
+
}
|
|
629
1155
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1156
|
+
if (interpolators.length !== itemsLength || hasNewSize) {
|
|
1157
|
+
activeItemRef.current = nextActiveItem;
|
|
1158
|
+
previousItemsLengthRef.current = itemsLength;
|
|
633
1159
|
|
|
634
|
-
|
|
635
|
-
|
|
1160
|
+
initPositionsAndInterpolators();
|
|
1161
|
+
if (previousItemsLengthRef.current > itemsLength) {
|
|
1162
|
+
hackActiveSlideAnimation(nextActiveItem);
|
|
636
1163
|
}
|
|
637
1164
|
|
|
638
|
-
if (
|
|
639
|
-
|
|
640
|
-
this._androidRepositioningTimeout = setTimeout(() => {
|
|
641
|
-
this._repositionScroll(index, false);
|
|
642
|
-
}, 400);
|
|
643
|
-
} else {
|
|
644
|
-
this._repositionScroll(index);
|
|
645
|
-
}
|
|
1165
|
+
if (hasNewSize) {
|
|
1166
|
+
snapToItem(nextActiveItem, false, false, true);
|
|
646
1167
|
}
|
|
1168
|
+
} else if (
|
|
1169
|
+
nextFirstItem !== previousFirstItemRef.current &&
|
|
1170
|
+
nextFirstItem !== activeItemRef.current
|
|
1171
|
+
) {
|
|
1172
|
+
activeItemRef.current = nextFirstItem;
|
|
1173
|
+
previousFirstItemRef.current = nextFirstItem;
|
|
1174
|
+
snapToItem(nextFirstItem, false, true, true);
|
|
647
1175
|
}
|
|
648
|
-
}
|
|
1176
|
+
}, [
|
|
1177
|
+
data,
|
|
1178
|
+
firstItem,
|
|
1179
|
+
interpolators.length,
|
|
1180
|
+
vertical,
|
|
1181
|
+
itemWidth,
|
|
1182
|
+
itemHeight,
|
|
1183
|
+
sliderWidth,
|
|
1184
|
+
sliderHeight,
|
|
1185
|
+
]);
|
|
1186
|
+
|
|
1187
|
+
// componentDidUpdate - handle onScroll prop changes
|
|
1188
|
+
useEffect(() => {
|
|
1189
|
+
setScrollHandler();
|
|
1190
|
+
}, [onScroll]);
|
|
1191
|
+
|
|
1192
|
+
// Render item
|
|
1193
|
+
const renderItemComponent = useCallback(
|
|
1194
|
+
({ item, index }: { item: any; index: number }) => {
|
|
1195
|
+
const animatedValue = interpolators && interpolators[index];
|
|
1196
|
+
|
|
1197
|
+
if (typeof animatedValue === 'undefined') {
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
649
1200
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1201
|
+
const animate = shouldAnimateSlides();
|
|
1202
|
+
const Component = animate ? Animated.View : View;
|
|
1203
|
+
const animatedStyle = animate
|
|
1204
|
+
? getSlideInterpolatedStyle(index, animatedValue)
|
|
1205
|
+
: {};
|
|
1206
|
+
const dataIndex = getDataIndex(index);
|
|
1207
|
+
|
|
1208
|
+
const mainDimension = vertical
|
|
1209
|
+
? { height: itemHeight }
|
|
1210
|
+
: { width: itemWidth };
|
|
1211
|
+
const specificProps = needsScrollView()
|
|
1212
|
+
? {
|
|
1213
|
+
key: keyExtractor
|
|
1214
|
+
? keyExtractor(item, index)
|
|
1215
|
+
: getKeyExtractor(item, index),
|
|
1216
|
+
}
|
|
1217
|
+
: {};
|
|
1218
|
+
|
|
1219
|
+
return (
|
|
1220
|
+
<Component
|
|
1221
|
+
style={[mainDimension, slideStyle, animatedStyle]}
|
|
1222
|
+
pointerEvents="box-none"
|
|
1223
|
+
{...specificProps}
|
|
1224
|
+
>
|
|
1225
|
+
{vertical
|
|
1226
|
+
? renderItem(
|
|
1227
|
+
{
|
|
1228
|
+
item,
|
|
1229
|
+
index,
|
|
1230
|
+
dataIndex,
|
|
1231
|
+
realIndex: getDataIndex(index),
|
|
1232
|
+
activeIndex: getDataIndex(activeItemRef.current),
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
scrollPosition: scrollPosRef.current,
|
|
1236
|
+
carouselRef: carouselRef.current,
|
|
1237
|
+
vertical: vertical,
|
|
1238
|
+
sliderHeight: sliderHeight,
|
|
1239
|
+
itemHeight: itemHeight,
|
|
1240
|
+
},
|
|
1241
|
+
)
|
|
1242
|
+
: renderItem(
|
|
1243
|
+
{
|
|
1244
|
+
item,
|
|
1245
|
+
index,
|
|
1246
|
+
dataIndex,
|
|
1247
|
+
realIndex: getDataIndex(index),
|
|
1248
|
+
activeIndex: getDataIndex(activeItemRef.current),
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
scrollPosition: scrollPosRef.current,
|
|
1252
|
+
carouselRef: carouselRef.current,
|
|
1253
|
+
vertical: false,
|
|
1254
|
+
sliderWidth: sliderWidth,
|
|
1255
|
+
itemWidth: itemWidth,
|
|
1256
|
+
},
|
|
1257
|
+
)}
|
|
1258
|
+
</Component>
|
|
1259
|
+
);
|
|
1260
|
+
},
|
|
1261
|
+
[
|
|
1262
|
+
interpolators,
|
|
1263
|
+
shouldAnimateSlides,
|
|
1264
|
+
getSlideInterpolatedStyle,
|
|
1265
|
+
getDataIndex,
|
|
1266
|
+
vertical,
|
|
1267
|
+
itemHeight,
|
|
1268
|
+
itemWidth,
|
|
1269
|
+
needsScrollView,
|
|
1270
|
+
keyExtractor,
|
|
1271
|
+
getKeyExtractor,
|
|
1272
|
+
slideStyle,
|
|
1273
|
+
renderItem,
|
|
1274
|
+
sliderHeight,
|
|
1275
|
+
sliderWidth,
|
|
1276
|
+
],
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1279
|
+
// Get component props
|
|
1280
|
+
const getComponentOverridableProps = useCallback(() => {
|
|
1281
|
+
const visibleItems =
|
|
1282
|
+
Math.max(
|
|
1283
|
+
1,
|
|
1284
|
+
Math.ceil(
|
|
1285
|
+
vertical
|
|
1286
|
+
? (sliderHeight || 1) / (itemHeight || 1)
|
|
1287
|
+
: (sliderWidth || 1) / (itemWidth || 1),
|
|
1288
|
+
),
|
|
1289
|
+
) + 1;
|
|
1290
|
+
const initialNumPerSide = enableLoop() ? loopClonesPerSide : 2;
|
|
1291
|
+
const initialNumToRender = Math.max(
|
|
1292
|
+
1,
|
|
1293
|
+
visibleItems + initialNumPerSide * 2,
|
|
1294
|
+
);
|
|
1295
|
+
const maxToRenderPerBatch = Math.max(
|
|
1296
|
+
1,
|
|
1297
|
+
initialNumToRender + initialNumPerSide * 2,
|
|
1298
|
+
);
|
|
1299
|
+
const windowSize = Math.max(2, maxToRenderPerBatch);
|
|
655
1300
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1301
|
+
const specificProps = !needsScrollView()
|
|
1302
|
+
? {
|
|
1303
|
+
initialNumToRender,
|
|
1304
|
+
maxToRenderPerBatch,
|
|
1305
|
+
windowSize,
|
|
1306
|
+
}
|
|
1307
|
+
: {};
|
|
659
1308
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1309
|
+
return {
|
|
1310
|
+
...specificProps,
|
|
1311
|
+
automaticallyAdjustContentInsets: false,
|
|
1312
|
+
decelerationRate: 'fast' as const,
|
|
1313
|
+
directionalLockEnabled: true,
|
|
1314
|
+
disableScrollViewPanResponder: false,
|
|
1315
|
+
inverted: needsRTLAdaptations(),
|
|
1316
|
+
overScrollMode: 'never' as const,
|
|
1317
|
+
pinchGestureEnabled: false,
|
|
1318
|
+
pointerEvents: hideCarousel ? ('none' as const) : ('auto' as const),
|
|
1319
|
+
scrollsToTop: false,
|
|
1320
|
+
showsHorizontalScrollIndicator: false,
|
|
1321
|
+
showsVerticalScrollIndicator: false,
|
|
1322
|
+
};
|
|
1323
|
+
}, [
|
|
1324
|
+
vertical,
|
|
1325
|
+
sliderHeight,
|
|
1326
|
+
itemHeight,
|
|
1327
|
+
sliderWidth,
|
|
1328
|
+
itemWidth,
|
|
1329
|
+
enableLoop,
|
|
1330
|
+
loopClonesPerSide,
|
|
1331
|
+
needsScrollView,
|
|
1332
|
+
needsRTLAdaptations,
|
|
1333
|
+
hideCarousel,
|
|
1334
|
+
]);
|
|
1335
|
+
|
|
1336
|
+
const getComponentStaticProps = useCallback(() => {
|
|
1337
|
+
const containerStyle = [
|
|
1338
|
+
containerCustomStyle || style || {},
|
|
1339
|
+
hideCarousel ? { opacity: 0 } : {},
|
|
1340
|
+
vertical
|
|
1341
|
+
? { height: sliderHeight, flexDirection: 'column' as const }
|
|
1342
|
+
: {
|
|
1343
|
+
width: sliderWidth,
|
|
1344
|
+
flexDirection: needsRTLAdaptations()
|
|
1345
|
+
? ('row-reverse' as const)
|
|
1346
|
+
: ('row' as const),
|
|
1347
|
+
},
|
|
1348
|
+
];
|
|
663
1349
|
|
|
664
|
-
|
|
665
|
-
? {
|
|
1350
|
+
const innerMarginStyle = vertical
|
|
1351
|
+
? {
|
|
1352
|
+
paddingTop: getContainerInnerMargin(),
|
|
1353
|
+
paddingBottom: getContainerInnerMargin(true),
|
|
1354
|
+
}
|
|
666
1355
|
: {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
index === this._getCustomDataLength() - 1 ? Spacing.M : Spacing.S,
|
|
1356
|
+
paddingLeft: getContainerInnerMargin(),
|
|
1357
|
+
paddingRight: getContainerInnerMargin(true),
|
|
670
1358
|
};
|
|
671
1359
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
return (
|
|
677
|
-
<Component
|
|
678
|
-
style={[
|
|
679
|
-
mainDimension,
|
|
680
|
-
animatedStyle,
|
|
681
|
-
{ overflow: 'hidden' },
|
|
682
|
-
spacingStyle,
|
|
683
|
-
slideStyle,
|
|
684
|
-
]}
|
|
685
|
-
pointerEvents="box-none"
|
|
686
|
-
>
|
|
687
|
-
{this.props.renderItem({
|
|
688
|
-
item,
|
|
689
|
-
index,
|
|
690
|
-
})}
|
|
691
|
-
</Component>
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
startAutoplay() {
|
|
696
|
-
const { autoplayInterval, autoplayDelay } = this.props;
|
|
697
|
-
this._autoplay = true;
|
|
698
|
-
|
|
699
|
-
if (this._autoplaying) {
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
1360
|
+
const contentContainerStyle = [
|
|
1361
|
+
!useExperimentalSnap ? innerMarginStyle : {},
|
|
1362
|
+
contentContainerCustomStyle || {},
|
|
1363
|
+
];
|
|
702
1364
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
this.snapToNext();
|
|
1365
|
+
const snapProps = useExperimentalSnap
|
|
1366
|
+
? {
|
|
1367
|
+
disableIntervalMomentum,
|
|
1368
|
+
snapToAlignment: activeSlideAlignment,
|
|
1369
|
+
snapToInterval: snapToInterval || getItemMainDimension(),
|
|
709
1370
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
pauseAutoPlay() {
|
|
715
|
-
this._autoplaying = false;
|
|
716
|
-
if (this._autoplayTimeout != null) clearTimeout(this._autoplayTimeout);
|
|
717
|
-
if (this._enableAutoplayTimeout != null)
|
|
718
|
-
clearTimeout(this._enableAutoplayTimeout);
|
|
719
|
-
if (this._autoplayInterval != null) clearInterval(this._autoplayInterval);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
stopAutoplay() {
|
|
723
|
-
this._autoplay = false;
|
|
724
|
-
this.pauseAutoPlay();
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
snapToItem(index: number, animated = true, fireCallback = true) {
|
|
728
|
-
if (!index || index < 0) {
|
|
729
|
-
index = 0;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
const positionIndex = this._getPositionIndex(index);
|
|
1371
|
+
: {
|
|
1372
|
+
snapToOffsets: getSnapOffsets(),
|
|
1373
|
+
};
|
|
733
1374
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1375
|
+
const specificProps = !needsScrollView()
|
|
1376
|
+
? {
|
|
1377
|
+
CellRendererComponent:
|
|
1378
|
+
CellRendererComponent || getCellRendererComponent,
|
|
1379
|
+
getItemLayout: getItemLayoutProp || getItemLayout,
|
|
1380
|
+
initialScrollIndex: getFirstItem(firstItem),
|
|
1381
|
+
keyExtractor: keyExtractor || getKeyExtractor,
|
|
1382
|
+
numColumns: 1,
|
|
1383
|
+
renderItem: renderItemComponent as any, // Type cast for compatibility
|
|
1384
|
+
}
|
|
1385
|
+
: {};
|
|
737
1386
|
|
|
738
|
-
|
|
1387
|
+
return {
|
|
1388
|
+
...specificProps,
|
|
1389
|
+
...snapProps,
|
|
1390
|
+
ref: carouselRef,
|
|
1391
|
+
contentContainerStyle: contentContainerStyle,
|
|
1392
|
+
data: getCustomData(),
|
|
1393
|
+
horizontal: !vertical,
|
|
1394
|
+
scrollEventThrottle: 1,
|
|
1395
|
+
style: containerStyle,
|
|
1396
|
+
onLayout: handleLayout,
|
|
1397
|
+
onMomentumScrollEnd: handleMomentumScrollEnd,
|
|
1398
|
+
onScroll: onScrollHandlerRef.current,
|
|
1399
|
+
onTouchStart: handleTouchStart,
|
|
1400
|
+
onTouchEnd: handleTouchEnd,
|
|
1401
|
+
};
|
|
1402
|
+
}, [
|
|
1403
|
+
containerCustomStyle,
|
|
1404
|
+
style,
|
|
1405
|
+
hideCarousel,
|
|
1406
|
+
vertical,
|
|
1407
|
+
sliderHeight,
|
|
1408
|
+
sliderWidth,
|
|
1409
|
+
needsRTLAdaptations,
|
|
1410
|
+
getContainerInnerMargin,
|
|
1411
|
+
useExperimentalSnap,
|
|
1412
|
+
contentContainerCustomStyle,
|
|
1413
|
+
disableIntervalMomentum,
|
|
1414
|
+
activeSlideAlignment,
|
|
1415
|
+
getItemMainDimension,
|
|
1416
|
+
getSnapOffsets,
|
|
1417
|
+
needsScrollView,
|
|
1418
|
+
CellRendererComponent,
|
|
1419
|
+
getCellRendererComponent,
|
|
1420
|
+
getItemLayout,
|
|
1421
|
+
getFirstItem,
|
|
1422
|
+
firstItem,
|
|
1423
|
+
keyExtractor,
|
|
1424
|
+
getKeyExtractor,
|
|
1425
|
+
renderItemComponent,
|
|
1426
|
+
getCustomData,
|
|
1427
|
+
handleLayout,
|
|
1428
|
+
handleMomentumScrollEnd,
|
|
1429
|
+
handleTouchStart,
|
|
1430
|
+
handleTouchEnd,
|
|
1431
|
+
]);
|
|
1432
|
+
|
|
1433
|
+
// Render
|
|
1434
|
+
if (!data || !renderItem) {
|
|
1435
|
+
return null;
|
|
739
1436
|
}
|
|
740
1437
|
|
|
741
|
-
|
|
742
|
-
|
|
1438
|
+
const overridableProps = getComponentOverridableProps();
|
|
1439
|
+
const staticProps = getComponentStaticProps();
|
|
743
1440
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
newIndex = 0;
|
|
747
|
-
}
|
|
748
|
-
this._snapToItem(newIndex, animated, fireCallback);
|
|
749
|
-
}
|
|
1441
|
+
// Filter out props that should not be passed to FlatList/ScrollView
|
|
1442
|
+
const { renderItem: _, ...propsWithoutRenderItem } = props;
|
|
750
1443
|
|
|
751
|
-
|
|
752
|
-
|
|
1444
|
+
const componentProps: any = {
|
|
1445
|
+
...overridableProps,
|
|
1446
|
+
...propsWithoutRenderItem,
|
|
1447
|
+
...staticProps,
|
|
1448
|
+
};
|
|
753
1449
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
newIndex = itemsLength - 1;
|
|
757
|
-
}
|
|
758
|
-
this._snapToItem(newIndex, animated, fireCallback);
|
|
759
|
-
}
|
|
1450
|
+
const ScrollViewComponent =
|
|
1451
|
+
typeof useScrollView === 'function' ? useScrollView : Animated.ScrollView;
|
|
760
1452
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
const initialNumPerSide = this._enableLoop() ? loopClonesPerSide : 2;
|
|
776
|
-
const initialNumToRender =
|
|
777
|
-
visibleItem > 2
|
|
778
|
-
? visibleItem + initialNumPerSide * 2
|
|
779
|
-
: initialNumPerSide * 2;
|
|
780
|
-
const maxToRenderPerBatch = initialNumToRender;
|
|
781
|
-
const windowSize = maxToRenderPerBatch;
|
|
782
|
-
|
|
783
|
-
const snapToInterval = enableSnap
|
|
784
|
-
? this._getItemMainDimension()
|
|
785
|
-
: undefined;
|
|
786
|
-
|
|
787
|
-
const specificProps = {
|
|
788
|
-
getItemLayout: getItemLayout || this._getItemLayout,
|
|
789
|
-
initialScrollIndex: this._getFirstItem(firstItem),
|
|
790
|
-
keyExtractor: keyExtractor || this._getKeyExtractor,
|
|
791
|
-
renderItem: this._renderItem,
|
|
792
|
-
};
|
|
1453
|
+
return needsScrollView() || !Animated.FlatList ? (
|
|
1454
|
+
<ScrollViewComponent {...componentProps}>
|
|
1455
|
+
{getCustomData().map((item: any, index: number) => {
|
|
1456
|
+
return renderItemComponent({
|
|
1457
|
+
item,
|
|
1458
|
+
index,
|
|
1459
|
+
});
|
|
1460
|
+
})}
|
|
1461
|
+
</ScrollViewComponent>
|
|
1462
|
+
) : (
|
|
1463
|
+
<Animated.FlatList {...componentProps} />
|
|
1464
|
+
);
|
|
1465
|
+
});
|
|
793
1466
|
|
|
794
|
-
|
|
795
|
-
<Animated.FlatList
|
|
796
|
-
{...this.props}
|
|
797
|
-
{...specificProps}
|
|
798
|
-
ref={c => {
|
|
799
|
-
this._carouselRef = c;
|
|
800
|
-
}}
|
|
801
|
-
overScrollMode={'never'}
|
|
802
|
-
snapToInterval={
|
|
803
|
-
this.props.snapToInterval ? this.props.snapToInterval : snapToInterval
|
|
804
|
-
}
|
|
805
|
-
disableIntervalMomentum={disableIntervalMomentum}
|
|
806
|
-
pointerEvents={hideCarousel ? 'none' : 'auto'}
|
|
807
|
-
decelerationRate={'fast'}
|
|
808
|
-
numColumns={1}
|
|
809
|
-
style={[
|
|
810
|
-
style,
|
|
811
|
-
{ width: '100%', flexDirection: 'row' },
|
|
812
|
-
hideCarousel ? { opacity: 0 } : {},
|
|
813
|
-
]}
|
|
814
|
-
automaticallyAdjustContentInsets={false}
|
|
815
|
-
directionalLockEnabled
|
|
816
|
-
contentContainerStyle={contentContainerStyle}
|
|
817
|
-
disableScrollViewPanResponder={false}
|
|
818
|
-
pinchGestureEnabled={false}
|
|
819
|
-
scrollsToTop={false}
|
|
820
|
-
showsHorizontalScrollIndicator={false}
|
|
821
|
-
showsVerticalScrollIndicator={false}
|
|
822
|
-
initialNumToRender={initialNumToRender}
|
|
823
|
-
maxToRenderPerBatch={maxToRenderPerBatch}
|
|
824
|
-
windowSize={windowSize}
|
|
825
|
-
pagingEnabled={enableSnap}
|
|
826
|
-
data={this._getCustomData()}
|
|
827
|
-
horizontal
|
|
828
|
-
scrollEventThrottle={1}
|
|
829
|
-
onLayout={this._onLayout}
|
|
830
|
-
onMomentumScrollEnd={this._onMomentumScrollEnd}
|
|
831
|
-
onScroll={this._onScrollHandler}
|
|
832
|
-
onTouchStart={this._onTouchStart}
|
|
833
|
-
onTouchEnd={this._onTouchEnd}
|
|
834
|
-
/>
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
1467
|
+
Carousel.displayName = 'Carousel';
|
|
838
1468
|
|
|
839
1469
|
export { Carousel };
|
|
840
|
-
export type {
|
|
1470
|
+
export type { CarouselRef, CarouselProps };
|