react-native-gesture-image-viewer 2.3.3 → 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 (137) hide show
  1. package/README.md +5 -3
  2. package/lib/module/GestureViewer.js +88 -140
  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/index.js.map +1 -1
  17. package/lib/module/platformTapGestures.js +4 -0
  18. package/lib/module/platformTapGestures.js.map +1 -0
  19. package/lib/module/platformTapGestures.web.js +4 -0
  20. package/lib/module/platformTapGestures.web.js.map +1 -0
  21. package/lib/module/renderWindow.js +160 -0
  22. package/lib/module/renderWindow.js.map +1 -0
  23. package/lib/module/useGestureViewer.js +386 -200
  24. package/lib/module/useGestureViewer.js.map +1 -1
  25. package/lib/module/useGestureViewerController.js +3 -3
  26. package/lib/module/useGestureViewerController.js.map +1 -1
  27. package/lib/module/useGestureViewerManagerBridge.js +85 -0
  28. package/lib/module/useGestureViewerManagerBridge.js.map +1 -0
  29. package/lib/module/useGestureViewerPaging.js +147 -65
  30. package/lib/module/useGestureViewerPaging.js.map +1 -1
  31. package/lib/module/useWebClickHandler.js +7 -0
  32. package/lib/module/useWebClickHandler.js.map +1 -0
  33. package/lib/module/useWebClickHandler.web.js +85 -0
  34. package/lib/module/useWebClickHandler.web.js.map +1 -0
  35. package/lib/module/useWebSingleTapTimer.js +12 -0
  36. package/lib/module/useWebSingleTapTimer.js.map +1 -0
  37. package/lib/module/useWebSingleTapTimer.web.js +25 -0
  38. package/lib/module/useWebSingleTapTimer.web.js.map +1 -0
  39. package/lib/module/utils/index.js +0 -77
  40. package/lib/module/utils/index.js.map +1 -1
  41. package/lib/module/utils/tapZoom.js +4 -3
  42. package/lib/module/utils/tapZoom.js.map +1 -1
  43. package/lib/typescript/src/GestureViewer.d.ts +1 -1
  44. package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
  45. package/lib/typescript/src/GestureViewerManager.d.ts +20 -20
  46. package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
  47. package/lib/typescript/src/gestureViewerAnimation.d.ts +15 -0
  48. package/lib/typescript/src/gestureViewerAnimation.d.ts.map +1 -0
  49. package/lib/typescript/src/gestureViewerPaging.d.ts +13 -0
  50. package/lib/typescript/src/gestureViewerPaging.d.ts.map +1 -0
  51. package/lib/typescript/src/gestureViewerRenderWindow.d.ts +15 -0
  52. package/lib/typescript/src/gestureViewerRenderWindow.d.ts.map +1 -0
  53. package/lib/typescript/src/getWebContentProps.d.ts +3 -0
  54. package/lib/typescript/src/getWebContentProps.d.ts.map +1 -0
  55. package/lib/typescript/src/getWebContentProps.web.d.ts +5 -0
  56. package/lib/typescript/src/getWebContentProps.web.d.ts.map +1 -0
  57. package/lib/typescript/src/index.d.ts +1 -1
  58. package/lib/typescript/src/index.d.ts.map +1 -1
  59. package/lib/typescript/src/platformTapGestures.d.ts +2 -0
  60. package/lib/typescript/src/platformTapGestures.d.ts.map +1 -0
  61. package/lib/typescript/src/platformTapGestures.web.d.ts +2 -0
  62. package/lib/typescript/src/platformTapGestures.web.d.ts.map +1 -0
  63. package/lib/typescript/src/renderWindow.d.ts +44 -0
  64. package/lib/typescript/src/renderWindow.d.ts.map +1 -0
  65. package/lib/typescript/src/types.d.ts +45 -52
  66. package/lib/typescript/src/types.d.ts.map +1 -1
  67. package/lib/typescript/src/useGestureViewer.d.ts +9 -8
  68. package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
  69. package/lib/typescript/src/useGestureViewerController.d.ts +1 -1
  70. package/lib/typescript/src/useGestureViewerManagerBridge.d.ts +28 -0
  71. package/lib/typescript/src/useGestureViewerManagerBridge.d.ts.map +1 -0
  72. package/lib/typescript/src/useGestureViewerPaging.d.ts +31 -2
  73. package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
  74. package/lib/typescript/src/useWebClickHandler.d.ts +35 -0
  75. package/lib/typescript/src/useWebClickHandler.d.ts.map +1 -0
  76. package/lib/typescript/src/useWebClickHandler.web.d.ts +36 -0
  77. package/lib/typescript/src/useWebClickHandler.web.d.ts.map +1 -0
  78. package/lib/typescript/src/useWebSingleTapTimer.d.ts +6 -0
  79. package/lib/typescript/src/useWebSingleTapTimer.d.ts.map +1 -0
  80. package/lib/typescript/src/useWebSingleTapTimer.web.d.ts +6 -0
  81. package/lib/typescript/src/useWebSingleTapTimer.web.d.ts.map +1 -0
  82. package/lib/typescript/src/utils/index.d.ts +0 -20
  83. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  84. package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
  85. package/package.json +1 -1
  86. package/src/GestureViewer.tsx +104 -191
  87. package/src/GestureViewerManager.ts +77 -122
  88. package/src/gestureViewerAnimation.ts +18 -0
  89. package/src/gestureViewerPaging.ts +116 -0
  90. package/src/gestureViewerRenderWindow.ts +56 -0
  91. package/src/getWebContentProps.ts +5 -0
  92. package/src/getWebContentProps.web.ts +5 -0
  93. package/src/index.tsx +1 -0
  94. package/src/platformTapGestures.ts +1 -0
  95. package/src/platformTapGestures.web.ts +1 -0
  96. package/src/renderWindow.ts +253 -0
  97. package/src/types.ts +44 -99
  98. package/src/useGestureViewer.ts +557 -258
  99. package/src/useGestureViewerController.ts +3 -3
  100. package/src/useGestureViewerManagerBridge.ts +118 -0
  101. package/src/useGestureViewerPaging.ts +277 -98
  102. package/src/useWebClickHandler.ts +44 -0
  103. package/src/useWebClickHandler.web.ts +146 -0
  104. package/src/useWebSingleTapTimer.ts +10 -0
  105. package/src/useWebSingleTapTimer.web.ts +30 -0
  106. package/src/utils/index.ts +0 -102
  107. package/src/utils/tapZoom.ts +4 -3
  108. package/lib/module/WebPagingFixStyle.js +0 -25
  109. package/lib/module/WebPagingFixStyle.js.map +0 -1
  110. package/lib/module/scheduleInitialScroll.js +0 -21
  111. package/lib/module/scheduleInitialScroll.js.map +0 -1
  112. package/lib/module/useGestureViewerPaging.types.js +0 -4
  113. package/lib/module/useGestureViewerPaging.types.js.map +0 -1
  114. package/lib/module/useGestureViewerPaging.web.js +0 -275
  115. package/lib/module/useGestureViewerPaging.web.js.map +0 -1
  116. package/lib/module/utils/FlashList.js +0 -9
  117. package/lib/module/utils/FlashList.js.map +0 -1
  118. package/lib/module/utils/webScroll.js +0 -70
  119. package/lib/module/utils/webScroll.js.map +0 -1
  120. package/lib/typescript/src/WebPagingFixStyle.d.ts +0 -6
  121. package/lib/typescript/src/WebPagingFixStyle.d.ts.map +0 -1
  122. package/lib/typescript/src/scheduleInitialScroll.d.ts +0 -3
  123. package/lib/typescript/src/scheduleInitialScroll.d.ts.map +0 -1
  124. package/lib/typescript/src/useGestureViewerPaging.types.d.ts +0 -41
  125. package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +0 -1
  126. package/lib/typescript/src/useGestureViewerPaging.web.d.ts +0 -3
  127. package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +0 -1
  128. package/lib/typescript/src/utils/FlashList.d.ts +0 -2
  129. package/lib/typescript/src/utils/FlashList.d.ts.map +0 -1
  130. package/lib/typescript/src/utils/webScroll.d.ts +0 -18
  131. package/lib/typescript/src/utils/webScroll.d.ts.map +0 -1
  132. package/src/WebPagingFixStyle.tsx +0 -25
  133. package/src/scheduleInitialScroll.ts +0 -36
  134. package/src/useGestureViewerPaging.types.ts +0 -41
  135. package/src/useGestureViewerPaging.web.ts +0 -434
  136. package/src/utils/FlashList.ts +0 -7
  137. package/src/utils/webScroll.ts +0 -96
