react-native-gesture-image-viewer 2.3.3 → 2.5.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 (51) hide show
  1. package/README.md +19 -5
  2. package/lib/module/GestureViewer.js +39 -15
  3. package/lib/module/GestureViewer.js.map +1 -1
  4. package/lib/module/GestureViewerManager.js +43 -14
  5. package/lib/module/GestureViewerManager.js.map +1 -1
  6. package/lib/module/index.js.map +1 -1
  7. package/lib/module/itemDimensions.js +86 -0
  8. package/lib/module/itemDimensions.js.map +1 -0
  9. package/lib/module/useGestureViewer.js +246 -28
  10. package/lib/module/useGestureViewer.js.map +1 -1
  11. package/lib/module/useGestureViewerPaging.js +17 -5
  12. package/lib/module/useGestureViewerPaging.js.map +1 -1
  13. package/lib/module/useGestureViewerPaging.web.js +31 -7
  14. package/lib/module/useGestureViewerPaging.web.js.map +1 -1
  15. package/lib/module/utils/index.js +43 -10
  16. package/lib/module/utils/index.js.map +1 -1
  17. package/lib/module/utils/tapZoom.js +51 -13
  18. package/lib/module/utils/tapZoom.js.map +1 -1
  19. package/lib/typescript/src/GestureViewer.d.ts.map +1 -1
  20. package/lib/typescript/src/GestureViewerManager.d.ts +16 -3
  21. package/lib/typescript/src/GestureViewerManager.d.ts.map +1 -1
  22. package/lib/typescript/src/index.d.ts +1 -1
  23. package/lib/typescript/src/index.d.ts.map +1 -1
  24. package/lib/typescript/src/itemDimensions.d.ts +26 -0
  25. package/lib/typescript/src/itemDimensions.d.ts.map +1 -0
  26. package/lib/typescript/src/types.d.ts +40 -1
  27. package/lib/typescript/src/types.d.ts.map +1 -1
  28. package/lib/typescript/src/useGestureViewer.d.ts +4 -2
  29. package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
  30. package/lib/typescript/src/useGestureViewerPaging.d.ts +1 -1
  31. package/lib/typescript/src/useGestureViewerPaging.d.ts.map +1 -1
  32. package/lib/typescript/src/useGestureViewerPaging.types.d.ts +3 -0
  33. package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +1 -1
  34. package/lib/typescript/src/useGestureViewerPaging.web.d.ts +1 -1
  35. package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +1 -1
  36. package/lib/typescript/src/utils/index.d.ts +7 -3
  37. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  38. package/lib/typescript/src/utils/tapZoom.d.ts +17 -1
  39. package/lib/typescript/src/utils/tapZoom.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/GestureViewer.tsx +64 -18
  42. package/src/GestureViewerManager.ts +58 -19
  43. package/src/index.tsx +5 -0
  44. package/src/itemDimensions.ts +162 -0
  45. package/src/types.ts +51 -1
  46. package/src/useGestureViewer.ts +381 -52
  47. package/src/useGestureViewerPaging.ts +21 -7
  48. package/src/useGestureViewerPaging.types.ts +3 -0
  49. package/src/useGestureViewerPaging.web.ts +46 -7
  50. package/src/utils/index.ts +86 -27
  51. package/src/utils/tapZoom.ts +66 -13
@@ -6,6 +6,8 @@ export type UseGestureViewerPagingArgs = {
6
6
  adjustedInitialIndex: number;
7
7
  autoPlay: boolean;
8
8
  autoPlayInterval: number;
9
+ contentHeight: SharedValue<number>;
10
+ contentWidth: SharedValue<number>;
9
11
  currentIndex: number;
10
12
  dataLength: number;
11
13
  enableDoubleTapZoom: boolean;
@@ -33,6 +35,7 @@ export type WebClickTarget = {
33
35
  };
34
36
  };
