@fountain-ui/lab 2.0.0-beta.11 → 2.0.0-beta.12

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 (265) hide show
  1. package/build/commonjs/Carousel/Carousel.js +137 -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 +64 -0
  19. package/build/commonjs/Carousel/components/ItemView.js.map +1 -0
  20. package/build/commonjs/Carousel/components/RootView.js +68 -0
  21. package/build/commonjs/Carousel/components/RootView.js.map +1 -0
  22. package/build/commonjs/Carousel/components/ScrollViewGesture.js +76 -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 +55 -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 +124 -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 +44 -47
  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 +23 -10
  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 +9 -7
  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 +3 -1
  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 +115 -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 +44 -0
  99. package/build/module/Carousel/components/ItemView.js.map +1 -0
  100. package/build/module/Carousel/components/RootView.js +47 -0
  101. package/build/module/Carousel/components/RootView.js.map +1 -0
  102. package/build/module/Carousel/components/ScrollViewGesture.js +62 -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 +45 -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 +115 -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 +40 -45
  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 +26 -13
  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 +10 -8
  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 +1 -0
  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 +32 -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 +7 -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 +57 -0
  184. package/build/typescript/ViewPager/PageStateContext.d.ts +7 -0
  185. package/build/typescript/ViewPager/ViewPagerNative.d.ts +1 -1
  186. package/build/typescript/ViewPager/ViewPagerProps.d.ts +11 -0
  187. package/build/typescript/ViewPager/ViewPagerWeb.d.ts +1 -1
  188. package/build/typescript/ViewPager/index.d.ts +1 -0
  189. package/build/typescript/ViewPager/index.native.d.ts +1 -0
  190. package/build/typescript/ViewPager/utils.d.ts +1 -0
  191. package/build/typescript/ViewabilityTrackerView/Viewability.d.ts +3 -0
  192. package/build/typescript/ViewabilityTrackerView/ViewabilityTrackerView.d.ts +2 -0
  193. package/build/typescript/ViewabilityTrackerView/ViewabilityTrackerViewProps.d.ts +22 -0
  194. package/build/typescript/ViewabilityTrackerView/index.d.ts +2 -0
  195. package/build/typescript/ViewabilityTrackerView/measureViewability.d.ts +11 -0
  196. package/build/typescript/ViewabilityTrackerView/useViewabilityTracker.d.ts +11 -0
  197. package/build/typescript/hooks/index.d.ts +2 -0
  198. package/build/typescript/hooks/useDeferredExecutor.d.ts +11 -0
  199. package/build/typescript/hooks/useIsMounted.d.ts +4 -0
  200. package/build/typescript/index.d.ts +2 -0
  201. package/package.json +4 -19
  202. package/src/Carousel/Carousel.tsx +151 -0
  203. package/src/Carousel/CarouselProps.ts +34 -53
  204. package/src/Carousel/animation/animationUtils.ts +1 -0
  205. package/src/Carousel/animation/createDefaultItemStyle.ts +3 -0
  206. package/src/Carousel/animation/createDefaultScrollAnimation.ts +13 -0
  207. package/src/Carousel/animation/index.ts +4 -0
  208. package/src/Carousel/animation/normalItemStyleFactory.ts +19 -0
  209. package/src/Carousel/animation/parallaxItemStyleFactory.ts +79 -0
  210. package/src/Carousel/components/InternalContext.ts +33 -0
  211. package/src/Carousel/components/ItemView.tsx +56 -0
  212. package/src/Carousel/components/RootView.tsx +66 -0
  213. package/src/Carousel/components/ScrollViewGesture.tsx +80 -0
  214. package/src/Carousel/components/index.ts +4 -0
  215. package/src/Carousel/hooks/index.ts +7 -0
  216. package/src/Carousel/hooks/useAutoplayController.ts +54 -0
  217. package/src/Carousel/hooks/useDimensionChangeReaction.ts +25 -0
  218. package/src/Carousel/hooks/useIndexController.tsx +76 -0
  219. package/src/Carousel/hooks/useItemInterpolation.ts +107 -0
  220. package/src/Carousel/hooks/useItemVisibilityStore.ts +107 -0
  221. package/src/Carousel/hooks/useLoopedData.ts +26 -0
  222. package/src/Carousel/hooks/usePagingAnimation.ts +148 -0
  223. package/src/Carousel/index.ts +4 -2
  224. package/src/Carousel/types.ts +67 -0
  225. package/src/ViewPager/ChildrenMemoizedPage.tsx +48 -43
  226. package/src/ViewPager/PageStateContext.ts +15 -0
  227. package/src/ViewPager/ViewPagerNative.tsx +35 -11
  228. package/src/ViewPager/ViewPagerProps.ts +13 -0
  229. package/src/ViewPager/ViewPagerWeb.tsx +9 -5
  230. package/src/ViewPager/index.native.ts +1 -0
  231. package/src/ViewPager/index.ts +1 -0
  232. package/src/ViewPager/utils.tsx +2 -1
  233. package/src/ViewabilityTrackerView/Viewability.ts +3 -0
  234. package/src/ViewabilityTrackerView/ViewabilityTrackerView.tsx +33 -0
  235. package/src/ViewabilityTrackerView/ViewabilityTrackerViewProps.ts +25 -0
  236. package/src/ViewabilityTrackerView/index.ts +2 -0
  237. package/src/ViewabilityTrackerView/measureViewability.ts +56 -0
  238. package/src/ViewabilityTrackerView/useViewabilityTracker.ts +85 -0
  239. package/src/hooks/index.ts +2 -0
  240. package/src/hooks/useDeferredExecutor.ts +46 -0
  241. package/src/hooks/useIsMounted.ts +19 -0
  242. package/src/index.ts +3 -0
  243. package/build/commonjs/Carousel/CarouselNative.js +0 -72
  244. package/build/commonjs/Carousel/CarouselNative.js.map +0 -1
  245. package/build/commonjs/Carousel/CarouselWeb.js +0 -237
  246. package/build/commonjs/Carousel/CarouselWeb.js.map +0 -1
  247. package/build/commonjs/Carousel/index.native.js.map +0 -1
  248. package/build/commonjs/Carousel/utils.js +0 -19
  249. package/build/commonjs/Carousel/utils.js.map +0 -1
  250. package/build/module/Carousel/CarouselNative.js +0 -54
  251. package/build/module/Carousel/CarouselNative.js.map +0 -1
  252. package/build/module/Carousel/CarouselWeb.js +0 -215
  253. package/build/module/Carousel/CarouselWeb.js.map +0 -1
  254. package/build/module/Carousel/index.native.js +0 -2
  255. package/build/module/Carousel/index.native.js.map +0 -1
  256. package/build/module/Carousel/utils.js +0 -7
  257. package/build/module/Carousel/utils.js.map +0 -1
  258. package/build/typescript/Carousel/CarouselNative.d.ts +0 -2
  259. package/build/typescript/Carousel/CarouselWeb.d.ts +0 -4
  260. package/build/typescript/Carousel/index.native.d.ts +0 -1
  261. package/build/typescript/Carousel/utils.d.ts +0 -6
  262. package/src/Carousel/CarouselNative.tsx +0 -67
  263. package/src/Carousel/CarouselWeb.tsx +0 -222
  264. package/src/Carousel/index.native.ts +0 -1
  265. package/src/Carousel/utils.ts +0 -11
