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.
- package/README.md +5 -3
- package/lib/module/GestureViewer.js +88 -140
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +49 -105
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/gestureViewerAnimation.js +18 -0
- package/lib/module/gestureViewerAnimation.js.map +1 -0
- package/lib/module/gestureViewerPaging.js +66 -0
- package/lib/module/gestureViewerPaging.js.map +1 -0
- package/lib/module/gestureViewerRenderWindow.js +29 -0
- package/lib/module/gestureViewerRenderWindow.js.map +1 -0
- package/lib/module/getWebContentProps.js +6 -0
- package/lib/module/getWebContentProps.js.map +1 -0
- package/lib/module/getWebContentProps.web.js +8 -0
- package/lib/module/getWebContentProps.web.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/platformTapGestures.js +4 -0
- package/lib/module/platformTapGestures.js.map +1 -0
- package/lib/module/platformTapGestures.web.js +4 -0
- package/lib/module/platformTapGestures.web.js.map +1 -0
- package/lib/module/renderWindow.js +160 -0
- package/lib/module/renderWindow.js.map +1 -0
- package/lib/module/useGestureViewer.js +386 -200
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerController.js +3 -3
- package/lib/module/useGestureViewerController.js.map +1 -1
- package/lib/module/useGestureViewerManagerBridge.js +85 -0
- package/lib/module/useGestureViewerManagerBridge.js.map +1 -0
- package/lib/module/useGestureViewerPaging.js +147 -65
- package/lib/module/useGestureViewerPaging.js.map +1 -1
- package/lib/module/useWebClickHandler.js +7 -0
- package/lib/module/useWebClickHandler.js.map +1 -0
- package/lib/module/useWebClickHandler.web.js +85 -0
- package/lib/module/useWebClickHandler.web.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.js +12 -0
- package/lib/module/useWebSingleTapTimer.js.map +1 -0
- package/lib/module/useWebSingleTapTimer.web.js +25 -0
- package/lib/module/useWebSingleTapTimer.web.js.map +1 -0
- package/lib/module/utils/index.js +0 -77
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +4 -3
- package/lib/module/utils/tapZoom.js.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +20 -20
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/gestureViewerAnimation.d.ts +15 -0
- package/lib/typescript/src/gestureViewerAnimation.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts +13 -0
- package/lib/typescript/src/gestureViewerPaging.d.ts.map +1 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts +15 -0
- package/lib/typescript/src/gestureViewerRenderWindow.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.d.ts +3 -0
- package/lib/typescript/src/getWebContentProps.d.ts.map +1 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts +5 -0
- package/lib/typescript/src/getWebContentProps.web.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/platformTapGestures.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.d.ts.map +1 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts +2 -0
- package/lib/typescript/src/platformTapGestures.web.d.ts.map +1 -0
- package/lib/typescript/src/renderWindow.d.ts +44 -0
- package/lib/typescript/src/renderWindow.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +45 -52
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +9 -8
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerController.d.ts +1 -1
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts +28 -0
- package/lib/typescript/src/useGestureViewerManagerBridge.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.d.ts +31 -2
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
- package/lib/typescript/src/useWebClickHandler.d.ts +35 -0
- package/lib/typescript/src/useWebClickHandler.d.ts.map +1 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts +36 -0
- package/lib/typescript/src/useWebClickHandler.web.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.d.ts.map +1 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts +6 -0
- package/lib/typescript/src/useWebSingleTapTimer.web.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +0 -20
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/GestureViewer.tsx +104 -191
- package/src/GestureViewerManager.ts +77 -122
- package/src/gestureViewerAnimation.ts +18 -0
- package/src/gestureViewerPaging.ts +116 -0
- package/src/gestureViewerRenderWindow.ts +56 -0
- package/src/getWebContentProps.ts +5 -0
- package/src/getWebContentProps.web.ts +5 -0
- package/src/index.tsx +1 -0
- package/src/platformTapGestures.ts +1 -0
- package/src/platformTapGestures.web.ts +1 -0
- package/src/renderWindow.ts +253 -0
- package/src/types.ts +44 -99
- package/src/useGestureViewer.ts +557 -258
- package/src/useGestureViewerController.ts +3 -3
- package/src/useGestureViewerManagerBridge.ts +118 -0
- package/src/useGestureViewerPaging.ts +277 -98
- package/src/useWebClickHandler.ts +44 -0
- package/src/useWebClickHandler.web.ts +146 -0
- package/src/useWebSingleTapTimer.ts +10 -0
- package/src/useWebSingleTapTimer.web.ts +30 -0
- package/src/utils/index.ts +0 -102
- package/src/utils/tapZoom.ts +4 -3
- package/lib/module/WebPagingFixStyle.js +0 -25
- package/lib/module/WebPagingFixStyle.js.map +0 -1
- package/lib/module/scheduleInitialScroll.js +0 -21
- package/lib/module/scheduleInitialScroll.js.map +0 -1
- package/lib/module/useGestureViewerPaging.types.js +0 -4
- package/lib/module/useGestureViewerPaging.types.js.map +0 -1
- package/lib/module/useGestureViewerPaging.web.js +0 -275
- package/lib/module/useGestureViewerPaging.web.js.map +0 -1
- package/lib/module/utils/FlashList.js +0 -9
- package/lib/module/utils/FlashList.js.map +0 -1
- package/lib/module/utils/webScroll.js +0 -70
- package/lib/module/utils/webScroll.js.map +0 -1
- package/lib/typescript/src/WebPagingFixStyle.d.ts +0 -6
- package/lib/typescript/src/WebPagingFixStyle.d.ts.map +0 -1
- package/lib/typescript/src/scheduleInitialScroll.d.ts +0 -3
- package/lib/typescript/src/scheduleInitialScroll.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +0 -41
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +0 -1
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +0 -3
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +0 -1
- package/lib/typescript/src/utils/FlashList.d.ts +0 -2
- package/lib/typescript/src/utils/FlashList.d.ts.map +0 -1
- package/lib/typescript/src/utils/webScroll.d.ts +0 -18
- package/lib/typescript/src/utils/webScroll.d.ts.map +0 -1
- package/src/WebPagingFixStyle.tsx +0 -25
- package/src/scheduleInitialScroll.ts +0 -36
- package/src/useGestureViewerPaging.types.ts +0 -41
- package/src/useGestureViewerPaging.web.ts +0 -434
- package/src/utils/FlashList.ts +0 -7
- 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
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
238
|
-
* @remarks
|
|
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
|
-
|
|
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
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* @
|
|
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
|
-
|
|
223
|
+
windowSize?: number;
|
|
279
224
|
/**
|
|
280
|
-
*
|
|
281
|
-
* @remarks
|
|
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
|
-
|
|
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
|
-
*
|
|
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.
|