react-native-reanimated-carousel 4.0.0-alpha.1 → 4.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +2 -3
  2. package/lib/commonjs/{layouts → components}/BaseLayout.js +5 -21
  3. package/lib/commonjs/components/BaseLayout.js.map +1 -0
  4. package/lib/commonjs/components/Carousel.js +25 -46
  5. package/lib/commonjs/components/Carousel.js.map +1 -1
  6. package/lib/commonjs/components/ItemRenderer.js +80 -0
  7. package/lib/commonjs/components/ItemRenderer.js.map +1 -0
  8. package/lib/commonjs/components/ScrollViewGesture.js +51 -33
  9. package/lib/commonjs/components/ScrollViewGesture.js.map +1 -1
  10. package/lib/commonjs/components/rnr-demo.test.js +45 -0
  11. package/lib/commonjs/components/rnr-demo.test.js.map +1 -0
  12. package/lib/commonjs/hooks/useCarouselController.js +12 -11
  13. package/lib/commonjs/hooks/useCarouselController.js.map +1 -1
  14. package/lib/commonjs/hooks/useCommonVariables.js +38 -12
  15. package/lib/commonjs/hooks/useCommonVariables.js.map +1 -1
  16. package/lib/commonjs/hooks/useCommonVariables.test.js +38 -0
  17. package/lib/commonjs/hooks/useCommonVariables.test.js.map +1 -0
  18. package/lib/commonjs/hooks/useLayoutConfig.js.map +1 -1
  19. package/lib/commonjs/hooks/useOffsetX.js +9 -6
  20. package/lib/commonjs/hooks/useOffsetX.js.map +1 -1
  21. package/lib/commonjs/hooks/useOffsetX.test.js +53 -0
  22. package/lib/commonjs/hooks/useOffsetX.test.js.map +1 -0
  23. package/lib/commonjs/hooks/usePanGestureProxy.js +84 -0
  24. package/lib/commonjs/hooks/usePanGestureProxy.js.map +1 -0
  25. package/lib/commonjs/hooks/usePanGestureProxy.test.js +397 -0
  26. package/lib/commonjs/hooks/usePanGestureProxy.test.js.map +1 -0
  27. package/lib/commonjs/hooks/useUpdateGestureConfig.js.map +1 -1
  28. package/lib/commonjs/hooks/useVisibleRanges.js +48 -19
  29. package/lib/commonjs/hooks/useVisibleRanges.js.map +1 -1
  30. package/lib/commonjs/hooks/useVisibleRanges.test.js +162 -0
  31. package/lib/commonjs/hooks/useVisibleRanges.test.js.map +1 -0
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/utils/{computeNewIndexWhenDataChanges.js → compute-offset-if-data-changed.js} +3 -3
  34. package/lib/commonjs/utils/compute-offset-if-data-changed.js.map +1 -0
  35. package/lib/commonjs/utils/compute-offset-if-data-changed.test.js +30 -0
  36. package/lib/commonjs/utils/compute-offset-if-data-changed.test.js.map +1 -0
  37. package/lib/commonjs/utils/compute-offset-if-size-changed.js +18 -0
  38. package/lib/commonjs/utils/compute-offset-if-size-changed.js.map +1 -0
  39. package/lib/commonjs/utils/compute-offset-if-size-changed.test.js +72 -0
  40. package/lib/commonjs/utils/compute-offset-if-size-changed.test.js.map +1 -0
  41. package/lib/commonjs/utils/handleroffset-direction.js +5 -5
  42. package/lib/commonjs/utils/handleroffset-direction.js.map +1 -1
  43. package/lib/commonjs/utils/handleroffset-direction.test.js +46 -0
  44. package/lib/commonjs/utils/handleroffset-direction.test.js.map +1 -0
  45. package/lib/commonjs/utils/index.test.js +6 -6
  46. package/lib/commonjs/utils/index.test.js.map +1 -1
  47. package/lib/module/{layouts → components}/BaseLayout.js +6 -16
  48. package/lib/module/components/BaseLayout.js.map +1 -0
  49. package/lib/module/components/Carousel.js +24 -42
  50. package/lib/module/components/Carousel.js.map +1 -1
  51. package/lib/module/components/ItemRenderer.js +62 -0
  52. package/lib/module/components/ItemRenderer.js.map +1 -0
  53. package/lib/module/components/ScrollViewGesture.js +53 -34
  54. package/lib/module/components/ScrollViewGesture.js.map +1 -1
  55. package/lib/module/components/rnr-demo.test.js +33 -0
  56. package/lib/module/components/rnr-demo.test.js.map +1 -0
  57. package/lib/module/hooks/useCarouselController.js +12 -11
  58. package/lib/module/hooks/useCarouselController.js.map +1 -1
  59. package/lib/module/hooks/useCommonVariables.js +38 -8
  60. package/lib/module/hooks/useCommonVariables.js.map +1 -1
  61. package/lib/module/hooks/useCommonVariables.test.js +34 -0
  62. package/lib/module/hooks/useCommonVariables.test.js.map +1 -0
  63. package/lib/module/hooks/useLayoutConfig.js.map +1 -1
  64. package/lib/module/hooks/useOffsetX.js +9 -6
  65. package/lib/module/hooks/useOffsetX.js.map +1 -1
  66. package/lib/module/hooks/useOffsetX.test.js +48 -0
  67. package/lib/module/hooks/useOffsetX.test.js.map +1 -0
  68. package/lib/module/hooks/usePanGestureProxy.js +71 -0
  69. package/lib/module/hooks/usePanGestureProxy.js.map +1 -0
  70. package/lib/module/hooks/usePanGestureProxy.test.js +383 -0
  71. package/lib/module/hooks/usePanGestureProxy.test.js.map +1 -0
  72. package/lib/module/hooks/useUpdateGestureConfig.js.map +1 -1
  73. package/lib/module/hooks/useVisibleRanges.js +47 -19
  74. package/lib/module/hooks/useVisibleRanges.js.map +1 -1
  75. package/lib/module/hooks/useVisibleRanges.test.js +157 -0
  76. package/lib/module/hooks/useVisibleRanges.test.js.map +1 -0
  77. package/lib/module/index.js.map +1 -1
  78. package/lib/module/utils/{computeNewIndexWhenDataChanges.js → compute-offset-if-data-changed.js} +2 -2
  79. package/lib/module/utils/compute-offset-if-data-changed.js.map +1 -0
  80. package/lib/module/utils/compute-offset-if-data-changed.test.js +27 -0
  81. package/lib/module/utils/compute-offset-if-data-changed.test.js.map +1 -0
  82. package/lib/module/utils/compute-offset-if-size-changed.js +11 -0
  83. package/lib/module/utils/compute-offset-if-size-changed.js.map +1 -0
  84. package/lib/module/utils/compute-offset-if-size-changed.test.js +69 -0
  85. package/lib/module/utils/compute-offset-if-size-changed.test.js.map +1 -0
  86. package/lib/module/utils/handleroffset-direction.js +5 -5
  87. package/lib/module/utils/handleroffset-direction.js.map +1 -1
  88. package/lib/module/utils/handleroffset-direction.test.js +41 -0
  89. package/lib/module/utils/handleroffset-direction.test.js.map +1 -0
  90. package/lib/module/utils/index.test.js +6 -6
  91. package/lib/module/utils/index.test.js.map +1 -1
  92. package/lib/typescript/components/ItemRenderer.d.ts +22 -0
  93. package/lib/typescript/components/ScrollViewGesture.d.ts +1 -1
  94. package/lib/typescript/components/rnr-demo.test.d.ts +1 -0
  95. package/lib/typescript/hooks/useCarouselController.d.ts +3 -2
  96. package/lib/typescript/hooks/useCommonVariables.test.d.ts +1 -0
  97. package/lib/typescript/hooks/useLayoutConfig.d.ts +1 -1
  98. package/lib/typescript/hooks/useOffsetX.test.d.ts +1 -0
  99. package/lib/typescript/hooks/usePanGestureProxy.d.ts +9 -0
  100. package/lib/typescript/hooks/usePanGestureProxy.test.d.ts +1 -0
  101. package/lib/typescript/hooks/useUpdateGestureConfig.d.ts +3 -2
  102. package/lib/typescript/hooks/useVisibleRanges.d.ts +8 -4
  103. package/lib/typescript/hooks/useVisibleRanges.test.d.ts +1 -0
  104. package/lib/typescript/index.d.ts +1 -0
  105. package/lib/typescript/types.d.ts +13 -4
  106. package/lib/typescript/utils/{computeNewIndexWhenDataChanges.d.ts → compute-offset-if-data-changed.d.ts} +1 -1
  107. package/lib/typescript/utils/compute-offset-if-data-changed.test.d.ts +1 -0
  108. package/lib/typescript/utils/compute-offset-if-size-changed.d.ts +5 -0
  109. package/lib/typescript/utils/compute-offset-if-size-changed.test.d.ts +1 -0
  110. package/lib/typescript/utils/handleroffset-direction.d.ts +2 -1
  111. package/lib/typescript/utils/handleroffset-direction.test.d.ts +1 -0
  112. package/package.json +16 -59
  113. package/src/{layouts → components}/BaseLayout.tsx +7 -35
  114. package/src/components/Carousel.tsx +24 -58
  115. package/src/components/ItemRenderer.tsx +105 -0
  116. package/src/components/ScrollViewGesture.tsx +74 -49
  117. package/src/components/rnr-demo.test.tsx +43 -0
  118. package/src/hooks/useCarouselController.tsx +24 -21
  119. package/src/hooks/useCommonVariables.test.tsx +41 -0
  120. package/src/hooks/useCommonVariables.ts +35 -10
  121. package/src/hooks/useLayoutConfig.ts +1 -1
  122. package/src/hooks/useOffsetX.test.ts +54 -0
  123. package/src/hooks/useOffsetX.ts +33 -31
  124. package/src/hooks/usePanGestureProxy.test.tsx +376 -0
  125. package/src/hooks/usePanGestureProxy.ts +110 -0
  126. package/src/hooks/useUpdateGestureConfig.ts +4 -2
  127. package/src/hooks/useVisibleRanges.test.tsx +179 -0
  128. package/src/hooks/useVisibleRanges.tsx +72 -24
  129. package/src/index.tsx +2 -0
  130. package/src/types.ts +13 -4
  131. package/src/utils/compute-offset-if-data-changed.test.ts +30 -0
  132. package/src/utils/{computeNewIndexWhenDataChanges.ts → compute-offset-if-data-changed.ts} +1 -1
  133. package/src/utils/compute-offset-if-size-changed.test.ts +78 -0
  134. package/src/utils/compute-offset-if-size-changed.ts +11 -0
  135. package/src/utils/handleroffset-direction.test.ts +52 -0
  136. package/src/utils/handleroffset-direction.ts +12 -9
  137. package/src/utils/index.test.ts +6 -6
  138. package/lib/commonjs/layouts/BaseLayout.js.map +0 -1
  139. package/lib/commonjs/layouts/ParallaxLayout.js +0 -84
  140. package/lib/commonjs/layouts/ParallaxLayout.js.map +0 -1
  141. package/lib/commonjs/utils/computeNewIndexWhenDataChanges.js.map +0 -1
  142. package/lib/module/layouts/BaseLayout.js.map +0 -1
  143. package/lib/module/layouts/ParallaxLayout.js +0 -61
  144. package/lib/module/layouts/ParallaxLayout.js.map +0 -1
  145. package/lib/module/utils/computeNewIndexWhenDataChanges.js.map +0 -1
  146. package/lib/typescript/layouts/ParallaxLayout.d.ts +0 -13
  147. package/src/layouts/ParallaxLayout.tsx +0 -141
  148. /package/lib/typescript/{layouts → components}/BaseLayout.d.ts +0 -0
