react-native-gesture-image-viewer 0.5.0 → 0.5.2

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 CHANGED
@@ -16,7 +16,7 @@ Existing libraries often have limited customization options or performance issue
16
16
  - ✅ **External Control API** - Programmatic control from buttons or other components
17
17
  - ✅ **Multi-Instance Management** - ID-based independent management of multiple viewers
18
18
  - ✅ **Flexible Integration** - Use with FlatList, FlashList, Expo Image, FastImage, and more
19
- - ✅ **Full TypeScript Support** - Type safety and enhanced developer experience
19
+ - ✅ **Full TypeScript Support** - Enhanced developer experience with smart type inference
20
20
  - ✅ **Cross-Platform** - iOS, Android, and Web support
21
21
  - ✅ **Easy-to-Use API** - Intuitive and simple implementation without complex setup
22
22
  - ✅ **Various Environment Support** - Expo Go and New Architecture support
@@ -168,17 +168,19 @@ function App() {
168
168
 
169
169
  #### List Components
170
170
 
171
- Support for `ScrollView`, `FlatList`, `FlashList`, and other list components through the `ListComponent` prop.
172
- You can also customize the props of each supported list component through `listProps`.
171
+ Support for any list component like `ScrollView`, `FlatList`, `FlashList` through the `ListComponent` prop.
172
+ The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
173
173
 
174
174
  ```tsx
175
+ import { FlashList } from '@shopify/flash-list';
176
+
175
177
  function App() {
176
178
  return (
177
179
  <GestureImageViewer
178
180
  data={images}
179
- ListComponent={FlatList}
181
+ ListComponent={FlashList}
180
182
  listProps={{
181
- // ....
183
+ // ✅ FlashList props autocompletion
182
184
  }}
183
185
  />
184
186
  );
@@ -2,11 +2,11 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo } from 'react';
4
4
  import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
5
- import { FlatList, Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
5
+ import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
6
6
  import Animated from 'react-native-reanimated';
7
7
  import { registry } from "./ImageViewerRegistry.js";
8
8
  import { useGestureImageViewer } from "./useGestureImageViewer.js";
9
- import { isFlatListLike, isScrollViewLike } from "./utils.js";
9
+ import { isFlashListLike, isFlatListLike, isScrollViewLike } from "./utils.js";
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  const WebPagingFix = () => {
12
12
  if (Platform.OS !== 'web') {
@@ -21,7 +21,7 @@ export function GestureImageViewer({
21
21
  data,
22
22
  renderImage,
23
23
  renderContainer,
24
- ListComponent = FlatList,
24
+ ListComponent,
25
25
  width: customWidth,
26
26
  listProps,
27
27
  backdropStyle: backdropStyleProps,
@@ -30,6 +30,7 @@ export function GestureImageViewer({
30
30
  itemSpacing = 0,
31
31
  ...props
32
32
  }) {
33
+ const Component = ListComponent;
33
34
  const {
34
35
  width: screenWidth
35
36
  } = useWindowDimensions();
@@ -98,7 +99,7 @@ export function GestureImageViewer({
98
99
  style: [styles.background, backdropStyleProps, backdropStyle]
99
100
  }), /*#__PURE__*/_jsx(Animated.View, {
100
101
  style: [styles.content, animatedStyle],
101
- children: isScrollViewLike(ListComponent) ? /*#__PURE__*/_jsx(ListComponent, {
102
+ children: isScrollViewLike(Component) ? /*#__PURE__*/_jsx(Component, {
102
103
  ref: listRef,
103
104
  ...commonProps,
104
105
  ...listProps,
@@ -106,19 +107,19 @@ export function GestureImageViewer({
106
107
  item,
107
108
  index
108
109
  }))
109
- }) : isFlatListLike(ListComponent) && /*#__PURE__*/_jsx(ListComponent, {
110
+ }) : isFlatListLike(Component) && /*#__PURE__*/_jsx(Component, {
110
111
  ref: listRef,
111
112
  ...commonProps,
112
113
  data: data,
113
114
  renderItem: renderItem,
114
115
  initialScrollIndex: initialIndex,
115
116
  keyExtractor: keyExtractor,
116
- estimatedItemSize: width + itemSpacing,
117
117
  windowSize: 3,
118
118
  maxToRenderPerBatch: 3,
119
- getItemLayout: getItemLayout
120
- // NOTE - https://github.com/necolas/react-native-web/issues/1299
121
- ,
119
+ getItemLayout: getItemLayout,
120
+ ...(isFlashListLike(Component) && {
121
+ estimatedItemSize: width + itemSpacing
122
+ }),
122
123
  ...(Platform.OS === 'web' && {
123
124
  dataSet: {
124
125
  'paging-enabled-fix': true
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useMemo","Platform","StyleSheet","useWindowDimensions","View","FlatList","Gesture","GestureDetector","GestureHandlerRootView","Animated","registry","useGestureImageViewer","isFlatListLike","isScrollViewLike","jsx","_jsx","jsxs","_jsxs","WebPagingFix","OS","children","GestureImageViewer","id","data","renderImage","renderContainer","ListComponent","width","customWidth","listProps","backdropStyle","backdropStyleProps","containerStyle","initialIndex","itemSpacing","props","screenWidth","listRef","isZoomed","dismissGesture","zoomGesture","onMomentumScrollEnd","animatedStyle","renderItem","item","index","style","height","justifyContent","alignItems","marginHorizontal","getItemLayout","_","length","offset","keyExtractor","gesture","Race","createManager","deleteManager","commonProps","horizontal","scrollEnabled","showsHorizontalScrollIndicator","snapToInterval","snapToAlignment","decelerationRate","scrollEventThrottle","removeClippedSubviews","listComponent","styles","container","background","content","ref","map","initialScrollIndex","estimatedItemSize","windowSize","maxToRenderPerBatch","dataSet","create","flex","position","top","left","right","bottom","backgroundColor"],"sourceRoot":"../../src","sources":["GestureImageViewer.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","Platform","StyleSheet","useWindowDimensions","View","Gesture","GestureDetector","GestureHandlerRootView","Animated","registry","useGestureImageViewer","isFlashListLike","isFlatListLike","isScrollViewLike","jsx","_jsx","jsxs","_jsxs","WebPagingFix","OS","children","GestureImageViewer","id","data","renderImage","renderContainer","ListComponent","width","customWidth","listProps","backdropStyle","backdropStyleProps","containerStyle","initialIndex","itemSpacing","props","Component","screenWidth","listRef","isZoomed","dismissGesture","zoomGesture","onMomentumScrollEnd","animatedStyle","renderItem","item","index","style","height","justifyContent","alignItems","marginHorizontal","getItemLayout","_","length","offset","keyExtractor","gesture","Race","createManager","deleteManager","commonProps","horizontal","scrollEnabled","showsHorizontalScrollIndicator","snapToInterval","snapToAlignment","decelerationRate","scrollEventThrottle","removeClippedSubviews","listComponent","styles","container","background","content","ref","map","initialScrollIndex","windowSize","maxToRenderPerBatch","estimatedItemSize","dataSet","create","flex","position","top","left","right","bottom","backgroundColor"],"sourceRoot":"../../src","sources":["GestureImageViewer.tsx"],"mappings":"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","ignoreList":[]}
@@ -6,6 +6,12 @@ export const isScrollViewLike = component => {
6
6
  return component === RNScrollView || component === GestureScrollView;
7
7
  };
8
8
  export const isFlatListLike = component => {
9
+ if (component === RNFlatList || component === GestureFlatList || isFlashListLike(component)) {
10
+ return true;
11
+ }
12
+ return false;
13
+ };
14
+ export const isFlashListLike = component => {
9
15
  try {
10
16
  const FlashList = require('@shopify/flash-list')?.FlashList;
11
17
  if (FlashList && component === FlashList) {
@@ -14,9 +20,6 @@ export const isFlatListLike = component => {
14
20
  } catch {
15
21
  // do nothing
16
22
  }
17
- if (component === RNFlatList || component === GestureFlatList) {
18
- return true;
19
- }
20
23
  return component?.name === 'FlashList';
21
24
  };
22
25
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["FlatList","RNFlatList","ScrollView","RNScrollView","GestureFlatList","GestureScrollView","isScrollViewLike","component","isFlatListLike","FlashList","require","name"],"sourceRoot":"../../src","sources":["utils.ts"],"mappings":";;AAAA,SAASA,QAAQ,IAAIC,UAAU,EAAEC,UAAU,IAAIC,YAAY,QAAQ,cAAc;AACjF,SAASH,QAAQ,IAAII,eAAe,EAAEF,UAAU,IAAIG,iBAAiB,QAAQ,8BAA8B;AAE3G,OAAO,MAAMC,gBAAgB,GAAIC,SAAc,IAAuC;EACpF,OAAOA,SAAS,KAAKJ,YAAY,IAAII,SAAS,KAAKF,iBAAiB;AACtE,CAAC;AAED,OAAO,MAAMG,cAAc,GAAID,SAAc,IAAqC;EAChF,IAAI;IACF,MAAME,SAAS,GAAGC,OAAO,CAAC,qBAAqB,CAAC,EAAED,SAAS;IAE3D,IAAIA,SAAS,IAAIF,SAAS,KAAKE,SAAS,EAAE;MACxC,OAAO,IAAI;IACb;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAGF,IAAIF,SAAS,KAAKN,UAAU,IAAIM,SAAS,KAAKH,eAAe,EAAE;IAC7D,OAAO,IAAI;EACb;EAEA,OAAOG,SAAS,EAAEI,IAAI,KAAK,WAAW;AACxC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["FlatList","RNFlatList","ScrollView","RNScrollView","GestureFlatList","GestureScrollView","isScrollViewLike","component","isFlatListLike","isFlashListLike","FlashList","require","name"],"sourceRoot":"../../src","sources":["utils.ts"],"mappings":";;AAAA,SAASA,QAAQ,IAAIC,UAAU,EAAEC,UAAU,IAAIC,YAAY,QAAQ,cAAc;AACjF,SAASH,QAAQ,IAAII,eAAe,EAAEF,UAAU,IAAIG,iBAAiB,QAAQ,8BAA8B;AAG3G,OAAO,MAAMC,gBAAgB,GAAIC,SAAc,IAAuC;EACpF,OAAOA,SAAS,KAAKJ,YAAY,IAAII,SAAS,KAAKF,iBAAiB;AACtE,CAAC;AAED,OAAO,MAAMG,cAAc,GAAID,SAAc,IAAqC;EAChF,IAAIA,SAAS,KAAKN,UAAU,IAAIM,SAAS,KAAKH,eAAe,IAAIK,eAAe,CAACF,SAAS,CAAC,EAAE;IAC3F,OAAO,IAAI;EACb;EAEA,OAAO,KAAK;AACd,CAAC;AAED,OAAO,MAAME,eAAe,GAAIF,SAAc,IAAuB;EACnE,IAAI;IACF,MAAMG,SAAS,GAAGC,OAAO,CAAC,qBAAqB,CAAC,EAAED,SAAS;IAE3D,IAAIA,SAAS,IAAIH,SAAS,KAAKG,SAAS,EAAE;MACxC,OAAO,IAAI;IACb;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAGF,OAAOH,SAAS,EAAEK,IAAI,KAAK,WAAW;AACxC,CAAC","ignoreList":[]}
@@ -1,3 +1,4 @@
1
+ import { type FlatList } from 'react-native';
1
2
  import type { GestureImageViewerProps } from './types';
2
- export declare function GestureImageViewer<T = any>({ id, data, renderImage, renderContainer, ListComponent, width: customWidth, listProps, backdropStyle: backdropStyleProps, containerStyle, initialIndex, itemSpacing, ...props }: GestureImageViewerProps<T>): import("react/jsx-runtime").JSX.Element;
3
+ export declare function GestureImageViewer<T = any, LC = typeof FlatList>({ id, data, renderImage, renderContainer, ListComponent, width: customWidth, listProps, backdropStyle: backdropStyleProps, containerStyle, initialIndex, itemSpacing, ...props }: GestureImageViewerProps<T, LC>): import("react/jsx-runtime").JSX.Element;
3
4
  //# sourceMappingURL=GestureImageViewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureImageViewer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAYvD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,EAC1C,EAAc,EACd,IAAI,EACJ,WAAW,EACX,eAAe,EACf,aAAwB,EACxB,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,WAAe,EACf,GAAG,KAAK,EACT,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAiH5B"}
1
+ {"version":3,"file":"GestureImageViewer.d.ts","sourceRoot":"","sources":["../../../src/GestureImageViewer.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAyE,MAAM,cAAc,CAAC;AAIpH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAYvD,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,OAAO,QAAQ,EAAE,EAChE,EAAc,EACd,IAAI,EACJ,WAAW,EACX,eAAe,EACf,aAAa,EACb,KAAK,EAAE,WAAW,EAClB,SAAS,EACT,aAAa,EAAE,kBAAkB,EACjC,cAAc,EACd,YAAgB,EAChB,WAAe,EACf,GAAG,KAAK,EACT,EAAE,uBAAuB,CAAC,CAAC,EAAE,EAAE,CAAC,2CAmHhC"}
@@ -1,6 +1,11 @@
1
- import type { StyleProp, ViewStyle } from 'react-native';
2
- import type { SharedValue } from 'react-native-reanimated';
3
- export interface GestureImageViewerProps<T = any> {
1
+ import type React from 'react';
2
+ import type { FlatList as RNFlatList, ScrollView as RNScrollView, StyleProp, ViewStyle } from 'react-native';
3
+ import type { FlatList as GHFlatList, ScrollView as GHScrollView } from 'react-native-gesture-handler';
4
+ export type FlatListComponent = typeof RNFlatList | typeof GHFlatList;
5
+ export type ScrollViewComponent = typeof RNScrollView | typeof GHScrollView;
6
+ type GetComponentProps<T> = T extends React.ComponentType<infer P> ? P : never;
7
+ type ConditionalListProps<LC> = LC extends FlatListComponent ? React.ComponentProps<LC> : LC extends ScrollViewComponent ? React.ComponentProps<LC> : GetComponentProps<LC>;
8
+ export interface GestureImageViewerProps<T = any, LC = typeof RNFlatList> {
4
9
  id?: string;
5
10
  data: T[];
6
11
  initialIndex?: number;
@@ -8,15 +13,13 @@ export interface GestureImageViewerProps<T = any> {
8
13
  onDismiss?: () => void;
9
14
  renderImage: (item: T, index: number) => React.ReactElement;
10
15
  renderContainer?: (children: React.ReactElement) => React.ReactElement;
11
- ListComponent?: any;
16
+ ListComponent: LC;
12
17
  width?: number;
13
18
  dismissThreshold?: number;
14
- swipeThreshold?: number;
15
- velocityThreshold?: number;
16
19
  enableDismissGesture?: boolean;
17
20
  enableSwipeGesture?: boolean;
18
21
  resistance?: number;
19
- listProps?: any;
22
+ listProps?: Partial<ConditionalListProps<LC>>;
20
23
  backdropStyle?: StyleProp<ViewStyle>;
21
24
  containerStyle?: StyleProp<ViewStyle>;
22
25
  animateBackdrop?: boolean;
@@ -26,10 +29,5 @@ export interface GestureImageViewerProps<T = any> {
26
29
  maxZoomScale?: number;
27
30
  itemSpacing?: number;
28
31
  }
29
- export interface GestureState {
30
- currentIndex: number;
31
- translateY: SharedValue<number>;
32
- isGestureActive: boolean;
33
- lockDirection: 'horizontal' | 'vertical' | null;
34
- }
32
+ export {};
35
33
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG;IAC9C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;IAC5D,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,CAAC;IACvE,aAAa,CAAC,EAAE,GAAG,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC;CACjD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,QAAQ,IAAI,UAAU,EAAE,UAAU,IAAI,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC7G,OAAO,KAAK,EAAE,QAAQ,IAAI,UAAU,EAAE,UAAU,IAAI,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEvG,MAAM,MAAM,iBAAiB,GAAG,OAAO,UAAU,GAAG,OAAO,UAAU,CAAC;AACtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,YAAY,GAAG,OAAO,YAAY,CAAC;AAE5E,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAE/E,KAAK,oBAAoB,CAAC,EAAE,IAAI,EAAE,SAAS,iBAAiB,GACxD,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,GACxB,EAAE,SAAS,mBAAmB,GAC5B,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,GACxB,iBAAiB,CAAC,EAAE,CAAC,CAAC;AAE5B,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,GAAG,OAAO,UAAU;IACtE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,YAAY,CAAC;IAC5D,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,YAAY,CAAC;IACvE,aAAa,EAAE,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
@@ -1,4 +1,5 @@
1
- import { FlatList as RNFlatList, ScrollView as RNScrollView } from 'react-native';
2
- export declare const isScrollViewLike: (component: any) => component is typeof RNScrollView;
3
- export declare const isFlatListLike: (component: any) => component is typeof RNFlatList;
1
+ import type { FlatListComponent, ScrollViewComponent } from './types';
2
+ export declare const isScrollViewLike: (component: any) => component is ScrollViewComponent;
3
+ export declare const isFlatListLike: (component: any) => component is FlatListComponent;
4
+ export declare const isFlashListLike: (component: any) => component is any;
4
5
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,UAAU,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAGlF,eAAO,MAAM,gBAAgB,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,OAAO,YAErE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,WAAW,GAAG,KAAG,SAAS,IAAI,OAAO,UAgBnE,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gesture-image-viewer",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
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",
@@ -13,7 +13,6 @@
13
13
  "./package.json": "./package.json"
14
14
  },
15
15
  "files": [
16
- "src",
17
16
  "lib",
18
17
  "android",
19
18
  "ios",
@@ -1,163 +0,0 @@
1
- import { useCallback, useEffect, useMemo } from 'react';
2
- import { Platform, type ScrollViewProps, StyleSheet, useWindowDimensions, View } from 'react-native';
3
- import { FlatList, Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
4
- import Animated from 'react-native-reanimated';
5
- import { registry } from './ImageViewerRegistry';
6
- import type { GestureImageViewerProps } from './types';
7
- import { useGestureImageViewer } from './useGestureImageViewer';
8
- import { isFlatListLike, isScrollViewLike } from './utils';
9
-
10
- const WebPagingFix = () => {
11
- if (Platform.OS !== 'web') {
12
- return null;
13
- }
14
-
15
- return <style>{`[data-paging-enabled-fix] > div > div > div {height: 100%;}`}</style>;
16
- };
17
-
18
- export function GestureImageViewer<T = any>({
19
- id = 'default',
20
- data,
21
- renderImage,
22
- renderContainer,
23
- ListComponent = FlatList,
24
- width: customWidth,
25
- listProps,
26
- backdropStyle: backdropStyleProps,
27
- containerStyle,
28
- initialIndex = 0,
29
- itemSpacing = 0,
30
- ...props
31
- }: GestureImageViewerProps<T>) {
32
- const { width: screenWidth } = useWindowDimensions();
33
-
34
- const width = customWidth || screenWidth;
35
-
36
- const { listRef, isZoomed, dismissGesture, zoomGesture, onMomentumScrollEnd, animatedStyle, backdropStyle } =
37
- useGestureImageViewer({
38
- id,
39
- data,
40
- width,
41
- initialIndex,
42
- itemSpacing,
43
- ...props,
44
- });
45
-
46
- const renderItem = useCallback(
47
- ({ item, index }: { item: T; index: number }) => {
48
- return (
49
- <View
50
- key={typeof item === 'string' ? item : index}
51
- style={[
52
- {
53
- width: width,
54
- height: '100%',
55
- justifyContent: 'center',
56
- alignItems: 'center',
57
- marginHorizontal: itemSpacing / 2,
58
- },
59
- ]}
60
- >
61
- {renderImage(item, index)}
62
- </View>
63
- );
64
- },
65
- [width, itemSpacing, renderImage],
66
- );
67
-
68
- const getItemLayout = useCallback(
69
- (_: any, index: number) => ({
70
- length: width + itemSpacing,
71
- offset: (width + itemSpacing) * index,
72
- index,
73
- }),
74
- [width, itemSpacing],
75
- );
76
-
77
- const keyExtractor = useCallback(
78
- (item: T, index: number) => (typeof item === 'string' ? item : `image-${index}`),
79
- [],
80
- );
81
-
82
- const gesture = useMemo(() => {
83
- return Gesture.Race(dismissGesture, zoomGesture);
84
- }, [zoomGesture, dismissGesture]);
85
-
86
- useEffect(() => {
87
- registry.createManager(id);
88
-
89
- return () => registry.deleteManager(id);
90
- }, [id]);
91
-
92
- const commonProps: ScrollViewProps = useMemo(
93
- () => ({
94
- horizontal: true,
95
- scrollEnabled: !isZoomed,
96
- showsHorizontalScrollIndicator: false,
97
- onMomentumScrollEnd: onMomentumScrollEnd,
98
- snapToInterval: width + itemSpacing,
99
- snapToAlignment: 'center',
100
- decelerationRate: 'fast',
101
- scrollEventThrottle: 16,
102
- removeClippedSubviews: true,
103
- }),
104
- [width, itemSpacing, isZoomed, onMomentumScrollEnd],
105
- );
106
-
107
- const listComponent = (
108
- <GestureHandlerRootView>
109
- <GestureDetector gesture={gesture}>
110
- <View style={[styles.container, containerStyle]}>
111
- <Animated.View style={[styles.background, backdropStyleProps, backdropStyle]} />
112
- <Animated.View style={[styles.content, animatedStyle]}>
113
- {isScrollViewLike(ListComponent) ? (
114
- <ListComponent ref={listRef} {...commonProps} {...listProps}>
115
- {data.map((item, index) => renderItem({ item, index }))}
116
- </ListComponent>
117
- ) : (
118
- isFlatListLike(ListComponent) && (
119
- <ListComponent
120
- ref={listRef}
121
- {...commonProps}
122
- data={data}
123
- renderItem={renderItem}
124
- initialScrollIndex={initialIndex}
125
- keyExtractor={keyExtractor}
126
- estimatedItemSize={width + itemSpacing}
127
- windowSize={3}
128
- maxToRenderPerBatch={3}
129
- getItemLayout={getItemLayout}
130
- // NOTE - https://github.com/necolas/react-native-web/issues/1299
131
- {...(Platform.OS === 'web' && { dataSet: { 'paging-enabled-fix': true } })}
132
- {...listProps}
133
- />
134
- )
135
- )}
136
- </Animated.View>
137
- <WebPagingFix />
138
- </View>
139
- </GestureDetector>
140
- </GestureHandlerRootView>
141
- );
142
-
143
- return renderContainer ? renderContainer(listComponent) : listComponent;
144
- }
145
-
146
- const styles = StyleSheet.create({
147
- container: {
148
- flex: 1,
149
- },
150
- content: {
151
- flex: 1,
152
- width: '100%',
153
- height: '100%',
154
- },
155
- background: {
156
- position: 'absolute',
157
- top: 0,
158
- left: 0,
159
- right: 0,
160
- bottom: 0,
161
- backgroundColor: 'black',
162
- },
163
- });
@@ -1,103 +0,0 @@
1
- export type ImageViewerManagerState = {
2
- currentIndex: number;
3
- dataLength: number;
4
- };
5
-
6
- class ImageViewerManager {
7
- private currentIndex = 0;
8
- private dataLength = 0;
9
- private width = 0;
10
- private listRef: any | null = null;
11
- private enableSwipeGesture = true;
12
- private listeners = new Set<(state: ImageViewerManagerState) => void>();
13
-
14
- // private updateState(newState: Partial<any>) {
15
- // Object.assign(this, newState);
16
- // this.notifyListeners();
17
- // }
18
-
19
- private notifyListeners() {
20
- const state = this.getState();
21
-
22
- this.listeners.forEach((listener) => listener(state));
23
- }
24
-
25
- subscribe(listener: (state: ImageViewerManagerState) => void) {
26
- this.listeners.add(listener);
27
-
28
- return () => {
29
- this.listeners.delete(listener);
30
- };
31
- }
32
-
33
- getState() {
34
- return {
35
- currentIndex: this.currentIndex,
36
- dataLength: this.dataLength,
37
- };
38
- }
39
-
40
- setWidth(width: number) {
41
- this.width = width;
42
- }
43
-
44
- setListRef(ref: any) {
45
- this.listRef = ref;
46
- }
47
-
48
- setDataLength(length: number) {
49
- this.dataLength = length;
50
- }
51
-
52
- setEnableSwipeGesture(enabled: boolean) {
53
- this.enableSwipeGesture = enabled;
54
- }
55
-
56
- setCurrentIndex(index: number) {
57
- if (index !== this.currentIndex) {
58
- this.currentIndex = index;
59
- }
60
- }
61
-
62
- notifyStateChange() {
63
- this.notifyListeners();
64
- }
65
-
66
- goToIndex = (index: number) => {
67
- if (index < 0 || index >= this.dataLength || !this.enableSwipeGesture || !this.listRef) {
68
- return;
69
- }
70
-
71
- this.currentIndex = index;
72
-
73
- if (this.listRef.scrollToIndex) {
74
- this.listRef.scrollToIndex({ index, animated: true });
75
- } else if (this.listRef.scrollTo) {
76
- this.listRef.scrollTo({ x: index * this.width, animated: true });
77
- }
78
-
79
- this.notifyListeners();
80
- };
81
-
82
- goToPrevious = () => {
83
- if (this.currentIndex > 0) {
84
- this.goToIndex(this.currentIndex - 1);
85
- }
86
- };
87
-
88
- goToNext = () => {
89
- if (this.currentIndex < this.dataLength - 1) {
90
- this.goToIndex(this.currentIndex + 1);
91
- }
92
- };
93
-
94
- cleanUp() {
95
- this.listeners.clear();
96
- this.listRef = null;
97
- this.enableSwipeGesture = true;
98
- this.currentIndex = 0;
99
- this.dataLength = 0;
100
- }
101
- }
102
-
103
- export default ImageViewerManager;
@@ -1,66 +0,0 @@
1
- import ImageViewerManager from './ImageViewerManager';
2
-
3
- class ImageViewerRegistry {
4
- private managers = new Map<string, ImageViewerManager>();
5
- private subscribers = new Map<string, Set<(manager: ImageViewerManager | null) => void>>();
6
-
7
- subscribeToManager(id: string, callback: (manager: ImageViewerManager | null) => void) {
8
- if (!this.subscribers.has(id)) {
9
- this.subscribers.set(id, new Set());
10
- }
11
-
12
- this.subscribers.get(id)?.add(callback);
13
-
14
- const manager = this.managers.get(id) || null;
15
-
16
- callback(manager);
17
-
18
- return () => {
19
- const subscribers = this.subscribers.get(id);
20
-
21
- subscribers?.delete(callback);
22
-
23
- if (subscribers && subscribers.size === 0) {
24
- this.subscribers.delete(id);
25
- }
26
- };
27
- }
28
-
29
- createManager(id: string): ImageViewerManager | null {
30
- if (this.managers.has(id)) {
31
- return this.managers.get(id) || null;
32
- }
33
-
34
- const manager = new ImageViewerManager();
35
- this.managers.set(id, manager);
36
-
37
- this.notifySubscribers(id, manager);
38
-
39
- return manager;
40
- }
41
-
42
- getManager(id: string): ImageViewerManager | null {
43
- return this.managers.get(id) || null;
44
- }
45
-
46
- deleteManager(id: string) {
47
- const manager = this.managers.get(id);
48
-
49
- if (manager) {
50
- manager.cleanUp();
51
- this.managers.delete(id);
52
-
53
- this.notifySubscribers(id, null);
54
- }
55
- }
56
-
57
- notifySubscribers(id: string, manager: ImageViewerManager | null) {
58
- const listeners = this.subscribers.get(id);
59
-
60
- if (listeners) {
61
- [...listeners].forEach((callback) => callback(manager));
62
- }
63
- }
64
- }
65
-
66
- export const registry = new ImageViewerRegistry();
package/src/index.tsx DELETED
@@ -1,3 +0,0 @@
1
- export { GestureImageViewer } from './GestureImageViewer';
2
- export type { GestureImageViewerProps } from './types';
3
- export { useImageViewerController } from './useImageViewerController';
package/src/types.ts DELETED
@@ -1,36 +0,0 @@
1
- import type { StyleProp, ViewStyle } from 'react-native';
2
- import type { SharedValue } from 'react-native-reanimated';
3
-
4
- export interface GestureImageViewerProps<T = any> {
5
- id?: string;
6
- data: T[];
7
- initialIndex?: number;
8
- onIndexChange?: (index: number) => void;
9
- onDismiss?: () => void;
10
- renderImage: (item: T, index: number) => React.ReactElement;
11
- renderContainer?: (children: React.ReactElement) => React.ReactElement;
12
- ListComponent?: any; // FlatList, FlashList 등
13
- width?: number;
14
- dismissThreshold?: number;
15
- swipeThreshold?: number;
16
- velocityThreshold?: number;
17
- enableDismissGesture?: boolean;
18
- enableSwipeGesture?: boolean;
19
- resistance?: number;
20
- listProps?: any; // FlatList, FlashList에 전달할 추가 props
21
- backdropStyle?: StyleProp<ViewStyle>;
22
- containerStyle?: StyleProp<ViewStyle>;
23
- animateBackdrop?: boolean;
24
- enableZoomPanGesture?: boolean;
25
- enableZoomGesture?: boolean;
26
- enableDoubleTapGesture?: boolean;
27
- maxZoomScale?: number;
28
- itemSpacing?: number;
29
- }
30
-
31
- export interface GestureState {
32
- currentIndex: number;
33
- translateY: SharedValue<number>;
34
- isGestureActive: boolean;
35
- lockDirection: 'horizontal' | 'vertical' | null;
36
- }
@@ -1,346 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import {
3
- InteractionManager,
4
- type NativeScrollEvent,
5
- type NativeSyntheticEvent,
6
- useWindowDimensions,
7
- } from 'react-native';
8
- import { Gesture } from 'react-native-gesture-handler';
9
- import {
10
- Easing,
11
- interpolate,
12
- runOnJS,
13
- useAnimatedReaction,
14
- useAnimatedStyle,
15
- useSharedValue,
16
- withSpring,
17
- withTiming,
18
- } from 'react-native-reanimated';
19
- import type ImageViewerManager from './ImageViewerManager';
20
- import { registry } from './ImageViewerRegistry';
21
- import type { GestureImageViewerProps } from './types';
22
-
23
- type UseGestureImageViewerProps<T = any> = Omit<
24
- GestureImageViewerProps<T>,
25
- 'renderImage' | 'renderContainer' | 'ListComponent' | 'listProps' | 'containerStyle' | 'backdropStyle'
26
- >;
27
-
28
- export const useGestureImageViewer = <T = any>({
29
- data,
30
- initialIndex = 0,
31
- onIndexChange,
32
- onDismiss,
33
- width: customWidth,
34
- dismissThreshold = 80,
35
- resistance = 2,
36
- // swipeThreshold = 0.5,
37
- // velocityThreshold = 200,
38
- animateBackdrop = true,
39
- enableDismissGesture = true,
40
- enableSwipeGesture = true,
41
- enableZoomGesture = true,
42
- enableDoubleTapGesture = true,
43
- enableZoomPanGesture = true,
44
- maxZoomScale = 2,
45
- itemSpacing = 0,
46
- id = 'default',
47
- }: UseGestureImageViewerProps<T>) => {
48
- const { width: screenWidth, height: screenHeight } = useWindowDimensions();
49
- const width = customWidth || screenWidth;
50
-
51
- const [isZoomed, setIsZoomed] = useState(false);
52
-
53
- const [currentIndex, setCurrentIndex] = useState(initialIndex);
54
- const [manager, setManager] = useState<ImageViewerManager | null>(null);
55
-
56
- const unsubscribeRef = useRef<(() => void) | null>(null);
57
-
58
- const initialTranslateY = useSharedValue(0);
59
- const initialTranslateX = useSharedValue(0);
60
- const startScale = useSharedValue(1);
61
-
62
- const translateY = useSharedValue(0);
63
- const translateX = useSharedValue(0);
64
- const scale = useSharedValue(1);
65
- const backdropOpacity = useSharedValue(1);
66
-
67
- const listRef = useRef<any>(null);
68
-
69
- const dataLength = data?.length || 0;
70
-
71
- useAnimatedReaction(
72
- () => scale.value,
73
- (currentScale) => {
74
- runOnJS(setIsZoomed)(currentScale > 1);
75
- },
76
- );
77
-
78
- useEffect(() => {
79
- const handleManagerChange = (manager: ImageViewerManager | null) => {
80
- unsubscribeRef.current?.();
81
- unsubscribeRef.current = null;
82
-
83
- setManager(manager);
84
-
85
- if (manager) {
86
- setCurrentIndex(manager.getState().currentIndex);
87
- unsubscribeRef.current = manager.subscribe((state) => {
88
- setCurrentIndex(state.currentIndex);
89
- });
90
- return;
91
- }
92
-
93
- setCurrentIndex(0);
94
- };
95
-
96
- const unsubscribeFromRegistry = registry.subscribeToManager(id, handleManagerChange);
97
-
98
- return () => {
99
- unsubscribeFromRegistry();
100
- unsubscribeRef.current?.();
101
- };
102
- }, [id]);
103
-
104
- useEffect(() => {
105
- if (!manager) {
106
- return;
107
- }
108
-
109
- manager.setDataLength(dataLength);
110
- manager.setEnableSwipeGesture(enableSwipeGesture);
111
- manager.setCurrentIndex(initialIndex);
112
- manager.setWidth(width + itemSpacing);
113
- manager.notifyStateChange();
114
- }, [dataLength, enableSwipeGesture, initialIndex, manager, width, itemSpacing]);
115
-
116
- useEffect(() => {
117
- if (!manager || !listRef.current) {
118
- return;
119
- }
120
-
121
- manager.setListRef(listRef.current);
122
- }, [manager]);
123
-
124
- useEffect(() => {
125
- onIndexChange?.(currentIndex);
126
- }, [currentIndex, onIndexChange]);
127
-
128
- useEffect(() => {
129
- translateY.value = 0;
130
- translateX.value = 0;
131
- scale.value = 1;
132
- backdropOpacity.value = 1;
133
- startScale.value = 1;
134
-
135
- if (initialIndex <= 0 || !listRef.current) {
136
- return;
137
- }
138
-
139
- const runAfterInteractions = InteractionManager.runAfterInteractions(() => {
140
- if (listRef.current.scrollToIndex) {
141
- listRef.current.scrollToIndex({
142
- index: initialIndex,
143
- animated: false,
144
- });
145
- } else if (listRef.current.scrollTo) {
146
- listRef.current.scrollTo({
147
- x: initialIndex * (width + itemSpacing),
148
- animated: false,
149
- });
150
- }
151
- });
152
-
153
- return () => {
154
- runAfterInteractions?.cancel();
155
- };
156
- }, [initialIndex, translateY, backdropOpacity, translateX, scale, startScale, width, itemSpacing]);
157
-
158
- const onMomentumScrollEnd = useCallback(
159
- (event: NativeSyntheticEvent<NativeScrollEvent>) => {
160
- if (!enableSwipeGesture) {
161
- return;
162
- }
163
-
164
- const contentOffset = event.nativeEvent.contentOffset;
165
- const newIndex = Math.round(contentOffset.x / (width + itemSpacing));
166
-
167
- if (newIndex !== currentIndex && newIndex >= 0 && newIndex < dataLength) {
168
- if (manager) {
169
- manager.setCurrentIndex(newIndex);
170
- setCurrentIndex(newIndex);
171
- manager.notifyStateChange();
172
- }
173
-
174
- translateX.value = withTiming(0);
175
- translateY.value = withTiming(0);
176
- initialTranslateX.value = withTiming(0);
177
- initialTranslateY.value = withTiming(0);
178
- startScale.value = withTiming(1);
179
- scale.value = withTiming(1);
180
- }
181
- },
182
- [
183
- manager,
184
- currentIndex,
185
- dataLength,
186
- width,
187
- itemSpacing,
188
- enableSwipeGesture,
189
- translateX,
190
- translateY,
191
- scale,
192
- initialTranslateX,
193
- initialTranslateY,
194
- startScale,
195
- ],
196
- );
197
-
198
- const dismissGesture = useMemo(() => {
199
- return Gesture.Pan()
200
- .minDistance(10)
201
- .averageTouches(true)
202
- .activeOffsetY([-10, 10])
203
- .failOffsetX([-10, 10])
204
- .enabled(!isZoomed)
205
- .onUpdate((event) => {
206
- translateY.value = event.translationY / resistance;
207
- })
208
- .onEnd((event) => {
209
- 'worklet';
210
-
211
- if (event.translationY > dismissThreshold && enableDismissGesture && onDismiss) {
212
- runOnJS(onDismiss)();
213
- return;
214
- }
215
-
216
- translateY.value = withSpring(0, {
217
- damping: 15,
218
- stiffness: 150,
219
- });
220
- });
221
- }, [translateY, dismissThreshold, enableDismissGesture, onDismiss, resistance, isZoomed]);
222
-
223
- const zoomPinchGesture = useMemo(() => {
224
- return Gesture.Pinch()
225
- .enabled(enableZoomGesture)
226
- .onBegin(() => {
227
- startScale.value = scale.value;
228
- })
229
- .onUpdate((event) => {
230
- scale.value = startScale.value * event.scale;
231
- })
232
- .onEnd(() => {
233
- if (scale.value > maxZoomScale) {
234
- scale.value = withTiming(maxZoomScale, {
235
- duration: 300,
236
- easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
237
- });
238
- return;
239
- }
240
-
241
- if (scale.value < 1) {
242
- scale.value = withTiming(1, {
243
- duration: 300,
244
- easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
245
- });
246
- translateX.value = withTiming(0);
247
- translateY.value = withTiming(0);
248
- }
249
- });
250
- }, [scale, enableZoomGesture, maxZoomScale, translateX, translateY, startScale]);
251
-
252
- const zoomPanGesture = useMemo(() => {
253
- return Gesture.Pan()
254
- .enabled(enableZoomPanGesture && isZoomed)
255
- .onBegin(() => {
256
- initialTranslateX.value = translateX.value;
257
- initialTranslateY.value = translateY.value;
258
- })
259
- .onUpdate((event) => {
260
- 'worklet';
261
-
262
- if (scale.value > 1) {
263
- const maxTranslateX = (width * scale.value - width) / 2;
264
- const maxTranslateY = (screenHeight * scale.value - screenHeight) / 2;
265
-
266
- translateX.value = Math.max(
267
- -maxTranslateX,
268
- Math.min(maxTranslateX, initialTranslateX.value + event.translationX),
269
- );
270
- translateY.value = Math.max(
271
- -maxTranslateY,
272
- Math.min(maxTranslateY, initialTranslateY.value + event.translationY),
273
- );
274
- }
275
- });
276
- }, [
277
- translateX,
278
- translateY,
279
- enableZoomPanGesture,
280
- isZoomed,
281
- scale,
282
- initialTranslateX,
283
- initialTranslateY,
284
- width,
285
- screenHeight,
286
- ]);
287
-
288
- const doubleTapGesture = useMemo(() => {
289
- return Gesture.Tap()
290
- .enabled(enableDoubleTapGesture)
291
- .numberOfTaps(2)
292
- .onEnd(() => {
293
- const nextScale = scale.value > 1 ? 1 : maxZoomScale;
294
-
295
- translateX.value = withTiming(0, {
296
- duration: 300,
297
- easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
298
- });
299
- translateY.value = withTiming(0, {
300
- duration: 300,
301
- easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
302
- });
303
-
304
- scale.value = withTiming(nextScale, {
305
- duration: 300,
306
- easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
307
- });
308
- });
309
- }, [scale, enableDoubleTapGesture, maxZoomScale, translateX, translateY]);
310
-
311
- const zoomGesture = useMemo(() => {
312
- return Gesture.Simultaneous(zoomPinchGesture, zoomPanGesture, doubleTapGesture);
313
- }, [zoomPinchGesture, zoomPanGesture, doubleTapGesture]);
314
-
315
- const animatedStyle = useAnimatedStyle(() => {
316
- return {
317
- transform: [{ translateY: translateY.value }, { translateX: translateX.value }, { scale: scale.value }],
318
- };
319
- });
320
-
321
- const backdropStyle = useAnimatedStyle(() => {
322
- if (!animateBackdrop || scale.value > 1) {
323
- return { opacity: 1 };
324
- }
325
-
326
- const opacity = interpolate(translateY.value, [0, 200], [1, 0], 'clamp');
327
-
328
- return { opacity };
329
- }, [animateBackdrop]);
330
-
331
- return {
332
- currentIndex,
333
- dataLength,
334
- translateY,
335
- listRef,
336
- isZoomed,
337
-
338
- dismissGesture,
339
- zoomGesture,
340
-
341
- onMomentumScrollEnd,
342
-
343
- animatedStyle,
344
- backdropStyle,
345
- };
346
- };
@@ -1,48 +0,0 @@
1
- import { useEffect, useMemo, useRef, useState } from 'react';
2
- import type ImageViewerManager from './ImageViewerManager';
3
- import type { ImageViewerManagerState } from './ImageViewerManager';
4
- import { registry } from './ImageViewerRegistry';
5
-
6
- export const useImageViewerController = (id = 'default') => {
7
- const [state, setState] = useState<ImageViewerManagerState>({
8
- currentIndex: 0,
9
- dataLength: 0,
10
- });
11
-
12
- const [manager, setManager] = useState<ImageViewerManager | null>(null);
13
- const unsubscribeRef = useRef<(() => void) | null>(null);
14
-
15
- useEffect(() => {
16
- const handleManagerChange = (newManager: ImageViewerManager | null) => {
17
- unsubscribeRef.current?.();
18
- unsubscribeRef.current = null;
19
-
20
- setManager(newManager);
21
-
22
- if (newManager) {
23
- setState(newManager.getState());
24
- unsubscribeRef.current = newManager.subscribe(setState);
25
- return;
26
- }
27
-
28
- setState({ currentIndex: 0, dataLength: 0 });
29
- };
30
-
31
- const unsubscribeFromRegistry = registry.subscribeToManager(id, handleManagerChange);
32
-
33
- return () => {
34
- unsubscribeFromRegistry();
35
- unsubscribeRef.current?.();
36
- };
37
- }, [id]);
38
-
39
- const noopFunction = useMemo(() => () => {}, []);
40
-
41
- return {
42
- goToIndex: manager?.goToIndex || noopFunction,
43
- goToPrevious: manager?.goToPrevious || noopFunction,
44
- goToNext: manager?.goToNext || noopFunction,
45
- currentIndex: state.currentIndex,
46
- totalCount: state.dataLength,
47
- };
48
- };
package/src/utils.ts DELETED
@@ -1,24 +0,0 @@
1
- import { FlatList as RNFlatList, ScrollView as RNScrollView } from 'react-native';
2
- import { FlatList as GestureFlatList, ScrollView as GestureScrollView } from 'react-native-gesture-handler';
3
-
4
- export const isScrollViewLike = (component: any): component is typeof RNScrollView => {
5
- return component === RNScrollView || component === GestureScrollView;
6
- };
7
-
8
- export const isFlatListLike = (component: any): component is typeof RNFlatList => {
9
- try {
10
- const FlashList = require('@shopify/flash-list')?.FlashList;
11
-
12
- if (FlashList && component === FlashList) {
13
- return true;
14
- }
15
- } catch {
16
- // do nothing
17
- }
18
-
19
- if (component === RNFlatList || component === GestureFlatList) {
20
- return true;
21
- }
22
-
23
- return component?.name === 'FlashList';
24
- };