react-native-gesture-image-viewer 2.1.3 → 2.2.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/lib/module/GestureTrigger.js +22 -5
- package/lib/module/GestureTrigger.js.map +1 -1
- package/lib/module/GestureViewer.js +50 -40
- package/lib/module/GestureViewer.js.map +1 -1
- package/lib/module/GestureViewerManager.js +26 -6
- package/lib/module/GestureViewerManager.js.map +1 -1
- package/lib/module/GestureViewerRegistry.js +52 -8
- package/lib/module/GestureViewerRegistry.js.map +1 -1
- package/lib/module/useGestureViewer.js +149 -113
- package/lib/module/useGestureViewer.js.map +1 -1
- package/lib/module/useGestureViewerPaging.js +82 -0
- package/lib/module/useGestureViewerPaging.js.map +1 -0
- package/lib/module/useGestureViewerPaging.types.js +4 -0
- package/lib/module/useGestureViewerPaging.types.js.map +1 -0
- package/lib/module/useGestureViewerPaging.web.js +254 -0
- package/lib/module/useGestureViewerPaging.web.js.map +1 -0
- package/lib/module/utils/index.js +9 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/tapZoom.js +34 -0
- package/lib/module/utils/tapZoom.js.map +1 -0
- package/lib/module/utils/webScroll.js +70 -0
- package/lib/module/utils/webScroll.js.map +1 -0
- package/lib/typescript/src/GestureTrigger.d.ts +9 -3
- package/lib/typescript/src/GestureTrigger.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerManager.d.ts +5 -0
- package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
- package/lib/typescript/src/GestureViewerRegistry.d.ts +11 -4
- package/lib/typescript/src/GestureViewerRegistry.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewer.d.ts +4 -3
- package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
- package/lib/typescript/src/useGestureViewerPaging.d.ts +3 -0
- package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts +33 -0
- package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +1 -0
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts +3 -0
- package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +7 -0
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/tapZoom.d.ts +12 -0
- package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -0
- package/lib/typescript/src/utils/webScroll.d.ts +18 -0
- package/lib/typescript/src/utils/webScroll.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/GestureTrigger.tsx +31 -6
- package/src/GestureViewer.tsx +39 -15
- package/src/GestureViewerManager.ts +29 -6
- package/src/GestureViewerRegistry.ts +67 -8
- package/src/useGestureViewer.ts +174 -165
- package/src/useGestureViewerPaging.ts +128 -0
- package/src/useGestureViewerPaging.types.ts +35 -0
- package/src/useGestureViewerPaging.web.ts +410 -0
- package/src/utils/index.ts +17 -1
- package/src/utils/tapZoom.ts +44 -0
- package/src/utils/webScroll.ts +96 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ScrollViewProps } from 'react-native';
|
|
2
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
3
|
+
import type GestureViewerManager from './GestureViewerManager';
|
|
4
|
+
export type UseGestureViewerPagingArgs = {
|
|
5
|
+
adjustedInitialIndex: number;
|
|
6
|
+
autoPlay: boolean;
|
|
7
|
+
autoPlayInterval: number;
|
|
8
|
+
currentIndex: number;
|
|
9
|
+
dataLength: number;
|
|
10
|
+
enableDoubleTapZoom: boolean;
|
|
11
|
+
enableHorizontalSwipe: boolean;
|
|
12
|
+
enableLoop: boolean;
|
|
13
|
+
height: number;
|
|
14
|
+
isRotated: boolean;
|
|
15
|
+
isZoomed: boolean;
|
|
16
|
+
itemSpacing: number;
|
|
17
|
+
manager: GestureViewerManager | null;
|
|
18
|
+
maxZoomScale: number;
|
|
19
|
+
scale: SharedValue<number>;
|
|
20
|
+
scrollTo: (index: number, animated: boolean) => void;
|
|
21
|
+
syncCurrentIndex: (nextIndex: number) => void;
|
|
22
|
+
syncPendingIndex: (nextIndex: number) => void;
|
|
23
|
+
translateX: SharedValue<number>;
|
|
24
|
+
translateY: SharedValue<number>;
|
|
25
|
+
width: number;
|
|
26
|
+
};
|
|
27
|
+
export type UseGestureViewerPagingResult = {
|
|
28
|
+
onMomentumScrollEnd?: ScrollViewProps['onMomentumScrollEnd'];
|
|
29
|
+
onScroll?: ScrollViewProps['onScroll'];
|
|
30
|
+
onScrollBeginDrag?: ScrollViewProps['onScrollBeginDrag'];
|
|
31
|
+
onWebDoubleClick?: (event: any) => void;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=useGestureViewerPaging.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGestureViewerPaging.types.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,oBAAoB,MAAM,wBAAwB,CAAC;AAE/D,MAAM,MAAM,0BAA0B,GAAG;IACvC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,mBAAmB,CAAC,EAAE,eAAe,CAAC,qBAAqB,CAAC,CAAC;IAC7D,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;IACvC,iBAAiB,CAAC,EAAE,eAAe,CAAC,mBAAmB,CAAC,CAAC;IACzD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACzC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { UseGestureViewerPagingArgs, UseGestureViewerPagingResult } from './useGestureViewerPaging.types';
|
|
2
|
+
export declare function useGestureViewerPaging({ adjustedInitialIndex, autoPlay, autoPlayInterval, currentIndex, dataLength, enableDoubleTapZoom, enableHorizontalSwipe, enableLoop, height, isRotated, isZoomed, itemSpacing, manager, maxZoomScale, scale, scrollTo, syncCurrentIndex, syncPendingIndex, translateX, translateY, width, }: UseGestureViewerPagingArgs): UseGestureViewerPagingResult;
|
|
3
|
+
//# sourceMappingURL=useGestureViewerPaging.web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGestureViewerPaging.web.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.web.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,gCAAgC,CAAC;AAqBxC,wBAAgB,sBAAsB,CAAC,EACrC,oBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,SAAS,EACT,QAAQ,EACR,WAAW,EACX,OAAO,EACP,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,KAAK,GACN,EAAE,0BAA0B,GAAG,4BAA4B,CAwW3D"}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { type PlatformOSType } from 'react-native';
|
|
1
2
|
import type { FlatListComponent, ScrollViewComponent } from '../types';
|
|
2
3
|
export declare const isScrollViewLike: (component: React.ComponentType<any>) => component is ScrollViewComponent;
|
|
3
4
|
export declare const isFlatListLike: (component: React.ComponentType<any>) => component is FlatListComponent<any>;
|
|
4
5
|
export declare const isFlashListLike: (component: React.ComponentType<any>) => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* iOS needs the extra Native gesture wrapper so the dismiss pan can resolve before the scrollable claims touches.
|
|
8
|
+
* We intentionally skip Android because the same require-to-fail relation regressed horizontal paging there,
|
|
9
|
+
* and we also avoid wrapping RNGH-provided scrollables to prevent double-applying Gesture.Native() to components that already participate in RNGH.
|
|
10
|
+
*/
|
|
11
|
+
export declare const shouldUseNativeScrollGesture: (platformOS: PlatformOSType, component: React.ComponentType<any>) => boolean;
|
|
5
12
|
export declare const createBoundsConstraint: ({ width, height }: {
|
|
6
13
|
width: number;
|
|
7
14
|
height: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAMtB,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAIvE,eAAO,MAAM,gBAAgB,GAC3B,WAAW,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAClC,SAAS,IAAI,mBAC+C,CAAC;AAEhE,eAAO,MAAM,cAAc,GACzB,WAAW,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAClC,SAAS,IAAI,iBAAiB,CAAC,GAAG,CAMpC,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,WAAW,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAAG,OAMrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,GACvC,YAAY,cAAc,EAC1B,WAAW,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,KAClC,OAEF,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAChC,mBAAmB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,MACpD,oCAIE;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;;;CAiBA,CAAC;AAEJ,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,SAAS,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,OAAO,KAAG,CAAC,EAevF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,MAAM,EACnB,oBAAoB,MAAM,EAC1B,YAAY,OAAO,KAClB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAoB/D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,GAAG,EAAE,OAAO,MAAM;sBAC1C,MAAM,YAAY,OAAO;CAO3C,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
2
|
+
export declare const applyTapZoomAtPoint: ({ x, y, width, height, maxZoomScale, scale, translateX, translateY, }: {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
maxZoomScale: number;
|
|
8
|
+
scale: SharedValue<number>;
|
|
9
|
+
translateX: SharedValue<number>;
|
|
10
|
+
translateY: SharedValue<number>;
|
|
11
|
+
}) => void;
|
|
12
|
+
//# sourceMappingURL=tapZoom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tapZoom.d.ts","sourceRoot":"","sources":["../../../../src/utils/tapZoom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,eAAO,MAAM,mBAAmB,GAAI,uEASjC;IACD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACjC,SAsBA,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const getWebScrollPhysicalIndex: (offsetX: number, pageWidth: number) => number;
|
|
2
|
+
export declare const resolveWebScrollFinalState: ({ dataLength, enableLoop, lastSettledPhysicalIndex, offsetX, pageWidth, }: {
|
|
3
|
+
offsetX: number;
|
|
4
|
+
pageWidth: number;
|
|
5
|
+
lastSettledPhysicalIndex: number;
|
|
6
|
+
dataLength: number;
|
|
7
|
+
enableLoop: boolean;
|
|
8
|
+
}) => {
|
|
9
|
+
logicalIndex: number;
|
|
10
|
+
rawPhysicalIndex: number;
|
|
11
|
+
settledPhysicalIndex: number;
|
|
12
|
+
} | null;
|
|
13
|
+
export declare const getWebAutoPlayTargetPhysicalIndex: ({ currentIndex, dataLength, enableLoop, }: {
|
|
14
|
+
currentIndex: number;
|
|
15
|
+
dataLength: number;
|
|
16
|
+
enableLoop: boolean;
|
|
17
|
+
}) => number | null;
|
|
18
|
+
//# sourceMappingURL=webScroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webScroll.d.ts","sourceRoot":"","sources":["../../../../src/utils/webScroll.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,EAAE,WAAW,MAAM,KAAG,MAM9E,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,2EAMxC;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB,KAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,GAAG,IAiDtF,CAAC;AAEF,eAAO,MAAM,iCAAiC,GAAI,2CAI/C;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB,KAAG,MAAM,GAAG,IAgBZ,CAAC"}
|
package/package.json
CHANGED
package/src/GestureTrigger.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
|
-
import { Children, cloneElement, isValidElement, useMemo, useRef } from 'react';
|
|
2
|
+
import { Children, cloneElement, isValidElement, useCallback, useMemo, useRef } from 'react';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
4
|
|
|
5
5
|
import { registry } from './GestureViewerRegistry';
|
|
@@ -16,11 +16,13 @@ type WithOnPress = { onPress?: (...args: unknown[]) => void };
|
|
|
16
16
|
* @typeParam T - The child's props type. Must include an optional `onPress` handler.
|
|
17
17
|
*
|
|
18
18
|
* @property id - Optional identifier to associate this trigger with a `GestureViewer`.
|
|
19
|
+
* @property index - Optional item index used to align dismiss animations with the current viewer index.
|
|
19
20
|
* @property children - A single React element whose props include `onPress` (e.g., `Pressable`, `Touchable*`).
|
|
20
21
|
* @property onPress - Optional handler invoked after the child's own `onPress`.
|
|
21
22
|
*/
|
|
22
23
|
export type GestureTriggerProps<T extends WithOnPress> = {
|
|
23
24
|
id?: string;
|
|
25
|
+
index?: number;
|
|
24
26
|
children: ReactElement<T>;
|
|
25
27
|
onPress?: (...args: unknown[]) => void;
|
|
26
28
|
};
|
|
@@ -30,15 +32,19 @@ export type GestureTriggerProps<T extends WithOnPress> = {
|
|
|
30
32
|
*
|
|
31
33
|
* @remarks
|
|
32
34
|
* Behavior on press:
|
|
33
|
-
* - Registers the child's native node
|
|
35
|
+
* - Registers the child's native node as the active trigger under the given `id`.
|
|
34
36
|
* - Invokes the child's own `onPress` first (if provided).
|
|
35
37
|
* - Invokes the `onPress` passed to `GestureTrigger` next (if provided).
|
|
36
38
|
*
|
|
39
|
+
* When `index` is provided, the trigger is also registered by item index while mounted,
|
|
40
|
+
* allowing dismiss animations to target the currently visible item instead of only the opening trigger.
|
|
41
|
+
*
|
|
37
42
|
* Type parameters:
|
|
38
43
|
* - `T` — The child's props type. Must include an optional `onPress` handler, ensuring the child is pressable.
|
|
39
44
|
*
|
|
40
45
|
* Props:
|
|
41
46
|
* - `id` — Optional identifier to associate this trigger with a `GestureViewer`. Defaults to `"default"`.
|
|
47
|
+
* - `index` — Optional item index used to support dismiss animations back to the current item trigger.
|
|
42
48
|
* - `children` — A single React element whose props include `onPress` (e.g., `Pressable`, `Touchable*`, custom button).
|
|
43
49
|
* - `onPress` — Optional handler invoked after the child's `onPress`. Receives the same arguments as the child's handler.
|
|
44
50
|
*
|
|
@@ -48,7 +54,7 @@ export type GestureTriggerProps<T extends WithOnPress> = {
|
|
|
48
54
|
*
|
|
49
55
|
* Example:
|
|
50
56
|
* ```tsx
|
|
51
|
-
* <GestureTrigger id="gallery" onPress={() => openModal(index)}>
|
|
57
|
+
* <GestureTrigger id="gallery" index={index} onPress={() => openModal(index)}>
|
|
52
58
|
* <Pressable style={styles.thumb}>
|
|
53
59
|
* <Image source={{ uri }} style={styles.thumbImage} />
|
|
54
60
|
* </Pressable>
|
|
@@ -57,10 +63,29 @@ export type GestureTriggerProps<T extends WithOnPress> = {
|
|
|
57
63
|
*/
|
|
58
64
|
export function GestureTrigger<T extends WithOnPress>({
|
|
59
65
|
id = 'default',
|
|
66
|
+
index,
|
|
60
67
|
children,
|
|
61
68
|
onPress,
|
|
62
69
|
}: GestureTriggerProps<T>) {
|
|
63
|
-
const ref = useRef<View>(null);
|
|
70
|
+
const ref = useRef<View | null>(null);
|
|
71
|
+
|
|
72
|
+
const handleRef = useCallback(
|
|
73
|
+
(node: View | null) => {
|
|
74
|
+
ref.current = node;
|
|
75
|
+
|
|
76
|
+
if (index === undefined) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (node) {
|
|
81
|
+
registry.setIndexedTriggerNode(id, index, node);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
registry.clearIndexedTriggerNode(id, index);
|
|
86
|
+
},
|
|
87
|
+
[id, index],
|
|
88
|
+
);
|
|
64
89
|
|
|
65
90
|
const wrapped = useMemo(() => {
|
|
66
91
|
const child = Children.only(children);
|
|
@@ -72,7 +97,7 @@ export function GestureTrigger<T extends WithOnPress>({
|
|
|
72
97
|
const originalOnPress = child.props?.onPress;
|
|
73
98
|
|
|
74
99
|
const handlePress = (...args: unknown[]) => {
|
|
75
|
-
registry.
|
|
100
|
+
registry.setActiveTriggerNode(id, ref.current);
|
|
76
101
|
originalOnPress?.(...args);
|
|
77
102
|
onPress?.(...args);
|
|
78
103
|
|
|
@@ -90,7 +115,7 @@ export function GestureTrigger<T extends WithOnPress>({
|
|
|
90
115
|
}, [id, onPress, children]);
|
|
91
116
|
|
|
92
117
|
return (
|
|
93
|
-
<View ref={
|
|
118
|
+
<View ref={handleRef} collapsable={false}>
|
|
94
119
|
{wrapped}
|
|
95
120
|
</View>
|
|
96
121
|
);
|
package/src/GestureViewer.tsx
CHANGED
|
@@ -12,7 +12,13 @@ import Animated from 'react-native-reanimated';
|
|
|
12
12
|
import { registry } from './GestureViewerRegistry';
|
|
13
13
|
import type { GestureViewerProps } from './types';
|
|
14
14
|
import { useGestureViewer } from './useGestureViewer';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createLoopData,
|
|
17
|
+
isFlashListLike,
|
|
18
|
+
isFlatListLike,
|
|
19
|
+
isScrollViewLike,
|
|
20
|
+
shouldUseNativeScrollGesture,
|
|
21
|
+
} from './utils';
|
|
16
22
|
import WebPagingFixStyle from './WebPagingFixStyle';
|
|
17
23
|
|
|
18
24
|
export function GestureViewer<ItemT, LC>({
|
|
@@ -53,7 +59,9 @@ export function GestureViewer<ItemT, LC>({
|
|
|
53
59
|
dismissGesture,
|
|
54
60
|
zoomGesture,
|
|
55
61
|
nativeScrollGesture,
|
|
62
|
+
onWebDoubleClick,
|
|
56
63
|
onMomentumScrollEnd,
|
|
64
|
+
onScroll,
|
|
57
65
|
onScrollBeginDrag,
|
|
58
66
|
animatedStyle,
|
|
59
67
|
backdropStyle,
|
|
@@ -130,7 +138,8 @@ export function GestureViewer<ItemT, LC>({
|
|
|
130
138
|
horizontal: true,
|
|
131
139
|
scrollEnabled: !isZoomed && !isRotated && !isPinching,
|
|
132
140
|
showsHorizontalScrollIndicator: false,
|
|
133
|
-
onMomentumScrollEnd
|
|
141
|
+
onMomentumScrollEnd,
|
|
142
|
+
onScroll,
|
|
134
143
|
onScrollBeginDrag,
|
|
135
144
|
...(enableSnapMode
|
|
136
145
|
? {
|
|
@@ -151,6 +160,7 @@ export function GestureViewer<ItemT, LC>({
|
|
|
151
160
|
isRotated,
|
|
152
161
|
isPinching,
|
|
153
162
|
onMomentumScrollEnd,
|
|
163
|
+
onScroll,
|
|
154
164
|
onScrollBeginDrag,
|
|
155
165
|
enableSnapMode,
|
|
156
166
|
],
|
|
@@ -158,6 +168,22 @@ export function GestureViewer<ItemT, LC>({
|
|
|
158
168
|
|
|
159
169
|
const control = useMemo(() => ({ dismiss: handleDismiss }), [handleDismiss]);
|
|
160
170
|
|
|
171
|
+
const shouldWrapScrollableWithNativeGesture = shouldUseNativeScrollGesture(
|
|
172
|
+
Platform.OS,
|
|
173
|
+
Component,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const maybeWrapWithNativeScrollGesture = useCallback(
|
|
177
|
+
(children: React.ReactNode) => {
|
|
178
|
+
if (!shouldWrapScrollableWithNativeGesture) {
|
|
179
|
+
return children;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return <GestureDetector gesture={nativeScrollGesture}>{children}</GestureDetector>;
|
|
183
|
+
},
|
|
184
|
+
[nativeScrollGesture, shouldWrapScrollableWithNativeGesture],
|
|
185
|
+
);
|
|
186
|
+
|
|
161
187
|
const listComponent = (
|
|
162
188
|
<GestureHandlerRootView>
|
|
163
189
|
<GestureDetector gesture={gesture}>
|
|
@@ -165,18 +191,18 @@ export function GestureViewer<ItemT, LC>({
|
|
|
165
191
|
<Animated.View style={[styles.background, backdropStyleProps, backdropStyle]} />
|
|
166
192
|
<Animated.View
|
|
167
193
|
style={[styles.content, animatedStyle]}
|
|
194
|
+
{...(Platform.OS === 'web' && { onClick: onWebDoubleClick })}
|
|
168
195
|
{...(Platform.OS === 'web' &&
|
|
169
196
|
isFlashListLike(Component) && { dataSet: { 'flash-list-paging-enabled-fix': true } })}
|
|
170
197
|
>
|
|
171
|
-
{isScrollView
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
<GestureDetector gesture={nativeScrollGesture}>
|
|
198
|
+
{isScrollView
|
|
199
|
+
? maybeWrapWithNativeScrollGesture(
|
|
200
|
+
<Component ref={listRef} {...commonProps} {...listProps}>
|
|
201
|
+
{loopData.map((item, index) => renderItem({ item, index }))}
|
|
202
|
+
</Component>,
|
|
203
|
+
)
|
|
204
|
+
: isFlatListLike(Component) &&
|
|
205
|
+
maybeWrapWithNativeScrollGesture(
|
|
180
206
|
<Component
|
|
181
207
|
ref={listRef}
|
|
182
208
|
{...commonProps}
|
|
@@ -196,10 +222,8 @@ export function GestureViewer<ItemT, LC>({
|
|
|
196
222
|
dataSet: { 'flat-list-paging-enabled-fix': true },
|
|
197
223
|
})}
|
|
198
224
|
{...listProps}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
)
|
|
202
|
-
)}
|
|
225
|
+
/>,
|
|
226
|
+
)}
|
|
203
227
|
</Animated.View>
|
|
204
228
|
<WebPagingFixStyle Component={Component} />
|
|
205
229
|
</View>
|
|
@@ -22,8 +22,10 @@ class GestureViewerManager {
|
|
|
22
22
|
private rotation: SharedValue<number> | null = null;
|
|
23
23
|
private translateX: SharedValue<number> | null = null;
|
|
24
24
|
private translateY: SharedValue<number> | null = null;
|
|
25
|
+
private resetTransformCallback: (() => void) | null = null;
|
|
25
26
|
|
|
26
27
|
private loopCallback: (() => void) | null = null;
|
|
28
|
+
private programmaticScrollVersion = 0;
|
|
27
29
|
|
|
28
30
|
private listeners = new Set<(state: GestureViewerState) => void>();
|
|
29
31
|
private eventListeners = new Map<GestureViewerEventType, Set<(data: any) => void>>();
|
|
@@ -91,6 +93,10 @@ class GestureViewerManager {
|
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
getProgrammaticScrollVersion() {
|
|
97
|
+
return this.programmaticScrollVersion;
|
|
98
|
+
}
|
|
99
|
+
|
|
94
100
|
setEnableLoop(enabled: boolean) {
|
|
95
101
|
this.enableLoop = enabled;
|
|
96
102
|
}
|
|
@@ -133,6 +139,10 @@ class GestureViewerManager {
|
|
|
133
139
|
this.maxZoomScale = maxZoomScale;
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
setResetTransformCallback(callback: (() => void) | null) {
|
|
143
|
+
this.resetTransformCallback = callback;
|
|
144
|
+
}
|
|
145
|
+
|
|
136
146
|
notifyStateChange() {
|
|
137
147
|
this.notifyListeners();
|
|
138
148
|
}
|
|
@@ -255,7 +265,7 @@ class GestureViewerManager {
|
|
|
255
265
|
return;
|
|
256
266
|
}
|
|
257
267
|
|
|
258
|
-
this.
|
|
268
|
+
this.cancelPendingLoopTransition();
|
|
259
269
|
|
|
260
270
|
const { scrollTo } = createScrollAction(this.listRef, this.width);
|
|
261
271
|
|
|
@@ -264,9 +274,11 @@ class GestureViewerManager {
|
|
|
264
274
|
this.loopCallback = () => {
|
|
265
275
|
scrollTo(this.dataLength, false);
|
|
266
276
|
this.updateCurrentIndex(this.dataLength - 1);
|
|
267
|
-
this.
|
|
277
|
+
this.cancelPendingLoopTransition();
|
|
268
278
|
};
|
|
269
279
|
|
|
280
|
+
this.resetTransformCallback?.();
|
|
281
|
+
this.programmaticScrollVersion += 1;
|
|
270
282
|
scrollTo(0, true);
|
|
271
283
|
return;
|
|
272
284
|
}
|
|
@@ -275,13 +287,17 @@ class GestureViewerManager {
|
|
|
275
287
|
this.loopCallback = () => {
|
|
276
288
|
scrollTo(1, false);
|
|
277
289
|
this.updateCurrentIndex(0);
|
|
278
|
-
this.
|
|
290
|
+
this.cancelPendingLoopTransition();
|
|
279
291
|
};
|
|
280
292
|
|
|
293
|
+
this.resetTransformCallback?.();
|
|
294
|
+
this.programmaticScrollVersion += 1;
|
|
281
295
|
scrollTo(this.dataLength + 1, true);
|
|
282
296
|
return;
|
|
283
297
|
}
|
|
284
298
|
|
|
299
|
+
this.resetTransformCallback?.();
|
|
300
|
+
this.programmaticScrollVersion += 1;
|
|
285
301
|
scrollTo(index + 1, true);
|
|
286
302
|
this.updateCurrentIndex(index);
|
|
287
303
|
|
|
@@ -292,6 +308,8 @@ class GestureViewerManager {
|
|
|
292
308
|
return;
|
|
293
309
|
}
|
|
294
310
|
|
|
311
|
+
this.resetTransformCallback?.();
|
|
312
|
+
this.programmaticScrollVersion += 1;
|
|
295
313
|
scrollTo(index, true);
|
|
296
314
|
this.updateCurrentIndex(index);
|
|
297
315
|
};
|
|
@@ -306,14 +324,18 @@ class GestureViewerManager {
|
|
|
306
324
|
return true;
|
|
307
325
|
}
|
|
308
326
|
|
|
309
|
-
this.
|
|
327
|
+
this.cancelPendingLoopTransition();
|
|
310
328
|
return true;
|
|
311
329
|
};
|
|
312
330
|
|
|
313
|
-
|
|
331
|
+
cancelPendingLoopTransition = () => {
|
|
314
332
|
this.loopCallback = null;
|
|
315
333
|
};
|
|
316
334
|
|
|
335
|
+
handleScrollBeginDrag = () => {
|
|
336
|
+
this.cancelPendingLoopTransition();
|
|
337
|
+
};
|
|
338
|
+
|
|
317
339
|
goToPrevious = () => {
|
|
318
340
|
this.goToIndex(this.currentIndex - 1);
|
|
319
341
|
};
|
|
@@ -323,7 +345,7 @@ class GestureViewerManager {
|
|
|
323
345
|
};
|
|
324
346
|
|
|
325
347
|
cleanUp() {
|
|
326
|
-
this.
|
|
348
|
+
this.cancelPendingLoopTransition();
|
|
327
349
|
this.listeners.clear();
|
|
328
350
|
this.listRef = null;
|
|
329
351
|
this.enableHorizontalSwipe = true;
|
|
@@ -334,6 +356,7 @@ class GestureViewerManager {
|
|
|
334
356
|
this.translateX = null;
|
|
335
357
|
this.translateY = null;
|
|
336
358
|
this.rotation = null;
|
|
359
|
+
this.resetTransformCallback = null;
|
|
337
360
|
this.eventListeners.clear();
|
|
338
361
|
}
|
|
339
362
|
|
|
@@ -5,7 +5,9 @@ import GestureViewerManager from './GestureViewerManager';
|
|
|
5
5
|
class GestureViewerRegistry {
|
|
6
6
|
private managers = new Map<string, GestureViewerManager>();
|
|
7
7
|
private subscribers = new Map<string, Set<(manager: GestureViewerManager | null) => void>>();
|
|
8
|
-
private
|
|
8
|
+
private activeTriggerSubscribers = new Map<string, Set<(node: View | null) => void>>();
|
|
9
|
+
private activeTriggers = new Map<string, View | null>();
|
|
10
|
+
private indexedTriggers = new Map<string, Map<number, View | null>>();
|
|
9
11
|
|
|
10
12
|
subscribeToManager(id: string, callback: (manager: GestureViewerManager | null) => void) {
|
|
11
13
|
if (!this.subscribers.has(id)) {
|
|
@@ -29,6 +31,26 @@ class GestureViewerRegistry {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
subscribeToActiveTrigger(id: string, callback: (node: View | null) => void) {
|
|
35
|
+
if (!this.activeTriggerSubscribers.has(id)) {
|
|
36
|
+
this.activeTriggerSubscribers.set(id, new Set());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.activeTriggerSubscribers.get(id)?.add(callback);
|
|
40
|
+
|
|
41
|
+
callback(this.getActiveTriggerNode(id));
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
const subscribers = this.activeTriggerSubscribers.get(id);
|
|
45
|
+
|
|
46
|
+
subscribers?.delete(callback);
|
|
47
|
+
|
|
48
|
+
if (subscribers && subscribers.size === 0) {
|
|
49
|
+
this.activeTriggerSubscribers.delete(id);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
createManager(id: string): GestureViewerManager | null {
|
|
33
55
|
if (this.managers.has(id)) {
|
|
34
56
|
return this.managers.get(id) || null;
|
|
@@ -55,7 +77,8 @@ class GestureViewerRegistry {
|
|
|
55
77
|
|
|
56
78
|
this.notifySubscribers(id, null);
|
|
57
79
|
|
|
58
|
-
this.
|
|
80
|
+
this.activeTriggers.delete(id);
|
|
81
|
+
this.notifyActiveTriggerSubscribers(id, null);
|
|
59
82
|
}
|
|
60
83
|
}
|
|
61
84
|
|
|
@@ -67,16 +90,52 @@ class GestureViewerRegistry {
|
|
|
67
90
|
}
|
|
68
91
|
}
|
|
69
92
|
|
|
70
|
-
|
|
71
|
-
this.
|
|
93
|
+
notifyActiveTriggerSubscribers(id: string, node: View | null) {
|
|
94
|
+
const listeners = this.activeTriggerSubscribers.get(id);
|
|
95
|
+
|
|
96
|
+
if (listeners) {
|
|
97
|
+
[...listeners].forEach((callback) => callback(node));
|
|
98
|
+
}
|
|
72
99
|
}
|
|
73
100
|
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
setActiveTriggerNode(id: string, node: View | null) {
|
|
102
|
+
this.activeTriggers.set(id, node);
|
|
103
|
+
this.notifyActiveTriggerSubscribers(id, node);
|
|
76
104
|
}
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
this.
|
|
106
|
+
getActiveTriggerNode(id: string): View | null {
|
|
107
|
+
return this.activeTriggers.get(id) ?? null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
clearActiveTriggerNode(id: string) {
|
|
111
|
+
this.activeTriggers.delete(id);
|
|
112
|
+
this.notifyActiveTriggerSubscribers(id, null);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setIndexedTriggerNode(id: string, index: number, node: View | null) {
|
|
116
|
+
if (!this.indexedTriggers.has(id)) {
|
|
117
|
+
this.indexedTriggers.set(id, new Map());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.indexedTriggers.get(id)?.set(index, node);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getIndexedTriggerNode(id: string, index: number): View | null {
|
|
124
|
+
return this.indexedTriggers.get(id)?.get(index) ?? null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
clearIndexedTriggerNode(id: string, index: number) {
|
|
128
|
+
const triggersByIndex = this.indexedTriggers.get(id);
|
|
129
|
+
|
|
130
|
+
if (!triggersByIndex) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
triggersByIndex.delete(index);
|
|
135
|
+
|
|
136
|
+
if (triggersByIndex.size === 0) {
|
|
137
|
+
this.indexedTriggers.delete(id);
|
|
138
|
+
}
|
|
80
139
|
}
|
|
81
140
|
}
|
|
82
141
|
|