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
@@ -1,9 +1,10 @@
1
- import { useCallback, useEffect, useRef } from 'react';
1
+ import { useCallback, useEffect, useRef, type MouseEvent } from 'react';
2
2
  import type { NativeScrollEvent, NativeSyntheticEvent } from 'react-native';
3
3
 
4
4
  import type {
5
5
  UseGestureViewerPagingArgs,
6
6
  UseGestureViewerPagingResult,
7
+ WebClickTarget,
7
8
  } from './useGestureViewerPaging.types';
8
9
  import { applyTapZoomAtPoint } from './utils/tapZoom';
9
10
  import {
@@ -35,6 +36,7 @@ export function useGestureViewerPaging({
35
36
  enableHorizontalSwipe,
36
37
  enableLoop,
37
38
  height,
39
+ onSingleTap,
38
40
  isRotated,
39
41
  isZoomed,
40
42
  itemSpacing,
@@ -48,6 +50,8 @@ export function useGestureViewerPaging({
48
50
  translateY,
49
51
  width,
50
52
  }: UseGestureViewerPagingArgs): UseGestureViewerPagingResult {
53
+ const webSingleTapTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
54
+
51
55
  const webScrollRuntimeRef = useRef<WebScrollRuntime>({
52
56
  actor: 'idle',
53
57
  isAutoplayPausedByUser: false,
@@ -59,6 +63,13 @@ export function useGestureViewerPaging({
59
63
  resumeAutoplayTimer: null,
60
64
  });
61
65
 
66
+ const clearWebSingleTapTimer = useCallback(() => {
67
+ if (webSingleTapTimerRef.current) {
68
+ clearTimeout(webSingleTapTimerRef.current);
69
+ webSingleTapTimerRef.current = null;
70
+ }
71
+ }, []);
72
+
62
73
  const clearWebSettleTimer = useCallback(() => {
63
74
  const runtime = webScrollRuntimeRef.current;
64
75
 
@@ -232,10 +243,11 @@ export function useGestureViewerPaging({
232
243
 
233
244
  useEffect(() => {
234
245
  return () => {
246
+ clearWebSingleTapTimer();
235
247
  clearWebSettleTimer();
236
248
  clearWebAutoplayResumeTimer();
237
249
  };
238
- }, [clearWebAutoplayResumeTimer, clearWebSettleTimer]);
250
+ }, [clearWebAutoplayResumeTimer, clearWebSettleTimer, clearWebSingleTapTimer]);
239
251
 
240
252
  useEffect(() => {
241
253
  if (
@@ -351,50 +363,62 @@ export function useGestureViewerPaging({
351
363
  manager?.handleScrollBeginDrag();
352
364
  }, [beginWebUserInteraction, clearWebSettleTimer, manager]);
353
365
 
354
- const onWebDoubleClick = useCallback(
355
- (event: any) => {
356
- if (!enableDoubleTapZoom || event?.nativeEvent?.detail !== 2) {
366
+ const onWebClick = useCallback(
367
+ (event: MouseEvent<WebClickTarget>) => {
368
+ const detail = event.detail;
369
+ const rect = event.currentTarget.getBoundingClientRect();
370
+ const resolvedX = event.clientX - rect.left;
371
+ const resolvedY = event.clientY - rect.top;
372
+
373
+ if (!enableDoubleTapZoom) {
374
+ if (!onSingleTap) {
375
+ return;
376
+ }
377
+
378
+ clearWebSingleTapTimer();
379
+ onSingleTap(resolvedX, resolvedY);
357
380
  return;
358
381
  }
359
382
 
360
- pauseWebAutoplayWithoutPagingInteraction();
361
- scheduleWebAutoplayResume();
383
+ if (detail === 2) {
384
+ clearWebSingleTapTimer();
362
385
 
363
- const nativeEvent = event?.nativeEvent;
364
- const eventTarget = event?.currentTarget ?? nativeEvent?.currentTarget ?? nativeEvent?.target;
365
- let locationX = nativeEvent?.locationX;
366
- let locationY = nativeEvent?.locationY;
386
+ pauseWebAutoplayWithoutPagingInteraction();
387
+ scheduleWebAutoplayResume();
367
388
 
368
- if (
369
- (!Number.isFinite(locationX) || !Number.isFinite(locationY)) &&
370
- typeof eventTarget?.getBoundingClientRect === 'function' &&
371
- Number.isFinite(nativeEvent?.clientX) &&
372
- Number.isFinite(nativeEvent?.clientY)
373
- ) {
374
- const rect = eventTarget.getBoundingClientRect();
389
+ applyTapZoomAtPoint({
390
+ x: resolvedX,
391
+ y: resolvedY,
392
+ width,
393
+ height,
394
+ maxZoomScale,
395
+ scale,
396
+ translateX,
397
+ translateY,
398
+ });
399
+ return;
400
+ }
375
401
 
376
- locationX = nativeEvent.clientX - rect.left;
377
- locationY = nativeEvent.clientY - rect.top;
402
+ if (detail !== 1 || !onSingleTap) {
403
+ return;
378
404
  }
379
405
 
380
- applyTapZoomAtPoint({
381
- x: Number.isFinite(locationX) ? locationX : width / 2,
382
- y: Number.isFinite(locationY) ? locationY : height / 2,
383
- width,
384
- height,
385
- maxZoomScale,
386
- scale,
387
- translateX,
388
- translateY,
389
- });
406
+ clearWebSingleTapTimer();
407
+
408
+ webSingleTapTimerRef.current = setTimeout(() => {
409
+ onSingleTap(resolvedX, resolvedY);
410
+ webSingleTapTimerRef.current = null;
411
+ }, 250);
390
412
  },
391
413
  [
414
+ clearWebSingleTapTimer,
392
415
  enableDoubleTapZoom,
393
416
  height,
394
417
  maxZoomScale,
418
+ onSingleTap,
395
419
  pauseWebAutoplayWithoutPagingInteraction,
396
- scheduleWebAutoplayResume,
397
420
  scale,
421
+ scheduleWebAutoplayResume,
398
422
  translateX,
399
423
  translateY,
400
424
  width,
@@ -405,6 +429,6 @@ export function useGestureViewerPaging({
405
429
  onMomentumScrollEnd,
406
430
  onScroll,
407
431
  onScrollBeginDrag,
408
- onWebDoubleClick,
432
+ onWebClick,
409
433
  };
410
434
  }
@@ -0,0 +1,36 @@
1
+ import type { GestureViewerDismissDirection } from '../types';
2
+
3
+ export const shouldDismissByDirection = (
4
+ translationY: number,
5
+ threshold: number,
6
+ direction: GestureViewerDismissDirection,
7
+ ): boolean => {
8
+ 'worklet';
9
+
10
+ if (direction === 'up') {
11
+ return translationY < -threshold;
12
+ }
13
+
14
+ if (direction === 'both') {
15
+ return Math.abs(translationY) > threshold;
16
+ }
17
+
18
+ return translationY > threshold;
19
+ };
20
+
21
+ export const getDismissDistance = (
22
+ translationY: number,
23
+ direction: GestureViewerDismissDirection,
24
+ ): number => {
25
+ 'worklet';
26
+
27
+ if (direction === 'up') {
28
+ return Math.max(-translationY, 0);
29
+ }
30
+
31
+ if (direction === 'both') {
32
+ return Math.abs(translationY);
33
+ }
34
+
35
+ return Math.max(translationY, 0);
36
+ };
@@ -22,7 +22,7 @@ export const applyTapZoomAtPoint = ({
22
22
  }) => {
23
23
  'worklet';
24
24
 
25
- const nextScale = scale.value > 1 ? 1 : maxZoomScale;
25
+ const nextScale = scale.get() > 1 ? 1 : maxZoomScale;
26
26
  const timingConfig = {
27
27
  duration: 300,
28
28
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
@@ -33,12 +33,12 @@ export const applyTapZoomAtPoint = ({
33
33
  const centerY = y - height / 2;
34
34
 
35
35
  // NOTE 확대로 밀려난 거리만큼 반대로 이동해서 탭 지점을 제자리에 유지
36
- translateX.value = withTiming(-centerX * (nextScale - 1), timingConfig);
37
- translateY.value = withTiming(-centerY * (nextScale - 1), timingConfig);
36
+ translateX.set(withTiming(-centerX * (nextScale - 1), timingConfig));
37
+ translateY.set(withTiming(-centerY * (nextScale - 1), timingConfig));
38
38
  } else {
39
- translateX.value = withTiming(0, timingConfig);
40
- translateY.value = withTiming(0, timingConfig);
39
+ translateX.set(withTiming(0, timingConfig));
40
+ translateY.set(withTiming(0, timingConfig));
41
41
  }
42
42
 
43
- scale.value = withTiming(nextScale, timingConfig);
43
+ scale.set(withTiming(nextScale, timingConfig));
44
44
  };