@@ -1,11 +1,8 @@
1
1
  import type { PropsWithChildren } from "react";
2
- import React, { useCallback, useMemo } from "react";
2
+ import React, { useCallback } from "react";
3
3
  import type { StyleProp, ViewStyle } from "react-native";
4
4
  import type { GestureStateChangeEvent, PanGestureHandlerEventPayload } from "react-native-gesture-handler";
5
- import {
6
- Gesture,
7
- GestureDetector,
8
- } from "react-native-gesture-handler";
5
+ import { GestureDetector } from "react-native-gesture-handler";
9
6
  import Animated, {
10
7
  cancelAnimation,
11
8
  measure,
@@ -18,7 +15,7 @@ import Animated, {
18
15
  } from "react-native-reanimated";
19
16
 
20
17
  import { Easing } from "../constants";
21
- import { useUpdateGestureConfig } from "../hooks/useUpdateGestureConfig";
18
+ import { usePanGestureProxy } from "../hooks/usePanGestureProxy";
22
19
  import { CTX } from "../store";
23
20
  import type { WithTimingAnimation } from "../types";
24
21
  import { dealWithAnimation } from "../utils/deal-with-animation";
@@ -28,7 +25,7 @@ interface Props {
28
25
  infinite?: boolean
29
26
  testID?: string
30
27
  style?: StyleProp<ViewStyle>
31
- onScrollBegin?: () => void
28
+ onScrollStart?: () => void
32
29
  onScrollEnd?: () => void
33
30
  onTouchBegin?: () => void
34
31
  onTouchEnd?: () => void
@@ -42,13 +39,15 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
42
39
  vertical,
43
40
  pagingEnabled,
44
41
  snapEnabled,
45
- loop: infinite,
42
+ loop,
46
43
  scrollAnimationDuration,
47
44
  withAnimation,
48
45
  enabled,
49
46
  dataLength,
50
47
  overscrollEnabled,
51
48
  maxScrollDistancePerSwipe,
49
+ minScrollDistancePerSwipe,
50
+ fixedDirection,
52
51
  },
53
52
  } = React.useContext(CTX);