@@ -0,0 +1,116 @@
1
+ export type HorizontalSwipeDirection = -1 | 0 | 1;
2
+
3
+ export type HorizontalPagingTarget =
4
+ | {
5
+ kind: 'move';
6
+ targetVirtualIndex: number;
7
+ }
8
+ | {
9
+ kind: 'settle';
10
+ };
11
+
12
+ export function normalizeHorizontalSwipeThreshold(
13
+ value: number | undefined,
14
+ fallback: number,
15
+ ): number {
16
+ if (value === undefined) {
17
+ return fallback;
18
+ }
19
+
20
+ if (Number.isFinite(value) && value >= 0) {
21
+ return value;
22
+ }
23
+
24
+ return fallback;
25
+ }
26
+
27
+ export function resolveHorizontalSwipeDirection(
28
+ translationX: number,
29
+ velocityX: number,
30
+ width: number,
31
+ thresholdRatio: number,
32
+ velocityThreshold: number,
33
+ ): HorizontalSwipeDirection {
34
+ 'worklet';
35
+
36
+ const translationThreshold = width * thresholdRatio;
37
+
38
+ if (-translationX > translationThreshold || velocityX < -velocityThreshold) {
39
+ return 1;
40
+ }
41
+
42
+ if (translationX > translationThreshold || velocityX > velocityThreshold) {
43
+ return -1;
44
+ }
45
+
46
+ return 0;
47
+ }
48
+
49
+ export function applyHorizontalEdgeResistance(
50
+ nextPage: number,
51
+ currentIndex: number,
52
+ dataLength: number,
53
+ centerVirtualIndex: number,
54
+ enableLoop: boolean,
55
+ resistance: number,
56
+ ): number {
57
+ 'worklet';
58
+
59
+ if (dataLength <= 1) {
60
+ return centerVirtualIndex;
61
+ }
62
+
63
+ if (enableLoop) {
64
+ return nextPage;
65
+ }
66
+
67
+ if (currentIndex <= 0 && nextPage < centerVirtualIndex) {
68
+ return centerVirtualIndex + (nextPage - centerVirtualIndex) * resistance;
69
+ }
70
+
71
+ if (currentIndex >= dataLength - 1 && nextPage > centerVirtualIndex) {
72
+ return centerVirtualIndex + (nextPage - centerVirtualIndex) * resistance;
73
+ }
74
+
75
+ return nextPage;
76
+ }
77
+
78
+ export function canMoveHorizontalPage(
79
+ currentIndex: number,
80
+ dataLength: number,
81
+ direction: HorizontalSwipeDirection,
82
+ enableLoop: boolean,
83
+ ): boolean {
84
+ 'worklet';
85
+
86
+ if (direction === 0 || dataLength <= 1) {
87
+ return false;
88
+ }
89
+
90
+ if (enableLoop) {
91
+ return true;
92
+ }
93
+
94
+ const nextIndex = currentIndex + direction;
95
+
96
+ return nextIndex >= 0 && nextIndex < dataLength;
97
+ }
98
+
99
+ export function resolveHorizontalPagingTarget(
100
+ centerVirtualIndex: number,
101
+ currentIndex: number,
102
+ dataLength: number,
103
+ direction: HorizontalSwipeDirection,
104
+ enableLoop: boolean,
105
+ ): HorizontalPagingTarget {
106
+ 'worklet';
107
+
108
+ if (!canMoveHorizontalPage(currentIndex, dataLength, direction, enableLoop)) {
109
+ return { kind: 'settle' };
110
+ }
111
+
112
+ return {
113
+ kind: 'move',
114
+ targetVirtualIndex: centerVirtualIndex + direction,
115
+ };
116
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ type RenderWindowSlot,
3
+ clampIndex,
4
+ createRenderWindow,
5
+ getVirtualIndexForLogicalIndex,
6
+ normalizeWindowSize,
7
+ } from './renderWindow';
8
+
9
+ export type GestureViewerRenderWindowState<ItemT> = {
10
+ centerVirtualIndex: number;
11
+ currentIndex: number;
12
+ slots: RenderWindowSlot<ItemT>[];
13
+ };
14
+
15
+ export function resolveGestureViewerRenderWindow<ItemT>({
16
+ centerVirtualIndex,
17
+ currentIndex,
18
+ data,
19
+ enableLoop,
20
+ isTriggerOpening,
21
+ windowSize,
22
+ }: {
23
+ centerVirtualIndex: number;
24
+ currentIndex: number;
25
+ data: ItemT[];
26
+ enableLoop: boolean;
27
+ isTriggerOpening: boolean;
28
+ windowSize?: number;
29
+ }): GestureViewerRenderWindowState<ItemT> {
30
+ const dataLength = data.length;
31
+ const normalizedWindowSize = normalizeWindowSize(windowSize);
32
+ const renderCurrentIndex = clampIndex(currentIndex, dataLength);
33
+ const renderCenterVirtualIndex =
34
+ getVirtualIndexForLogicalIndex(
35
+ renderCurrentIndex,
36
+ centerVirtualIndex,
37
+ dataLength,
38
+ enableLoop,
39
+ ) ?? renderCurrentIndex;
40
+
41
+ const fullSlots = createRenderWindow({
42
+ centerVirtualIndex: renderCenterVirtualIndex,
43
+ data,
44
+ enableLoop,
45
+ windowSize: normalizedWindowSize,
46
+ });
47
+ const slots = isTriggerOpening
48
+ ? fullSlots.filter((slot) => slot.virtualIndex === renderCenterVirtualIndex)
49
+ : fullSlots;
50
+
51
+ return {
52
+ centerVirtualIndex: renderCenterVirtualIndex,
53
+ currentIndex: renderCurrentIndex,
54
+ slots,
55
+ };
56
+ }
@@ -0,0 +1,5 @@
1
+ import type { WebClickHandler } from './useWebClickHandler';
2
+
3
+ export function getWebContentProps(_onWebClick: WebClickHandler) {
4
+ return {};
5
+ }
@@ -0,0 +1,5 @@
1
+ import type { WebClickHandler } from './useWebClickHandler';
2
+
3
+ export function getWebContentProps(onWebClick: WebClickHandler) {
4
+ return { onClick: onWebClick };
5
+ }
package/src/index.tsx CHANGED
@@ -7,6 +7,7 @@ export type {
7
7
  GestureViewerEventData,
8
8
  GestureViewerEventType,
9
9
  GestureViewerProps,
10
+ GestureViewerRenderItemInfo,
10
11
  GestureViewerState,
11
12
  GestureViewerSingleTapEvent,
12
13
  } from './types';
