react-native-gesture-image-viewer 2.2.0 → 2.3.1

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 (47) hide show
  1. package/README.md +6 -1
  2. package/lib/module/GestureViewer.js +2 -2
  3. package/lib/module/GestureViewer.js.map +1 -1
  4. package/lib/module/GestureViewerManager.js +27 -23
  5. package/lib/module/GestureViewerManager.js.map +1 -1
  6. package/lib/module/index.js.map +1 -1
  7. package/lib/module/useGestureViewer.js +156 -106
  8. package/lib/module/useGestureViewer.js.map +1 -1
  9. package/lib/module/useGestureViewerEvent.js +15 -1
  10. package/lib/module/useGestureViewerEvent.js.map +1 -1
  11. package/lib/module/useGestureViewerPaging.js +1 -1
  12. package/lib/module/useGestureViewerPaging.js.map +1 -1
  13. package/lib/module/useGestureViewerPaging.web.js +47 -26
  14. package/lib/module/useGestureViewerPaging.web.js.map +1 -1
  15. package/lib/module/utils/dismiss.js +25 -0
  16. package/lib/module/utils/dismiss.js.map +1 -0
  17. package/lib/module/utils/tapZoom.js +6 -6
  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 +2 -1
  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/types.d.ts +33 -4
  25. package/lib/typescript/src/types.d.ts.map +1 -1
  26. package/lib/typescript/src/useGestureViewer.d.ts +2 -2
  27. package/lib/typescript/src/useGestureViewer.d.ts.map +1 -1
  28. package/lib/typescript/src/useGestureViewerEvent.d.ts +15 -1
  29. package/lib/typescript/src/useGestureViewerEvent.d.ts.map +1 -1
  30. package/lib/typescript/src/useGestureViewerPaging.types.d.ts +9 -1
  31. package/lib/typescript/src/useGestureViewerPaging.types.d.ts.map +1 -1
  32. package/lib/typescript/src/useGestureViewerPaging.web.d.ts +1 -1
  33. package/lib/typescript/src/useGestureViewerPaging.web.d.ts.map +1 -1
  34. package/lib/typescript/src/utils/dismiss.d.ts +4 -0
  35. package/lib/typescript/src/utils/dismiss.d.ts.map +1 -0
  36. package/package.json +1 -1
  37. package/src/GestureViewer.tsx +4 -2
  38. package/src/GestureViewerManager.ts +31 -25
  39. package/src/index.tsx +1 -0
  40. package/src/types.ts +31 -4
  41. package/src/useGestureViewer.ts +239 -157
  42. package/src/useGestureViewerEvent.ts +15 -1
  43. package/src/useGestureViewerPaging.ts +1 -1
  44. package/src/useGestureViewerPaging.types.ts +7 -1
  45. package/src/useGestureViewerPaging.web.ts +56 -32
  46. package/src/utils/dismiss.ts +36 -0
  47. package/src/utils/tapZoom.ts +6 -6
@@ -86,6 +86,10 @@ class GestureViewerManager {
86
86
  this.emitEvent('rotationChange', { rotation, previousRotation });
87
87
  };
88
88
 