35
37
  export type UseGestureViewerPagingResult = {
38
+ activeListIndex: number;
36
39
  onMomentumScrollEnd?: ScrollViewProps['onMomentumScrollEnd'];
37
40
  onScroll?: ScrollViewProps['onScroll'];
38
41
  onScrollBeginDrag?: ScrollViewProps['onScrollBeginDrag'];
@@ -1 +1 @@
1
- {"version":3,"file":"useGestureViewerPaging.types.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,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,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,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,cAAc,GAAG;IAC3B,qBAAqB,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,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,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;CAC1D,CAAC"}
1
+ {"version":3,"file":"useGestureViewerPaging.types.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,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,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,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,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,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,cAAc,GAAG;IAC3B,qBAAqB,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,eAAe,EAAE,MAAM,CAAC;IACxB,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,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;CAC1D,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { UseGestureViewerPagingArgs, UseGestureViewerPagingResult } from './useGestureViewerPaging.types';
2
- export declare function useGestureViewerPaging({ adjustedInitialIndex, autoPlay, autoPlayInterval, currentIndex, dataLength, enableDoubleTapZoom, enableHorizontalSwipe, enableLoop, height, onSingleTap, isRotated, isZoomed, itemSpacing, manager, maxZoomScale, scale, scrollTo, syncCurrentIndex, syncPendingIndex, translateX, translateY, width, }: UseGestureViewerPagingArgs): UseGestureViewerPagingResult;
2
+ export declare function useGestureViewerPaging({ adjustedInitialIndex, autoPlay, autoPlayInterval, contentHeight, contentWidth, currentIndex, dataLength, enableDoubleTapZoom, enableHorizontalSwipe, enableLoop, height, onSingleTap, isRotated, isZoomed, itemSpacing, manager, maxZoomScale, scale, scrollTo, syncCurrentIndex, syncPendingIndex, translateX, translateY, width, }: UseGestureViewerPagingArgs): UseGestureViewerPagingResult;
3
3
  //# sourceMappingURL=useGestureViewerPaging.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGestureViewerPaging.web.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.web.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAE7B,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,WAAW,EACX,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,CA8X3D"}
1
+ {"version":3,"file":"useGestureViewerPaging.web.d.ts","sourceRoot":"","sources":["../../../src/useGestureViewerPaging.web.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAE7B,MAAM,gCAAgC,CAAC;AAqBxC,wBAAgB,sBAAsB,CAAC,EACrC,oBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,qBAAqB,EACrB,UAAU,EACV,MAAM,EACN,WAAW,EACX,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,CAma3D"}
@@ -9,10 +9,13 @@ export declare const isFlashListLike: (component: React.ComponentType<any>) => b
9
9
  * and we also avoid wrapping RNGH-provided scrollables to prevent double-applying Gesture.Native() to components that already participate in RNGH.
10
10
  */
11
11
  export declare const shouldUseNativeScrollGesture: (platformOS: PlatformOSType, component: React.ComponentType<any>) => boolean;
12
- export declare const createBoundsConstraint: ({ width, height }: {
12
+ export declare const clampIndex: (index: number | undefined, dataLength: number) => number;
13
+ export declare const resolveGeometrySyncTranslationMode: (previousIndex: number | null, nextIndex: number, scale: number) => "constrain" | "none" | "reset";
14
+ export declare const clampTranslationToBounds: ({ width, height, contentWidth, contentHeight, scale, translateX, translateY, }: {
13
15
  width: number;
14
16
  height: number;
15
- }) => ({ scale, translateX, translateY, }: {
17
+ contentWidth?: number;
18
+ contentHeight?: number;
16
19
  translateX: number;
17
20
  translateY: number;
18
21
  scale: number;
@@ -20,7 +23,8 @@ export declare const createBoundsConstraint: ({ width, height }: {
20
23
  translateX: number;
21
24
  translateY: number;
22
25
  };
23
- export declare const createLoopData: <T>(dataRef: React.RefObject<T[]>, enableLoop: boolean) => T[];
26
+ export declare const createLoopData: <T>(data: T[], enableLoop: boolean) => T[];
27
+ export declare const getLoopPhysicalIndex: (logicalIndex: number, dataLength: number, enableLoop: boolean) => number;
24
28
  export declare const getLoopAdjustedIndex: (scrollIndex: number, originalDataLength: number, enableLoop: boolean) => {
25
29
  realIndex: number;
26
30
  needsJump: boolean;
@@ -1 +1 @@
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"}
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,UAAU,GAAI,OAAO,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,KAAG,MAY1E,CAAC;AAEF,eAAO,MAAM,kCAAkC,GAC7C,eAAe,MAAM,GAAG,IAAI,EAC5B,WAAW,MAAM,EACjB,OAAO,MAAM,KACZ,WAAW,GAAG,MAAM,GAAG,OAMzB,CAAC;AAoBF,eAAO,MAAM,wBAAwB,GAAI,gFAQtC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;;;CAsBA,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,YAAY,OAAO,KAAG,CAAC,EAanE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EACpB,YAAY,MAAM,EAClB,YAAY,OAAO,KAClB,MAA0E,CAAC;AAE9E,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"}
@@ -1,9 +1,25 @@
1
1
  import type { SharedValue } from 'react-native-reanimated';
2
- export declare const applyTapZoomAtPoint: ({ x, y, width, height, maxZoomScale, scale, translateX, translateY, }: {
2
+ export declare const getTapZoomTarget: ({ x, y, width, height, contentWidth, contentHeight, maxZoomScale, scale, }: {
3
3
  x: number;
4
4
  y: number;
5
5
  width: number;
6
6
  height: number;
7
+ contentWidth?: number;
8
+ contentHeight?: number;
9
+ maxZoomScale: number;
10
+ scale: number;
11
+ }) => {
12
+ scale: number;
13
+ translateX: number;
14
+ translateY: number;
15
+ };
16
+ export declare const applyTapZoomAtPoint: ({ x, y, width, height, contentWidth, contentHeight, maxZoomScale, scale, translateX, translateY, }: {
17
+ x: number;
18
+ y: number;
19
+ width: number;
20
+ height: number;
21
+ contentWidth?: number;
22
+ contentHeight?: number;
7
23
  maxZoomScale: number;
8
24
  scale: SharedValue<number>;
9
25
  translateX: SharedValue<number>;
@@ -1 +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"}
1
+ {"version":3,"file":"tapZoom.d.ts","sourceRoot":"","sources":["../../../../src/utils/tapZoom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAK3D,eAAO,MAAM,gBAAgB,GAAI,4EAS9B;IACD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;;;;CA2BA,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,oGAWjC;IACD,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-image-viewer",
3
- "version": "2.3.3",
3
+ "version": "2.5.0",
4
4
  "description": "🖼️ React Native Image Viewer - Reanimated-powered image gestures with full control",
5
5
  "keywords": [
6
6
  "android",
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo, useRef } from 'react';
1
+ import { useCallback, useEffect, useMemo, type ReactElement } from 'react';
2
2
  import {
3
3
  Platform,
4
4
  type ScrollViewProps,
@@ -10,10 +10,16 @@ import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-g
10
10
  import Animated from 'react-native-reanimated';
11
11
 
12
12
  import { registry } from './GestureViewerRegistry';
13
- import type { GestureViewerProps } from './types';
13
+ import type {
14
+ GestureViewerItemDimensions,
15
+ GestureViewerProps,
16
+ GestureViewerRenderItemInfo,
17
+ } from './types';
14
18
  import { useGestureViewer } from './useGestureViewer';
15
19
  import {
20
+ clampIndex,
16
21
  createLoopData,
22
+ getLoopPhysicalIndex,
17
23
  isFlashListLike,
18
24
  isFlatListLike,
19
25
  isScrollViewLike,
@@ -21,6 +27,31 @@ import {
21
27
  } from './utils';
22
28
  import WebPagingFixStyle from './WebPagingFixStyle';
23
29
 
30
+ type GestureViewerItemCellProps<ItemT> = {
31
+ item: ItemT;
32
+ index: number;
33
+ isActive: boolean;
34
+ renderItem: (item: ItemT, index: number, info: GestureViewerRenderItemInfo) => ReactElement;
35
+ setItemDimensions: (index: number, item: ItemT, dimensions: GestureViewerItemDimensions) => void;
36
+ };
37
+
38
+ function GestureViewerItemCell<ItemT>({
39
+ item,
40
+ index,
41
+ isActive,
42
+ renderItem,
43
+ setItemDimensions,
44
+ }: GestureViewerItemCellProps<ItemT>) {
45
+ const registerItemDimensions = (dimensions: GestureViewerItemDimensions) => {
46
+ setItemDimensions(index, item, dimensions);
47
+ };
48
+
49
+ return renderItem(item, index, {
50
+ isActive,
51
+ setItemDimensions: registerItemDimensions,
52
+ });
53
+ }
54
+
24
55
  export function GestureViewer<ItemT, LC>({
25
56
  id = 'default',
26
57
  data,
@@ -40,18 +71,20 @@ export function GestureViewer<ItemT, LC>({
40
71
  }: GestureViewerProps<ItemT, LC>) {
41
72
  const Component = ListComponent as React.ComponentType<any>;
42
73
 
43
- const dataRef = useRef(data);
44
-
45
74
  const { width: screenWidth, height: screenHeight } = useWindowDimensions();
46
75
 
47
76
  const width = customWidth || screenWidth;
48
77
  const height = customHeight || screenHeight;
78
+ const normalizedInitialIndex = clampIndex(initialIndex, data.length);
79
+ const initialListIndex = getLoopPhysicalIndex(normalizedInitialIndex, data.length, enableLoop);
49
80
 
50
- const loopData = useMemo(() => createLoopData(dataRef, enableLoop), [enableLoop]);
81
+ const loopData = useMemo(() => createLoopData(data, enableLoop), [data, enableLoop]);
51
82
 
52
83
  const isScrollView = isScrollViewLike(Component);
84
+ const isFlashList = isFlashListLike(Component);
53
85
 
54
86
  const {
87
+ activeListIndex,
55
88
  listRef,
56
89
  isZoomed,
57
90
  isRotated,
@@ -66,12 +99,13 @@ export function GestureViewer<ItemT, LC>({
66
99
  animatedStyle,
67
100
  backdropStyle,
68
101
  handleDismiss,
102
+ setItemDimensions,
69
103
  } = useGestureViewer({
70
104
  id,
71
105
  data,
72
106
  width,
73
107
  height,
74
- initialIndex,
108
+ initialIndex: normalizedInitialIndex,
75
109
  itemSpacing,
76
110
  enableLoop,
77
111
  ...props,
@@ -89,7 +123,9 @@ export function GestureViewer<ItemT, LC>({
89
123
  );
90
124
 
91
125
  const renderItem = useCallback(
92
- ({ item, index }: { item: ItemT; index: number }) => {
126
+ ({ item, index, target }: { item: ItemT; index: number; target?: string }) => {
127
+ const isFlashListCell = !isFlashList || target === undefined || target === 'Cell';
128
+
93
129
  return (
94
130
  <View
95
131
  key={isScrollView ? keyExtractor(item, index) : undefined}
@@ -102,11 +138,27 @@ export function GestureViewer<ItemT, LC>({
102
138
  styles.item,
103
139
  ]}
104
140
  >
105
- {renderItemProp(item, index)}
141
+ <GestureViewerItemCell
142
+ index={index}
143
+ isActive={index === activeListIndex && isFlashListCell}
144
+ item={item}
145
+ renderItem={renderItemProp}
146
+ setItemDimensions={setItemDimensions}
147
+ />
106
148
  </View>
107
149
  );
108
150
  },
109
- [width, itemSpacing, renderItemProp, keyExtractor, isScrollView, height],
151
+ [
152
+ activeListIndex,
153
+ width,
154
+ itemSpacing,
155
+ renderItemProp,
156
+ keyExtractor,
157
+ isScrollView,
158
+ isFlashList,
159
+ height,
160
+ setItemDimensions,
161
+ ],
110
162
  );
111
163
 
112
164
  const getItemLayout = useCallback(
@@ -122,10 +174,6 @@ export function GestureViewer<ItemT, LC>({
122
174
  return Gesture.Race(dismissGesture, zoomGesture);
123
175
  }, [zoomGesture, dismissGesture]);
124
176
 
125
- useEffect(() => {
126
- dataRef.current = data;
127
- }, [data]);
128
-
129
177
  useEffect(() => {
130
178
  registry.createManager(id);
131
179
 
@@ -195,7 +243,7 @@ export function GestureViewer<ItemT, LC>({
195
243
  onClick: onWebClick,
196
244
  })}
197
245
  {...(Platform.OS === 'web' &&
198
- isFlashListLike(Component) && { dataSet: { 'flash-list-paging-enabled-fix': true } })}
246
+ isFlashList && { dataSet: { 'flash-list-paging-enabled-fix': true } })}
199
247
  >
200
248
  {isScrollView
201
249
  ? maybeWrapWithNativeScrollGesture(
@@ -210,11 +258,9 @@ export function GestureViewer<ItemT, LC>({
210
258
  {...commonProps}
211
259
  data={loopData}
212
260
  renderItem={renderItem}
213
- initialScrollIndex={
214
- enableLoop && data.length > 1 ? initialIndex + 1 : initialIndex
215
- }
261
+ initialScrollIndex={initialListIndex}
216
262
  keyExtractor={keyExtractor}
217
- {...(isFlashListLike(Component)
263
+ {...(isFlashList
218
264
  ? // NOTE - Deprecated estimatedItemSize for FlashList V2 (https://shopify.github.io/flash-list/docs/v2-changes#deprecated)
219
265
  { estimatedItemSize: width + itemSpacing }
220
266
  : { windowSize: 3, maxToRenderPerBatch: 3, getItemLayout })}
@@ -6,12 +6,22 @@ import type {
6
6
  GestureViewerEventType,
7
7
  GestureViewerState,
8
8
  } from './types';
9
- import { createBoundsConstraint, createScrollAction } from './utils';
9
+ import { clampTranslationToBounds, createScrollAction } from './utils';
10
+
11
+ type ZoomSharedValuesOptions = {
12
+ scale: SharedValue<number>;
13
+ translateX: SharedValue<number>;
14
+ translateY: SharedValue<number>;
15
+ maxZoomScale: number;
16
+ contentWidth?: SharedValue<number>;
17
+ contentHeight?: SharedValue<number>;
18
+ };
10
19
 
11
20
  class GestureViewerManager {
12
21
  private currentIndex = 0;
13
22
  private dataLength = 0;
14
- private width = 0;
23
+ private pagingStride = 0;
24
+ private viewportWidth = 0;
15
25
  private height = 0;
16
26
  private maxZoomScale = 2;
17
27
  private enableHorizontalSwipe = true;
@@ -22,6 +32,8 @@ class GestureViewerManager {
22
32
  private rotation: SharedValue<number> | null = null;
23
33
  private translateX: SharedValue<number> | null = null;
24
34
  private translateY: SharedValue<number> | null = null;
35
+ private contentWidth: SharedValue<number> | null = null;
36
+ private contentHeight: SharedValue<number> | null = null;
25
37
  private resetTransformCallback: (() => void) | null = null;
26
38
 
27
39
  private loopCallback: (() => void) | null = null;
@@ -105,8 +117,12 @@ class GestureViewerManager {
105
117
  this.enableLoop = enabled;
106
118
  }
107
119
 
108
- setWidth(width: number) {
109
- this.width = width;
120
+ setPagingStride(pagingStride: number) {
121
+ this.pagingStride = pagingStride;
122
+ }
123
+
124
+ setViewportWidth(width: number) {
125
+ this.viewportWidth = width;
110
126
  }
111
127
 
112
128
  setHeight(height: number) {
@@ -131,16 +147,20 @@ class GestureViewerManager {
131
147
  }
132
148
  }
133
149
 
134
- setZoomSharedValues(
135
- scale: SharedValue<number>,
136
- translateX: SharedValue<number>,
137
- translateY: SharedValue<number>,
138
- maxZoomScale: number,
139
- ) {
150
+ setZoomSharedValues({
151
+ scale,
152
+ translateX,
153
+ translateY,
154
+ maxZoomScale,
155
+ contentWidth,
156
+ contentHeight,
157
+ }: ZoomSharedValuesOptions) {
140
158
  this.scale = scale;
141
159
  this.translateX = translateX;
142
160
  this.translateY = translateY;
143
161
  this.maxZoomScale = maxZoomScale;
162
+ this.contentWidth = contentWidth ?? null;
163
+ this.contentHeight = contentHeight ?? null;
144
164
  }
145
165
 
146
166
  setResetTransformCallback(callback: (() => void) | null) {
@@ -203,10 +223,7 @@ class GestureViewerManager {
203
223
 
204
224
  this.scale.set(withTiming(nextScale));
205
225
 
206
- const { translateX, translateY } = createBoundsConstraint({
207
- width: this.width,
208
- height: this.height,
209
- })({
226
+ const { translateX, translateY } = this.constrainZoomTranslation({
210
227
  translateX: this.translateX.get(),
211
228
  translateY: this.translateY.get(),
212
229
  scale: nextScale,
@@ -237,10 +254,7 @@ class GestureViewerManager {
237
254
  return;
238
255
  }
239
256
 
240
- const { translateX, translateY } = createBoundsConstraint({
241
- width: this.width,
242
- height: this.height,
243
- })({
257
+ const { translateX, translateY } = this.constrainZoomTranslation({
244
258
  translateX: this.translateX.get(),
245
259
  translateY: this.translateY.get(),
246
260
  scale: nextScale,
@@ -273,7 +287,7 @@ class GestureViewerManager {
273
287
 
274
288
  this.cancelPendingLoopTransition();
275
289
 
276
- const { scrollTo } = createScrollAction(this.listRef, this.width);
290
+ const { scrollTo } = createScrollAction(this.listRef, this.pagingStride || this.viewportWidth);
277
291
 
278
292
  if (this.enableLoop && this.dataLength > 1) {
279
293
  if (index < 0) {
@@ -358,9 +372,14 @@ class GestureViewerManager {
358
372
  this.currentIndex = 0;
359
373
  this.dataLength = 0;
360
374
  this.maxZoomScale = 2;
375
+ this.pagingStride = 0;
376
+ this.viewportWidth = 0;
377
+ this.height = 0;
361
378
  this.scale = null;
362
379
  this.translateX = null;
363
380
  this.translateY = null;
381
+ this.contentWidth = null;
382
+ this.contentHeight = null;
364
383
  this.rotation = null;
365
384
  this.resetTransformCallback = null;
366
385
  this.eventListeners.clear();
@@ -370,6 +389,26 @@ class GestureViewerManager {
370
389
  this.currentIndex = targetIndex;
371
390
  this.notifyListeners();
372
391
  };
392
+
393
+ private constrainZoomTranslation = ({
394
+ scale,
395
+ translateX,
396
+ translateY,
397
+ }: {
398
+ scale: number;
399
+ translateX: number;
400
+ translateY: number;
401
+ }) => {
402
+ return clampTranslationToBounds({
403
+ contentHeight: this.contentHeight?.get(),
404
+ contentWidth: this.contentWidth?.get(),
405
+ height: this.height,
406
+ scale,
407
+ translateX,
408
+ translateY,
409
+ width: this.viewportWidth || this.pagingStride,
410
+ });
411
+ };
373
412
  }
374
413
 
375
414
  export default GestureViewerManager;
package/src/index.tsx CHANGED
@@ -6,7 +6,12 @@ export type {
6
6
  GestureViewerEventCallback,
7
7
  GestureViewerEventData,
8
8
  GestureViewerEventType,
9
+ GestureViewerItemDimensions,
10
+ GestureViewerItemDimensionsResolver,
11
+ GestureViewerItemKey,
12
+ GestureViewerItemKeyResolver,
9
13
  GestureViewerProps,
14
+ GestureViewerRenderItemInfo,
10
15
  GestureViewerState,
11
16
  GestureViewerSingleTapEvent,
12
17
  } from './types';
@@ -0,0 +1,162 @@
1
+ import type {
2
+ GestureViewerItemDimensions,
3
+ GestureViewerItemDimensionsResolver,
4
+ GestureViewerItemKey,
5
+ GestureViewerItemKeyResolver,
6
+ } from './types';
7
+
8
+ type ItemDimensionsRegistryEntry<ItemT> = Readonly<{
9
+ item: ItemT;
10
+ itemKey?: GestureViewerItemKey;
11
+ dimensions: GestureViewerItemDimensions;
12
+ }>;
13
+
14
+ export type ItemDimensionsRegistry<ItemT> = Map<number, ItemDimensionsRegistryEntry<ItemT>>;
15
+
16
+ export const isValidItemDimensions = (
17
+ dimensions: GestureViewerItemDimensions | null | undefined,
18
+ ): dimensions is GestureViewerItemDimensions => {
19
+ return (
20
+ dimensions !== null &&
21
+ dimensions !== undefined &&
22
+ Number.isFinite(dimensions.width) &&
23
+ Number.isFinite(dimensions.height) &&
24
+ dimensions.width > 0 &&
25
+ dimensions.height > 0
26
+ );
27
+ };
28
+
29
+ const resolveItemKey = <ItemT>(
30
+ getItemKey: GestureViewerItemKeyResolver<ItemT> | undefined,
31
+ item: ItemT,
32
+ index: number,
33
+ ): GestureViewerItemKey | undefined => {
34
+ const itemKey = getItemKey?.(item, index);
35
+
36
+ if (typeof itemKey === 'string') {
37
+ return itemKey;
38
+ }
39
+
40
+ return typeof itemKey === 'number' && Number.isFinite(itemKey) ? itemKey : undefined;
41
+ };
42
+
43
+ const doesEntryMatchItem = <ItemT>(
44
+ entry: ItemDimensionsRegistryEntry<ItemT>,
45
+ item: ItemT,
46
+ itemKey: GestureViewerItemKey | undefined,
47
+ ): boolean => {
48
+ if (itemKey !== undefined && entry.itemKey !== undefined) {
49
+ return entry.itemKey === itemKey;
50
+ }
51
+
52
+ return Object.is(entry.item, item);
53
+ };
54
+
55
+ export const pruneItemDimensionsRegistry = <ItemT>(
56
+ registry: ItemDimensionsRegistry<ItemT>,
57
+ dataLength: number,
58
+ ): void => {
59
+ for (const index of registry.keys()) {
60
+ if (index < 0 || index >= dataLength) {
61
+ registry.delete(index);
62
+ }
63
+ }
64
+ };
65
+
66
+ export const resolveItemDimensions = <ItemT>({
67
+ registry,
68
+ data,
69
+ index,
70
+ getItemDimensions,
71
+ getItemKey,
72
+ }: {
73
+ registry: ItemDimensionsRegistry<ItemT>;
74
+ data: readonly ItemT[];
75
+ index: number;
76
+ getItemDimensions?: GestureViewerItemDimensionsResolver<ItemT>;
77
+ getItemKey?: GestureViewerItemKeyResolver<ItemT>;
78
+ }): GestureViewerItemDimensions | undefined => {
79
+ if (index < 0 || index >= data.length) {
80
+ return undefined;
81
+ }
82
+
83
+ // `as ItemT` restores the viewer's dense-list invariant, which TypeScript cannot infer from
84
+ // generic indexed access when `noUncheckedIndexedAccess` is enabled.
85
+ const item = data[index] as ItemT;
86
+ const registered = registry.get(index);
87
+ const itemKey = registered ? resolveItemKey(getItemKey, item, index) : undefined;
88
+
89
+ if (
90
+ registered &&
91
+ doesEntryMatchItem(registered, item, itemKey) &&
92
+ isValidItemDimensions(registered.dimensions)
93
+ ) {
94
+ return registered.dimensions;
95
+ }
96
+
97
+ const resolved = getItemDimensions?.(item, index);
98
+
99
+ if (isValidItemDimensions(resolved)) {
100
+ return resolved;
101
+ }
102
+
103
+ return undefined;
104
+ };
105
+
106
+ export const registerItemDimensions = <ItemT>({
107
+ registry,
108
+ data,
109
+ index,
110
+ item,
111
+ dimensions,
112
+ getItemKey,
113
+ }: {
114
+ registry: ItemDimensionsRegistry<ItemT>;
115
+ data: readonly ItemT[];
116
+ index: number;
117
+ item: ItemT;
118
+ dimensions: GestureViewerItemDimensions;
119
+ getItemKey?: GestureViewerItemKeyResolver<ItemT>;
120
+ }): boolean => {
121
+ if (index < 0 || index >= data.length || !isValidItemDimensions(dimensions)) {
122
+ return false;
123
+ }
124
+
125
+ // `as ItemT` restores the viewer's dense-list invariant, which TypeScript cannot infer from
126
+ // generic indexed access when `noUncheckedIndexedAccess` is enabled.
127
+ const currentItem = data[index] as ItemT;
128
+ const currentItemKey = resolveItemKey(getItemKey, currentItem, index);
129
+ const reportedItemKey = Object.is(currentItem, item)
130
+ ? currentItemKey
131
+ : resolveItemKey(getItemKey, item, index);
132
+ const hasMatchingKey =
133
+ currentItemKey !== undefined &&
134
+ reportedItemKey !== undefined &&
135
+ currentItemKey === reportedItemKey;
136
+
137
+ if (!Object.is(currentItem, item) && !hasMatchingKey) {
138
+ return false;
139
+ }
140
+
141
+ const registered = registry.get(index);
142
+
143
+ if (
144
+ registered &&
145
+ doesEntryMatchItem(registered, currentItem, currentItemKey) &&
146
+ registered.dimensions.width === dimensions.width &&
147
+ registered.dimensions.height === dimensions.height
148
+ ) {
149
+ return false;
150
+ }
151
+
152
+ registry.set(index, {
153
+ item: currentItem,
154
+ itemKey: currentItemKey,
155
+ dimensions: {
156
+ height: dimensions.height,
157
+ width: dimensions.width,
158
+ },
159
+ });
160
+
161
+ return true;
162
+ };