@fountain-ui/lab 2.0.0-beta.10 → 2.0.0-beta.13

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 (266) hide show
  1. package/build/commonjs/Carousel/Carousel.js +142 -0
  2. package/build/commonjs/Carousel/Carousel.js.map +1 -0
  3. package/build/commonjs/Carousel/CarouselProps.js.map +1 -1
  4. package/build/commonjs/Carousel/animation/animationUtils.js +9 -0
  5. package/build/commonjs/Carousel/animation/animationUtils.js.map +1 -0
  6. package/build/commonjs/Carousel/animation/createDefaultItemStyle.js +15 -0
  7. package/build/commonjs/Carousel/animation/createDefaultItemStyle.js.map +1 -0
  8. package/build/commonjs/Carousel/animation/createDefaultScrollAnimation.js +20 -0
  9. package/build/commonjs/Carousel/animation/createDefaultScrollAnimation.js.map +1 -0
  10. package/build/commonjs/Carousel/animation/index.js +40 -0
  11. package/build/commonjs/Carousel/animation/index.js.map +1 -0
  12. package/build/commonjs/Carousel/animation/normalItemStyleFactory.js +27 -0
  13. package/build/commonjs/Carousel/animation/normalItemStyleFactory.js.map +1 -0
  14. package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js +65 -0
  15. package/build/commonjs/Carousel/animation/parallaxItemStyleFactory.js.map +1 -0
  16. package/build/commonjs/Carousel/components/InternalContext.js +29 -0
  17. package/build/commonjs/Carousel/components/InternalContext.js.map +1 -0
  18. package/build/commonjs/Carousel/components/ItemView.js +66 -0
  19. package/build/commonjs/Carousel/components/ItemView.js.map +1 -0
  20. package/build/commonjs/Carousel/components/RootView.js +86 -0
  21. package/build/commonjs/Carousel/components/RootView.js.map +1 -0
  22. package/build/commonjs/Carousel/components/ScrollViewGesture.js +81 -0
  23. package/build/commonjs/Carousel/components/ScrollViewGesture.js.map +1 -0
  24. package/build/commonjs/Carousel/components/index.js +40 -0
  25. package/build/commonjs/Carousel/components/index.js.map +1 -0
  26. package/build/commonjs/Carousel/hooks/index.js +64 -0
  27. package/build/commonjs/Carousel/hooks/index.js.map +1 -0
  28. package/build/commonjs/Carousel/hooks/useAutoplayController.js +58 -0
  29. package/build/commonjs/Carousel/hooks/useAutoplayController.js.map +1 -0
  30. package/build/commonjs/Carousel/hooks/useDimensionChangeReaction.js +23 -0
  31. package/build/commonjs/Carousel/hooks/useDimensionChangeReaction.js.map +1 -0
  32. package/build/commonjs/Carousel/hooks/useIndexController.js +70 -0
  33. package/build/commonjs/Carousel/hooks/useIndexController.js.map +1 -0
  34. package/build/commonjs/Carousel/hooks/useItemInterpolation.js +63 -0
  35. package/build/commonjs/Carousel/hooks/useItemInterpolation.js.map +1 -0
  36. package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js +95 -0
  37. package/build/commonjs/Carousel/hooks/useItemVisibilityStore.js.map +1 -0
  38. package/build/commonjs/Carousel/hooks/useLoopedData.js +33 -0
  39. package/build/commonjs/Carousel/hooks/useLoopedData.js.map +1 -0
  40. package/build/commonjs/Carousel/hooks/usePagingAnimation.js +162 -0
  41. package/build/commonjs/Carousel/hooks/usePagingAnimation.js.map +1 -0
  42. package/build/commonjs/Carousel/index.js +16 -2
  43. package/build/commonjs/Carousel/index.js.map +1 -1
  44. package/build/commonjs/Carousel/types.js +7 -0
  45. package/build/commonjs/Carousel/types.js.map +1 -0
  46. package/build/commonjs/ViewPager/ChildrenMemoizedPage.js +86 -21
  47. package/build/commonjs/ViewPager/ChildrenMemoizedPage.js.map +1 -1
  48. package/build/commonjs/ViewPager/PageStateContext.js +22 -0
  49. package/build/commonjs/ViewPager/PageStateContext.js.map +1 -0
  50. package/build/commonjs/ViewPager/ViewPagerNative.js +82 -34
  51. package/build/commonjs/ViewPager/ViewPagerNative.js.map +1 -1
  52. package/build/commonjs/ViewPager/ViewPagerProps.js.map +1 -1
  53. package/build/commonjs/ViewPager/ViewPagerWeb.js +35 -26
  54. package/build/commonjs/ViewPager/ViewPagerWeb.js.map +1 -1
  55. package/build/commonjs/ViewPager/index.js +8 -0
  56. package/build/commonjs/ViewPager/index.js.map +1 -1
  57. package/build/commonjs/ViewPager/index.native.js +8 -0
  58. package/build/commonjs/ViewPager/index.native.js.map +1 -1
  59. package/build/commonjs/ViewPager/utils.js +5 -41
  60. package/build/commonjs/ViewPager/utils.js.map +1 -1
  61. package/build/commonjs/ViewabilityTrackerView/Viewability.js +2 -0
  62. package/build/commonjs/ViewabilityTrackerView/Viewability.js.map +1 -0
  63. package/build/commonjs/ViewabilityTrackerView/ViewabilityTrackerView.js +45 -0
  64. package/build/commonjs/ViewabilityTrackerView/ViewabilityTrackerView.js.map +1 -0
  65. package/build/commonjs/ViewabilityTrackerView/ViewabilityTrackerViewProps.js +2 -0
  66. package/build/commonjs/ViewabilityTrackerView/ViewabilityTrackerViewProps.js.map +1 -0
  67. package/build/commonjs/{Carousel/index.native.js → ViewabilityTrackerView/index.js} +3 -3
  68. package/build/commonjs/ViewabilityTrackerView/index.js.map +1 -0
  69. package/build/commonjs/ViewabilityTrackerView/measureViewability.js +42 -0
  70. package/build/commonjs/ViewabilityTrackerView/measureViewability.js.map +1 -0
  71. package/build/commonjs/ViewabilityTrackerView/useViewabilityTracker.js +88 -0
  72. package/build/commonjs/ViewabilityTrackerView/useViewabilityTracker.js.map +1 -0
  73. package/build/commonjs/hooks/index.js +16 -0
  74. package/build/commonjs/hooks/index.js.map +1 -1
  75. package/build/commonjs/hooks/useDeferredExecutor.js +45 -0
  76. package/build/commonjs/hooks/useDeferredExecutor.js.map +1 -0
  77. package/build/commonjs/hooks/useIsMounted.js +22 -0
  78. package/build/commonjs/hooks/useIsMounted.js.map +1 -0
  79. package/build/commonjs/index.js +22 -1
  80. package/build/commonjs/index.js.map +1 -1
  81. package/build/module/Carousel/Carousel.js +120 -0
  82. package/build/module/Carousel/Carousel.js.map +1 -0
  83. package/build/module/Carousel/CarouselProps.js.map +1 -1
  84. package/build/module/Carousel/animation/animationUtils.js +2 -0
  85. package/build/module/Carousel/animation/animationUtils.js.map +1 -0
  86. package/build/module/Carousel/animation/createDefaultItemStyle.js +3 -0
  87. package/build/module/Carousel/animation/createDefaultItemStyle.js.map +1 -0
  88. package/build/module/Carousel/animation/createDefaultScrollAnimation.js +11 -0
  89. package/build/module/Carousel/animation/createDefaultScrollAnimation.js.map +1 -0
  90. package/build/module/Carousel/animation/index.js +5 -0
  91. package/build/module/Carousel/animation/index.js.map +1 -0
  92. package/build/module/Carousel/animation/normalItemStyleFactory.js +18 -0
  93. package/build/module/Carousel/animation/normalItemStyleFactory.js.map +1 -0
  94. package/build/module/Carousel/animation/parallaxItemStyleFactory.js +55 -0
  95. package/build/module/Carousel/animation/parallaxItemStyleFactory.js.map +1 -0
  96. package/build/module/Carousel/components/InternalContext.js +19 -0
  97. package/build/module/Carousel/components/InternalContext.js.map +1 -0
  98. package/build/module/Carousel/components/ItemView.js +46 -0
  99. package/build/module/Carousel/components/ItemView.js.map +1 -0
  100. package/build/module/Carousel/components/RootView.js +65 -0
  101. package/build/module/Carousel/components/RootView.js.map +1 -0
  102. package/build/module/Carousel/components/ScrollViewGesture.js +67 -0
  103. package/build/module/Carousel/components/ScrollViewGesture.js.map +1 -0
  104. package/build/module/Carousel/components/index.js +5 -0
  105. package/build/module/Carousel/components/index.js.map +1 -0
  106. package/build/module/Carousel/hooks/index.js +8 -0
  107. package/build/module/Carousel/hooks/index.js.map +1 -0
  108. package/build/module/Carousel/hooks/useAutoplayController.js +48 -0
  109. package/build/module/Carousel/hooks/useAutoplayController.js.map +1 -0
  110. package/build/module/Carousel/hooks/useDimensionChangeReaction.js +14 -0
  111. package/build/module/Carousel/hooks/useDimensionChangeReaction.js.map +1 -0
  112. package/build/module/Carousel/hooks/useIndexController.js +54 -0
  113. package/build/module/Carousel/hooks/useIndexController.js.map +1 -0
  114. package/build/module/Carousel/hooks/useItemInterpolation.js +51 -0
  115. package/build/module/Carousel/hooks/useItemInterpolation.js.map +1 -0
  116. package/build/module/Carousel/hooks/useItemVisibilityStore.js +87 -0
  117. package/build/module/Carousel/hooks/useItemVisibilityStore.js.map +1 -0
  118. package/build/module/Carousel/hooks/useLoopedData.js +24 -0
  119. package/build/module/Carousel/hooks/useLoopedData.js.map +1 -0
  120. package/build/module/Carousel/hooks/usePagingAnimation.js +153 -0
  121. package/build/module/Carousel/hooks/usePagingAnimation.js.map +1 -0
  122. package/build/module/Carousel/index.js +2 -1
  123. package/build/module/Carousel/index.js.map +1 -1
  124. package/build/module/Carousel/types.js +3 -0
  125. package/build/module/Carousel/types.js.map +1 -0
  126. package/build/module/ViewPager/ChildrenMemoizedPage.js +80 -23
  127. package/build/module/ViewPager/ChildrenMemoizedPage.js.map +1 -1
  128. package/build/module/ViewPager/PageStateContext.js +10 -0
  129. package/build/module/ViewPager/PageStateContext.js.map +1 -0
  130. package/build/module/ViewPager/ViewPagerNative.js +81 -34
  131. package/build/module/ViewPager/ViewPagerNative.js.map +1 -1
  132. package/build/module/ViewPager/ViewPagerProps.js.map +1 -1
  133. package/build/module/ViewPager/ViewPagerWeb.js +29 -24
  134. package/build/module/ViewPager/ViewPagerWeb.js.map +1 -1
  135. package/build/module/ViewPager/index.js +1 -0
  136. package/build/module/ViewPager/index.js.map +1 -1
  137. package/build/module/ViewPager/index.native.js +1 -0
  138. package/build/module/ViewPager/index.native.js.map +1 -1
  139. package/build/module/ViewPager/utils.js +2 -32
  140. package/build/module/ViewPager/utils.js.map +1 -1
  141. package/build/module/ViewabilityTrackerView/Viewability.js +2 -0
  142. package/build/module/ViewabilityTrackerView/Viewability.js.map +1 -0
  143. package/build/module/ViewabilityTrackerView/ViewabilityTrackerView.js +28 -0
  144. package/build/module/ViewabilityTrackerView/ViewabilityTrackerView.js.map +1 -0
  145. package/build/module/ViewabilityTrackerView/ViewabilityTrackerViewProps.js +2 -0
  146. package/build/module/ViewabilityTrackerView/ViewabilityTrackerViewProps.js.map +1 -0
  147. package/build/module/ViewabilityTrackerView/index.js +2 -0
  148. package/build/module/ViewabilityTrackerView/index.js.map +1 -0
  149. package/build/module/ViewabilityTrackerView/measureViewability.js +34 -0
  150. package/build/module/ViewabilityTrackerView/measureViewability.js.map +1 -0
  151. package/build/module/ViewabilityTrackerView/useViewabilityTracker.js +73 -0
  152. package/build/module/ViewabilityTrackerView/useViewabilityTracker.js.map +1 -0
  153. package/build/module/hooks/index.js +2 -0
  154. package/build/module/hooks/index.js.map +1 -1
  155. package/build/module/hooks/useDeferredExecutor.js +33 -0
  156. package/build/module/hooks/useDeferredExecutor.js.map +1 -0
  157. package/build/module/hooks/useIsMounted.js +13 -0
  158. package/build/module/hooks/useIsMounted.js.map +1 -0
  159. package/build/module/index.js +2 -0
  160. package/build/module/index.js.map +1 -1
  161. package/build/typescript/Carousel/Carousel.d.ts +5 -0
  162. package/build/typescript/Carousel/CarouselProps.d.ts +33 -48
  163. package/build/typescript/Carousel/animation/animationUtils.d.ts +1 -0
  164. package/build/typescript/Carousel/animation/createDefaultItemStyle.d.ts +2 -0
  165. package/build/typescript/Carousel/animation/createDefaultScrollAnimation.d.ts +2 -0
  166. package/build/typescript/Carousel/animation/index.d.ts +4 -0
  167. package/build/typescript/Carousel/animation/normalItemStyleFactory.d.ts +2 -0
  168. package/build/typescript/Carousel/animation/parallaxItemStyleFactory.d.ts +9 -0
  169. package/build/typescript/Carousel/components/InternalContext.d.ts +14 -0
  170. package/build/typescript/Carousel/components/ItemView.d.ts +9 -0
  171. package/build/typescript/Carousel/components/RootView.d.ts +16 -0
  172. package/build/typescript/Carousel/components/ScrollViewGesture.d.ts +12 -0
  173. package/build/typescript/Carousel/components/index.d.ts +4 -0
  174. package/build/typescript/Carousel/hooks/index.d.ts +7 -0
  175. package/build/typescript/Carousel/hooks/useAutoplayController.d.ts +7 -0
  176. package/build/typescript/Carousel/hooks/useDimensionChangeReaction.d.ts +7 -0
  177. package/build/typescript/Carousel/hooks/useIndexController.d.ts +10 -0
  178. package/build/typescript/Carousel/hooks/useItemInterpolation.d.ts +2 -0
  179. package/build/typescript/Carousel/hooks/useItemVisibilityStore.d.ts +7 -0
  180. package/build/typescript/Carousel/hooks/useLoopedData.d.ts +1 -0
  181. package/build/typescript/Carousel/hooks/usePagingAnimation.d.ts +18 -0
  182. package/build/typescript/Carousel/index.d.ts +3 -1
  183. package/build/typescript/Carousel/types.d.ts +79 -0
  184. package/build/typescript/ViewPager/ChildrenMemoizedPage.d.ts +2 -1
  185. package/build/typescript/ViewPager/PageStateContext.d.ts +7 -0
  186. package/build/typescript/ViewPager/ViewPagerNative.d.ts +4 -1
  187. package/build/typescript/ViewPager/ViewPagerProps.d.ts +31 -19
  188. package/build/typescript/ViewPager/ViewPagerWeb.d.ts +4 -1
  189. package/build/typescript/ViewPager/index.d.ts +2 -1
  190. package/build/typescript/ViewPager/index.native.d.ts +1 -0
  191. package/build/typescript/ViewPager/utils.d.ts +3 -19
  192. package/build/typescript/ViewabilityTrackerView/Viewability.d.ts +3 -0
  193. package/build/typescript/ViewabilityTrackerView/ViewabilityTrackerView.d.ts +2 -0
  194. package/build/typescript/ViewabilityTrackerView/ViewabilityTrackerViewProps.d.ts +22 -0
  195. package/build/typescript/ViewabilityTrackerView/index.d.ts +2 -0
  196. package/build/typescript/ViewabilityTrackerView/measureViewability.d.ts +11 -0
  197. package/build/typescript/ViewabilityTrackerView/useViewabilityTracker.d.ts +11 -0
  198. package/build/typescript/hooks/index.d.ts +2 -0
  199. package/build/typescript/hooks/useDeferredExecutor.d.ts +11 -0
  200. package/build/typescript/hooks/useIsMounted.d.ts +4 -0
  201. package/build/typescript/index.d.ts +2 -0
  202. package/package.json +4 -19
  203. package/src/Carousel/Carousel.tsx +152 -0
  204. package/src/Carousel/CarouselProps.ts +35 -53
  205. package/src/Carousel/animation/animationUtils.ts +1 -0
  206. package/src/Carousel/animation/createDefaultItemStyle.ts +3 -0
  207. package/src/Carousel/animation/createDefaultScrollAnimation.ts +13 -0
  208. package/src/Carousel/animation/index.ts +4 -0
  209. package/src/Carousel/animation/normalItemStyleFactory.ts +19 -0
  210. package/src/Carousel/animation/parallaxItemStyleFactory.ts +79 -0
  211. package/src/Carousel/components/InternalContext.ts +33 -0
  212. package/src/Carousel/components/ItemView.tsx +66 -0
  213. package/src/Carousel/components/RootView.tsx +79 -0
  214. package/src/Carousel/components/ScrollViewGesture.tsx +87 -0
  215. package/src/Carousel/components/index.ts +4 -0
  216. package/src/Carousel/hooks/index.ts +7 -0
  217. package/src/Carousel/hooks/useAutoplayController.ts +57 -0
  218. package/src/Carousel/hooks/useDimensionChangeReaction.ts +25 -0
  219. package/src/Carousel/hooks/useIndexController.tsx +76 -0
  220. package/src/Carousel/hooks/useItemInterpolation.ts +107 -0
  221. package/src/Carousel/hooks/useItemVisibilityStore.ts +107 -0
  222. package/src/Carousel/hooks/useLoopedData.ts +26 -0
  223. package/src/Carousel/hooks/usePagingAnimation.ts +195 -0
  224. package/src/Carousel/index.ts +4 -2
  225. package/src/Carousel/types.ts +97 -0
  226. package/src/ViewPager/ChildrenMemoizedPage.tsx +97 -23
  227. package/src/ViewPager/PageStateContext.ts +15 -0
  228. package/src/ViewPager/ViewPagerNative.tsx +101 -39
  229. package/src/ViewPager/ViewPagerProps.ts +33 -19
  230. package/src/ViewPager/ViewPagerWeb.tsx +38 -29
  231. package/src/ViewPager/index.native.ts +1 -0
  232. package/src/ViewPager/index.ts +2 -1
  233. package/src/ViewPager/utils.tsx +4 -56
  234. package/src/ViewabilityTrackerView/Viewability.ts +3 -0
  235. package/src/ViewabilityTrackerView/ViewabilityTrackerView.tsx +33 -0
  236. package/src/ViewabilityTrackerView/ViewabilityTrackerViewProps.ts +25 -0
  237. package/src/ViewabilityTrackerView/index.ts +2 -0
  238. package/src/ViewabilityTrackerView/measureViewability.ts +56 -0
  239. package/src/ViewabilityTrackerView/useViewabilityTracker.ts +85 -0
  240. package/src/hooks/index.ts +2 -0
  241. package/src/hooks/useDeferredExecutor.ts +46 -0
  242. package/src/hooks/useIsMounted.ts +19 -0
  243. package/src/index.ts +3 -0
  244. package/build/commonjs/Carousel/CarouselNative.js +0 -72
  245. package/build/commonjs/Carousel/CarouselNative.js.map +0 -1
  246. package/build/commonjs/Carousel/CarouselWeb.js +0 -237
  247. package/build/commonjs/Carousel/CarouselWeb.js.map +0 -1
  248. package/build/commonjs/Carousel/index.native.js.map +0 -1
  249. package/build/commonjs/Carousel/utils.js +0 -19
  250. package/build/commonjs/Carousel/utils.js.map +0 -1
  251. package/build/module/Carousel/CarouselNative.js +0 -54
  252. package/build/module/Carousel/CarouselNative.js.map +0 -1
  253. package/build/module/Carousel/CarouselWeb.js +0 -215
  254. package/build/module/Carousel/CarouselWeb.js.map +0 -1
  255. package/build/module/Carousel/index.native.js +0 -2
  256. package/build/module/Carousel/index.native.js.map +0 -1
  257. package/build/module/Carousel/utils.js +0 -7
  258. package/build/module/Carousel/utils.js.map +0 -1
  259. package/build/typescript/Carousel/CarouselNative.d.ts +0 -2
  260. package/build/typescript/Carousel/CarouselWeb.d.ts +0 -4
  261. package/build/typescript/Carousel/index.native.d.ts +0 -1
  262. package/build/typescript/Carousel/utils.d.ts +0 -6
  263. package/src/Carousel/CarouselNative.tsx +0 -67
  264. package/src/Carousel/CarouselWeb.tsx +0 -222
  265. package/src/Carousel/index.native.ts +0 -1
  266. package/src/Carousel/utils.ts +0 -11