@@ -0,0 +1,26 @@
1
+ import { useMemo } from 'react';
2
+
3
+ export default function useLoopedData<ItemT>(
4
+ originalData: ReadonlyArray<ItemT>,
5
+ loop: boolean,
6
+ ): ReadonlyArray<ItemT> {
7
+ return useMemo(() => {
8
+ if (!loop) {
9
+ return originalData;
10
+ }
11
+
12
+ const numberOfData = originalData.length;
13
+
14
+ if (numberOfData === 1) {
15
+ const [first] = originalData;
16
+ return [first, first, first];
17
+ }
18
+
19
+ if (numberOfData === 2) {
20
+ const [first, second] = originalData;
21
+ return [first, second, first, second];
22
+ }
23
+
24
+ return originalData;
25
+ }, [originalData, loop]);
26
+ };
@@ -0,0 +1,148 @@
1
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
+ import { Animated, Platform } from 'react-native';
3
+ import type { CreateScrollAnimation, GetCurrentIndex, PagingDirection, StartPagingAnimation } from '../types';
4
+
5
+ export interface PagingAnimationConfig {
6
+ controlledTx: Animated.Value;
7
+ createScrollAnimation: CreateScrollAnimation;
8
+ getCurrentIndex: GetCurrentIndex;
9
+ itemWidth: number;
10
+ lastIndex: number;
11
+ loop: boolean;
12
+ numberOfData: number;
13
+ offsetTx: Animated.Value;
14
+ }
15
+
16
+ export interface UsePagingAnimation {
17
+ finalizeAnimation: () => void;
18
+ globalInterpolation: Animated.AnimatedInterpolation;
19
+ startAnimation: StartPagingAnimation;
20
+ }
21
+
22
+ function directionToValue(itemWidth: number) {
23
+ return function (direction: PagingDirection): number {
24
+ switch (direction) {
25
+ case 'next':
26
+ return -itemWidth;
27
+ case 'prev':
28
+ return itemWidth;
29
+ case 'stay':
30
+ return 0;
31
+ }
32
+ };
33
+ }
34
+
35
+ export default function usePagingAnimation(config: PagingAnimationConfig): UsePagingAnimation {
36
+ const {
37
+ controlledTx,
38
+ createScrollAnimation,
39
+ getCurrentIndex,
40
+ itemWidth,
41
+ lastIndex,
42
+ loop,
43
+ numberOfData,
44
+ offsetTx,
45
+ } = config;
46
+
47
+ const animationRef = useRef<Animated.CompositeAnimation | null>(null);
48
+ const toValueRef = useRef<number>(0);
49
+
50
+ const globalInterpolation = useMemo(
51
+ () => Animated.add(controlledTx, offsetTx),
52
+ [controlledTx, offsetTx],
53
+ );
54
+
55
+ useEffect(() => {
56
+ const subscriptionId = controlledTx.addListener((value) => {
57
+ const currentTx = value.value;
58
+
59
+ // Prevent infinite loop
60
+ if (currentTx !== 0) {
61
+ const maxWidth = numberOfData * itemWidth;
62
+
63
+ if (Math.abs(Math.round(currentTx)) === Math.round(maxWidth)) {
64
+ // reset position
65
+ controlledTx.setValue(0);
66
+ }
67
+ }
68
+ });
69
+
70
+ return () => {
71
+ controlledTx.removeListener(subscriptionId);
72
+ };
73
+ }, [numberOfData, itemWidth]);
74
+
75
+ const finalizeAnimation = useCallback(() => {
76
+ const stopUnfinishedSnapAnimation = () => {
77
+ if (animationRef.current) {
78
+ animationRef.current?.stop();
79
+ animationRef.current = null;
80
+ }
81
+ };
82
+
83
+ const resetBoundary = () => {
84
+ controlledTx.setOffset(toValueRef.current);
85
+ controlledTx.flattenOffset();
86
+
87
+ // FIXME: react-native-web bug maybe?
88
+ // `AnimatedValue.flattenOffset()` does not trigger any event listener.
89
+ // Accessing value directly via `_value` is dangerous but working on web (`useNativeDriver` always false).
90
+ // So setting same value with `value.setValue(value._value)` will trigger event listener.
91
+ if (Platform.OS === 'web') {
92
+ // @ts-ignore
93
+ controlledTx.setValue(controlledTx._value);
94
+ }
95
+
96
+ offsetTx.setValue(0);
97
+ toValueRef.current = 0;
98
+ };
99
+
100
+ stopUnfinishedSnapAnimation();
101
+
102
+ resetBoundary();
103
+ }, [controlledTx]);
104
+
105
+ const startAnimation = useCallback((direction: PagingDirection, isGesture: boolean = false) => {
106
+ const getToValueByDirection = directionToValue(itemWidth);
107
+
108
+ const currentIndex = getCurrentIndex();
109
+
110
+ const computeToValueOnNoLoop = (): number => {
111
+ if (currentIndex === 0 && direction === 'prev') {
112
+ return isGesture
113
+ ? getToValueByDirection('stay')
114
+ : -lastIndex * itemWidth; // last position
115
+ } else if (currentIndex === lastIndex && direction === 'next') {
116
+ return isGesture
117
+ ? getToValueByDirection('stay')
118
+ : lastIndex * itemWidth; // first position
119
+ }
120
+ return getToValueByDirection(direction);
121
+ };
122
+
123
+ const toValue = loop ? getToValueByDirection(direction) : computeToValueOnNoLoop();
124
+ const animation = createScrollAnimation(offsetTx, toValue);
125
+
126
+ animationRef.current = animation;
127
+ toValueRef.current = toValue;
128
+
129
+ animation.start(({ finished }) => {
130
+ if (finished) {
131
+ finalizeAnimation();
132
+ }
133
+ });
134
+ }, [
135
+ createScrollAnimation,
136
+ getCurrentIndex,
137
+ finalizeAnimation,
138
+ itemWidth,
139
+ lastIndex,
140
+ loop,
141
+ ]);
142
+
143
+ return {
144
+ globalInterpolation,
145
+ finalizeAnimation,
146
+ startAnimation,
147
+ };
148
+ };
@@ -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,67 @@
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 interface RenderItem<T> {
9
+ (info: { item: T, index: number, interpolation: Animated.AnimatedInterpolation }): ReactElement | null;
10
+ }
11
+
12
+ export interface CreateScrollAnimation {
13
+ (aValue: Animated.AnimatedValue, toValue: number): Animated.CompositeAnimation;
14
+ }
15
+
16
+ export interface CreateItemStyle {
17
+ (itemInterpolation: Animated.AnimatedInterpolation, itemWidth: number): Animated.AnimatedProps<ViewProps['style']>;
18
+ }
19
+
20
+ export interface GetCurrentIndex {
21
+ (): number;
22
+ }
23
+
24
+ export interface IndexController {
25
+ currentIndex: number;
26
+ getCurrentIndex: GetCurrentIndex;
27
+ lastIndex: number;
28
+ monitorElement: ReactElement;
29
+ }
30
+
31
+ export interface StartPagingAnimation {
32
+ (direction: PagingDirection, isGesture?: boolean): void;
33
+ }
34
+
35
+ export type VisibleIndexRanges = Array<[number, number]>;
36
+
37
+ export interface StoreSubscription {
38
+ (): void;
39
+ }
40
+
41
+ export interface ItemVisibilityStore {
42
+ dispatch: (ranges: VisibleIndexRanges) => void;
43
+ subscribe: (listener: (ranges: VisibleIndexRanges) => void) => StoreSubscription;
44
+ removeAllListeners: () => void;
45
+ }
46
+
47
+ export interface AutoplayController {
48
+ pause: () => void;
49
+ resume: () => void;
50
+ }
51
+
52
+ export interface CarouselInstance {
53
+ /**
54
+ * Scroll to next visible item.
55
+ */
56
+ next: () => void;
57
+
58
+ /**
59
+ * Scroll to previous visible item.
60
+ */
61
+ prev: () => void;
62
+
63
+ /**
64
+ * Get current visible item index.
65
+ */
66
+ getCurrentIndex: GetCurrentIndex;
67
+ }
@@ -1,18 +1,18 @@
1
- import React, { memo, useState } from 'react';
1
+ import React, { memo, useMemo, useState } from 'react';
2
2
  import { Platform, View } from 'react-native';
