react-native-gesture-image-viewer 1.5.0 → 1.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"GestureViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureViewer.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAyE,MAAM,cAAc,CAAC;AAIpH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAKlD,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,OAAO,QAAQ,EAAE,EAC3D,EAAc,EACd,IAAI,EACJ,UAAU,EAAE,cAAc,EAC1B,eAAe,EACf,aAAa,EACb,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,WAAe,EACf,OAAe,EACf,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,2CAuI3B"}
1
+ {"version":3,"file":"GestureViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureViewer.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAyE,MAAM,cAAc,CAAC;AAIpH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAKlD,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,OAAO,QAAQ,EAAE,EAC3D,EAAc,EACd,IAAI,EACJ,UAAU,EAAE,cAAc,EAC1B,eAAe,EACf,aAAa,EACb,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,WAAe,EACf,OAAe,EACf,UAAkB,EAClB,GAAG,KAAK,EACT,EAAE,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,2CAsJ3B"}
@@ -11,9 +11,12 @@ declare class GestureViewerManager {
11
11
  private maxZoomScale;
12
12
  private listRef;
13
13
  private enableSwipeGesture;
14
+ private enableLoop;
14
15
  private listeners;
15
16
  private rotation;
16
17
  private eventListeners;
18
+ private animationTimeout;
19
+ private static readonly LOOP_ANIMATION_DURATION;
17
20
  private notifyListeners;
18
21
  subscribe(listener: (state: GestureViewerControllerState) => void): () => void;
19
22
  addEventListener<T extends GestureViewerEventType>(eventType: T, callback: GestureViewerEventCallback<T>): () => void;
@@ -24,6 +27,7 @@ declare class GestureViewerManager {
24
27
  currentIndex: number;
25
28
  totalCount: number;
26
29
  };
30
+ setEnableLoop(enabled: boolean): void;
27
31
  setWidth(width: number): void;
28
32
  setHeight(height: number): void;
29
33
  setListRef(ref: any): void;
@@ -38,9 +42,11 @@ declare class GestureViewerManager {
38
42
  zoomOut: (multiplier?: number) => void;
39
43
  resetZoom: (scale?: number) => void;
40
44
  goToIndex: (index: number) => void;
45
+ cancelAnimation: () => void;
41
46
  goToPrevious: () => void;
42
47
  goToNext: () => void;
43
48
  cleanUp(): void;
49
+ private updateCurrentIndex;
44
50
  }
45
51
  export default GestureViewerManager;
46
52
  //# sourceMappingURL=GestureViewerManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GestureViewerManager.d.ts","sourceRoot":"","sources":["../../../src/GestureViewerManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EACV,4BAA4B,EAC5B,0BAA0B,EAE1B,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAGjB,cAAM,oBAAoB;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,SAAS,CAA4D;IAC7E,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,cAAc,CAA+D;IAErF,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,4BAA4B,KAAK,IAAI;IAQjE,gBAAgB,CAAC,CAAC,SAAS,sBAAsB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,0BAA0B,CAAC,CAAC,CAAC;IAoBxG,OAAO,CAAC,SAAS;IAQjB,cAAc,GAAI,OAAO,MAAM,EAAE,eAAe,MAAM,GAAG,IAAI,UAE3D;IAEF,kBAAkB,GAAI,UAAU,MAAM,EAAE,kBAAkB,MAAM,GAAG,IAAI,UAErE;IAEF,QAAQ;;;;IAOR,QAAQ,CAAC,KAAK,EAAE,MAAM;IAItB,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,UAAU,CAAC,GAAG,EAAE,GAAG;IAInB,aAAa,CAAC,MAAM,EAAE,MAAM;IAI5B,qBAAqB,CAAC,OAAO,EAAE,OAAO;IAItC,eAAe,CAAC,KAAK,EAAE,MAAM;IAM7B,mBAAmB,CACjB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAC1B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,YAAY,EAAE,MAAM;IAQtB,iBAAiB;IAIjB,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC;IAIzC,MAAM,GAAI,QAAO,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAQ,EAAE,mBAAgB,UA2B9D;IAEF,MAAM,GAAI,mBAAiB,UAoBzB;IAEF,OAAO,GAAI,mBAAiB,UA0B1B;IAEF,SAAS,GAAI,cAAS,UAQpB;IAEF,SAAS,GAAI,OAAO,MAAM,UAcxB;IAEF,YAAY,aAIV;IAEF,QAAQ,aAIN;IAEF,OAAO;CAcR;AAED,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"GestureViewerManager.d.ts","sourceRoot":"","sources":["../../../src/GestureViewerManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EACV,4BAA4B,EAC5B,0BAA0B,EAE1B,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAGjB,cAAM,oBAAoB;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAA4D;IAC7E,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,cAAc,CAA+D;IACrF,OAAO,CAAC,gBAAgB,CAA+B;IAEvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAO;IAEtD,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,4BAA4B,KAAK,IAAI;IAQjE,gBAAgB,CAAC,CAAC,SAAS,sBAAsB,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,0BAA0B,CAAC,CAAC,CAAC;IAoBxG,OAAO,CAAC,SAAS;IAQjB,cAAc,GAAI,OAAO,MAAM,EAAE,eAAe,MAAM,GAAG,IAAI,UAE3D;IAEF,kBAAkB,GAAI,UAAU,MAAM,EAAE,kBAAkB,MAAM,GAAG,IAAI,UAErE;IAEF,QAAQ;;;;IAOR,aAAa,CAAC,OAAO,EAAE,OAAO;IAI9B,QAAQ,CAAC,KAAK,EAAE,MAAM;IAItB,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,UAAU,CAAC,GAAG,EAAE,GAAG;IAInB,aAAa,CAAC,MAAM,EAAE,MAAM;IAI5B,qBAAqB,CAAC,OAAO,EAAE,OAAO;IAItC,eAAe,CAAC,KAAK,EAAE,MAAM;IAM7B,mBAAmB,CACjB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAC1B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,YAAY,EAAE,MAAM;IAQtB,iBAAiB;IAIjB,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC;IAIzC,MAAM,GAAI,QAAO,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAQ,EAAE,mBAAgB,UA2B9D;IAEF,MAAM,GAAI,mBAAiB,UAoBzB;IAEF,OAAO,GAAI,mBAAiB,UA0B1B;IAEF,SAAS,GAAI,cAAS,UAQpB;IAEF,SAAS,GAAI,OAAO,MAAM,UA4CxB;IAEF,eAAe,aAKb;IAEF,YAAY,aAEV;IAEF,QAAQ,aAEN;IAEF,OAAO;IAeP,OAAO,CAAC,kBAAkB,CAGxB;CACH;AAED,eAAe,oBAAoB,CAAC"}
@@ -120,6 +120,11 @@ export interface GestureViewerProps<T = any, LC = typeof RNFlatList> {
120
120
  * @defaultValue true
121
121
  */
122
122
  enableDoubleTapGesture?: boolean;
123
+ /**
124
+ * Enables infinite loop navigation.
125
+ * @defaultValue false
126
+ */
127
+ enableLoop?: boolean;
123
128
  /**
124
129
  * The maximum zoom scale.
125
130
  * @defaultValue 2
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { type NativeScrollEvent, type NativeSyntheticEvent } from 'react-native';
2
2
  import type { GestureViewerProps } from './types';
3
3
  type UseGestureViewerProps<T = any> = Omit<GestureViewerProps<T>, 'renderItem' | 'renderContainer' | 'ListComponent' | 'listProps' | 'containerStyle' | 'backdropStyle'>;
4
- export declare const useGestureViewer: <T = any>({ data, initialIndex, onIndexChange, onDismiss, width: customWidth, dismissThreshold, resistance, animateBackdrop, enableDismissGesture, enableSwipeGesture, enableZoomGesture, enableDoubleTapGesture, enableZoomPanGesture, maxZoomScale, itemSpacing, useSnap, id, }: UseGestureViewerProps<T>) => {
4
+ export declare const useGestureViewer: <T = any>({ data, initialIndex, onIndexChange, onDismiss, width: customWidth, dismissThreshold, resistance, animateBackdrop, enableDismissGesture, enableSwipeGesture, enableZoomGesture, enableDoubleTapGesture, enableZoomPanGesture, enableLoop, maxZoomScale, itemSpacing, useSnap, id, }: UseGestureViewerProps<T>) => {
5
5
  currentIndex: number;
6
6
  dataLength: number;
7
7
  translateY: import("react-native-reanimated").SharedValue<number>;
@@ -11,6 +11,7 @@ export declare const useGestureViewer: <T = any>({ data, initialIndex, onIndexCh
11
11
  dismissGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/panGesture").PanGesture;
12
12
  zoomGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/gestureComposition").ComposedGesture;
13
13
  onMomentumScrollEnd: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
14
+ onScrollBeginDrag: () => void;
14
15
  animatedStyle: {
15
16
  transform: ({
16
17
  translateY: number;
@@ -1 +1 @@
1
- {"version":3,"file":"useGestureViewer.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewer.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,cAAc,CAAC;AActB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGlD,KAAK,qBAAqB,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CACxC,kBAAkB,CAAC,CAAC,CAAC,EACrB,YAAY,GAAG,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,CACtG,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,GAAG,EAAE,yQAoBvC,qBAAqB,CAAC,CAAC,CAAC;;;;;;;;;iCAoKf,oBAAoB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4QlD,CAAC"}
1
+ {"version":3,"file":"useGestureViewer.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewer.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAE1B,MAAM,cAAc,CAAC;AActB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGlD,KAAK,qBAAqB,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,CACxC,kBAAkB,CAAC,CAAC,CAAC,EACrB,YAAY,GAAG,iBAAiB,GAAG,eAAe,GAAG,WAAW,GAAG,gBAAgB,GAAG,eAAe,CACtG,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,GAAG,EAAE,qRAqBvC,qBAAqB,CAAC,CAAC,CAAC;;;;;;;;;iCA6Kf,oBAAoB,CAAC,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyRlD,CAAC"}
@@ -13,4 +13,13 @@ export declare const createBoundsConstraint: ({ width, height }: {
13
13
  translateX: number;
14
14
  translateY: number;
15
15
  };
16
+ export declare const createLoopData: <T>(dataRef: React.RefObject<T[]>, enableLoop: boolean) => T[];
17
+ export declare const getLoopAdjustedIndex: (scrollIndex: number, originalDataLength: number, enableLoop: boolean) => {
18
+ realIndex: number;
19
+ needsJump: boolean;
20
+ jumpToIndex?: number;
21
+ };
22
+ export declare const createScrollAction: (listRef: any, width: number) => {
23
+ scrollTo: (index: number, animated: boolean) => void;
24
+ };
16
25
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEtE,eAAO,MAAM,gBAAgB,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,mBAE9D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,iBAM5D,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,GAY7D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAChC,mBAAmB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,MACpD,mCAAmC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;;;CAiB5F,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEtE,eAAO,MAAM,gBAAgB,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,mBAE9D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,iBAM5D,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,GAY7D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAChC,mBAAmB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,MACpD,mCAAmC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;;;CAiB5F,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-image-viewer",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "🖼️ A highly customizable and easy-to-use React Native image viewer with gesture support and external controls",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,11 +1,11 @@
1
- import { useCallback, useEffect, useMemo } from 'react';
1
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import { type FlatList, Platform, type ScrollViewProps, StyleSheet, useWindowDimensions, View } from 'react-native';
3
3
  import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
4
4
  import Animated from 'react-native-reanimated';
5
5
  import { registry } from './GestureViewerRegistry';
6
6
  import type { GestureViewerProps } from './types';
7
7
  import { useGestureViewer } from './useGestureViewer';
8
- import { isFlashListLike, isFlatListLike, isScrollViewLike } from './utils';
8
+ import { createLoopData, isFlashListLike, isFlatListLike, isScrollViewLike } from './utils';
9
9
  import WebPagingFixStyle from './WebPagingFixStyle';
10
10
 
11
11
  export function GestureViewer<T = any, LC = typeof FlatList>({
@@ -21,14 +21,20 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
21
21
  initialIndex = 0,
22
22
  itemSpacing = 0,
23
23
  useSnap = false,
24
+ enableLoop = false,
24
25
  ...props
25
26
  }: GestureViewerProps<T, LC>) {
26
27
  const Component = ListComponent as React.ComponentType<any>;
27
28
 
29
+ const dataRef = useRef(data);
30
+ dataRef.current = data;
31
+
28
32
  const { width: screenWidth } = useWindowDimensions();
29
33
 
30
34
  const width = useSnap ? customWidth || screenWidth : screenWidth;
31
35
 
36
+ const loopData = useMemo(() => createLoopData(dataRef, enableLoop), [enableLoop]);
37
+
32
38
  const {
33
39
  listRef,
34
40
  isZoomed,
@@ -36,6 +42,7 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
36
42
  dismissGesture,
37
43
  zoomGesture,
38
44
  onMomentumScrollEnd,
45
+ onScrollBeginDrag,
39
46
  animatedStyle,
40
47
  backdropStyle,
41
48
  } = useGestureViewer({
@@ -45,14 +52,26 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
45
52
  initialIndex,
46
53
  itemSpacing,
47
54
  useSnap,
55
+ enableLoop,
48
56
  ...props,
49
57
  });
50
58
 
59
+ const keyExtractor = useCallback(
60
+ (item: T, index: number) => {
61
+ if (enableLoop) {
62
+ return typeof item === 'string' ? `${item}-${index}` : `item-${index}`;
63
+ }
64
+
65
+ return typeof item === 'string' ? item : `image-${index}`;
66
+ },
67
+ [enableLoop],
68
+ );
69
+
51
70
  const renderItem = useCallback(
52
71
  ({ item, index }: { item: T; index: number }) => {
53
72
  return (
54
73
  <View
55
- key={typeof item === 'string' ? item : index}
74
+ key={keyExtractor(item, index)}
56
75
  style={[
57
76
  {
58
77
  width,
@@ -67,7 +86,7 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
67
86
  </View>
68
87
  );
69
88
  },
70
- [width, itemSpacing, renderItemProp],
89
+ [width, itemSpacing, renderItemProp, keyExtractor],
71
90
  );
72
91
 
73
92
  const getItemLayout = useCallback(
@@ -79,11 +98,6 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
79
98
  [width, itemSpacing],
80
99
  );
81
100
 
82
- const keyExtractor = useCallback(
83
- (item: T, index: number) => (typeof item === 'string' ? item : `image-${index}`),
84
- [],
85
- );
86
-
87
101
  const gesture = useMemo(() => {
88
102
  return Gesture.Race(dismissGesture, zoomGesture);
89
103
  }, [zoomGesture, dismissGesture]);
@@ -94,25 +108,27 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
94
108
  return () => registry.deleteManager(id);
95
109
  }, [id]);
96
110
 
97
- const commonProps: ScrollViewProps = useMemo(
98
- () => ({
99
- horizontal: true,
100
- scrollEnabled: !isZoomed && !isRotated,
101
- showsHorizontalScrollIndicator: false,
102
- onMomentumScrollEnd: onMomentumScrollEnd,
103
- ...(useSnap
104
- ? {
105
- snapToInterval: width + itemSpacing,
106
- snapToAlignment: 'center',
107
- decelerationRate: 'fast',
108
- }
109
- : {
110
- pagingEnabled: true,
111
- }),
112
- scrollEventThrottle: 16,
113
- removeClippedSubviews: true,
114
- }),
115
- [width, itemSpacing, isZoomed, isRotated, onMomentumScrollEnd, useSnap],
111
+ const commonProps = useMemo(
112
+ () =>
113
+ ({
114
+ horizontal: true,
115
+ scrollEnabled: !isZoomed && !isRotated,
116
+ showsHorizontalScrollIndicator: false,
117
+ onMomentumScrollEnd: onMomentumScrollEnd,
118
+ onScrollBeginDrag,
119
+ ...(useSnap
120
+ ? {
121
+ snapToInterval: width + itemSpacing,
122
+ snapToAlignment: 'center',
123
+ decelerationRate: 'fast',
124
+ }
125
+ : {
126
+ pagingEnabled: true,
127
+ }),
128
+ scrollEventThrottle: 16,
129
+ removeClippedSubviews: true,
130
+ }) satisfies ScrollViewProps,
131
+ [width, itemSpacing, isZoomed, isRotated, onMomentumScrollEnd, onScrollBeginDrag, useSnap],
116
132
  );
117
133
 
118
134
  const listComponent = (
@@ -127,16 +143,16 @@ export function GestureViewer<T = any, LC = typeof FlatList>({
127
143
  >
128
144
  {isScrollViewLike(Component) ? (
129
145
  <Component ref={listRef} {...commonProps} {...listProps}>
130
- {data.map((item, index) => renderItem({ item, index }))}
146
+ {loopData.map((item, index) => renderItem({ item, index }))}
131
147
  </Component>
132
148
  ) : (
133
149
  isFlatListLike(Component) && (
134
150
  <Component
135
151
  ref={listRef}
136
152
  {...commonProps}
137
- data={data}
153
+ data={loopData}
138
154
  renderItem={renderItem}
139
- initialScrollIndex={initialIndex}
155
+ initialScrollIndex={enableLoop && data.length > 1 ? initialIndex + 1 : initialIndex}
140
156
  keyExtractor={keyExtractor}
141
157
  windowSize={3}
142
158
  maxToRenderPerBatch={3}
@@ -5,7 +5,7 @@ import type {
5
5
  GestureViewerEventData,
6
6
  GestureViewerEventType,
7
7
  } from './types';
8
- import { createBoundsConstraint } from './utils';
8
+ import { createBoundsConstraint, createScrollAction } from './utils';
9
9
 
10
10
  class GestureViewerManager {
11
11
  private currentIndex = 0;
@@ -18,9 +18,13 @@ class GestureViewerManager {
18
18
  private maxZoomScale = 2;
19
19
  private listRef: any | null = null;
20
20
  private enableSwipeGesture = true;
21
+ private enableLoop = false;
21
22
  private listeners = new Set<(state: GestureViewerControllerState) => void>();
22
23
  private rotation: SharedValue<number> | null = null;
23
24
  private eventListeners = new Map<GestureViewerEventType, Set<(data: any) => void>>();
25
+ private animationTimeout: NodeJS.Timeout | null = null;
26
+
27
+ private static readonly LOOP_ANIMATION_DURATION = 300;
24
28
 
25
29
  private notifyListeners() {
26
30
  const state = this.getState();
@@ -79,6 +83,10 @@ class GestureViewerManager {
79
83
  };
80
84
  }
81
85
 
86
+ setEnableLoop(enabled: boolean) {
87
+ this.enableLoop = enabled;
88
+ }
89
+
82
90
  setWidth(width: number) {
83
91
  this.width = width;
84
92
  }
@@ -215,40 +223,73 @@ class GestureViewerManager {
215
223
  };
216
224
 
217
225
  goToIndex = (index: number) => {
218
- if (index < 0 || index >= this.dataLength || !this.enableSwipeGesture || !this.listRef) {
226
+ if (!this.enableSwipeGesture || !this.listRef) {
219
227
  return;
220
228
  }
221
229
 
222
- this.currentIndex = index;
230
+ this.cancelAnimation();
231
+
232
+ const { scrollTo } = createScrollAction(this.listRef, this.width);
233
+
234
+ if (this.enableLoop && this.dataLength > 1) {
235
+ if (index < 0) {
236
+ scrollTo(0, true);
237
+
238
+ this.animationTimeout = setTimeout(() => {
239
+ scrollTo(this.dataLength, false);
240
+ this.updateCurrentIndex(this.dataLength - 1);
241
+ }, GestureViewerManager.LOOP_ANIMATION_DURATION);
242
+ return;
243
+ }
244
+
245
+ if (index >= this.dataLength) {
246
+ scrollTo(this.dataLength + 1, true);
247
+
248
+ this.animationTimeout = setTimeout(() => {
249
+ scrollTo(1, false);
250
+ this.updateCurrentIndex(0);
251
+ }, GestureViewerManager.LOOP_ANIMATION_DURATION);
252
+ return;
253
+ }
254
+
255
+ const scrollIndex = index + 1;
256
+
257
+ scrollTo(scrollIndex, true);
258
+ this.updateCurrentIndex(index);
259
+
260
+ return;
261
+ }
223
262
 
224
- if (this.listRef.scrollToIndex) {
225
- this.listRef.scrollToIndex({ index, animated: true });
226
- } else if (this.listRef.scrollTo) {
227
- this.listRef.scrollTo({ x: index * this.width, animated: true });
263
+ if (index < 0 || index >= this.dataLength) {
264
+ return;
228
265
  }
229
266
 
230
- this.notifyListeners();
267
+ scrollTo(index, true);
268
+ this.updateCurrentIndex(index);
231
269
  };
232
270
 
233
- goToPrevious = () => {
234
- if (this.currentIndex > 0) {
235
- this.goToIndex(this.currentIndex - 1);
271
+ cancelAnimation = () => {
272
+ if (this.animationTimeout) {
273
+ clearTimeout(this.animationTimeout);
274
+ this.animationTimeout = null;
236
275
  }
237
276
  };
238
277
 
278
+ goToPrevious = () => {
279
+ this.goToIndex(this.currentIndex - 1);
280
+ };
281
+
239
282
  goToNext = () => {
240
- if (this.currentIndex < this.dataLength - 1) {
241
- this.goToIndex(this.currentIndex + 1);
242
- }
283
+ this.goToIndex(this.currentIndex + 1);
243
284
  };
244
285
 
245
286
  cleanUp() {
287
+ this.cancelAnimation();
246
288
  this.listeners.clear();
247
289
  this.listRef = null;
248
290
  this.enableSwipeGesture = true;
249
291
  this.currentIndex = 0;
250
292
  this.dataLength = 0;
251
-
252
293
  this.maxZoomScale = 2;
253
294
  this.scale = null;
254
295
  this.translateX = null;
@@ -256,6 +297,11 @@ class GestureViewerManager {
256
297
  this.rotation = null;
257
298
  this.eventListeners.clear();
258
299
  }
300
+
301
+ private updateCurrentIndex = (targetIndex: number) => {
302
+ this.currentIndex = targetIndex;
303
+ this.notifyListeners();
304
+ };
259
305
  }
260
306
 
261
307
  export default GestureViewerManager;
package/src/types.ts CHANGED
@@ -130,6 +130,11 @@ export interface GestureViewerProps<T = any, LC = typeof RNFlatList> {
130
130
  * @defaultValue true
131
131
  */
132
132
  enableDoubleTapGesture?: boolean;
133
+ /**
134
+ * Enables infinite loop navigation.
135
+ * @defaultValue false
136
+ */
137
+ enableLoop?: boolean;
133
138
  /**
134
139
  * The maximum zoom scale.
135
140
  * @defaultValue 2
@@ -19,7 +19,7 @@ import {
19
19
  import type GestureViewerManager from './GestureViewerManager';
20
20
  import { registry } from './GestureViewerRegistry';
21
21
  import type { GestureViewerProps } from './types';
22
- import { createBoundsConstraint } from './utils';
22
+ import { createBoundsConstraint, createScrollAction, getLoopAdjustedIndex } from './utils';
23
23
 
24
24
  type UseGestureViewerProps<T = any> = Omit<
25
25
  GestureViewerProps<T>,
@@ -42,6 +42,7 @@ export const useGestureViewer = <T = any>({
42
42
  enableZoomGesture = true,
43
43
  enableDoubleTapGesture = true,
44
44
  enableZoomPanGesture = true,
45
+ enableLoop = false,
45
46
  maxZoomScale = 2,
46
47
  itemSpacing = 0,
47
48
  useSnap = false,
@@ -71,11 +72,28 @@ export const useGestureViewer = <T = any>({
71
72
 
72
73
  const dataLength = data?.length || 0;
73
74
 
75
+ const adjustedInitialIndex = useMemo(() => {
76
+ if (enableLoop && data.length > 1) {
77
+ return initialIndex + 1;
78
+ }
79
+
80
+ return initialIndex;
81
+ }, [enableLoop, data.length, initialIndex]);
82
+
74
83
  const constrainTranslation = useMemo(
75
84
  () => createBoundsConstraint({ width, height: screenHeight }),
76
85
  [width, screenHeight],
77
86
  );
78
87
 
88
+ const scrollTo = useCallback(
89
+ (index: number, animated: boolean) => {
90
+ const scrollAction = createScrollAction(listRef.current, width + itemSpacing);
91
+
92
+ return scrollAction.scrollTo(index, animated);
93
+ },
94
+ [width, itemSpacing],
95
+ );
96
+
79
97
  const emitZoomChange = useCallback(
80
98
  (currentScale: number, prevScale: number | null) => {
81
99
  manager?.emitZoomChange(currentScale, prevScale);
@@ -150,6 +168,7 @@ export const useGestureViewer = <T = any>({
150
168
  manager.setHeight(screenHeight);
151
169
  manager.setZoomSharedValues(scale, translateX, translateY, maxZoomScale);
152
170
  manager.setRotation(rotation);
171
+ manager.setEnableLoop(enableLoop);
153
172
  manager.notifyStateChange();
154
173
  }, [
155
174
  dataLength,
@@ -159,6 +178,7 @@ export const useGestureViewer = <T = any>({
159
178
  width,
160
179
  itemSpacing,
161
180
  maxZoomScale,
181
+ enableLoop,
162
182
  scale,
163
183
  screenHeight,
164
184
  translateX,
@@ -186,28 +206,18 @@ export const useGestureViewer = <T = any>({
186
206
  startScale.value = 1;
187
207
  rotation.value = 0;
188
208
 
189
- if (initialIndex <= 0 || !listRef.current) {
209
+ if (adjustedInitialIndex <= 0 || !listRef.current) {
190
210
  return;
191
211
  }
192
212
 
193
213
  const runAfterInteractions = InteractionManager.runAfterInteractions(() => {
194
- if (listRef.current.scrollToIndex) {
195
- listRef.current.scrollToIndex({
196
- index: initialIndex,
197
- animated: false,
198
- });
199
- } else if (listRef.current.scrollTo) {
200
- listRef.current.scrollTo({
201
- x: initialIndex * (width + itemSpacing),
202
- animated: false,
203
- });
204
- }
214
+ scrollTo(adjustedInitialIndex, false);
205
215
  });
206
216
 
207
217
  return () => {
208
218
  runAfterInteractions?.cancel();
209
219
  };
210
- }, [initialIndex, translateY, backdropOpacity, translateX, scale, startScale, width, itemSpacing, rotation]);
220
+ }, [adjustedInitialIndex, translateY, backdropOpacity, translateX, scale, startScale, rotation, scrollTo]);
211
221
 
212
222
  const onMomentumScrollEnd = useCallback(
213
223
  (event: NativeSyntheticEvent<NativeScrollEvent>) => {
@@ -216,12 +226,18 @@ export const useGestureViewer = <T = any>({
216
226
  }
217
227
 
218
228
  const contentOffset = event.nativeEvent.contentOffset;
219
- const newIndex = Math.round(contentOffset.x / (width + itemSpacing));
229
+ const scrollIndex = Math.round(contentOffset.x / (width + itemSpacing));
220
230
 
221
- if (newIndex !== currentIndex && newIndex >= 0 && newIndex < dataLength) {
231
+ const { realIndex, needsJump, jumpToIndex } = getLoopAdjustedIndex(scrollIndex, dataLength, enableLoop);
232
+
233
+ if (needsJump && jumpToIndex !== undefined) {
234
+ scrollTo(jumpToIndex, false);
235
+ }
236
+
237
+ if (realIndex !== currentIndex && realIndex >= 0 && realIndex < dataLength) {
222
238
  if (manager) {
223
- manager.setCurrentIndex(newIndex);
224
- setCurrentIndex(newIndex);
239
+ manager.setCurrentIndex(realIndex);
240
+ setCurrentIndex(realIndex);
225
241
  manager.notifyStateChange();
226
242
  }
227
243
 
@@ -235,12 +251,14 @@ export const useGestureViewer = <T = any>({
235
251
  }
236
252
  },
237
253
  [
254
+ scrollTo,
238
255
  manager,
239
256
  currentIndex,
240
257
  dataLength,
241
258
  width,
242
259
  itemSpacing,
243
260
  enableSwipeGesture,
261
+ enableLoop,
244
262
  translateX,
245
263
  translateY,
246
264
  scale,
@@ -463,6 +481,10 @@ export const useGestureViewer = <T = any>({
463
481
  return { opacity };
464
482
  }, [animateBackdrop]);
465
483
 
484
+ const onScrollBeginDrag = useCallback(() => {
485
+ manager?.cancelAnimation();
486
+ }, [manager]);
487
+
466
488
  return {
467
489
  currentIndex,
468
490
  dataLength,
@@ -474,6 +496,7 @@ export const useGestureViewer = <T = any>({
474
496
  zoomGesture,
475
497
 
476
498
  onMomentumScrollEnd,
499
+ onScrollBeginDrag,
477
500
 
478
501
  animatedStyle,
479
502
  backdropStyle,
package/src/utils.ts CHANGED
@@ -48,3 +48,56 @@ export const createBoundsConstraint =
48
48
  translateY: Math.max(-maxTranslateY, Math.min(maxTranslateY, translateY)),
49
49
  };
50
50
  };
51
+
52
+ export const createLoopData = <T>(dataRef: React.RefObject<T[]>, enableLoop: boolean): T[] => {
53
+ const data = dataRef.current;
54
+
55
+ if (!enableLoop || !data?.length || data.length <= 1) {
56
+ return data;
57
+ }
58
+
59
+ const lastItem = data[data.length - 1];
60
+ const firstItem = data[0];
61
+
62
+ if (lastItem === undefined || firstItem === undefined) {
63
+ return data;
64
+ }
65
+
66
+ return [lastItem, ...data, firstItem];
67
+ };
68
+
69
+ export const getLoopAdjustedIndex = (
70
+ scrollIndex: number,
71
+ originalDataLength: number,
72
+ enableLoop: boolean,
73
+ ): { realIndex: number; needsJump: boolean; jumpToIndex?: number } => {
74
+ if (!enableLoop || originalDataLength <= 1) {
75
+ return { realIndex: scrollIndex, needsJump: false };
76
+ }
77
+
78
+ if (scrollIndex === 0) {
79
+ return {
80
+ realIndex: originalDataLength - 1,
81
+ needsJump: true,
82
+ jumpToIndex: originalDataLength,
83
+ };
84
+ } else if (scrollIndex === originalDataLength + 1) {
85
+ return {
86
+ realIndex: 0,
87
+ needsJump: true,
88
+ jumpToIndex: 1,
89
+ };
90
+ }
91
+
92
+ return { realIndex: scrollIndex - 1, needsJump: false };
93
+ };
94
+
95
+ export const createScrollAction = (listRef: any, width: number) => ({
96
+ scrollTo: (index: number, animated: boolean) => {
97
+ if (listRef?.scrollToIndex) {
98
+ listRef.scrollToIndex({ index, animated });
99
+ } else if (listRef?.scrollTo) {
100
+ listRef.scrollTo({ x: index * width, animated });
101
+ }
102
+ },
103
+ });