@@ -0,0 +1 @@
1
+ export const enableNativeTapGestures = true;
@@ -0,0 +1 @@
1
+ export const enableNativeTapGestures = false;
@@ -0,0 +1,253 @@
1
+ export const DEFAULT_WINDOW_SIZE = 3;
2
+ export const MIN_WINDOW_SIZE = 3;
3
+
4
+ type NavigationDirection = -1 | 1;
5
+
6
+ export type RenderWindowSlot<ItemT> = {
7
+ item: ItemT;
8
+ logicalIndex: number;
9
+ slotKey: string;
10
+ virtualIndex: number;
11
+ };
12
+
13
+ export type NavigationResolution =
14
+ | {
15
+ kind: 'noop';
16
+ }
17
+ | {
18
+ direction: NavigationDirection;
19
+ kind: 'step';
20
+ targetIndex: number;
21
+ }
22
+ | {
23
+ kind: 'jump';
24
+ targetIndex: number;
25
+ };
26
+
27
+ export type NavigationOptions = {
28
+ animated?: boolean;
29
+ };
30
+
31
+ const positiveModulo = (value: number, divisor: number) => ((value % divisor) + divisor) % divisor;
32
+
33
+ const getStepTargetIndex = (
34
+ currentIndex: number,
35
+ dataLength: number,
36
+ enableLoop: boolean,
37
+ direction: NavigationDirection,
38
+ ) => {
39
+ const adjacentIndex = currentIndex + direction;
40
+
41
+ if (adjacentIndex >= 0 && adjacentIndex < dataLength) {
42
+ return adjacentIndex;
43
+ }
44
+
45
+ if (!enableLoop || dataLength <= 1) {
46
+ return null;
47
+ }
48
+
49
+ if (direction === 1 && currentIndex === dataLength - 1) {
50
+ return 0;
51
+ }
52
+
53
+ if (direction === -1 && currentIndex === 0) {
54
+ return dataLength - 1;
55
+ }
56
+
57
+ return null;
58
+ };
59
+
60
+ export function normalizeWindowSize(windowSize?: number): number {
61
+ const candidateWindowSize = windowSize ?? DEFAULT_WINDOW_SIZE;
62
+
63
+ if (!Number.isFinite(candidateWindowSize)) {
64
+ return DEFAULT_WINDOW_SIZE;
65
+ }
66
+
67
+ const integerWindowSize = Math.floor(candidateWindowSize);
68
+ const boundedWindowSize = Math.max(MIN_WINDOW_SIZE, integerWindowSize);
69
+
70
+ return boundedWindowSize % 2 === 0 ? boundedWindowSize + 1 : boundedWindowSize;
71
+ }
72
+
73
+ export function normalizePageSpacing(pageSpacing?: number): number {
74
+ const candidatePageSpacing = pageSpacing ?? 0;
75
+
76
+ if (!Number.isFinite(candidatePageSpacing) || candidatePageSpacing < 0) {
77
+ return 0;
78
+ }
79
+
80
+ return candidatePageSpacing;
81
+ }
82
+
83
+ export function getPageStride(width: number, pageSpacing?: number): number {
84
+ return width + normalizePageSpacing(pageSpacing);
85
+ }
86
+
87
+ export function clampIndex(index: number | undefined, dataLength: number): number {
88
+ if (dataLength <= 0) {
89
+ return 0;
90
+ }
91
+
92
+ const candidateIndex = index ?? 0;
93
+
94
+ if (!Number.isFinite(candidateIndex)) {
95
+ return 0;
96
+ }
97
+
98
+ return Math.min(Math.max(Math.trunc(candidateIndex), 0), dataLength - 1);
99
+ }
100
+
101
+ export function getLogicalIndex(
102
+ virtualIndex: number,
103
+ dataLength: number,
104
+ enableLoop: boolean,
105
+ ): number | null {
106
+ if (dataLength <= 0 || !Number.isFinite(virtualIndex)) {
107
+ return null;
108
+ }
109
+
110
+ if (enableLoop) {
111
+ return positiveModulo(Math.trunc(virtualIndex), dataLength);
112
+ }
113
+
114
+ if (virtualIndex < 0 || virtualIndex >= dataLength) {
115
+ return null;
116
+ }
117
+
118
+ return Math.trunc(virtualIndex);
119
+ }
120
+
121
+ export function getVirtualIndexForLogicalIndex(
122
+ logicalIndex: number,
123
+ centerVirtualIndex: number,
124
+ dataLength: number,
125
+ enableLoop: boolean,
126
+ ): number | null {
127
+ if (dataLength <= 0 || logicalIndex < 0 || logicalIndex >= dataLength) {
128
+ return null;
129
+ }
130
+
131
+ if (!enableLoop) {
132
+ return logicalIndex;
133
+ }
134
+
135
+ const currentLogicalIndex = getLogicalIndex(centerVirtualIndex, dataLength, true);
136
+
137
+ if (currentLogicalIndex === null) {
138
+ return logicalIndex;
139
+ }
140
+
141
+ const forwardDistance = positiveModulo(logicalIndex - currentLogicalIndex, dataLength);
142
+ const backwardDistance = positiveModulo(currentLogicalIndex - logicalIndex, dataLength);
143
+
144
+ return forwardDistance <= backwardDistance
145
+ ? centerVirtualIndex + forwardDistance
146
+ : centerVirtualIndex - backwardDistance;
147
+ }
148
+
149
+ export function createRenderWindow<ItemT>({
150
+ centerVirtualIndex,
151
+ data,
152
+ enableLoop,
153
+ windowSize,
154
+ }: {
155
+ centerVirtualIndex: number;
156
+ data: ItemT[];
157
+ enableLoop: boolean;
158
+ windowSize?: number;
159
+ }): RenderWindowSlot<ItemT>[] {
160
+ const dataLength = data.length;
161
+
162
+ if (dataLength === 0) {
163
+ return [];
164
+ }
165
+
166
+ const normalizedWindowSize = normalizeWindowSize(windowSize);
167
+ const halfWindowSize = enableLoop && dataLength === 1 ? 0 : Math.floor(normalizedWindowSize / 2);
168
+ const slots: RenderWindowSlot<ItemT>[] = [];
169
+
170
+ for (let offset = -halfWindowSize; offset <= halfWindowSize; offset += 1) {
171
+ const virtualIndex = centerVirtualIndex + offset;
172
+ const logicalIndex = getLogicalIndex(virtualIndex, dataLength, enableLoop);
173
+
174
+ if (logicalIndex === null || !(logicalIndex in data)) {
175
+ continue;
176
+ }
177
+
178
+ slots.push({
179
+ item: data[logicalIndex] as ItemT,
180
+ logicalIndex,
181
+ slotKey: `slot-${virtualIndex}`,
182
+ virtualIndex,
183
+ });
184
+ }
185
+
186
+ return slots;
187
+ }
188
+
189
+ export function resolveNavigation({
190
+ currentIndex,
191
+ dataLength,
192
+ enableLoop,
193
+ preferredDirection,
194
+ targetIndex,
195
+ }: {
196
+ currentIndex: number;
197
+ dataLength: number;
198
+ enableLoop: boolean;
199
+ preferredDirection?: NavigationDirection;
200
+ targetIndex: number;
201
+ }): NavigationResolution {
202
+ if (
203
+ dataLength <= 0 ||
204
+ targetIndex < 0 ||
205
+ targetIndex >= dataLength ||
206
+ currentIndex < 0 ||
207
+ currentIndex >= dataLength
208
+ ) {
209
+ return { kind: 'noop' };
210
+ }
211
+
212
+ if (targetIndex === currentIndex) {
213
+ return { kind: 'noop' };
214
+ }
215
+
216
+ if (preferredDirection !== undefined) {
217
+ const preferredTargetIndex = getStepTargetIndex(
218
+ currentIndex,
219
+ dataLength,
220
+ enableLoop,
221
+ preferredDirection,
222
+ );
223
+
224
+ if (preferredTargetIndex === targetIndex) {
225
+ return { direction: preferredDirection, kind: 'step', targetIndex };
226
+ }
227
+ }
228
+
229
+ const forwardTargetIndex = getStepTargetIndex(currentIndex, dataLength, enableLoop, 1);
230
+
231
+ if (forwardTargetIndex === targetIndex) {
232
+ return { direction: 1, kind: 'step', targetIndex };
233
+ }
234
+
235
+ const backwardTargetIndex = getStepTargetIndex(currentIndex, dataLength, enableLoop, -1);
236
+
237
+ if (backwardTargetIndex === targetIndex) {
238
+ return { direction: -1, kind: 'step', targetIndex };
239
+ }
240
+
241
+ return { kind: 'jump', targetIndex };
242
+ }
243
+
244
+ export function shouldRunNavigationDuringTransition(
245
+ resolution: NavigationResolution,
246
+ options?: NavigationOptions,
247
+ ): boolean {
248
+ if (resolution.kind === 'noop') {
249
+ return false;
250
+ }
251
+
252
+ return resolution.kind === 'jump' || options?.animated === false;
253
+ }
package/src/types.ts CHANGED
@@ -1,73 +1,7 @@
1
1
  import type React from 'react';