54
53
 
@@ -57,7 +56,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
57
56
  translation,
58
57
  testID,
59
58
  style = {},
60
- onScrollBegin,
59
+ onScrollStart,
61
60
  onScrollEnd,
62
61
  onTouchBegin,
63
62
  onTouchEnd,
@@ -73,12 +72,13 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
73
72
  const scrollEndVelocity = useSharedValue(0);
74
73
  const containerRef = useAnimatedRef<Animated.View>();
75
74
  const maxScrollDistancePerSwipeIsSet = typeof maxScrollDistancePerSwipe === "number";
75
+ const minScrollDistancePerSwipeIsSet = typeof minScrollDistancePerSwipe === "number";
76
76
 
77
77
  // Get the limit of the scroll.
78
78
  const getLimit = React.useCallback(() => {
79
79
  "worklet";
80
80
 
81
- if (!infinite && !overscrollEnabled) {
81
+ if (!loop && !overscrollEnabled) {
82
82
  const { width: containerWidth = 0 } = measure(containerRef);
83
83
 
84
84
  // If the item's total width is less than the container's width, then there is no need to scroll.
@@ -90,7 +90,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
90
90
  }
91
91
 
92
92
  return dataLength * size;
93
- }, [infinite, size, dataLength, overscrollEnabled]);
93
+ }, [loop, size, dataLength, overscrollEnabled]);
94
94
 