3
3
  import { runOnJS, useAnimatedReaction } from 'react-native-reanimated';
4
4
  import { StyleSheet } from '@fountain-ui/core';
5
5
  import type { PageProps } from './ViewPagerProps';
6
-
7
- const OFFSCREEN_RERENDER_LIMIT = 1;
6
+ import PageStateContext from './PageStateContext';
8
7
 
9
8
  const styles = StyleSheet.create({
9
+ fill: { width: '100%', height: '100%' },
10
10
  none: { display: 'none' },
11
11
  });
12
12
 
13
- interface PageState {
14
- active: boolean;
15
- visited: boolean;
13
+ interface InternalPageState {
14
+ isActive: boolean;
15
+ isLoaded: boolean;
16
16
  }
17
17
 
18
18
  function Page(props: PageProps) {
@@ -20,79 +20,84 @@ function Page(props: PageProps) {
20
20
  index,
21
21
  children,
22
22
  loading,
23
+ offscreenPageRerenderLimit,
23
24
  sharedIndex,
24
25
  } = props;
25
26
 
26
- const assumeInitialPageState = (): PageState => {
27
+ const assumeInitialPageState = (): InternalPageState => {
27
28
  const activeIndex = sharedIndex.value;
28
29
 
29
- const active = index === activeIndex;
30
-
31
- if (Platform.OS === 'web') {
32
- return { active, visited: active };
33
- }
30
+ const isActive = index === activeIndex;
34
31
 
35
32
  if (loading === 'eager') {
36
- return { active, visited: true };
33
+ return { isActive, isLoaded: true };
37
34
  }
38
35
 
39
- const visited = index >= activeIndex - OFFSCREEN_RERENDER_LIMIT
40
- && index <= activeIndex + OFFSCREEN_RERENDER_LIMIT;
36
+ const isLoaded = index >= activeIndex - offscreenPageRerenderLimit
37
+ && index <= activeIndex + offscreenPageRerenderLimit;
41
38
 
42
- return { active, visited };
39
+ return { isActive, isLoaded };
43
40
  };
44
41
 
45
- const [pageState, setPageState] = useState<PageState>(assumeInitialPageState);
42
+ const [pageState, setPageState] = useState<InternalPageState>(assumeInitialPageState);
46
43
 
47
- const content = pageState.visited ? children : null;
48
-
49
- const updatePageState = (active: boolean, neighbor: boolean) => {
50
- setPageState(prevState => {
51
- if (prevState.active !== active) {
52
- return { active, visited: active || prevState.visited };
53
- }
44
+ const content = pageState.isLoaded ? children : null;
54
45
 
55
- if (neighbor && !prevState.visited) {
56
- return { ...prevState, visited: true };
57
- }
46
+ const onActiveStateChange = (isActive: boolean) => {
47
+ setPageState(prevState => ({
48
+ ...prevState,
49
+ isActive,
50
+ isLoaded: isActive || prevState.isLoaded,
51
+ }));
52
+ };
58
53
 
59
- return prevState;
60
- });
54
+ const onBecomeNeighbor = () => {
55
+ setPageState(prevState => ({
56
+ ...prevState,
57
+ isLoaded: true,
58
+ }));
61
59
  };
62
60
 
63
61
  useAnimatedReaction(
64
62
  () => {
65
63
  const activeIndex = sharedIndex.value;
66
64
 
67
- const willVisible = index >= activeIndex - OFFSCREEN_RERENDER_LIMIT
68
- && index <= activeIndex + OFFSCREEN_RERENDER_LIMIT;
65
+ const isActive = index === activeIndex;
66
+
67
+ const shouldRerender = index >= activeIndex - offscreenPageRerenderLimit
68
+ && index <= activeIndex + offscreenPageRerenderLimit;
69
69
 
70
- const willActive = index === activeIndex;
71
- const willNeighbor = Platform.OS === 'web' ? false : (willVisible && !willActive);
70
+ const becomeNeighbor = shouldRerender && !isActive;
72
71
 
73
- return { willActive, willNeighbor };
72
+ return { isActive, becomeNeighbor };
74
73
  },
75
74
  (nextState, prevState) => {
76
- const activeStateChanged = nextState.willActive !== prevState?.willActive;
77
- const willNewNeighbor = nextState.willNeighbor && !prevState?.willNeighbor;
78
-
79
- if (activeStateChanged || willNewNeighbor) {
80
- runOnJS(updatePageState)(nextState.willActive, nextState.willNeighbor);
75
+ if (nextState.isActive !== prevState?.isActive) {
76
+ runOnJS(onActiveStateChange)(nextState.isActive);
77
+ } else if (nextState.becomeNeighbor) {
78
+ runOnJS(onBecomeNeighbor)();
81
79
  }
82
80
  },
83
81
  [index],
84
82
  );
85
83
 
84
+ const contextValue = useMemo(() => ({
85
+ isActive: pageState.isActive,
86
+ }), [pageState.isActive]);
87
+
86
88
  const style = Platform.OS === 'web'
87
- ? (pageState.active ? StyleSheet.absoluteFill : styles.none)
88
- : undefined;
89
+ ? (pageState.isActive ? StyleSheet.absoluteFill : styles.none)
90
+ : styles.fill;
89
91
 
90
92
  return (
91
93
  <View
92
- children={content}
93
94
  collapsable={false}
94
95
  style={style}
95
- />
96
+ >
97
+ <PageStateContext.Provider value={contextValue}>
98
+ {content}
99
+ </PageStateContext.Provider>
100
+ </View>
96
101
  );
97
102
  }
98
103
 
@@ -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,10 +1,10 @@
1
- import React, { Children, forwardRef, useCallback, useImperativeHandle, 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 { useSharedValue } from 'react-native-reanimated';
4
+ import { runOnJS, useAnimatedReaction, useSharedValue } from 'react-native-reanimated';
5
5
  import type ViewPagerProps from './ViewPagerProps';
6
6
  import type { ViewPagerInstance } from './ViewPagerProps';
7
- import { defaultInitialPage, defaultLoading, defaultPageComponent } from './utils';
7
+ import { defaultInitialPage, defaultLoading, defaultOffscreenPageRerenderLimit, defaultPageComponent } from './utils';
8
8
 
9
9
  const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPager(props, ref) {
10
10
  const {
@@ -12,26 +12,43 @@ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPag
12
12
  initialPage = defaultInitialPage,
13
13
  keyboardDismissMode = 'on-drag',
14
14
  loading = defaultLoading,
15
+ offscreenPageRerenderLimit = defaultOffscreenPageRerenderLimit,
15
16
  onChange,
16
17
  pageComponent = defaultPageComponent,
17
18
  pageForceRerenderKey,
18
19
  scrollEnabled = true,
19
20
  style,
21
+ UNSTABLE_sharedIndex,
20
22
  } = props;
21
23
 
22
- const sharedIndex = useSharedValue<number>(initialPage);
24
+ const fallbackSharedIndex = useSharedValue<number>(initialPage);
25
+
26
+ const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
27
+
23
28
  const pagerRef = useRef<RNViewPager | null>(null);
24
29
 
25
- const setPage = (newIndex: number) => {
26
- requestAnimationFrame(() => {
30
+ const animationFrameRef = useRef<number>(NaN);
31
+
32
+ const setPage = useCallback((newIndex: number) => {
33
+ animationFrameRef.current = requestAnimationFrame(() => {
27
34
  pagerRef.current?.setPage(newIndex);
28
35
  });
29
- };
36
+ }, []);
30
37
 
31
- useImperativeHandle(
32
- ref,
33
- () => ({ setPage }),
34
- [],
38
+ useEffect(() => {
39
+ return () => {
40
+ if (animationFrameRef.current) {
41
+ cancelAnimationFrame(animationFrameRef.current);
42
+ }
43
+ };
44
+ }, []);
45
+
46
+ useAnimatedReaction(
47
+ () => sharedIndex.value,
48
+ (result) => {
49
+ runOnJS(setPage)(result);
50
+ },
51
+ [setPage],
35
52
  );
36
53
 
37
54
  const handlePageSelected = useCallback((e: ViewPagerOnPageSelectedEvent) => {
@@ -49,6 +66,12 @@ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPag
49
66
  }
50
67
  }, [onChange]);
51
68
 
69
+ useImperativeHandle(
70
+ ref,
71
+ () => ({ setPage }),
72
+ [setPage],
73
+ );
74
+
52
75
  const PageComponent = pageComponent;
53
76
 
54
77
  return (
@@ -67,6 +90,7 @@ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPag
67
90
  children={child}
68
91
  index={index}
69
92
  loading={loading}
93
+ offscreenPageRerenderLimit={offscreenPageRerenderLimit}
70
94
  rerenderKey={pageForceRerenderKey}
71
95
  sharedIndex={sharedIndex}
72
96
  />
@@ -8,6 +8,7 @@ export type KeyboardDismissMode = 'none' | 'on-drag';
8
8
  export type PageProps = PropsWithChildren<ViewProps> & {
9
9
  index: number;
10
10
  loading: ViewPagerProps['loading'];
11
+ offscreenPageRerenderLimit: number;
11
12
  sharedIndex: SharedValue<number>;
12
13
  rerenderKey?: ViewPagerProps['pageForceRerenderKey'];
13
14
  };
@@ -44,6 +45,13 @@ export default interface ViewPagerProps extends ComponentProps<{
44
45
  */
45
46
  loading?: Loading;
46
47
 
48
+ /**
49
+ * Mobile only. The number of pages that should be re-rendered to either side of the current page.
50
+ * Changing this value after the component mount has no effect.
51
+ * @default 0
52
+ */
53
+ offscreenPageRerenderLimit?: number;
54
+
47
55
  /**
48
56
  * Callback fired when an index is changed.
49
57
  */
@@ -71,4 +79,9 @@ export default interface ViewPagerProps extends ComponentProps<{
71
79
  * @default 'on-drag'
72
80
  */
73
81
  keyboardDismissMode?: KeyboardDismissMode;
82
+
83
+ /**
84
+ * Unstable API.
85
+ */
86
+ UNSTABLE_sharedIndex?: SharedValue<number>;
74
87
  }> {}
@@ -1,4 +1,4 @@
1
- import React, { Children, forwardRef, useImperativeHandle } from 'react';
1
+ import React, { Children, forwardRef, useImperativeHandle, useCallback } from 'react';
2
2
  import { View } from 'react-native';
3
3
  import { useSharedValue } from 'react-native-reanimated';
4
4
  import { StyleSheet } from '@fountain-ui/core';
@@ -22,18 +22,21 @@ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPag
22
22
  pageComponent = defaultPageComponent,
23
23
  pageForceRerenderKey,
24
24
  style,
25
+ UNSTABLE_sharedIndex,
25
26
  } = props;
26
27
 
27
- const sharedIndex = useSharedValue<number>(initialPage);
28
+ const fallbackSharedIndex = useSharedValue<number>(initialPage);
28
29
 
29
- const setPage = (newIndex: number) => {
30
+ const sharedIndex = UNSTABLE_sharedIndex ?? fallbackSharedIndex;
31
+
32
+ const setPage = useCallback((newIndex: number) => {
30
33
  sharedIndex.value = newIndex;
31
- };
34
+ }, [sharedIndex]);
32
35
 
33
36
  useImperativeHandle(
34
37
  ref,
35
38
  () => ({ setPage }),
36
- [],
39
+ [setPage],
37
40
  );
38
41
 
39
42
  const PageComponent = pageComponent;
@@ -46,6 +49,7 @@ const ViewPager = forwardRef<ViewPagerInstance, ViewPagerProps>(function ViewPag
46
49
  children={child}
47
50
  index={index}
48
51
  loading={loading}
52
+ offscreenPageRerenderLimit={0} // All offscreen pages will not be re-rendered
49
53
  rerenderKey={pageForceRerenderKey}
50
54
  sharedIndex={sharedIndex}
51
55
  />
@@ -1 +1,2 @@
1
1
  export { default } from './ViewPagerNative';
2
+ export { useViewPagerPageState } from './PageStateContext';
@@ -1,2 +1,3 @@
1
1
  export { default } from './ViewPagerWeb';
2
2
  export type { default as ViewPagerProps, ViewPagerInstance } from './ViewPagerProps';
3
+ export { useViewPagerPageState } from './PageStateContext';
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { Loading, PageComponent } from './ViewPagerProps';
3
2
  import ChildrenMemoizedPage from './ChildrenMemoizedPage';
4
3
 
@@ -6,4 +5,6 @@ export const defaultInitialPage: number = 0;
6
5
 
7
6
  export const defaultLoading: Loading = 'lazy';
8
7
 
8
+ export const defaultOffscreenPageRerenderLimit: number = 0;
9
+
9
10
  export const defaultPageComponent: PageComponent = ChildrenMemoizedPage;
@@ -0,0 +1,3 @@
1
+ export default interface Viewability {
2
+ visible: boolean;
3
+ }
@@ -0,0 +1,33 @@
1
+ import React, { useRef } from 'react';
2
+ import { View } from 'react-native';
3
+ import useViewabilityTracker from './useViewabilityTracker';
4
+ import type ViewabilityTrackerViewProps from './ViewabilityTrackerViewProps';
5
+
6
+
7
+ export default function ViewabilityTrackerView(props: ViewabilityTrackerViewProps) {
8
+ const {
9
+ enabled = true,
10
+ measurementIntervalMillis = 1000,
11
+ onViewabilityChange,
12
+ visiblePercentThreshold = 50,
13
+ ...otherProps
14
+ } = props;
15
+
16
+ const viewRef = useRef<View>(null);
17
+
18
+ useViewabilityTracker({
19
+ enabled,
20
+ measurementIntervalMillis,
21
+ onViewabilityChange,
22
+ viewRef,
23
+ visiblePercentThreshold,
24
+ });
25
+
26
+ return (
27
+ <View
28
+ {...otherProps}
29
+ ref={viewRef}
30
+ collapsable={false}
31
+ />
32
+ );
33
+ };
@@ -0,0 +1,25 @@
1
+ import type { ViewProps } from 'react-native';
2
+ import type { OverridableComponentProps } from '@fountain-ui/core';
3
+ import type Viewability from './Viewability';
4
+
5
+ export default interface ViewabilityTrackerViewProps extends OverridableComponentProps<ViewProps, {
6
+ /**
7
+ * @default true
8
+ */
9
+ enabled?: boolean;
10
+
11
+ /**
12
+ * @default 1000
13
+ */
14
+ measurementIntervalMillis?: number;
15
+
16
+ /**
17
+ *
18
+ */
19
+ onViewabilityChange?: (viewability: Viewability) => void;
20
+
21
+ /**
22
+ * @default 50
23
+ */
24
+ visiblePercentThreshold?: number;
25
+ }> {}
@@ -0,0 +1,2 @@
1
+ export { default } from './ViewabilityTrackerView';
2
+ export type { default as ViewabilityTrackerViewProps } from './ViewabilityTrackerViewProps';