2
- import type {
3
- FlatListProps,
4
- ListRenderItem,
5
- FlatList as RNFlatList,
6
- ScrollView as RNScrollView,
7
- StyleProp,
8
- ViewabilityConfig,
9
- ViewStyle,
10
- ViewToken,
11
- } from 'react-native';
12
- import type {
13
- FlatList as GHFlatList,
14
- ScrollView as GHScrollView,
15
- } from 'react-native-gesture-handler';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
16
3
  import type { WithTimingConfig } from 'react-native-reanimated';
17
4
 
18
- export type FlatListComponent<ItemT> = typeof RNFlatList<ItemT> | typeof GHFlatList<ItemT>;
19
- export type ScrollViewComponent = typeof RNScrollView | typeof GHScrollView;
20
-
21
- interface ViewabilityConfigCallbackPair<TItem> {
22
- viewabilityConfig: ViewabilityConfig;
23
- onViewableItemsChanged:
24
- | ((info: { viewableItems: ViewToken<TItem>[]; changed: ViewToken<TItem>[] }) => void)
25
- | null;
26
- }
27
-
28
- // Helper type to instantiate generic components with specific type parameter
29
- export type InstantiateGeneric<ItemT, LC> =
30
- LC extends React.ComponentType<infer P>
31
- ? P extends Record<string, any>
32
- ? {
33
- [K in keyof P]: K extends 'data'
34
- ? ArrayLike<ItemT> | null | undefined
35
- : K extends 'renderItem'
36
- ? ListRenderItem<ItemT>
37
- : K extends 'keyExtractor'
38
- ? (item: ItemT, index: number) => string
39
- : K extends 'getItemType'
40
- ? (item: ItemT, index: number, extraData?: any) => string | number | undefined
41
- : K extends 'overrideItemLayout'
42
- ? (
43
- layout: { span?: number; size?: number },
44
- item: ItemT,
45
- index: number,
46
- maxColumns?: number,
47
- extraData?: any,
48
- ) => void
49
- : K extends 'onViewableItemsChanged'
50
- ? FlatListProps<ItemT>['onViewableItemsChanged']
51
- : K extends 'viewabilityConfigCallbackPairs'
52
- ? ViewabilityConfigCallbackPair<ItemT>[]
53
- : P[K];
54
- }
55
- : P
56
- : never;
57
-
58
- export type GetComponentProps<ItemT, LC> =
59
- LC extends React.ComponentType<any> ? InstantiateGeneric<ItemT, LC> : never;
60
-
61
- type ConditionalListProps<ItemT, LC> = LC extends typeof RNFlatList<ItemT>
62
- ? React.ComponentProps<typeof RNFlatList<ItemT>>
63
- : LC extends typeof GHFlatList<ItemT>
64
- ? React.ComponentProps<typeof GHFlatList<ItemT>>
65
- : LC extends typeof RNScrollView
66
- ? React.ComponentProps<typeof RNScrollView>
67
- : LC extends typeof GHScrollView
68
- ? React.ComponentProps<typeof GHScrollView>
69
- : GetComponentProps<ItemT, LC>;
70
-
71
5
  export type TriggerRect = {
72
6
  x: number;
73
7
  y: number;
@@ -82,6 +16,14 @@ export type GestureViewerSingleTapEvent<ItemT> = {
82
16
  item: ItemT;
83
17
  };
84
18
 
19
+ export type GestureViewerRenderItemInfo = {
20
+ /**
21
+ * Whether the rendered item is currently active.
22
+ * @remarks The current item remains active during a page transition. When the transition finishes on another item, that item becomes active.
23
+ */
24
+ readonly isActive: boolean;
25
+ };
26
+
85
27
  export type GestureViewerDismissDirection = 'down' | 'up' | 'both';
86
28
 
87
29
  export interface TriggerAnimationConfig extends WithTimingConfig {
@@ -101,7 +43,7 @@ export interface TriggerAnimationConfig extends WithTimingConfig {
101
43
  onAnimationComplete?: () => void;
102
44
  }
103
45
 
104
- export interface GestureViewerProps<ItemT, LC> {
46
+ export interface GestureViewerProps<ItemT> {
105
47
  /**
106
48
  * When you want to efficiently manage multiple `GestureViewer` instances, you can use the `id` prop to use multiple `GestureViewer` components.
107
49
  * @remarks `GestureViewer` automatically removes instances from memory when components are unmounted, so no manual memory management is required.
@@ -128,8 +70,11 @@ export interface GestureViewerProps<ItemT, LC> {
128
70
  onDismissStart?: () => void;
129
71
  /**
130
72
  * A callback function that is called to render the item.
73
+ * @param item - The item to render.
74
+ * @param index - The index of the item in `data`.
75
+ * @param info - Render state for this mounted slot.
131
76
  */
132
- renderItem: (item: ItemT, index: number) => React.ReactElement;
77
+ renderItem: (item: ItemT, index: number, info: GestureViewerRenderItemInfo) => React.ReactElement;
133
78
  /**
134
79
  * A callback function that is called when a single tap is confirmed on the viewer content.
135
80
  * @remarks
@@ -152,10 +97,6 @@ export interface GestureViewerProps<ItemT, LC> {
152
97
  children: React.ReactElement,
153
98
  helpers: { dismiss: () => void },
154
99
  ) => React.ReactElement;
155
- /**
156
- * Support for any list component like `ScrollView`, `FlatList`, `FlashList` through the `ListComponent` prop.
157
- */
158
- ListComponent: LC;
159
100
  /**
160
101
  * The width of the `GestureViewer`.
161
102
  * @remarks If you don't set this prop, the width of the `GestureViewer` will be the same as the width of the screen.
@@ -168,11 +109,6 @@ export interface GestureViewerProps<ItemT, LC> {
168
109
  * @defaultValue screen height
169
110
  */
170
111
  height?: number;
171
- /**
172
- * The props to pass to the list component.
173
- * @remarks The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
174
- */
175
- listProps?: Partial<ConditionalListProps<ItemT, LC>>;
176
112
  /**
177
113
  * The style of the backdrop.
178
114
  */
@@ -234,11 +170,28 @@ export interface GestureViewerProps<ItemT, LC> {
234
170
  fadeBackdrop?: boolean;
235
171
  };
236
172
  /**
237
- * Controls left/right swipe gestures.
238
- * @remarks When `false`, horizontal gestures are disabled.
239
- * @defaultValue true
173
+ * Horizontal swipe gesture options.
174
+ * @remarks Controls user-driven left/right page swipe gestures. Controller navigation and autoplay remain available when disabled.
240
175
  */
241
- enableHorizontalSwipe?: boolean;
176
+ horizontalSwipe?: {
177
+ /**
178
+ * When `false`, user-driven horizontal page swipe gestures are disabled.
179
+ * @defaultValue true
180
+ */
181
+ enabled?: boolean;
182
+ /**
183
+ * Distance threshold as a ratio of viewer width.
184
+ * @remarks A page commits when absolute horizontal translation is strictly greater than `width * distanceThresholdRatio`, or when velocity passes `velocityThreshold`. Zero and finite values greater than `1` are accepted; negative and non-finite values fall back to the default.
185
+ * @defaultValue 0.25
186
+ */
187
+ distanceThresholdRatio?: number;
188
+ /**
189
+ * Horizontal velocity threshold in points per second.
190
+ * @remarks A page commits when absolute horizontal velocity is strictly greater than `velocityThreshold`, or when distance passes `distanceThresholdRatio`. Zero and every finite non-negative value are accepted; negative and non-finite values fall back to the default.
191
+ * @defaultValue 800
192
+ */
193
+ velocityThreshold?: number;
194
+ };
242
195
  /**
243
196
  * Only works when zoom is active, allows moving item position when zoomed.
244
197
  * @remarks When `false`, gesture movement is disabled during zoom.
@@ -263,25 +216,17 @@ export interface GestureViewerProps<ItemT, LC> {
263
216
  */
264
217
  enableLoop?: boolean;
265
218
  /**
266
- * Enables snap scrolling mode.
267
- *
268
- * @remarks
269
- * **`false` (default)**: Paging mode (`pagingEnabled: true`)
270
- * - Scrolls by full screen size increments
271
- *
272
- * **`true`**: Snap mode (`snapToInterval` auto-calculated)
273
- * - `snapToInterval` is automatically calculated based on `width` and `itemSpacing` values
274
- * - Use this option when you need item spacing
275
- * @defaultValue false
276
- *
219
+ * Number of mounted render-window slots including the current item.
220
+ * @remarks Values are normalized to an odd number of at least 3. For example, `4` becomes `5`.
221
+ * @defaultValue 3
277
222
  */
278
- enableSnapMode?: boolean;
223
+ windowSize?: number;
279
224
  /**
280
- * The spacing between items in pixels.
281
- * @remarks Only applied when `enableSnapMode` is `true`.
225
+ * Horizontal visual space between pages.
226
+ * @remarks Page stride is `width + pageSpacing`; zoom bounds still use `width` and `height`.
282
227
  * @defaultValue 0
283
228
  */
284
- itemSpacing?: number;
229
+ pageSpacing?: number;
285
230
  /**
286
231
  * The maximum zoom scale.
287
232
  * @defaultValue 2
@@ -323,7 +268,7 @@ export type GestureViewerController = {
323
268
  * Updates the currentIndex in the controller state.
324
269
  *
325
270
  * @param index - The target index (must be between 0 and totalCount - 1)
326
- * @throws Will throw an error if index is out of bounds
271
+ * Out-of-range indexes are ignored.
327
272
  *
328
273
  * @example
329
274
  * ```typescript
@@ -333,7 +278,7 @@ export type GestureViewerController = {
333
278
  * controller.goToIndex(totalCount - 1); // Go to last item
334
279
  * ```
335
280
  */
336
- goToIndex: (index: number) => void;
281
+ goToIndex: (index: number, options?: { animated?: boolean }) => void;
337
282
 
338
283
  /**
339
284
  * Navigates to the previous item in the sequence.