95
95
  const withSpring = React.useCallback(
96
96
  (toValue: number, onFinished?: () => void) => {
@@ -141,7 +141,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
141
141
  const computed = offset < 0 ? Math.ceil : Math.floor;
142
142
  const page = computed(-translation.value / size);
143
143
 
144
- if (infinite) {
144
+ if (loop) {
145
145
  const finalPage = page + offset;
146
146
  finalTranslation = withSpring(withProcessTranslation(-finalPage * size), onFinished);
147
147
  }
@@ -161,7 +161,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
161
161
  translation.value = finalTranslation;
162
162
 
163
163
  function withProcessTranslation(translation: number) {
164
- if (!infinite && !overscrollEnabled) {
164
+ if (!loop && !overscrollEnabled) {
165
165
  const limit = getLimit();
166
166
  const sign = Math.sign(translation);
167
167
  return sign * Math.max(0, Math.min(limit, Math.abs(translation)));
@@ -174,7 +174,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
174
174
  withSpring,
175
175
  size,
176
176
  maxPage,
177
- infinite,
177
+ loop,
178
178
  snapEnabled,
179
179
  translation,
180
180
  pagingEnabled,
@@ -215,7 +215,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
215
215
  activeDecay();
216
216
  return;
217
217
  }
218
- if (!infinite) {
218
+ if (!loop) {
219
219
  translation.value = withSpring(0);
220
220
  return;
221
221
  }
@@ -226,7 +226,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
226
226
  activeDecay();
227
227
  return;
228
228
  }
229
- if (!infinite)
229
+ if (!loop)
230
230
  translation.value = withSpring(-((maxPage - 1) * size));
231
231
  }
232
232
  }, [
@@ -235,7 +235,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
235
235
  maxPage,
236
236
  size,
237
237
  scrollEndTranslation.value,
238
- infinite,
238
+ loop,
239
239
  activeDecay,
240
240
  withSpring,
241
241
  ]);
@@ -252,7 +252,7 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
252
252
  function withProcessTranslation(translation: number) {
253
253
  "worklet";
254
254
 
255
- if (!infinite && !overscrollEnabled) {
255
+ if (!loop && !overscrollEnabled) {
256
256
  const limit = getLimit();
257
257
  const sign = Math.sign(translation);
258
258
  return sign * Math.max(0, Math.min(limit, Math.abs(translation)));
@@ -261,15 +261,15 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
261
261
  return translation;
262
262
  }
263
263
 
264
- const onGestureBegin = useCallback(() => {
264
+ const onGestureStart = useCallback((_: PanGestureHandlerEventPayload) => {
265
265
  "worklet";
266
266
 
267
267
  touching.value = true;
268
268
  validStart.value = true;
269
- onScrollBegin && runOnJS(onScrollBegin)();
269
+ onScrollStart && runOnJS(onScrollStart)();
270
270
 
271
271
  max.value = (maxPage - 1) * size;
272
- if (!infinite && !overscrollEnabled)
272
+ if (!loop && !overscrollEnabled)
273
273
  max.value = getLimit();
274
274
 
275
275
  panOffset.value = translation.value;
@@ -277,14 +277,14 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
277
277
  max,
278
278
  size,
279
279
  maxPage,
280
- infinite,
280
+ loop,
281
281
  touching,
282
282
  panOffset,
283
283
  validStart,
284
284
  translation,
285
285
  overscrollEnabled,
286
286
  getLimit,
287
- onScrollBegin,
287
+ onScrollStart,
288
288
  ]);
289
289
 
290
290
  const onGestureUpdate = useCallback((e: PanGestureHandlerEventPayload) => {
@@ -296,10 +296,18 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
296
296
  }
297
297
  touching.value = true;
298
298
  const { translationX, translationY } = e;
299
- const panTranslation = isHorizontal.value
299
+
300
+ let panTranslation = isHorizontal.value
300
301
  ? translationX
301
302
  : translationY;
302
- if (!infinite) {
303
+
304
+ if (fixedDirection === "negative")
305
+ panTranslation = -Math.abs(panTranslation);
306
+
307
+ else if (fixedDirection === "positive")
308
+ panTranslation = +Math.abs(panTranslation);
309
+
310
+ if (!loop) {
303
311
  if ((translation.value > 0 || translation.value < -max.value)) {
304
312
  const boundary = translation.value > 0 ? 0 : -max.value;
305
313
  const fixed = boundary - panOffset.value;
@@ -315,71 +323,88 @@ const IScrollViewGesture: React.FC<PropsWithChildren<Props>> = (props) => {
315
323
  isHorizontal,
316
324
  max,
317
325
  panOffset,
318
- infinite,
326
+ loop,
319
327
  overscrollEnabled,
328
+ fixedDirection,
320
329
  translation,
321
330
  validStart,
322
331
  touching,
323
332
  ]);
324
333
 
325
- const onGestureFinish = useCallback((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => {
334
+ const onGestureEnd = useCallback((e: GestureStateChangeEvent<PanGestureHandlerEventPayload>, _success: boolean) => {
326
335
  "worklet";
327
336
 
328
337
  const { velocityX, velocityY, translationX, translationY } = e;
329
338
  scrollEndVelocity.value = isHorizontal.value
330
339
  ? velocityX
331
340
  : velocityY;
332
- scrollEndTranslation.value = isHorizontal.value
341
+
342
+ let panTranslation = isHorizontal.value
333
343
  ? translationX
334
344
  : translationY;
335
345
 
346
+ if (fixedDirection === "negative")
347
+ panTranslation = -Math.abs(panTranslation);
348
+
349
+ else if (fixedDirection === "positive")
350
+ panTranslation = +Math.abs(panTranslation);
351
+
352
+ scrollEndTranslation.value = panTranslation;
353
+
336
354
  const totalTranslation = scrollEndVelocity.value + scrollEndTranslation.value;
337
355
 
338
- if (maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe) {
356
+ /**
357
+ * If the maximum scroll distance is set and the translation `exceeds the maximum scroll distance`,
358
+ * the carousel will keep the view at the current position.
359
+ */
360
+ if (
361
+ maxScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) > maxScrollDistancePerSwipe
362
+ ) {
339
363
  const nextPage = Math.round((panOffset.value + maxScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
340
364
  translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
341
365
  }
366
+ /**
367
+ * If the minimum scroll distance is set and the translation `didn't exceeds the minimum scroll distance`,
368
+ * the carousel will keep the view at the current position.
369
+ */
370
+ else if (
371
+ minScrollDistancePerSwipeIsSet && Math.abs(totalTranslation) < minScrollDistancePerSwipe
372
+ ) {
373
+ const nextPage = Math.round((panOffset.value + minScrollDistancePerSwipe * Math.sign(totalTranslation)) / size) * size;
374
+ translation.value = withSpring(withProcessTranslation(nextPage), onScrollEnd);
375
+ }
342
376
  else {
343
377
  endWithSpring(onScrollEnd);
344
378
  }
345
379
 
346
- if (!infinite)
380
+ if (!loop)
347
381
  touching.value = false;
348
382
  }, [
349
383
  size,
350
- infinite,
384
+ loop,
351
385
  touching,
352
386
  panOffset,
353
387
  translation,
354
388
  isHorizontal,
355
389
  scrollEndVelocity,
356
390
  scrollEndTranslation,
391
+ fixedDirection,
357
392
  maxScrollDistancePerSwipeIsSet,
358
393
  maxScrollDistancePerSwipe,
394
+ maxScrollDistancePerSwipeIsSet,
395
+ minScrollDistancePerSwipe,
359
396
  endWithSpring,
360
397
  withSpring,
361
398
  onScrollEnd,
362
399
  ]);
363
400
 
364
- const gesture = useMemo(() => {
365
- const gesture = Gesture.Pan()
366
- .onBegin(onGestureBegin)
367
- .onUpdate(onGestureUpdate)
368
- .onEnd(onGestureFinish);
369
-
370
- if (onConfigurePanGesture)
371
- onConfigurePanGesture(gesture);
372
-
373
- return gesture;
374
- },
375
- [
376
- onGestureBegin,
377
- onGestureUpdate,
378
- onGestureFinish,
401
+ const gesture = usePanGestureProxy({
379
402
  onConfigurePanGesture,
380
- ]);
381
-
382
- useUpdateGestureConfig(gesture, { enabled });
403
+ onGestureStart,
404
+ onGestureUpdate,
405
+ onGestureEnd,
406
+ options: { enabled },
407
+ });
383
408
 
384
409
  return (
385
410
  <GestureDetector gesture={gesture}>
@@ -0,0 +1,43 @@
1
+ import type { FC } from "react";
2
+ import React from "react";
3
+ import Animated, { useAnimatedStyle, useDerivedValue } from "react-native-reanimated";
4
+ import renderer from "react-test-renderer";
5
+
6
+ describe("useSharedValue", () => {
7
+ it("retains value on rerender", () => {
8
+ const initialValue = 0;
9
+ const updatedValue = 1;
10
+
11
+ interface Props {
12
+ key: string
13
+ value: number
14
+ }
15
+
16
+ const TestComponent: FC<Props> = (props) => {
17
+ const opacity = useDerivedValue(() => props.value, [props.value]);
18
+ const animatedStyle = useAnimatedStyle(() => ({
19
+ opacity: opacity.value,
20
+ }), [opacity]);
21
+
22
+ return <Animated.View style={animatedStyle} />;
23
+ };
24
+
25
+ // When rendering with initial value
26
+ const wrapper = renderer.create(<TestComponent key="box" value={initialValue} />);
27
+
28
+ expect(
29
+ typeof wrapper.root.children[0] !== "string"
30
+ ? wrapper.root.children[0].props.style.animatedStyle.current.value.opacity
31
+ : false,
32
+ ).toBe(initialValue);
33
+
34
+ // When rendering with updated value
35
+ wrapper.update(<TestComponent key="box" value={updatedValue} />);
36
+
37
+ expect(
38
+ typeof wrapper.root.children[0] !== "string"
39
+ ? wrapper.root.children[0].props.style.animatedStyle.current.value.opacity
40
+ : false,
41
+ ).toBe(initialValue);
42
+ });
43
+ });
@@ -21,12 +21,13 @@ interface IOpts {
21
21
  loop: boolean
22
22
  size: number
23
23
  dataLength: number
24
- autoFillData: TCarouselProps["autoFillData"]
25
24
  handlerOffset: Animated.SharedValue<number>
25
+ autoFillData: TCarouselProps["autoFillData"]
26
26
  withAnimation?: TCarouselProps["withAnimation"]
27
+ fixedDirection?: TCarouselProps["fixedDirection"]
27
28
  duration?: number
28
29
  defaultIndex?: number
29
- onScrollBegin?: () => void
30
+ onScrollStart?: () => void
30
31
  onScrollEnd?: () => void
31
32
  }
32
33
 
@@ -48,6 +49,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
48
49
  defaultIndex = 0,
49
50
  duration,
50
51
  autoFillData,
52
+ fixedDirection,
51
53
  } = options;
52
54
 
53
55
  const dataInfo = React.useMemo(
@@ -136,8 +138,8 @@ export function useCarouselController(options: IOpts): ICarouselController {
136
138
  options.onScrollEnd?.();
137
139
  }, [options]);
138
140
 
139
- const onScrollBegin = React.useCallback(() => {
140
- options.onScrollBegin?.();
141
+ const onScrollStart = React.useCallback(() => {
142
+ options.onScrollStart?.();
141
143
  }, [options]);
142
144
 
143
145
  const scrollWithTiming = React.useCallback(
@@ -171,7 +173,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
171
173
  if (!canSliding() || (!loop && index.value >= dataInfo.length - 1))
172
174
  return;
173
175
 
174
- onScrollBegin?.();
176
+ onScrollStart?.();
175
177
 
176
178
  const nextPage = currentFixedPage() + count;
177
179
  index.value = nextPage;
@@ -192,7 +194,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
192
194
  loop,
193
195
  index,
194
196
  dataInfo,
195
- onScrollBegin,
197
+ onScrollStart,
196
198
  handlerOffset,
197
199
  size,
198
200
  scrollWithTiming,
@@ -205,7 +207,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
205
207
  const { count = 1, animated = true, onFinished } = opts;
206
208
  if (!canSliding() || (!loop && index.value <= 0)) return;
207
209
 
208
- onScrollBegin?.();
210
+ onScrollStart?.();
209
211
 
210
212
  const prevPage = currentFixedPage() - count;
211
213
  index.value = prevPage;
@@ -225,7 +227,7 @@ export function useCarouselController(options: IOpts): ICarouselController {
225
227
  canSliding,
226
228
  loop,
227
229
  index,
228
- onScrollBegin,
230
+ onScrollStart,
229
231
  handlerOffset,
230
232
  size,
231
233
  scrollWithTiming,
@@ -239,9 +241,9 @@ export function useCarouselController(options: IOpts): ICarouselController {
239
241
  if (i === index.value) return;
240
242
  if (!canSliding()) return;
241
243
 
242
- onScrollBegin?.();
244
+ onScrollStart?.();
243
245
  // direction -> 1 | -1
244
- const direction = handlerOffsetDirection(handlerOffset);
246
+ const direction = handlerOffsetDirection(handlerOffset, fixedDirection);
245
247
 
246
248
  // target offset
247
249
  const offset = i * size * direction;
@@ -252,16 +254,16 @@ export function useCarouselController(options: IOpts): ICarouselController {
252
254
 
253
255
  if (loop) {
254
256
  isCloseToNextLoop
255
- = Math.abs(handlerOffset.value % totalSize) / totalSize
256
- >= 0.5;
257
+ = Math.abs(handlerOffset.value % totalSize) / totalSize
258
+ >= 0.5;
257
259
  }
258
260
 
259
261
  const finalOffset
260
- = (Math.floor(Math.abs(handlerOffset.value / totalSize))
261
- + (isCloseToNextLoop ? 1 : 0))
262
- * totalSize
263
- * direction
264
- + offset;
262
+ = (Math.floor(Math.abs(handlerOffset.value / totalSize))
263
+ + (isCloseToNextLoop ? 1 : 0))
264
+ * totalSize
265
+ * direction
266
+ + offset;
265
267
 
266
268
  if (animated) {
267
269
  index.value = i;
@@ -274,13 +276,14 @@ export function useCarouselController(options: IOpts): ICarouselController {
274
276
  }
275
277
  },
276
278
  [
279
+ size,
280
+ loop,
277
281
  index,
278
- canSliding,
279
- onScrollBegin,
282
+ fixedDirection,
280
283
  handlerOffset,
281
- size,
282
284
  dataInfo.length,
283
- loop,
285
+ canSliding,
286
+ onScrollStart,
284
287
  scrollWithTiming,
285
288
  ],
286
289
  );
@@ -0,0 +1,41 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+
3
+ import { useCommonVariables } from "./useCommonVariables";
4
+
5
+ type UseCommonVariablesInput = Parameters<typeof useCommonVariables>[0];
6
+
7
+ const input = {
8
+ vertical: false,
9
+ width: 700,
10
+ height: 350,
11
+ loop: true,
12
+ enabled: true,
13
+ testID: "xxx",
14
+ style: {
15
+ width: "100%",
16
+ },
17
+ autoPlay: false,
18
+ autoPlayInterval: 2000,
19
+ data: [0, 1, 2, 3],
20
+ pagingEnabled: true,
21
+ defaultIndex: 0,
22
+ autoFillData: true,
23
+ dataLength: 4,
24
+ rawData: [0, 1, 2, 3],
25
+ rawDataLength: 4,
26
+ scrollAnimationDuration: 500,
27
+ snapEnabled: true,
28
+ overscrollEnabled: true,
29
+ } as unknown as UseCommonVariablesInput;
30
+
31
+ describe("useCommonVariables", () => {
32
+ it("should return the correct values", async () => {
33
+ const hook = renderHook(() => useCommonVariables(input));
34
+
35
+ expect(hook.result.current.size).toMatchInlineSnapshot("700");
36
+ expect(hook.result.current.validLength).toMatchInlineSnapshot("3");
37
+ expect(hook.result.current.handlerOffset.value).toMatchInlineSnapshot(
38
+ "-0",
39
+ );
40
+ });
41
+ });
@@ -1,10 +1,10 @@
1
- import React from "react";
2
1
  import type Animated from "react-native-reanimated";
3
2
  import { useSharedValue, useAnimatedReaction } from "react-native-reanimated";
4
3
 
5
4
  import type { TInitializeCarouselProps } from "./useInitProps";
6
5
 
7
- import { computeNewIndexWhenDataChanges } from "../utils/computeNewIndexWhenDataChanges";
6
+ import { computeOffsetIfDataChanged } from "../utils/compute-offset-if-data-changed";
7
+ import { computeOffsetIfSizeChanged } from "../utils/compute-offset-if-size-changed";
8
8
  import { handlerOffsetDirection } from "../utils/handleroffset-direction";
9
9
 
10
10
  interface ICommonVariables {
@@ -26,21 +26,20 @@ export function useCommonVariables(
26
26
  loop,
27
27
  } = props;
28
28
  const size = vertical ? height : width;
29
- const validLength = dataLength - 1;
30
29
  const defaultHandlerOffsetValue = -Math.abs(defaultIndex * size);
31
30
  const _handlerOffset = useSharedValue<number>(defaultHandlerOffsetValue);
32
31
  const handlerOffset = defaultScrollOffsetValue ?? _handlerOffset;
33
32
  const prevDataLength = useSharedValue(dataLength);
33
+ const prevSize = useSharedValue(size);
34
34
 
35
- React.useEffect(() => {
36
- handlerOffset.value = defaultHandlerOffsetValue;
37
- }, [vertical, handlerOffset, defaultHandlerOffsetValue]);
38
-
35
+ /**
36
+ * When data changes, we need to compute new index for handlerOffset
37
+ */
39
38
  useAnimatedReaction(() => {
40
39
  const previousLength = prevDataLength.value;
41
40
  const currentLength = dataLength;
42
41
  const isLengthChanged = previousLength !== currentLength;
43
- const shouldComputed = isLengthChanged && loop;
42
+ const shouldComputed = (isLengthChanged && loop);
44
43
 
45
44
  if (shouldComputed)
46
45
  prevDataLength.value = dataLength;
@@ -55,7 +54,7 @@ export function useCommonVariables(
55
54
  // direction -> 1 | -1
56
55
  const direction = handlerOffsetDirection(handlerOffset);
57
56
 
58
- handlerOffset.value = computeNewIndexWhenDataChanges({
57
+ handlerOffset.value = computeOffsetIfDataChanged({
59
58
  direction,
60
59
  previousLength,
61
60
  currentLength,
@@ -65,9 +64,35 @@ export function useCommonVariables(
65
64
  }
66
65
  }, [dataLength, loop]);
67
66
 
67
+ /**
68
+ * When size changes, we need to compute new index for handlerOffset
69
+ */
70
+ useAnimatedReaction(() => {
71
+ const previousSize = prevSize.value;
72
+ const isSizeChanged = previousSize !== size;
73
+ const shouldComputed = isSizeChanged;
74
+
75
+ if (shouldComputed)
76
+ prevSize.value = size;
77
+
78
+ return {
79
+ shouldComputed,
80
+ previousSize,
81
+ size,
82
+ };
83
+ }, ({ shouldComputed, previousSize, size }) => {
84
+ if (shouldComputed) {
85
+ handlerOffset.value = computeOffsetIfSizeChanged({
86
+ handlerOffset: handlerOffset.value,
87
+ prevSize: previousSize,
88
+ size,
89
+ });
90
+ }
91
+ }, [size]);
92
+
68
93
  return {
69
94
  size,
70
- validLength,
95
+ validLength: dataLength - 1,
71
96
  handlerOffset,
72
97
  };
73
98
  }
@@ -2,8 +2,8 @@ import React from "react";
2
2
 
3
3
  import type { TInitializeCarouselProps } from "./useInitProps";
4
4
 
5
+ import type { TAnimationStyle } from "../components/BaseLayout";
5
6
  import { Layouts } from "../layouts";
6
- import type { TAnimationStyle } from "../layouts/BaseLayout";
7
7
 
8
8
  type TLayoutConfigOpts<T> = TInitializeCarouselProps<T> & { size: number };
9
9
 
@@ -0,0 +1,54 @@
1
+ import { useSharedValue } from "react-native-reanimated";
2
+
3
+ import { renderHook } from "@testing-library/react-hooks";
4
+
5
+ import type { IOpts } from "./useOffsetX";
6
+ import { useOffsetX } from "./useOffsetX";
7
+ import type { IVisibleRanges } from "./useVisibleRanges";
8
+
9
+ describe("useSharedValue", () => {
10
+ it("should return the correct values", async () => {
11
+ const hook = renderHook(() => {
12
+ const range = useSharedValue({
13
+ negativeRange: [7, 9],
14
+ positiveRange: [0, 3],
15
+ }) as IVisibleRanges;
16
+ const inputs: Array<{
17
+ config: IOpts
18
+ range: IVisibleRanges
19
+ }> = Array.from({ length: 10 }).map((_, index) => ({
20
+ config: {
21
+ dataLength: 10,
22
+ handlerOffset: useSharedValue(-0),
23
+ index,
24
+ loop: false,
25
+ size: 393,
26
+ },
27
+ range,
28
+ }));
29
+
30
+ return inputs.map((input) => {
31
+ const { config, range } = input;
32
+
33
+ return useOffsetX(config, range);
34
+ });
35
+ });
36
+
37
+ const expected = hook.result.current.map(v => v.value).slice();
38
+
39
+ expect(expected).toMatchInlineSnapshot(`
40
+ [
41
+ 0,
42
+ 393,
43
+ 786,
44
+ 1179,
45
+ 9007199254740991,
46
+ 9007199254740991,
47
+ 9007199254740991,
48
+ 2751,
49
+ 3144,
50
+ 3537,
51
+ ]
52
+ `);
53
+ });
54
+ });