@r0b0t3d/react-native-carousel 3.4.7 → 3.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@r0b0t3d/react-native-carousel",
3
- "version": "3.4.7",
3
+ "version": "3.4.9",
4
4
  "description": "React Native Carousel",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -5,7 +5,7 @@ import React, {
5
5
  useMemo,
6
6
  useRef,
7
7
  } from 'react';
8
- import { Dimensions, StyleSheet, Platform, AppState } from 'react-native';
8
+ import { StyleSheet, Platform, AppState, useWindowDimensions } from 'react-native';
9
9
  import { useInterval } from '@r0b0t3d/react-native-hooks';
10
10
  import Animated, {
11
11
  useSharedValue,
@@ -31,11 +31,13 @@ import { useInternalCarouselContext } from './useInternalCarouselContext';
31
31
  // Transitions:
32
32
  //
33
33
  // IDLE ── onBeginDrag ──► DRAGGING
34
+ // IDLE ── programmatic scroll ──► (stays IDLE, onMomentumEnd fires)
34
35
  // DRAGGING ── onMomentumEnd ──► IDLE (or LOOP_JUMP at boundary)
35
36
  // LOOP_JUMP ── onScroll (arrival) ──► IDLE
36
37
  // LOOP_JUMP ── onBeginDrag (user overrides) ──► DRAGGING
37
38
  //
38
- // Page change (onPageChange / animatedPage) only fires at IDLE transitions.
39
+ // Page change (onPageChange / animatedPage) fires from onMomentumEnd for
40
+ // both user drags and programmatic scrolls (goNext / goPrev / snapToItem).
39
41
  // Autoplay only advances when state === IDLE.
40
42
  // ---------------------------------------------------------------------------
41
43
 
@@ -54,10 +56,6 @@ enum CarouselState {
54
56
  // All consumer-facing callbacks receive logical indices.
55
57
  // ---------------------------------------------------------------------------
56
58
 
57
- const getScreenWidth = () => {
58
- return Dimensions.get('screen').width;
59
- };
60
-
61
59
  function Carousel<TData>({
62
60
  style,
63
61
  data,
@@ -67,8 +65,8 @@ function Carousel<TData>({
67
65
  autoPlay = false,
68
66
  duration = 1000,
69
67
  animation,
70
- sliderWidth = getScreenWidth(),
71
- itemWidth = getScreenWidth(),
68
+ sliderWidth,
69
+ itemWidth,
72
70
  firstItemAlignment = 'center',
73
71
  inactiveOpacity = 1,
74
72
  inactiveScale = 1,
@@ -82,6 +80,10 @@ function Carousel<TData>({
82
80
  disableItemPress = false,
83
81
  scrollViewRef: externalScrollViewRef,
84
82
  }: CarouselProps<TData>) {
83
+ const { width: screenWidth } = useWindowDimensions();
84
+ const effectiveSliderWidth = sliderWidth ?? screenWidth;
85
+ const effectiveItemWidth = itemWidth ?? screenWidth;
86
+
85
87
  // ---- shared values (UI-thread state) ------------------------------------
86
88
 
87
89
  const currentRenderPage = useSharedValue(0);
@@ -105,18 +107,18 @@ function Carousel<TData>({
105
107
  // ---- derived values -----------------------------------------------------
106
108
 
107
109
  const horizontalPadding = useMemo(() => {
108
- const padding = (sliderWidth - itemWidth) / 2;
110
+ const padding = (effectiveSliderWidth - effectiveItemWidth) / 2;
109
111
  return firstItemAlignment === 'center' || loop ? padding : spaceHeadTail;
110
- }, [sliderWidth, itemWidth, firstItemAlignment, loop, spaceHeadTail]);
112
+ }, [effectiveSliderWidth, effectiveItemWidth, firstItemAlignment, loop, spaceHeadTail]);
111
113
 
112
114
  const offsets = useMemo(() => {
113
115
  return generateOffsets({
114
- sliderWidth,
115
- itemWidth,
116
+ sliderWidth: effectiveSliderWidth,
117
+ itemWidth: effectiveItemWidth,
116
118
  itemCount: data.length + (loop ? additionalPagesPerSide * 2 : 0),
117
119
  horizontalPadding,
118
120
  });
119
- }, [sliderWidth, itemWidth, data.length, loop, additionalPagesPerSide, horizontalPadding]);
121
+ }, [effectiveSliderWidth, effectiveItemWidth, data.length, loop, additionalPagesPerSide, horizontalPadding]);
120
122
 
121
123
  const pageItems = useMemo(() => {
122
124
  if (!data || data.length === 0) {
@@ -133,7 +135,7 @@ function Carousel<TData>({
133
135
  return data;
134
136
  }, [data, loop, additionalPagesPerSide]);
135
137
 
136
- // ---- page-change callback (JS thread) -----------------------------------
138
+ // ---- page-change callback (JS thread) -----------------------------------
137
139
 
138
140
  const handlePageChange = useCallback(
139
141
  (logicalPage: number) => {
@@ -185,7 +187,7 @@ function Carousel<TData>({
185
187
  if (safeLogicalIdx !== prevLogical) {
186
188
  handlePageChange(safeLogicalIdx);
187
189
  }
188
- }, [data.length, sliderWidth, itemWidth, loop, additionalPagesPerSide, offsets.length, handlePageChange]);
190
+ }, [data.length, effectiveSliderWidth, effectiveItemWidth, loop, additionalPagesPerSide, offsets.length, handlePageChange]);
189
191
 
190
192
  // ---- app-state listener (pause autoplay when backgrounded) --------------
191
193
 
@@ -316,6 +318,12 @@ function Carousel<TData>({
316
318
 
317
319
  // ---- scroll handler (UI thread worklet) ---------------------------------
318
320
 
321
+ // Snapshot as a primitive so the worklets below close over a number,
322
+ // not the `data` array itself — `data` items may contain non-shareable
323
+ // values (e.g. JSX elements), which Reanimated can't clone for the UI
324
+ // thread ([Worklets] Cannot copy value of type `FiberNode`).
325
+ const dataLength = data.length;
326
+
319
327
  const scrollHandler = useAnimatedScrollHandler(
320
328
  {
321
329
  onScroll: (event) => {
@@ -352,9 +360,10 @@ function Carousel<TData>({
352
360
  },
353
361
 
354
362
  onMomentumEnd: (event) => {
355
- // Only process page changes when dragging ended naturally.
356
- // Loop jumps are handled in onScroll.
357
- if (carouselState.value !== CarouselState.DRAGGING) {
363
+ // Process page changes after both user drags and programmatic
364
+ // scrolls (goNext / goPrev / snapToItem). Loop jumps are
365
+ // instant scrolls that are resolved via onScroll instead.
366
+ if (carouselState.value === CarouselState.LOOP_JUMP) {
358
367
  return;
359
368
  }
360
369
 
@@ -371,7 +380,7 @@ function Carousel<TData>({
371
380
  if (loop) {
372
381
  const boundaryTarget = getLoopBoundaryTarget(
373
382
  renderPage,
374
- data.length,
383
+ dataLength,
375
384
  additionalPagesPerSide
376
385
  );
377
386
  if (boundaryTarget !== -1) {
@@ -387,7 +396,7 @@ function Carousel<TData>({
387
396
  currentRenderPage.value = boundaryTarget;
388
397
  currentLogicalPage.value = getLogicalPage(
389
398
  boundaryTarget,
390
- data.length,
399
+ dataLength,
391
400
  additionalPagesPerSide,
392
401
  loop
393
402
  );
@@ -401,7 +410,7 @@ function Carousel<TData>({
401
410
  currentRenderPage.value = renderPage;
402
411
  currentLogicalPage.value = getLogicalPage(
403
412
  renderPage,
404
- data.length,
413
+ dataLength,
405
414
  additionalPagesPerSide,
406
415
  loop
407
416
  );
@@ -413,7 +422,7 @@ function Carousel<TData>({
413
422
  [
414
423
  offsets,
415
424
  loop,
416
- data.length,
425
+ dataLength,
417
426
  additionalPagesPerSide,
418
427
  handlePageChange,
419
428
  internalAnimatedRef,
@@ -489,7 +498,7 @@ function Carousel<TData>({
489
498
  item={item}
490
499
  index={logicalIndex}
491
500
  offset={offsets[i]}
492
- itemWidth={itemWidth}
501
+ itemWidth={effectiveItemWidth}
493
502
  animatedValue={animatedScroll}
494
503
  animation={animation}
495
504
  renderItem={renderItem}
@@ -507,7 +516,7 @@ function Carousel<TData>({
507
516
  getItemKey,
508
517
  containerStyles,
509
518
  offsets,
510
- itemWidth,
519
+ effectiveItemWidth,
511
520
  animatedScroll,
512
521
  animation,
513
522
  renderItem,