react-native-gesture-image-viewer 2.4.0 → 3.0.0-beta.0

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 (133) hide show
  1. package/README.md +5 -3
  2. package/lib/module/GestureViewer.js +88 -146
  3. package/lib/module/GestureViewer.js.map +1 -1
  4. package/lib/module/GestureViewerManager.js +49 -105
  5. package/lib/module/GestureViewerManager.js.map +1 -1
  6. package/lib/module/gestureViewerAnimation.js +18 -0
  7. package/lib/module/gestureViewerAnimation.js.map +1 -0
  8. package/lib/module/gestureViewerPaging.js +66 -0
  9. package/lib/module/gestureViewerPaging.js.map +1 -0
  10. package/lib/module/gestureViewerRenderWindow.js +29 -0
  11. package/lib/module/gestureViewerRenderWindow.js.map +1 -0
  12. package/lib/module/getWebContentProps.js +6 -0
  13. package/lib/module/getWebContentProps.js.map +1 -0
  14. package/lib/module/getWebContentProps.web.js +8 -0
  15. package/lib/module/getWebContentProps.web.js.map +1 -0
  16. package/lib/module/platformTapGestures.js +4 -0
  17. package/lib/module/platformTapGestures.js.map +1 -0
  18. package/lib/module/platformTapGestures.web.js +4 -0
  19. package/lib/module/platformTapGestures.web.js.map +1 -0
  20. package/lib/module/renderWindow.js +160 -0
  21. package/lib/module/renderWindow.js.map +1 -0
  22. package/lib/module/useGestureViewer.js +386 -206
  23. package/lib/module/useGestureViewer.js.map +1 -1
  24. package/lib/module/useGestureViewerController.js +3 -3
  25. package/lib/module/useGestureViewerController.js.map +1 -1
  26. package/lib/module/useGestureViewerManagerBridge.js +85 -0
  27. package/lib/module/useGestureViewerManagerBridge.js.map +1 -0
  28. package/lib/module/useGestureViewerPaging.js +147 -79
  29. package/lib/module/useGestureViewerPaging.js.map +1 -1
  30. package/lib/module/useWebClickHandler.js +7 -0
  31. package/lib/module/useWebClickHandler.js.map +1 -0
  32. package/lib/module/useWebClickHandler.web.js +85 -0
  33. package/lib/module/useWebClickHandler.web.js.map +1 -0
  34. package/lib/module/useWebSingleTapTimer.js +12 -0
  35. package/lib/module/useWebSingleTapTimer.js.map +1 -0
  36. package/lib/module/useWebSingleTapTimer.web.js +25 -0
  37. package/lib/module/useWebSingleTapTimer.web.js.map +1 -0
  38. package/lib/module/utils/index.js +0 -77
  39. package/lib/module/utils/index.js.map +1 -1
  40. package/lib/module/utils/tapZoom.js +4 -3
  41. package/lib/module/utils/tapZoom.js.map +1 -1
  42. package/lib/typescript/src/GestureViewer.d.ts +1 -1
  43. package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
  44. package/lib/typescript/src/GestureViewerManager.d.ts +20 -20
  45. package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
  46. package/lib/typescript/src/gestureViewerAnimation.d.ts +15 -0
  47. package/lib/typescript/src/gestureViewerAnimation.d.ts.map +1 -0
  48. package/lib/typescript/src/gestureViewerPaging.d.ts +13 -0
  49. package/lib/typescript/src/gestureViewerPaging.d.ts.map +1 -0
  50. package/lib/typescript/src/gestureViewerRenderWindow.d.ts +15 -0
  51. package/lib/typescript/src/gestureViewerRenderWindow.d.ts.map +1 -0
  52. package/lib/typescript/src/getWebContentProps.d.ts +3 -0
  53. package/lib/typescript/src/getWebContentProps.d.ts.map +1 -0
  54. package/lib/typescript/src/getWebContentProps.web.d.ts +5 -0
  55. package/lib/typescript/src/getWebContentProps.web.d.ts.map +1 -0
  56. package/lib/typescript/src/platformTapGestures.d.ts +2 -0
  57. package/lib/typescript/src/platformTapGestures.d.ts.map +1 -0
  58. package/lib/typescript/src/platformTapGestures.web.d.ts +2 -0
  59. package/lib/typescript/src/platformTapGestures.web.d.ts.map +1 -0
  60. package/lib/typescript/src/renderWindow.d.ts +44 -0
  61. package/lib/typescript/src/renderWindow.d.ts.map +1 -0
  62. package/lib/typescript/src/types.d.ts +36 -53
  63. package/lib/typescript/src/types.d.ts.map +1 -1
  64. package/lib/typescript/src/useGestureViewer.d.ts +9 -9
  65. package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
  66. package/lib/typescript/src/useGestureViewerController.d.ts +1 -1
  67. package/lib/typescript/src/useGestureViewerManagerBridge.d.ts +28 -0
  68. package/lib/typescript/src/useGestureViewerManagerBridge.d.ts.map +1 -0
  69. package/lib/typescript/src/useGestureViewerPaging.d.ts +31 -2
  70. package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
  71. package/lib/typescript/src/useWebClickHandler.d.ts +35 -0
  72. package/lib/typescript/src/useWebClickHandler.d.ts.map +1 -0
  73. package/lib/typescript/src/useWebClickHandler.web.d.ts +36 -0
  74. package/lib/typescript/src/useWebClickHandler.web.d.ts.map +1 -0
  75. package/lib/typescript/src/useWebSingleTapTimer.d.ts +6 -0
  76. package/lib/typescript/src/useWebSingleTapTimer.d.ts.map +1 -0
  77. package/lib/typescript/src/useWebSingleTapTimer.web.d.ts +6 -0
  78. package/lib/typescript/src/useWebSingleTapTimer.web.d.ts.map +1 -0
  79. package/lib/typescript/src/utils/index.d.ts +0 -20
  80. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  81. package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/src/GestureViewer.tsx +104 -206
  84. package/src/GestureViewerManager.ts +77 -122
  85. package/src/gestureViewerAnimation.ts +18 -0
  86. package/src/gestureViewerPaging.ts +116 -0
  87. package/src/gestureViewerRenderWindow.ts +56 -0
  88. package/src/getWebContentProps.ts +5 -0
  89. package/src/getWebContentProps.web.ts +5 -0
  90. package/src/platformTapGestures.ts +1 -0
  91. package/src/platformTapGestures.web.ts +1 -0
  92. package/src/renderWindow.ts +253 -0
  93. package/src/types.ts +34 -100
  94. package/src/useGestureViewer.ts +557 -271
  95. package/src/useGestureViewerController.ts +3 -3
  96. package/src/useGestureViewerManagerBridge.ts +118 -0
  97. package/src/useGestureViewerPaging.ts +277 -114
  98. package/src/useWebClickHandler.ts +44 -0
  99. package/src/useWebClickHandler.web.ts +146 -0
  100. package/src/useWebSingleTapTimer.ts +10 -0
  101. package/src/useWebSingleTapTimer.web.ts +30 -0
  102. package/src/utils/index.ts +0 -102
  103. package/src/utils/tapZoom.ts +4 -3
  104. package/lib/module/WebPagingFixStyle.js +0 -25
  105. package/lib/module/WebPagingFixStyle.js.map +0 -1
  106. package/lib/module/scheduleInitialScroll.js +0 -21
  107. package/lib/module/scheduleInitialScroll.js.map +0 -1
  108. package/lib/module/useGestureViewerPaging.types.js +0 -4
  109. package/lib/module/useGestureViewerPaging.types.js.map +0 -1
  110. package/lib/module/useGestureViewerPaging.web.js +0 -291
  111. package/lib/module/useGestureViewerPaging.web.js.map +0 -1
  112. package/lib/module/utils/FlashList.js +0 -9
  113. package/lib/module/utils/FlashList.js.map +0 -1
  114. package/lib/module/utils/webScroll.js +0 -70
  115. package/lib/module/utils/webScroll.js.map +0 -1
  116. package/lib/typescript/src/WebPagingFixStyle.d.ts +0 -6
  117. package/lib/typescript/src/WebPagingFixStyle.d.ts.map +0 -1
  118. package/lib/typescript/src/scheduleInitialScroll.d.ts +0 -3
  119. package/lib/typescript/src/scheduleInitialScroll.d.ts.map +0 -1
  120. package/lib/typescript/src/useGestureViewerPaging.types.d.ts +0 -42
  121. package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +0 -1
  122. package/lib/typescript/src/useGestureViewerPaging.web.d.ts +0 -3
  123. package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +0 -1
  124. package/lib/typescript/src/utils/FlashList.d.ts +0 -2
  125. package/lib/typescript/src/utils/FlashList.d.ts.map +0 -1
  126. package/lib/typescript/src/utils/webScroll.d.ts +0 -18
  127. package/lib/typescript/src/utils/webScroll.d.ts.map +0 -1
  128. package/src/WebPagingFixStyle.tsx +0 -25
  129. package/src/scheduleInitialScroll.ts +0 -36
  130. package/src/useGestureViewerPaging.types.ts +0 -42
  131. package/src/useGestureViewerPaging.web.ts +0 -453
  132. package/src/utils/FlashList.ts +0 -7
  133. package/src/utils/webScroll.ts +0 -96