@@ -0,0 +1,195 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
+ import { Animated, Platform } from 'react-native';
3
+ import type {
4
+ CreateScrollAnimation,
5
+ DirectionalPagingAnimationConfig,
6
+ GetCurrentIndex,
7
+ IndexPagingAnimationConfig,
8
+ PagingAnimationConfig,
9
+ PagingAnimationType,
10
+ PagingDirection,
11
+ StartPagingAnimation,
12
+ } from '../types';
13
+
14
+ export interface PagingAnimationParameters {
15
+ controlledTx: Animated.Value;
16
+ createScrollAnimation: CreateScrollAnimation;
17
+ getCurrentIndex: GetCurrentIndex;
18
+ itemWidth: number;
19
+ lastIndex: number;
20
+ loop: boolean;
21
+ numberOfData: number;
22
+ offsetTx: Animated.Value;
23
+ }
24
+
25
+ export interface UsePagingAnimation {
26
+ finalizeAnimation: () => void;
27
+ globalInterpolation: Animated.AnimatedInterpolation;
28
+ startPagingAnimation: StartPagingAnimation;
29
+ }
30
+
31
+ function directionToValue(itemWidth: number) {
32
+ return function (direction: PagingDirection): number {
33
+ switch (direction) {
34
+ case 'next':
35
+ return -itemWidth;
36
+ case 'prev':
37
+ return itemWidth;
38
+ case 'stay':
39
+ return 0;
40
+ }
41
+ };
42
+ }
43
+
44
+ export default function usePagingAnimation(params: PagingAnimationParameters): UsePagingAnimation {
45
+ const {
46
+ controlledTx,
47
+ createScrollAnimation,
48
+ getCurrentIndex,
49
+ itemWidth,
50
+ lastIndex,
51
+ loop,
52
+ numberOfData,
53
+ offsetTx,
54
+ } = params;
55
+
56
+ const animationRef = useRef<Animated.CompositeAnimation | null>(null);
57
+ const toValueRef = useRef<number>(0);
58
+
59
+ const globalInterpolation = useMemo(
60
+ () => Animated.add(controlledTx, offsetTx),
61
+ [controlledTx, offsetTx],
62
+ );
63
+
64
+ useEffect(() => {
65
+ const subscriptionId = controlledTx.addListener((value) => {
66
+ const currentTx = value.value;
67
+
68
+ // Prevent infinite loop
69
+ if (currentTx !== 0) {
70
+ const maxWidth = numberOfData * itemWidth;
71
+
72
+ if (Math.abs(Math.round(currentTx)) === Math.round(maxWidth)) {
73
+ // reset position
74
+ controlledTx.setValue(0);
75
+ }
76
+ }
77
+ });
78
+
79
+ return () => {
80
+ controlledTx.removeListener(subscriptionId);
81
+ };
82
+ }, [numberOfData, itemWidth]);
83
+
84
+ const finalizeAnimation = useCallback(() => {
85
+ const stopUnfinishedSnapAnimation = () => {
86
+ if (animationRef.current) {
87
+ animationRef.current?.stop();
88
+ animationRef.current = null;
89
+ }
90
+ };
91
+
92
+ const resetBoundary = () => {
93
+ controlledTx.setOffset(toValueRef.current);
94
+ controlledTx.flattenOffset();
95
+
96
+ // FIXME: react-native-web bug maybe?
97
+ // `AnimatedValue.flattenOffset()` does not trigger any event listener.
98
+ // Accessing value directly via `_value` is dangerous but working on web (`useNativeDriver` always false).
99
+ // So setting same value with `value.setValue(value._value)` will trigger event listener.
100
+ if (Platform.OS === 'web') {
101
+ // @ts-ignore
102
+ controlledTx.setValue(controlledTx._value);
103
+ }
104
+
105
+ offsetTx.setValue(0);
106
+ toValueRef.current = 0;
107
+ };
108
+
109
+ stopUnfinishedSnapAnimation();
110
+
111
+ resetBoundary();
112
+ }, [controlledTx]);
113
+
114
+ const startPagingAnimation = useCallback((type: PagingAnimationType, config: PagingAnimationConfig) => {
115
+ const configWithDefaults: PagingAnimationConfig = {
116
+ animated: true,
117
+ ...config,
118
+ };
119
+
120
+ const currentIndex = getCurrentIndex();
121
+
122
+ const getValueByDirectionOnAllAdjacentItemsVisible = directionToValue(itemWidth);
123
+
124
+ const getValueByDirectionalPagingOnLoopDisabled = (_config: DirectionalPagingAnimationConfig): number => {
125
+ const { direction, isOriginatedFromGesture } = _config;
126
+
127
+ if (currentIndex === 0 && direction === 'prev') {
128
+ return isOriginatedFromGesture
129
+ ? getValueByDirectionOnAllAdjacentItemsVisible('stay')
130
+ : -lastIndex * itemWidth; // last position
131
+ } else if (currentIndex === lastIndex && direction === 'next') {
132
+ return isOriginatedFromGesture
133
+ ? getValueByDirectionOnAllAdjacentItemsVisible('stay')
134
+ : lastIndex * itemWidth; // first position
135
+ }
136
+ return getValueByDirectionOnAllAdjacentItemsVisible(direction);
137
+ };
138
+
139
+ const getValueByDirectionalPaging = (_config: DirectionalPagingAnimationConfig): number => {
140
+ const _configWithDefaults: DirectionalPagingAnimationConfig = {
141
+ isOriginatedFromGesture: false,
142
+ ..._config,
143
+ };
144
+
145
+ return loop
146
+ ? getValueByDirectionOnAllAdjacentItemsVisible(_configWithDefaults.direction)
147
+ : getValueByDirectionalPagingOnLoopDisabled(_configWithDefaults);
148
+ };
149
+
150
+ const getValueByIndexPaging = ({ index }: IndexPagingAnimationConfig): number => {
151
+ if (index < 0 || index > lastIndex || index === currentIndex) {
152
+ // no animation if index is invalid or equals to current index
153
+ return 0;
154
+ }
155
+
156
+ const distance = Math.abs(currentIndex - index) * itemWidth;
157
+ const direction = index > currentIndex ? -1 : 1;
158
+
159
+ return distance * direction;
160
+ };
161
+
162
+ const toValue = type === 'directional'
163
+ // @ts-ignore
164
+ ? getValueByDirectionalPaging(configWithDefaults)
165
+ // @ts-ignore
166
+ : getValueByIndexPaging(configWithDefaults);
167
+
168
+ toValueRef.current = toValue;
169
+
170
+ if (configWithDefaults.animated) {
171
+ const animation = createScrollAnimation(offsetTx, toValue);
172
+ animationRef.current = animation;
173
+ animation.start(({ finished }) => {
174
+ if (finished) {
175
+ finalizeAnimation();
176
+ }
177
+ });
178
+ } else {
179
+ finalizeAnimation();
180
+ }
181
+ }, [
182
+ createScrollAnimation,
183
+ getCurrentIndex,
184
+ finalizeAnimation,
185
+ itemWidth,
186
+ lastIndex,
187
+ loop,
188
+ ]);
189
+
190
+ return {
191
+ globalInterpolation,
192
+ finalizeAnimation,
193
+ startPagingAnimation,
194
+ };
195
+ };
@@ -1,2 +1,4 @@
1
- export { default } from './CarouselWeb';
2
- export type { default as CarouselProps } from './CarouselProps';
1
+ export { default } from './Carousel';
2
+ export type { default as CarouselProps } from './CarouselProps';
3
+ export type { CarouselInstance } from './types';
4
+ export { normalItemStyleFactory, parallaxItemStyleFactory } from './animation';
@@ -0,0 +1,97 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { Animated, ViewProps } from 'react-native';
3
+
4
+ const directions = ['next', 'prev', 'stay'] as const;
5
+
6
+ export type PagingDirection = (typeof directions)[number];
7
+
8
+ export type ItemHeight = number | 'auto';
9
+
10
+ export interface RenderItem<T> {
11
+ (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;
12
+ }
13
+
14
+ export interface CreateScrollAnimation {
15
+ (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;
16
+ }
17
+
18
+ export interface CreateItemStyle {
19
+ (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;
20
+ }
21
+
22
+ export interface GetCurrentIndex {
23
+ (): number;
24
+ }
25
+
26
+ export interface IndexController {
27
+ currentIndex: number;
28
+ getCurrentIndex: GetCurrentIndex;
29
+ lastIndex: number;
30
+ monitorElement: ReactElement;
31
+ }
32
+
33
+ export type PagingAnimationType = 'directional' | 'index';
34
+
35
+ export interface BasePagingAnimationConfig {
36
+ animated?: boolean;
37
+ }
38
+
39
+ export interface DirectionalPagingAnimationConfig extends BasePagingAnimationConfig {
40
+ direction: PagingDirection;
41
+ isOriginatedFromGesture?: boolean;
42
+ }
43
+
44
+ export interface IndexPagingAnimationConfig extends BasePagingAnimationConfig {
45
+ index: number;
46
+ }
47
+
48
+ export type PagingAnimationConfig = DirectionalPagingAnimationConfig | IndexPagingAnimationConfig;
49
+
50
+ export interface StartPagingAnimation {
51
+ (type: PagingAnimationType, config: PagingAnimationConfig): void;
52
+ }
53
+
54
+ export type VisibleIndexRanges = Array<[number, number]>;
55
+
56
+ export interface StoreSubscription {
57
+ (): void;
58
+ }
59
+
60
+ export interface ItemVisibilityStore {
61
+ dispatch: (ranges: VisibleIndexRanges) => void;
62
+ subscribe: (listener: (ranges: VisibleIndexRanges) => void) => StoreSubscription;
63
+ removeAllListeners: () => void;
64
+ }
65
+
66
+ export interface AutoplayController {
67
+ pause: () => void;
68
+ resume: () => void;
69
+ }
70
+
71
+ export interface ScrollToOption {
72
+ index: number;
73
+ animated?: boolean;
74
+ }
75
+
76
+ export interface CarouselInstance {
77
+ /**
78
+ * Get current visible item index.
79
+ */
80
+ getCurrentIndex: GetCurrentIndex;
81
+
82
+ /**
83
+ * Scroll to next visible item.
84
+ */
85
+ next: () => void;
86
+
87
+ /**
88
+ * Scroll to previous visible item.
89
+ */
90
+ prev: () => void;
91
+
92
+ /**
93
+ * Scroll to desired indexed item.
94
+ * Invalid index is ignored.
95
+ */
96
+ scrollTo: (option: ScrollToOption) => void;
97
+ }
@@ -1,36 +1,110 @@
1
- import React from 'react';
2
- import { View } from 'react-native';
1
+ import React, { memo, useMemo, useState } from 'react';
2
+ import { Platform, View } from 'react-native';
3
+ import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
4
+ import { StyleSheet } from '@fountain-ui/core';
3
5
  import type { PageProps } from './ViewPagerProps';
6
+ import PageStateContext from './PageStateContext';
4
7
 
5
- const Page = function Page(props: PageProps) {
6
- const { isVisible, ...otherProps } = props;
8
+ const styles = StyleSheet.create({
9
+ fill: { width: '100%', height: '100%' },
10
+ none: { display: 'none' },
11
+ });
7
12
 
8
- return (
9
- <View
10
- collapsable={false}
11
- {...otherProps}
12
- />
13
+ interface InternalPageState {
14
+ isActive: boolean;
15
+ isLoaded: boolean;
16
+ }
17
+
18
+ function Page(props: PageProps) {
19
+ const {
20
+ index,
21
+ children,
22
+ loading,
23
+ offscreenPageRerenderLimit,
24
+ sharedIndex,
25
+ } = props;
26
+
27
+ const assumeInitialPageState = (): InternalPageState => {
28
+ const activeIndex = sharedIndex.value;
29
+
30
+ const isActive = index === activeIndex;
31
+
32
+ if (loading === 'eager') {
33
+ return { isActive, isLoaded: true };
34
+ }
35
+
36
+ const isLoaded = index >= activeIndex - offscreenPageRerenderLimit
37
+ && index <= activeIndex + offscreenPageRerenderLimit;
38
+
39
+ return { isActive, isLoaded };
40
+ };
41
+
42
+ const [pageState, setPageState] = useState<InternalPageState>(assumeInitialPageState);
43
+
44
+ const content = pageState.isLoaded ? children : null;
45
+
46
+ const onActiveStateChange = (isActive: boolean) => {
47
+ setPageState(prevState => ({
48
+ ...prevState,
49
+ isActive,
50
+ isLoaded: isActive || prevState.isLoaded,
51
+ }));
52
+ };
53
+
54
+ const onBecomeNeighbor = () => {
55
+ setPageState(prevState => ({
56
+ ...prevState,
57
+ isLoaded: true,
58
+ }));
59
+ };
60
+
61
+ useAnimatedReaction(
62
+ () => {
63
+ const activeIndex = sharedIndex.value;
64
+
65
+ const isActive = index === activeIndex;
66
+
67
+ const shouldRerender = index >= activeIndex - offscreenPageRerenderLimit
68
+ && index <= activeIndex + offscreenPageRerenderLimit;
69
+
70
+ const becomeNeighbor = shouldRerender && !isActive;
71
+
72
+ return { isActive, becomeNeighbor };
73
+ },
74
+ (nextState, prevState) => {
75
+ if (nextState.isActive !== prevState?.isActive) {
76
+ runOnJS(onActiveStateChange)(nextState.isActive);
77
+ } else if (nextState.becomeNeighbor) {
78
+ runOnJS(onBecomeNeighbor)();
79
+ }
80
+ },
81
+ [index],
13
82
  );
14
- };
15
83
 
16
- export default React.memo(Page, (prevProps, nextProps) => {
17
- if (prevProps.children === null && nextProps.children === null) {
18
- return true;
19
- }
84
+ const contextValue = useMemo(() => ({
85
+ isActive: pageState.isActive,
86
+ }), [pageState.isActive]);
20
87
 
21
- // isVisible is an important condition for determining memos on the web.
22
- // Reference ViewPagerWeb's return.
23
- if (prevProps.isVisible !== nextProps.isVisible) {
24
- return false;
25
- }
88
+ const style = Platform.OS === 'web'
89
+ ? (pageState.isActive ? StyleSheet.absoluteFill : styles.none)
90
+ : styles.fill;
26
91
 
27
- if (nextProps.isVisible || nextProps.isNeighbor) {
28
- return false;
29
- }
92
+ return (
93
+ <View
94
+ collapsable={false}
95
+ style={style}
96
+ >
97
+ <PageStateContext.Provider value={contextValue}>
98
+ {content}
99
+ </PageStateContext.Provider>
100
+ </View>
101
+ );
102
+ }
30
103
 
104
+ export default memo(Page, (prevProps, nextProps) => {
31
105
  if (prevProps.rerenderKey !== nextProps.rerenderKey) {
32
106
  return false;
33
107
  }
34
108
 
35
- return prevProps.children !== null && nextProps.children !== null;
109
+ return prevProps.index !== nextProps.index;
36
110
  });
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ interface PageState {
4
+ isActive: boolean;
5
+ }
6
+
7
+ const initialPageState: Readonly<PageState> = { isActive: false };
8
+
9
+ const PageStateContext = createContext<PageState>(initialPageState);
10
+
11
+ export const useViewPagerPageState = () => {
12
+ return useContext(PageStateContext);
13
+ };
14
+
15
+ export default PageStateContext;
@@ -1,73 +1,135 @@
1
- import React, { useCallback, useEffect, useRef } from 'react';
1
+ import React, { Children, forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
2
2
  import type { ViewPagerOnPageSelectedEvent } from 'react-native-pager-view';
3
3
  import RNViewPager from 'react-native-pager-view';
4
+ import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated';
4
5
  import type ViewPagerProps from './ViewPagerProps';
5
- import {
6
- defaultEnableNeighborPageRerender,
7
- defaultEnableVisibleHint,
8
- defaultLoading,
9
- defaultPageComponent,
10
- usePageRenderer,
11
- } from './utils';
12
-
13
- export default function ViewPager(props: ViewPagerProps) {
6
+ import type { ViewPagerInstance } from './ViewPagerProps';
7
+ import { defaultInitialPage, defaultLoading, defaultOffscreenPageRerenderLimit, defaultPageComponent } from './utils';
8
+
9
+ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPager(props, ref) {
14
10
  const {
15
11
  children,
16
- enableVisibleHint = defaultEnableVisibleHint,
17
- index,
12
+ initialPage = defaultInitialPage,
18
13
  keyboardDismissMode = 'on-drag',
19
14
  loading = defaultLoading,
15
+ offscreenPageRerenderLimit = defaultOffscreenPageRerenderLimit,
20
16
  onChange,
21
17
  pageComponent = defaultPageComponent,
22
- enableNeighborPageRerender = defaultEnableNeighborPageRerender,
23
18
  pageForceRerenderKey,
24
19
  scrollEnabled = true,
25
20
  style,
21
+ UNSTABLE_sharedPage,
26
22
  } = props;
27
23
 
28
- const indexRef = useRef<number>(index);
29
- const pagerRef = useRef<RNViewPager | null>(null);
24
+ const fallbackSharedPage = useSharedValue<number>(initialPage);
30
25
 
31
- const renderer = usePageRenderer({
32
- enableVisibleHint,
33
- index,
34
- loading,
35
- pageComponent,
36
- pageForceRerenderKey,
37
- enableNeighborPageRerender,
38
- });
26
+ const sharedPage = UNSTABLE_sharedPage ?? fallbackSharedPage;
39
27
 
40
- const handlePageSelected = useCallback((e: ViewPagerOnPageSelectedEvent) => {
41
- indexRef.current = e.nativeEvent.position;
28
+ const desiredPageRef = useRef<number>(NaN);
42
29
 
43
- if (onChange) {
44
- onChange(indexRef.current);
30
+ const pagerRef = useRef<RNViewPager | null>(null);
31
+
32
+ const animationFrameRef = useRef<number>(NaN);
33
+
34
+ const setPage = useCallback((newPage: number) => {
35
+ if (!Number.isInteger(newPage)) {
36
+ return;
45
37
  }
46
- }, [onChange]);
47
38
 
48
- useEffect(() => {
49
- const handleNumber = index !== indexRef.current
50
- ? requestAnimationFrame(() => pagerRef.current?.setPage(index))
51
- : undefined;
39
+ animationFrameRef.current = requestAnimationFrame(() => {
40
+ if (Number.isFinite(desiredPageRef.current)) {
41
+ if (sharedPage.value === desiredPageRef.current) {
42
+ // end of state machine. clear desired page.
43
+ desiredPageRef.current = NaN;
44
+ return;
45
+ }
46
+
47
+ if (newPage === desiredPageRef.current) {
48
+ return;
49
+ }
50
+ }
51
+
52
+ desiredPageRef.current = newPage;
53
+ pagerRef.current?.setPage(newPage);
54
+ });
55
+ }, []);
52
56
 
57
+ useEffect(() => {
53
58
  return () => {
54
- if (handleNumber) {
55
- cancelAnimationFrame(handleNumber);
59
+ if (animationFrameRef.current) {
60
+ cancelAnimationFrame(animationFrameRef.current);
56
61
  }
57
62
  };
58
- }, [index]);
63
+ }, []);
64
+
65
+ useAnimatedReaction(
66
+ () => sharedPage.value,
67
+ (result) => {
68
+ runOnJS(setPage)(result);
69
+ },
70
+ [setPage],
71
+ );
72
+
73
+ const handlePageSelected = useCallback((e: ViewPagerOnPageSelectedEvent) => {
74
+ if (Number.isFinite(desiredPageRef.current)) {
75
+ // onPageSelected is called from outer controlling mechanism
76
+ // e.g. sharedPage.value is changed, setPage() is called
77
+ // Due to the onPageSelected bug,
78
+ // we don't trust the value from event, (e.nativeEvent.position)
79
+ // other than that trust the value from desiredPageRef.
80
+
81
+ const desiredPage = desiredPageRef.current;
82
+
83
+ if (sharedPage.value === desiredPage) {
84
+ // end of state machine. clear desired page.
85
+ desiredPageRef.current = NaN;
86
+ } else {
87
+ sharedPage.value = desiredPage;
88
+ }
89
+
90
+ onChange?.(desiredPage);
91
+ } else {
92
+ const trustfulNextPage = e.nativeEvent.position;
93
+
94
+ desiredPageRef.current = trustfulNextPage;
95
+
96
+ sharedPage.value = trustfulNextPage;
97
+
98
+ onChange?.(trustfulNextPage);
99
+ }
100
+ }, [onChange]);
101
+
102
+ useImperativeHandle(
103
+ ref,
104
+ () => ({ setPage }),
105
+ [setPage],
106
+ );
107
+
108
+ const PageComponent = pageComponent;
59
109
 
60
110
  return (
61
111
  <RNViewPager
62
- initialPage={index}
112
+ ref={pagerRef}
113
+ initialPage={sharedPage.value}
63
114
  keyboardDismissMode={keyboardDismissMode}
64
115
  onPageSelected={handlePageSelected}
65
116
  pageMargin={8}
66
- ref={pagerRef}
67
117
  scrollEnabled={scrollEnabled}
68
118
  style={style}
69
119
  >
70
- {renderer(children)}
120
+ {Children.map(children, (child, index) => (
121
+ <PageComponent
122
+ key={index}
123
+ children={child}
124
+ index={index}
125
+ loading={loading}
126
+ offscreenPageRerenderLimit={offscreenPageRerenderLimit}
127
+ rerenderKey={pageForceRerenderKey}
128
+ sharedIndex={sharedPage}
129
+ />
130
+ ))}
71
131
  </RNViewPager>
72
132
  );
73
- };
133
+ });
134
+
135
+ export default ViewPager;