89
+ emitTap = (data: GestureViewerEventData['tap']) => {
90
+ this.emitEvent('tap', data);
91
+ };
92
+
89
93
  getState(): GestureViewerState {
90
94
  return {
91
95
  currentIndex: this.currentIndex,
@@ -154,32 +158,34 @@ class GestureViewerManager {
154
158
  rotate = (angle: 0 | 90 | 180 | 270 | 360 = 90, clockwise = true) => {
155
159
  const MAX_ANGLE = 360;
156
160
 
161
+ const currentRotation = this.rotation?.get();
162
+
157
163
  if (
158
- !this.rotation ||
164
+ currentRotation === undefined ||
159
165
  angle < 0 ||
160
166
  angle > MAX_ANGLE ||
161
- (angle !== 0 && this.rotation.value % angle !== 0 && angle !== 360)
167
+ (angle !== 0 && currentRotation % angle !== 0 && angle !== 360)
162
168
  ) {
163
169
  return;
164
170
  }
165
171
 
166
172
  if (angle === 0) {
167
- const nextAngle = Math.floor(this.rotation.value / MAX_ANGLE) * MAX_ANGLE;
173
+ const nextAngle = Math.floor(currentRotation / MAX_ANGLE) * MAX_ANGLE;
168
174
 
169
- this.rotation.value = withTiming(clockwise ? nextAngle : nextAngle - MAX_ANGLE);
175
+ this.rotation?.set(withTiming(clockwise ? nextAngle : nextAngle - MAX_ANGLE));
170
176
  return;
171
177
  }
172
178
 
173
179
  if (angle === 360) {
174
- this.rotation.value = withTiming(
175
- clockwise ? this.rotation.value + MAX_ANGLE : this.rotation.value - MAX_ANGLE,
180
+ this.rotation?.set(
181
+ withTiming(clockwise ? currentRotation + MAX_ANGLE : currentRotation - MAX_ANGLE),
176
182
  );
177
183
  return;
178
184
  }
179
185
 
180
- const nextAngle = clockwise ? this.rotation.value + angle : this.rotation.value - angle;
186
+ const nextAngle = clockwise ? currentRotation + angle : currentRotation - angle;
181
187
 
182
- this.rotation.value = withTiming(nextAngle);
188
+ this.rotation?.set(withTiming(nextAngle));
183
189
  };
184
190
 
185
191
  zoomIn = (multiplier = 0.25) => {
@@ -193,21 +199,21 @@ class GestureViewerManager {
193
199
  return;
194
200
  }
195
201
 
196
- const nextScale = Math.min(this.scale.value * (1 + multiplier), this.maxZoomScale);
202
+ const nextScale = Math.min(this.scale.get() * (1 + multiplier), this.maxZoomScale);
197
203
 
198
- this.scale.value = withTiming(nextScale);
204
+ this.scale.set(withTiming(nextScale));
199
205
 
200
206
  const { translateX, translateY } = createBoundsConstraint({
201
207
  width: this.width,
202
208
  height: this.height,
203
209
  })({
204
- translateX: this.translateX.value,
205
- translateY: this.translateY.value,
210
+ translateX: this.translateX.get(),
211
+ translateY: this.translateY.get(),
206
212
  scale: nextScale,
207
213
  });
208
214
 
209
- this.translateX.value = withTiming(translateX);
210
- this.translateY.value = withTiming(translateY);
215
+ this.translateX.set(withTiming(translateX));
216
+ this.translateY.set(withTiming(translateY));
211
217
  };
212
218
 
213
219
  zoomOut = (multiplier = 0.25) => {
@@ -221,13 +227,13 @@ class GestureViewerManager {
221
227
  return;
222
228
  }
223
229
 
224
- const nextScale = Math.max(this.scale.value / (1 + multiplier), 1);
230
+ const nextScale = Math.max(this.scale.get() / (1 + multiplier), 1);
225
231
 
226
- this.scale.value = withTiming(nextScale);
232
+ this.scale.set(withTiming(nextScale));
227
233
 
228
234
  if (nextScale === 1) {
229
- this.translateX.value = withTiming(0);
230
- this.translateY.value = withTiming(0);
235
+ this.translateX.set(withTiming(0));
236
+ this.translateY.set(withTiming(0));
231
237
  return;
232
238
  }
233
239
 
@@ -235,13 +241,13 @@ class GestureViewerManager {
235
241
  width: this.width,
236
242
  height: this.height,
237
243
  })({
238
- translateX: this.translateX.value,
239
- translateY: this.translateY.value,
244
+ translateX: this.translateX.get(),
245
+ translateY: this.translateY.get(),
240
246
  scale: nextScale,
241
247
  });
242
248
 
243
- this.translateX.value = withTiming(translateX);
244
- this.translateY.value = withTiming(translateY);
249
+ this.translateX.set(withTiming(translateX));
250
+ this.translateY.set(withTiming(translateY));
245
251
  };
246
252
 
247
253
  resetZoom = (scale = 1) => {
@@ -255,9 +261,9 @@ class GestureViewerManager {
255
261
  return;
256
262
  }
257
263
 
258
- this.scale.value = withTiming(scale);
259
- this.translateX.value = withTiming(0);
260
- this.translateY.value = withTiming(0);
264
+ this.scale.set(withTiming(scale));
265
+ this.translateX.set(withTiming(0));
266
+ this.translateY.set(withTiming(0));
261
267
  };
262
268
 
263
269
  goToIndex = (index: number) => {
package/src/index.tsx CHANGED
@@ -8,6 +8,7 @@ export type {
8
8
  GestureViewerEventType,
9
9
  GestureViewerProps,
10
10
  GestureViewerState,
11
+ GestureViewerSingleTapEvent,
11
12
  } from './types';
12
13
  export { useGestureViewerController } from './useGestureViewerController';
13
14
  export { useGestureViewerEvent } from './useGestureViewerEvent';
package/src/types.ts CHANGED
@@ -75,6 +75,15 @@ export type TriggerRect = {
75
75
  height: number;
76
76
  };
77
77
 
78
+ export type GestureViewerSingleTapEvent<ItemT> = {
79
+ x: number;
80
+ y: number;
81
+ index: number;
82
+ item: ItemT;
83
+ };
84
+
85
+ export type GestureViewerDismissDirection = 'down' | 'up' | 'both';
86
+
78
87
  export interface TriggerAnimationConfig extends WithTimingConfig {
79
88
  /**
80
89
  * Animation duration in milliseconds
@@ -121,9 +130,18 @@ export interface GestureViewerProps<ItemT, LC> {
121
130
  * A callback function that is called to render the item.
122
131
  */
123
132
  renderItem: (item: ItemT, index: number) => React.ReactElement;
133
+ /**
134
+ * A callback function that is called when a single tap is confirmed on the viewer content.
135
+ * @remarks
136
+ * - The callback runs only after the tap is resolved as a single tap, so it may be slightly delayed when double-tap zoom is enabled.
137
+ * - It is not called for swipe, pinch, dismiss, or double-tap zoom gestures.
138
+ * - Prefer this callback over overlaying a pressable in `renderContainer` for fullscreen tap handling.
139
+ */
140
+ onSingleTap?: (event: GestureViewerSingleTapEvent<ItemT>) => void;
124
141
  /**
125
142
  * A callback function that is called to render the container.
126
143
  * @remarks Useful for composing additional UI (e.g., close button, toolbars) around the viewer.
144
+ * Prefer `onSingleTap` for fullscreen tap handling instead of overlaying a pressable over the viewer content.
127
145
  * The second argument provides control helpers such as `dismiss()` to close the viewer.
128
146
  *
129
147
  * @param children - The viewer content to be rendered inside your container.
@@ -183,8 +201,8 @@ export interface GestureViewerProps<ItemT, LC> {
183
201
  */
184
202
  autoPlayInterval?: number;
185
203
  /**
186
- * Dismiss gesture options. Calls `onDismiss` function when swiping down.
187
- * @remarks Useful for closing modals with downward swipe gestures.
204
+ * Dismiss gesture options.
205
+ * @remarks Useful for closing modals with configurable vertical swipe gestures.
188
206
  */
189
207
  dismiss?: {
190
208
  /**
@@ -192,6 +210,12 @@ export interface GestureViewerProps<ItemT, LC> {
192
210
  * @defaultValue true
193
211
  */
194
212
  enabled?: boolean;
213
+ /**
214
+ * Controls which vertical swipe direction can trigger `onDismiss`.
215
+ * @remarks Use `down` for backward-compatible behavior, `up` for upward-only dismiss, or `both` for either direction.
216
+ * @defaultValue 'down'
217
+ */
218
+ direction?: GestureViewerDismissDirection;
195
219
  /**
196
220
  * `threshold` controls when `onDismiss` is called by applying a threshold value during vertical gestures.
197
221
  * @defaultValue 80
@@ -203,7 +227,7 @@ export interface GestureViewerProps<ItemT, LC> {
203
227
  */
204
228
  resistance?: number;
205
229
  /**
206
- * By default, the background `opacity` gradually decreases from 1 to 0 during downward swipe gestures.
230
+ * By default, the background `opacity` gradually decreases as you drag in the configured dismiss direction.
207
231
  * @remarks When `false`, this animation is disabled.
208
232
  * @defaultValue true
209
233
  */
@@ -425,11 +449,14 @@ export type GestureViewerState = {
425
449
  readonly totalCount: number;
426
450
  };
427
451
 
428
- export type GestureViewerEventType = 'zoomChange' | 'rotationChange';
452
+ export type GestureViewerEventType = 'zoomChange' | 'rotationChange' | 'tap';
453
+
454
+ export type SingleTapEventData = { kind: 'single'; x: number; y: number; index: number };
429
455
 
430
456
  export type GestureViewerEventData = {
431
457
  zoomChange: { scale: number; previousScale: number | null };
432
458
  rotationChange: { rotation: number; previousRotation: number | null };
459
+ tap: SingleTapEventData;
433
460
  };
434
461
 
435
462
  export type GestureViewerEventCallback<T extends GestureViewerEventType> = (