@@ -1,17 +1,27 @@
1
1
  "use strict";
2
2
 
3
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
- import { Platform, useWindowDimensions } from 'react-native';
3
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
4
+ import { useWindowDimensions } from 'react-native';
5
5
  import { Gesture } from 'react-native-gesture-handler';
6
- import { Easing, interpolate, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
6
+ import { Easing, cancelAnimation, interpolate, useAnimatedReaction, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
7
7
  import { scheduleOnRN } from 'react-native-worklets';
8
+ import { PAGE_SPRING_CONFIG, SWIPE_THRESHOLD_RATIO, SWIPE_VELOCITY_THRESHOLD } from "./gestureViewerAnimation.js";
9
+ import { normalizeHorizontalSwipeThreshold } from "./gestureViewerPaging.js";
8
10
  import { registry } from "./GestureViewerRegistry.js";
9
- import { scheduleInitialScroll } from "./scheduleInitialScroll.js";
10
- import { useGestureViewerPaging } from './useGestureViewerPaging';
11
- import { createBoundsConstraint, createScrollAction } from "./utils/index.js";
11
+ import { resolveGestureViewerRenderWindow } from "./gestureViewerRenderWindow.js";
12
+ import { enableNativeTapGestures } from './platformTapGestures';
13
+ import { getLogicalIndex, getPageStride, getVirtualIndexForLogicalIndex, normalizePageSpacing, clampIndex, resolveNavigation, shouldRunNavigationDuringTransition } from "./renderWindow.js";
14
+ import { useGestureViewerManagerBridge } from "./useGestureViewerManagerBridge.js";
15
+ import { useGestureViewerPaging } from "./useGestureViewerPaging.js";
16
+ import { useWebClickHandler } from './useWebClickHandler';
17
+ import { useWebSingleTapTimer } from './useWebSingleTapTimer';
18
+ import { createBoundsConstraint } from "./utils/index.js";
12
19
  import { getDismissDistance, shouldDismissByDirection } from "./utils/dismiss.js";
13
20
  import { applyTapZoomAtPoint } from "./utils/tapZoom.js";
14
21
  import { calculateFocalPointTranslation, shouldAcceptFocalPoint } from "./utils/zoom.js";
22
+ const isValidTriggerRect = rect => {
23
+ return !!rect && rect.width > 0 && rect.height > 0;
24
+ };
15
25
  export const useGestureViewer = ({
16
26
  data,
17
27
  initialIndex = 0,
@@ -21,11 +31,12 @@ export const useGestureViewer = ({
21
31
  dismiss,
22
32
  enableDoubleTapZoom = true,
23
33
  enablePinchZoom = true,
24
- enableHorizontalSwipe = true,
34
+ horizontalSwipe,
25
35
  enablePanWhenZoomed = true,
26
36
  enableLoop = false,
27
37
  maxZoomScale = 2,
28
- itemSpacing = 0,
38
+ pageSpacing = 0,
39
+ windowSize = 3,
29
40
  height: customHeight,
30
41
  id = 'default',
31
42
  onDismissStart,
@@ -39,43 +50,59 @@ export const useGestureViewer = ({
39
50
  } = useWindowDimensions();
40
51
  const width = customWidth || screenWidth;
41
52
  const height = customHeight || screenHeight;
53
+ const dataLength = data?.length || 0;
54
+ const normalizedPageSpacing = normalizePageSpacing(pageSpacing);
55
+ const pageStride = getPageStride(width, normalizedPageSpacing);
56
+ const initialCurrentIndex = clampIndex(initialIndex, dataLength);
57
+ const horizontalSwipeEnabled = horizontalSwipe?.enabled ?? true;
58
+ const horizontalSwipeDistanceThresholdRatio = normalizeHorizontalSwipeThreshold(horizontalSwipe?.distanceThresholdRatio, SWIPE_THRESHOLD_RATIO);
59
+ const horizontalSwipeVelocityThreshold = normalizeHorizontalSwipeThreshold(horizontalSwipe?.velocityThreshold, SWIPE_VELOCITY_THRESHOLD);
42
60
  const dismissGestureRef = useRef(undefined);
43
61
  const [isZoomed, setIsZoomed] = useState(false);
44
62
  const [isRotated, setIsRotated] = useState(false);
45
63
  const [isPinching, setIsPinching] = useState(false);
46
64
  const [shouldStartTriggerAnimation, setShouldStartTriggerAnimation] = useState(false);
47
- const [manager, setManager] = useState(null);
48
- const [currentIndex, setCurrentIndex] = useState(initialIndex);
49
- const [activeTriggerNode, setActiveTriggerNode] = useState(null);
50
- const listRef = useRef(null);
65
+ const [currentIndex, setCurrentIndex] = useState(initialCurrentIndex);
66
+ const [centerVirtualIndex, setCenterVirtualIndex] = useState(initialCurrentIndex);
67
+ const [activeTriggerNode, setActiveTriggerNode] = useState(() => registry.getActiveTriggerNode(id));
68
+ const [isTriggerOpening, setIsTriggerOpening] = useState(() => !!registry.getActiveTriggerNode(id));
51
69
  const triggerRectRef = useRef(null);
52
- const pendingIndexRef = useRef(initialIndex);
70
+ const pendingIndexRef = useRef(initialCurrentIndex);
71
+ const currentIndexRef = useRef(initialCurrentIndex);
72
+ const centerVirtualIndexRef = useRef(initialCurrentIndex);
73
+ const previousInitialIndexRef = useRef(initialIndex);
74
+ const hasResolvedInitialIndexRef = useRef(dataLength > 0);
53
75
  const onAnimationCompleteRef = useRef(triggerAnimation?.onAnimationComplete);
54
76
  const onSingleTapRef = useRef(onSingleTap);
55
77
  const dataRef = useRef(data);
56
- const managerRef = useRef(manager);
57
- const isValidTriggerRect = useCallback(rect => {
58
- return !!rect && rect.width > 0 && rect.height > 0;
59
- }, []);
78
+ const dataLengthRef = useRef(dataLength);
79
+ const enableLoopRef = useRef(enableLoop);
80
+ const managerRef = useRef(null);
81
+ const isZoomedRef = useRef(isZoomed);
82
+ const isRotatedRef = useRef(isRotated);
83
+ const isPinchingRef = useRef(isPinching);
60
84
  const initialTranslateY = useSharedValue(0);
61
85
  const initialTranslateX = useSharedValue(0);
62
86
  const startScale = useSharedValue(1);
63
87
  const translateY = useSharedValue(0);
64
88
  const translateX = useSharedValue(0);
65
89
  const scale = useSharedValue(1);
66
- const backdropOpacity = useSharedValue(1);
67
90
  const rotation = useSharedValue(0);
68
91
  const triggerScale = useSharedValue(1);
69
92
  const triggerTranslateX = useSharedValue(0);
70
93
  const triggerTranslateY = useSharedValue(0);
71
- const triggerOpacity = useSharedValue(1);
94
+ const triggerOpacity = useSharedValue(activeTriggerNode ? 0 : 1);
95
+ const hasZoomChangeListeners = useSharedValue(false);
96
+ const hasRotationChangeListeners = useSharedValue(false);
72
97
  const lastFocalX = useSharedValue(0);
73
98
  const lastFocalY = useSharedValue(0);
74
99
  const startFocalX = useSharedValue(0);
75
100
  const startFocalY = useSharedValue(0);
76
101
  const hasActiveFocal = useSharedValue(false);
77
- const dataLength = data?.length || 0;
78
- const previousDataLengthRef = useRef(dataLength);
102
+ const {
103
+ clearPendingWebSingleTap,
104
+ scheduleWebSingleTap
105
+ } = useWebSingleTapTimer();
79
106
  const animationConfig = useMemo(() => ({
80
107
  duration: triggerAnimation?.duration ?? 300,
81
108
  easing: triggerAnimation?.easing ?? Easing.bezier(0.25, 0.1, 0.25, 1),
@@ -88,20 +115,22 @@ export const useGestureViewer = ({
88
115
  resistance: dismiss?.resistance ?? 2,
89
116
  threshold: dismiss?.threshold ?? 80
90
117
  }), [dismiss?.direction, dismiss?.enabled, dismiss?.threshold, dismiss?.resistance, dismiss?.fadeBackdrop]);
91
- const adjustedInitialIndex = useMemo(() => {
92
- if (enableLoop && dataLength > 1) {
93
- return initialIndex + 1;
94
- }
95
- return initialIndex;
96
- }, [enableLoop, dataLength, initialIndex]);
118
+ const {
119
+ centerVirtualIndex: renderCenterVirtualIndex,
120
+ currentIndex: renderCurrentIndex,
121
+ slots: renderWindowSlots
122
+ } = resolveGestureViewerRenderWindow({
123
+ centerVirtualIndex,
124
+ currentIndex,
125
+ data,
126
+ enableLoop,
127
+ isTriggerOpening,
128
+ windowSize
129
+ });
97
130
  const constrainTranslation = useMemo(() => createBoundsConstraint({
98
131
  height,
99
132
  width
100
133
  }), [width, height]);
101
- const scrollTo = useCallback((index, animated) => {
102
- const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
103
- return scrollAction.scrollTo(index, animated);
104
- }, [width, itemSpacing]);
105
134
  const resetTransformState = useCallback(() => {
106
135
  translateX.set(withTiming(0));
107
136
  translateY.set(withTiming(0));
@@ -111,114 +140,240 @@ export const useGestureViewer = ({
111
140
  scale.set(withTiming(1));
112
141
  rotation.set(0);
113
142
  }, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
114
- const syncPendingIndex = useCallback(nextIndex => {
115
- if (nextIndex < 0 || nextIndex >= dataLength) {
143
+ const resetTransformStateImmediately = useCallback(() => {
144
+ cancelAnimation(translateX);
145
+ cancelAnimation(translateY);
146
+ cancelAnimation(initialTranslateX);
147
+ cancelAnimation(initialTranslateY);
148
+ cancelAnimation(startScale);
149
+ cancelAnimation(scale);
150
+ cancelAnimation(rotation);
151
+ translateX.set(0);
152
+ translateY.set(0);
153
+ initialTranslateX.set(0);
154
+ initialTranslateY.set(0);
155
+ startScale.set(1);
156
+ scale.set(1);
157
+ rotation.set(0);
158
+ }, [initialTranslateX, initialTranslateY, rotation, scale, startScale, translateX, translateY]);
159
+ const commitCurrentIndex = useCallback(nextIndex => {
160
+ pendingIndexRef.current = nextIndex;
161
+ currentIndexRef.current = nextIndex;
162
+ setCurrentIndex(nextIndex);
163
+ managerRef.current?.notifyStateChange();
164
+ }, []);
165
+ const commitVirtualIndexOnly = useCallback(nextVirtualIndex => {
166
+ const nextLogicalIndex = getLogicalIndex(nextVirtualIndex, dataLengthRef.current, enableLoopRef.current);
167
+ if (nextLogicalIndex === null) {
116
168
  return;
117
169
  }
118
- pendingIndexRef.current = nextIndex;
119
- }, [dataLength]);
120
- const syncCurrentIndex = useCallback(nextIndex => {
121
- if (!manager || nextIndex < 0 || nextIndex >= dataLength) {
170
+ centerVirtualIndexRef.current = nextVirtualIndex;
171
+ setCenterVirtualIndex(nextVirtualIndex);
172
+ commitCurrentIndex(nextLogicalIndex);
173
+ }, [commitCurrentIndex]);
174
+ const {
175
+ animateToVirtualPage,
176
+ horizontalPagingGesture,
177
+ isPageTransitioningRef,
178
+ pageTransitionLocked,
179
+ snapToVirtualPage,
180
+ visualPage
181
+ } = useGestureViewerPaging({
182
+ centerVirtualIndex,
183
+ clearPendingWebSingleTap,
184
+ commitVirtualIndexOnly,
185
+ currentIndex,
186
+ dataLength,
187
+ enableLoop,
188
+ horizontalSwipeDistanceThresholdRatio,
189
+ horizontalSwipeEnabled,
190
+ horizontalSwipeVelocityThreshold,
191
+ initialPage: initialCurrentIndex,
192
+ isPinching,
193
+ isRotated,
194
+ isTriggerOpening,
195
+ isZoomed,
196
+ pageStride,
197
+ width
198
+ });
199
+ const navigateToIndex = useCallback((targetIndex, options, preferredDirection) => {
200
+ const resolution = resolveNavigation({
201
+ currentIndex: currentIndexRef.current,
202
+ dataLength: dataLengthRef.current,
203
+ enableLoop: enableLoopRef.current,
204
+ preferredDirection,
205
+ targetIndex
206
+ });
207
+ if (resolution.kind === 'noop') {
122
208
  return;
123
209
  }
124
- pendingIndexRef.current = nextIndex;
125
- const managerCurrentIndex = manager.getState().currentIndex;
126
- if (nextIndex === managerCurrentIndex) {
210
+ if (isPageTransitioningRef.current && !shouldRunNavigationDuringTransition(resolution, options)) {
211
+ return;
212
+ }
213
+ const targetVirtualIndex = resolution.kind === 'step' ? centerVirtualIndexRef.current + resolution.direction : getVirtualIndexForLogicalIndex(resolution.targetIndex, centerVirtualIndexRef.current, dataLengthRef.current, enableLoopRef.current);
214
+ if (targetVirtualIndex === null) {
215
+ return;
216
+ }
217
+ if (resolution.kind !== 'step' || options?.animated === false) {
218
+ resetTransformStateImmediately();
219
+ snapToVirtualPage(targetVirtualIndex);
127
220
  return;
128
221
  }
129
- manager.setCurrentIndex(nextIndex);
130
- manager.notifyStateChange();
131
222
  resetTransformState();
132
- }, [dataLength, manager, resetTransformState]);
223
+ animateToVirtualPage(targetVirtualIndex);
224
+ }, [animateToVirtualPage, isPageTransitioningRef, resetTransformStateImmediately, resetTransformState, snapToVirtualPage]);
225
+ const navigateByDirection = useCallback(direction => {
226
+ const currentDataLength = dataLengthRef.current;
227
+ if (currentDataLength <= 0) {
228
+ return;
229
+ }
230
+ const nextIndex = currentIndexRef.current + direction;
231
+ if (nextIndex >= 0 && nextIndex < currentDataLength) {
232
+ navigateToIndex(nextIndex, undefined, direction);
233
+ return;
234
+ }
235
+ if (!enableLoopRef.current || currentDataLength <= 1) {
236
+ return;
237
+ }
238
+ navigateToIndex(direction === 1 ? 0 : currentDataLength - 1, undefined, direction);
239
+ }, [navigateToIndex]);
240
+ const navigateToNext = useCallback(() => {
241
+ navigateByDirection(1);
242
+ }, [navigateByDirection]);
243
+ const navigateToPrevious = useCallback(() => {
244
+ navigateByDirection(-1);
245
+ }, [navigateByDirection]);
246
+ useGestureViewerManagerBridge({
247
+ currentIndexRef,
248
+ dataLengthRef,
249
+ goToIndex: navigateToIndex,
250
+ goToNext: navigateToNext,
251
+ goToPrevious: navigateToPrevious,
252
+ hasRotationChangeListeners,
253
+ hasZoomChangeListeners,
254
+ height,
255
+ id,
256
+ managerRef,
257
+ maxZoomScale,
258
+ rotation,
259
+ scale,
260
+ translateX,
261
+ translateY,
262
+ width
263
+ });
133
264
  const emitZoomChange = useCallback((currentScale, prevScale) => {
134
265
  managerRef.current?.emitZoomChange(currentScale, prevScale);
135
266
  }, []);
136
267
  const emitRotationChange = useCallback((currentRotation, prevRotation) => {
137
268
  managerRef.current?.emitRotationChange(currentRotation, prevRotation);
138
269
  }, []);
139
- const onAnimationComplete = useCallback(() => {
270
+ const finishTriggerOpening = useCallback(() => {
271
+ setIsTriggerOpening(false);
140
272
  onAnimationCompleteRef.current?.();
141
273
  }, []);
142
274
  useAnimatedReaction(() => scale.get(), (currentScale, previousScale) => {
143
- if (currentScale !== previousScale) {
275
+ if (currentScale !== previousScale && hasZoomChangeListeners.get()) {
144
276
  scheduleOnRN(emitZoomChange, currentScale, previousScale);
145
277
  }
146
- scheduleOnRN(setIsZoomed, currentScale > 1);
278
+ const currentIsZoomed = currentScale > 1;
279
+ const previousIsZoomed = (previousScale ?? 1) > 1;
280
+ if (previousScale === null || currentIsZoomed !== previousIsZoomed) {
281
+ scheduleOnRN(setIsZoomed, currentIsZoomed);
282
+ }
147
283
  });
148
284
  useAnimatedReaction(() => rotation.get(), (currentRotation, previousRotation) => {
149
- if (currentRotation !== previousRotation) {
285
+ if (currentRotation !== previousRotation && hasRotationChangeListeners.get()) {
150
286
  scheduleOnRN(emitRotationChange, currentRotation, previousRotation);
151
287
  }
152
- scheduleOnRN(setIsRotated, currentRotation % 360 !== 0);
288
+ const currentIsRotated = currentRotation % 360 !== 0;
289
+ const previousIsRotated = (previousRotation ?? 0) % 360 !== 0;
290
+ if (previousRotation === null || currentIsRotated !== previousIsRotated) {
291
+ scheduleOnRN(setIsRotated, currentIsRotated);
292
+ }
153
293
  });
154
294
  useEffect(() => {
155
- let unsubscribe;
156
- const unsubscribeFromRegistry = registry.subscribeToManager(id, managerInstance => {
157
- setManager(managerInstance);
158
- unsubscribe = managerInstance?.subscribe(state => {
159
- pendingIndexRef.current = state.currentIndex;
160
- setCurrentIndex(state.currentIndex);
161
- });
295
+ return registry.subscribeToActiveTrigger(id, node => {
296
+ setActiveTriggerNode(node);
297
+ if (node) {
298
+ setIsTriggerOpening(true);
299
+ }
162
300
  });
163
- return () => {
164
- unsubscribeFromRegistry();
165
- unsubscribe?.();
166
- };
167
301
  }, [id]);
168
302
  useEffect(() => {
169
- return registry.subscribeToActiveTrigger(id, setActiveTriggerNode);
170
- }, [id]);
303
+ dataRef.current = data;
304
+ }, [data]);
171
305
  useEffect(() => {
172
- pendingIndexRef.current = initialIndex;
173
- if (!manager) {
174
- return;
175
- }
176
- manager.setDataLength(dataLength);
177
- manager.setEnableHorizontalSwipe(enableHorizontalSwipe);
178
- manager.setCurrentIndex(initialIndex);
179
- manager.setWidth(width + itemSpacing);
180
- manager.setHeight(height);
181
- manager.setZoomSharedValues(scale, translateX, translateY, maxZoomScale);
182
- manager.setResetTransformCallback(resetTransformState);
183
- manager.setRotation(rotation);
184
- manager.setEnableLoop(enableLoop);
185
- manager.notifyStateChange();
186
- }, [dataLength, enableHorizontalSwipe, initialIndex, manager, width, itemSpacing, maxZoomScale, enableLoop, scale, height, resetTransformState, translateX, translateY, rotation]);
306
+ isZoomedRef.current = isZoomed;
307
+ }, [isZoomed]);
187
308
  useEffect(() => {
188
- if (!manager || !listRef.current) {
189
- return;
190
- }
191
- manager.setListRef(listRef.current);
192
- }, [manager]);
309
+ isRotatedRef.current = isRotated;
310
+ }, [isRotated]);
193
311
  useEffect(() => {
194
- const hasDataLengthChanged = previousDataLengthRef.current !== dataLength;
195
- const hasValidInitialIndex = initialIndex >= 0 && initialIndex < dataLength;
196
- previousDataLengthRef.current = dataLength;
197
- translateY.set(0);
198
- translateX.set(0);
199
- scale.set(1);
200
- backdropOpacity.set(1);
201
- startScale.set(1);
202
- rotation.set(0);
203
- if (!hasValidInitialIndex || !hasDataLengthChanged && adjustedInitialIndex <= 0 || !listRef.current) {
204
- return;
205
- }
206
- return scheduleInitialScroll(() => {
207
- scrollTo(adjustedInitialIndex, false);
208
- });
209
- }, [adjustedInitialIndex, dataLength, initialIndex, translateY, backdropOpacity, translateX, scale, startScale, rotation, scrollTo]);
312
+ isPinchingRef.current = isPinching;
313
+ }, [isPinching]);
314
+ useEffect(() => {
315
+ onSingleTapRef.current = onSingleTap;
316
+ }, [onSingleTap]);
210
317
  useEffect(() => {
211
318
  onAnimationCompleteRef.current = triggerAnimation?.onAnimationComplete;
212
319
  }, [triggerAnimation?.onAnimationComplete]);
320
+ useLayoutEffect(() => {
321
+ const initialIndexChanged = previousInitialIndexRef.current !== initialIndex;
322
+ const shouldResolveInitialIndex = initialIndexChanged || !hasResolvedInitialIndexRef.current && dataLength > 0;
323
+ previousInitialIndexRef.current = initialIndex;
324
+ dataLengthRef.current = dataLength;
325
+ enableLoopRef.current = enableLoop;
326
+ hasResolvedInitialIndexRef.current = dataLength > 0;
327
+ const nextIndex = shouldResolveInitialIndex ? clampIndex(initialIndex, dataLength) : clampIndex(currentIndexRef.current, dataLength);
328
+ const nextVirtualIndex = getVirtualIndexForLogicalIndex(nextIndex, centerVirtualIndexRef.current, dataLength, enableLoop) ?? nextIndex;
329
+ const previousIndex = currentIndexRef.current;
330
+ const previousVirtualIndex = centerVirtualIndexRef.current;
331
+ const shouldSyncVirtualPage = isPageTransitioningRef.current || previousVirtualIndex !== nextVirtualIndex;
332
+ const shouldResetTransform = initialIndexChanged || previousIndex !== nextIndex || shouldSyncVirtualPage;
333
+ if (shouldSyncVirtualPage) {
334
+ snapToVirtualPage(nextVirtualIndex);
335
+ if (dataLength === 0) {
336
+ centerVirtualIndexRef.current = nextVirtualIndex;
337
+ setCenterVirtualIndex(nextVirtualIndex);
338
+ }
339
+ }
340
+ const shouldCommitIndexOutsidePaging = !shouldSyncVirtualPage || dataLength === 0;
341
+ if (shouldCommitIndexOutsidePaging && previousIndex !== nextIndex) {
342
+ commitCurrentIndex(nextIndex);
343
+ }
344
+ if (shouldCommitIndexOutsidePaging && previousIndex === nextIndex) {
345
+ managerRef.current?.notifyStateChange();
346
+ }
347
+ if (shouldResetTransform) {
348
+ resetTransformState();
349
+ }
350
+ }, [commitCurrentIndex, dataLength, enableLoop, initialIndex, isPageTransitioningRef, resetTransformState, snapToVirtualPage]);
213
351
  useEffect(() => {
214
- dataRef.current = data;
215
- }, [data]);
216
- useEffect(() => {
217
- managerRef.current = manager;
218
- }, [manager]);
352
+ return () => {
353
+ triggerRectRef.current = null;
354
+ clearPendingWebSingleTap();
355
+ };
356
+ }, [clearPendingWebSingleTap]);
219
357
  useEffect(() => {
220
- onSingleTapRef.current = onSingleTap;
221
- }, [onSingleTap]);
358
+ if (!autoPlay || dataLength <= 1 || isTriggerOpening || isZoomed || isRotated || isPinching || !enableLoop && currentIndex >= dataLength - 1) {
359
+ return;
360
+ }
361
+ const interval = Math.max(250, Math.floor(autoPlayInterval || 0));
362
+ if (!Number.isFinite(interval)) {
363
+ return;
364
+ }
365
+ const timer = setInterval(() => {
366
+ if (isPageTransitioningRef.current || isZoomedRef.current || isRotatedRef.current || isPinchingRef.current) {
367
+ return;
368
+ }
369
+ const nextIndex = currentIndexRef.current >= dataLengthRef.current - 1 ? enableLoopRef.current ? 0 : null : currentIndexRef.current + 1;
370
+ if (nextIndex === null) {
371
+ return;
372
+ }
373
+ navigateToIndex(nextIndex);
374
+ }, interval);
375
+ return () => clearInterval(timer);
376
+ }, [autoPlay, autoPlayInterval, currentIndex, dataLength, enableLoop, isPinching, isRotated, isTriggerOpening, isZoomed, isPageTransitioningRef, navigateToIndex]);
222
377
  useEffect(() => {
223
378
  if (shouldStartTriggerAnimation && triggerRectRef.current) {
224
379
  const startX = triggerRectRef.current.x + triggerRectRef.current.width / 2 - width / 2;
@@ -230,7 +385,7 @@ export const useGestureViewer = ({
230
385
  triggerOpacity.set(0);
231
386
  triggerScale.set(withTiming(1, animationConfig, finished => {
232
387
  if (finished) {
233
- scheduleOnRN(onAnimationComplete);
388
+ scheduleOnRN(finishTriggerOpening);
234
389
  }
235
390
  }));
236
391
  triggerTranslateX.set(withTiming(0, animationConfig));
@@ -242,9 +397,15 @@ export const useGestureViewer = ({
242
397
  }));
243
398
  setShouldStartTriggerAnimation(false);
244
399
  }
245
- }, [shouldStartTriggerAnimation, animationConfig, width, height, triggerOpacity, triggerScale, triggerTranslateX, triggerTranslateY, onAnimationComplete]);
400
+ }, [shouldStartTriggerAnimation, animationConfig, width, height, triggerOpacity, triggerScale, triggerTranslateX, triggerTranslateY, finishTriggerOpening]);
246
401
  useEffect(() => {
247
- if (!activeTriggerNode || typeof activeTriggerNode.measure !== 'function') {
402
+ if (!activeTriggerNode) {
403
+ return;
404
+ }
405
+ if (typeof activeTriggerNode.measure !== 'function') {
406
+ setIsTriggerOpening(false);
407
+ triggerOpacity.set(1);
408
+ registry.clearActiveTriggerNode(id);
248
409
  return;
249
410
  }
250
411
  activeTriggerNode.measure((_x, _y, measuredWidth, measuredHeight, pageX, pageY) => {
@@ -255,6 +416,8 @@ export const useGestureViewer = ({
255
416
  y: pageY
256
417
  };
257
418
  if (!isValidTriggerRect(nextTriggerRect)) {
419
+ setIsTriggerOpening(false);
420
+ triggerOpacity.set(1);
258
421
  registry.clearActiveTriggerNode(id);
259
422
  return;
260
423
  }
@@ -263,12 +426,7 @@ export const useGestureViewer = ({
263
426
  setShouldStartTriggerAnimation(true);
264
427
  registry.clearActiveTriggerNode(id);
265
428
  });
266
- }, [activeTriggerNode, id, isValidTriggerRect, triggerOpacity]);
267
- useEffect(() => {
268
- return () => {
269
- triggerRectRef.current = null;
270
- };
271
- }, []);
429
+ }, [activeTriggerNode, id, triggerOpacity]);
272
430
  const animateDismissToRect = useCallback(rect => {
273
431
  const endX = rect.x + rect.width / 2 - width / 2;
274
432
  const endY = rect.y + rect.height / 2 - height / 2;
@@ -288,6 +446,9 @@ export const useGestureViewer = ({
288
446
  }
289
447
  }, [onDismiss]);
290
448
  const handleDismiss = useCallback(() => {
449
+ if (isPageTransitioningRef.current) {
450
+ return;
451
+ }
291
452
  onDismissStart?.();
292
453
  const dismissTargetIndex = pendingIndexRef.current;
293
454
  const indexedTriggerNode = registry.getIndexedTriggerNode(id, dismissTargetIndex);
@@ -316,30 +477,98 @@ export const useGestureViewer = ({
316
477
  return;
317
478
  }
318
479
  dismissWithoutTrigger();
319
- }, [animateDismissToRect, dismissWithoutTrigger, id, isValidTriggerRect, onDismissStart]);
480
+ }, [animateDismissToRect, dismissWithoutTrigger, id, isPageTransitioningRef, onDismissStart]);
320
481
  const dismissGesture = useMemo(() => {
321
- const canDismiss = !isZoomed && dismissOptions.enabled;
482
+ const canDismiss = !isTriggerOpening && !isZoomed && dismissOptions.enabled;
322
483
  return Gesture.Pan().minDistance(10).averageTouches(true).activeCursor('grabbing').activeOffsetY([-10, 10]).failOffsetX([-10, 10]).withRef(dismissGestureRef).enabled(canDismiss).onUpdate(event => {
484
+ if (pageTransitionLocked.get()) {
485
+ return;
486
+ }
323
487
  translateY.set(event.translationY / dismissOptions.resistance);
324
488
  }).onEnd(event => {
489
+ if (pageTransitionLocked.get()) {
490
+ translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
491
+ return;
492
+ }
325
493
  if (canDismiss && shouldDismissByDirection(event.translationY, dismissOptions.threshold, dismissOptions.direction)) {
326
494
  scheduleOnRN(handleDismiss);
327
495
  return;
328
496
  }
329
- translateY.set(withSpring(0, {
330
- damping: 50,
331
- energyThreshold: 6e-9,
332
- mass: 4,
333
- overshootClamping: false,
334
- stiffness: 600
335
- }));
497
+ translateY.set(withSpring(0, PAGE_SPRING_CONFIG));
336
498
  });
337
- }, [translateY, dismissOptions, handleDismiss, isZoomed]);
499
+ }, [translateY, dismissOptions, handleDismiss, isTriggerOpening, isZoomed, pageTransitionLocked]);
500
+ const getCurrentTapTarget = useCallback(() => {
501
+ const index = pendingIndexRef.current;
502
+ if (index < 0 || index >= dataRef.current.length || !(index in dataRef.current)) {
503
+ return null;
504
+ }
505
+ return {
506
+ index,
507
+ item: dataRef.current[index]
508
+ };
509
+ }, []);
510
+ const isWebInteractionLocked = useCallback(() => isPageTransitioningRef.current || pageTransitionLocked.get(), [isPageTransitioningRef, pageTransitionLocked]);
511
+ const emitSingleTap = useCallback((x, y, tapTarget) => {
512
+ if (isPageTransitioningRef.current) {
513
+ return;
514
+ }
515
+ const target = tapTarget ?? getCurrentTapTarget();
516
+ if (!target) {
517
+ return;
518
+ }
519
+ const {
520
+ index: targetIndex,
521
+ item: targetItem
522
+ } = target;
523
+ if (targetIndex < 0) {
524
+ return;
525
+ }
526
+ managerRef.current?.emitTap({
527
+ kind: 'single',
528
+ x,
529
+ y,
530
+ index: targetIndex
531
+ });
532
+ onSingleTapRef.current?.({
533
+ x,
534
+ y,
535
+ index: targetIndex,
536
+ item: targetItem
537
+ });
538
+ }, [getCurrentTapTarget, isPageTransitioningRef]);
539
+ const singleTapGesture = useMemo(() => Gesture.Tap().enabled(enableNativeTapGestures).numberOfTaps(1).onEnd((event, success) => {
540
+ if (!success || pageTransitionLocked.get()) {
541
+ return;
542
+ }
543
+ scheduleOnRN(emitSingleTap, event.x, event.y);
544
+ }), [emitSingleTap, pageTransitionLocked]);
545
+ const doubleTapGesture = useMemo(() => Gesture.Tap().enabled(enableDoubleTapZoom && enableNativeTapGestures).numberOfTaps(2).onEnd(event => {
546
+ if (pageTransitionLocked.get()) {
547
+ return;
548
+ }
549
+ applyTapZoomAtPoint({
550
+ x: event.x,
551
+ y: event.y,
552
+ width,
553
+ height,
554
+ maxZoomScale,
555
+ scale,
556
+ translateX,
557
+ translateY
558
+ });
559
+ }), [enableDoubleTapZoom, height, maxZoomScale, pageTransitionLocked, scale, translateX, translateY, width]);
560
+ const tapGesture = useMemo(() => Gesture.Exclusive(doubleTapGesture, singleTapGesture), [doubleTapGesture, singleTapGesture]);
338
561
  const zoomPinchGesture = useMemo(() => Gesture.Pinch().enabled(enablePinchZoom).onTouchesDown(event => {
562
+ if (pageTransitionLocked.get()) {
563
+ return;
564
+ }
339
565
  if (event.numberOfTouches === 2) {
340
566
  scheduleOnRN(setIsPinching, true);
341
567
  }
342
568
  }).onStart(event => {
569
+ if (pageTransitionLocked.get()) {
570
+ return;
571
+ }
343
572
  startScale.set(scale.get());
344
573
  initialTranslateX.set(translateX.get());
345
574
  initialTranslateY.set(translateY.get());
@@ -349,6 +578,9 @@ export const useGestureViewer = ({
349
578
  lastFocalY.set(event.focalY);
350
579
  hasActiveFocal.set(false);
351
580
  }).onUpdate(event => {
581
+ if (pageTransitionLocked.get()) {
582
+ return;
583
+ }
352
584
  const initialScale = startScale.get();
353
585
  const newScale = initialScale * event.scale;
354
586
  scale.set(newScale);
@@ -402,6 +634,9 @@ export const useGestureViewer = ({
402
634
  translateX.set(constrainedTranslateX);
403
635
  translateY.set(constrainedTranslateY);
404
636
  }).onEnd(() => {
637
+ if (pageTransitionLocked.get()) {
638
+ return;
639
+ }
405
640
  const currentScale = scale.get();
406
641
  if (currentScale > maxZoomScale) {
407
642
  scale.set(withTiming(maxZoomScale, {
@@ -447,11 +682,17 @@ export const useGestureViewer = ({
447
682
  }).onFinalize(() => {
448
683
  hasActiveFocal.set(false);
449
684
  scheduleOnRN(setIsPinching, false);
450
- }), [scale, enablePinchZoom, maxZoomScale, translateX, translateY, startScale, initialTranslateX, initialTranslateY, width, height, constrainTranslation, lastFocalX, lastFocalY, startFocalX, startFocalY, hasActiveFocal]);
685
+ }), [scale, enablePinchZoom, maxZoomScale, translateX, translateY, startScale, initialTranslateX, initialTranslateY, width, height, constrainTranslation, lastFocalX, lastFocalY, startFocalX, startFocalY, hasActiveFocal, pageTransitionLocked]);
451
686
  const zoomPanGesture = useMemo(() => Gesture.Pan().enabled(enablePanWhenZoomed && isZoomed).activeCursor('grabbing').averageTouches(true).onBegin(() => {
687
+ if (pageTransitionLocked.get()) {
688
+ return;
689
+ }
452
690
  initialTranslateX.set(translateX.get());
453
691
  initialTranslateY.set(translateY.get());
454
692
  }).onUpdate(event => {
693
+ if (pageTransitionLocked.get()) {
694
+ return;
695
+ }
455
696
  const currentScale = scale.get();
456
697
  if (currentScale > 1) {
457
698
  const newTranslateX = initialTranslateX.get() + event.translationX;
@@ -467,50 +708,22 @@ export const useGestureViewer = ({
467
708
  translateX.set(constrainedTranslateX);
468
709
  translateY.set(constrainedTranslateY);
469
710
  }
470
- }), [translateX, translateY, enablePanWhenZoomed, isZoomed, scale, initialTranslateX, initialTranslateY, constrainTranslation]);
471
- const emitSingleTap = useCallback((x, y) => {
472
- const index = pendingIndexRef.current;
473
- const currentData = dataRef.current;
474
- if (index < 0 || index >= currentData.length) {
475
- return;
476
- }
477
- managerRef.current?.emitTap({
478
- kind: 'single',
479
- x,
480
- y,
481
- index
482
- });
483
- const item = currentData[index];
484
- if (item === undefined) {
485
- return;
486
- }
487
- onSingleTapRef.current?.({
488
- x,
489
- y,
490
- index,
491
- item
492
- });
493
- }, []);
494
- const singleTapGesture = useMemo(() => Gesture.Tap().enabled(Platform.OS !== 'web').numberOfTaps(1).onEnd((event, success) => {
495
- if (!success) {
496
- return;
497
- }
498
- scheduleOnRN(emitSingleTap, event.x, event.y);
499
- }), [emitSingleTap]);
500
- const doubleTapGesture = useMemo(() => Gesture.Tap().enabled(enableDoubleTapZoom && Platform.OS !== 'web').numberOfTaps(2).onEnd(event => {
501
- applyTapZoomAtPoint({
502
- x: event.x,
503
- y: event.y,
504
- width,
505
- height,
506
- maxZoomScale,
507
- scale,
508
- translateX,
509
- translateY
510
- });
511
- }), [enableDoubleTapZoom, height, maxZoomScale, scale, translateX, translateY, width]);
512
- const tapGesture = useMemo(() => Gesture.Exclusive(doubleTapGesture, singleTapGesture), [doubleTapGesture, singleTapGesture]);
711
+ }), [translateX, translateY, enablePanWhenZoomed, isZoomed, scale, initialTranslateX, initialTranslateY, constrainTranslation, pageTransitionLocked]);
513
712
  const zoomGesture = useMemo(() => Gesture.Race(zoomPinchGesture, Gesture.Exclusive(zoomPanGesture, tapGesture)), [zoomPinchGesture, zoomPanGesture, tapGesture]);
713
+ const onWebClick = useWebClickHandler({
714
+ clearPendingWebSingleTap,
715
+ emitSingleTap,
716
+ enableDoubleTapZoom,
717
+ getCurrentTapTarget,
718
+ height,
719
+ maxZoomScale,
720
+ scale,
721
+ scheduleWebSingleTap,
722
+ isInteractionLocked: isWebInteractionLocked,
723
+ translateX,
724
+ translateY,
725
+ width
726
+ });
514
727
  const animatedStyle = useAnimatedStyle(() => ({
515
728
  opacity: triggerOpacity.get(),
516
729
  transform: [{
@@ -542,55 +755,22 @@ export const useGestureViewer = ({
542
755
  opacity: baseOpacity * dismissOpacity
543
756
  };
544
757
  }, [dismissOptions.direction, dismissOptions.fadeBackdrop]);
545
- const nativeScrollGesture = useMemo(() => {
546
- return Gesture.Native().requireExternalGestureToFail(dismissGestureRef);
547
- }, []);
548
- const {
549
- activeListIndex,
550
- onMomentumScrollEnd,
551
- onScroll,
552
- onScrollBeginDrag,
553
- onWebClick
554
- } = useGestureViewerPaging({
555
- adjustedInitialIndex,
556
- autoPlay,
557
- autoPlayInterval,
558
- currentIndex,
559
- dataLength,
560
- enableDoubleTapZoom,
561
- enableHorizontalSwipe,
562
- enableLoop,
563
- height,
564
- isRotated,
565
- isZoomed,
566
- itemSpacing,
567
- manager,
568
- maxZoomScale,
569
- onSingleTap: emitSingleTap,
570
- scale,
571
- scrollTo,
572
- syncCurrentIndex,
573
- syncPendingIndex,
574
- translateX,
575
- translateY,
576
- width
577
- });
578
758
  return {
579
- activeListIndex,
580
759
  animatedStyle,
581
760
  backdropStyle,
761
+ centerVirtualIndex: renderCenterVirtualIndex,
762
+ currentIndex: renderCurrentIndex,
582
763
  dataLength,
583
764
  dismissGesture,
584
765
  handleDismiss,
766
+ horizontalPagingGesture,
585
767
  isPinching,
586
768
  isRotated,
587
769
  isZoomed,
588
- listRef,
589
- nativeScrollGesture,
590
770
  onWebClick,
591
- onMomentumScrollEnd,
592
- onScroll,
593
- onScrollBeginDrag,
771
+ pageStride,
772
+ renderWindowSlots,
773
+ visualPage,
594
774
  zoomGesture
595
775
  };
596
776
  };