zkit-ui 2.0.5 → 2.0.7

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 (80) hide show
  1. package/README.md +1 -1
  2. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryLayoutSupport.java → NativeImagePreviewLayoutSupport.java} +12 -12
  3. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryMediaItem.java → NativeImagePreviewMediaItem.java} +2 -2
  4. package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewModule.kt +43 -0
  5. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryPagerAdapter.java → NativeImagePreviewPagerAdapter.java} +18 -18
  6. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySharedElementGeometry.java → NativeImagePreviewSharedElementGeometry.java} +2 -2
  7. package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementNames.java +39 -0
  8. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySharedElementState.java → NativeImagePreviewSharedElementState.java} +4 -4
  9. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySourceViewRegistry.java → NativeImagePreviewSourceViewRegistry.java} +2 -2
  10. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryTransitionSnapshotView.java → NativeImagePreviewTransitionSnapshotView.java} +3 -3
  11. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryVideoPlayerView.java → NativeImagePreviewVideoPlayerView.java} +7 -7
  12. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GaleriaView.kt → NativeImagePreviewView.kt} +18 -18
  13. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerOverlayView.java → NativeImagePreviewViewerOverlayView.java} +49 -49
  14. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerPageView.java → NativeImagePreviewViewerPageView.java} +27 -27
  15. package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerTransitionController.java → NativeImagePreviewViewerTransitionController.java} +22 -22
  16. package/dist/ui/Button/index.d.ts +1 -1
  17. package/dist/ui/Button/index.d.ts.map +1 -1
  18. package/dist/ui/Button/index.js +18 -3
  19. package/dist/ui/NativeImagePreview/NativeImagePreview.js +6 -6
  20. package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts +1 -0
  21. package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -1
  22. package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +105 -11
  23. package/dist/ui/NativeImagePreview/context.d.ts +1 -1
  24. package/dist/ui/NativeImagePreview/context.d.ts.map +1 -1
  25. package/dist/ui/NativeImagePreview/types.d.ts +3 -1
  26. package/dist/ui/NativeImagePreview/types.d.ts.map +1 -1
  27. package/dist/ui/NativeImagePreview/utils.d.ts.map +1 -1
  28. package/dist/ui/NativeImagePreview/utils.js +1 -0
  29. package/dist/ui/NativeImagePreview/web/geometry.d.ts +20 -0
  30. package/dist/ui/NativeImagePreview/web/geometry.d.ts.map +1 -0
  31. package/dist/ui/NativeImagePreview/web/geometry.js +121 -0
  32. package/dist/ui/NativeImagePreview/web/media.d.ts +6 -0
  33. package/dist/ui/NativeImagePreview/web/media.d.ts.map +1 -0
  34. package/dist/ui/NativeImagePreview/web/media.js +90 -0
  35. package/dist/ui/NativeImagePreview/web/styles.css +137 -0
  36. package/dist/ui/NativeImagePreview/web/transition.d.ts +15 -0
  37. package/dist/ui/NativeImagePreview/web/transition.d.ts.map +1 -0
  38. package/dist/ui/NativeImagePreview/web/transition.js +77 -0
  39. package/dist/ui/NativeImagePreview/web/types.d.ts +74 -0
  40. package/dist/ui/NativeImagePreview/web/types.d.ts.map +1 -0
  41. package/dist/ui/NativeImagePreview/web/types.js +3 -0
  42. package/dist/ui/NativeImagePreview/web/viewer.d.ts +104 -0
  43. package/dist/ui/NativeImagePreview/web/viewer.d.ts.map +1 -0
  44. package/dist/ui/NativeImagePreview/web/viewer.js +894 -0
  45. package/expo-module.config.json +2 -2
  46. package/ios/{GalleryViewer/GaleriaImageLoader.swift → NativeImagePreview/NativeImagePreviewImageLoader.swift} +7 -7
  47. package/ios/NativeImagePreview/NativeImagePreviewImageViewInteraction.swift +245 -0
  48. package/ios/{GalleryViewer/GaleriaLayoutSupport.swift → NativeImagePreview/NativeImagePreviewLayoutSupport.swift} +19 -19
  49. package/ios/NativeImagePreview/NativeImagePreviewMedia.swift +28 -0
  50. package/ios/NativeImagePreview/NativeImagePreviewSourceViewRegistry.swift +80 -0
  51. package/ios/{GalleryViewer/GaleriaVideoPlayerView.swift → NativeImagePreview/NativeImagePreviewVideoPlayerView.swift} +1 -1
  52. package/ios/{GalleryViewer/GaleriaViewerConfiguration.swift → NativeImagePreview/NativeImagePreviewViewerConfiguration.swift} +7 -7
  53. package/ios/{GalleryViewer/GaleriaViewerController.swift → NativeImagePreview/NativeImagePreviewViewerController.swift} +36 -36
  54. package/ios/{GalleryViewer/GaleriaViewerPageView.swift → NativeImagePreview/NativeImagePreviewViewerPageView.swift} +33 -33
  55. package/ios/{GalleryViewer/GaleriaViewerTransitionCoordinator.swift → NativeImagePreview/NativeImagePreviewViewerTransitionCoordinator.swift} +16 -16
  56. package/ios/{GaleriaModule.swift → NativeImagePreviewModule.swift} +4 -4
  57. package/ios/{GaleriaView.swift → NativeImagePreviewView.swift} +26 -27
  58. package/package.json +18 -12
  59. package/src/services/ImagePreviewService/README.md +1 -1
  60. package/src/types.d.ts +1 -0
  61. package/src/ui/Button/README.md +1 -1
  62. package/src/ui/Button/index.tsx +27 -7
  63. package/src/ui/NativeImagePreview/NativeImagePreview.tsx +7 -7
  64. package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +175 -11
  65. package/src/ui/NativeImagePreview/README.md +3 -3
  66. package/src/ui/NativeImagePreview/context.tsx +1 -1
  67. package/src/ui/NativeImagePreview/types.ts +3 -1
  68. package/src/ui/NativeImagePreview/utils.ts +1 -0
  69. package/src/ui/NativeImagePreview/web/geometry.ts +134 -0
  70. package/src/ui/NativeImagePreview/web/media.ts +91 -0
  71. package/src/ui/NativeImagePreview/web/styles.css +137 -0
  72. package/src/ui/NativeImagePreview/web/transition.ts +111 -0
  73. package/src/ui/NativeImagePreview/web/types.ts +88 -0
  74. package/src/ui/NativeImagePreview/web/viewer.ts +1024 -0
  75. package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaModule.kt +0 -43
  76. package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementNames.java +0 -39
  77. package/ios/GalleryViewer/GaleriaImageViewInteraction.swift +0 -245
  78. package/ios/GalleryViewer/GaleriaMedia.swift +0 -28
  79. package/ios/GalleryViewer/GaleriaSourceViewRegistry.swift +0 -80
  80. /package/android/src/main/res/layout/{galeria_player_view.xml → native_image_preview_player_view.xml} +0 -0
@@ -0,0 +1,894 @@
1
+ "use strict";
2
+ /// <reference lib="dom" />
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.NativeImagePreviewWebViewer = void 0;
5
+ exports.createNativeImagePreviewWeb = createNativeImagePreviewWeb;
6
+ exports.sourceRectFromElement = sourceRectFromElement;
7
+ const geometry_1 = require("./geometry");
8
+ const media_1 = require("./media");
9
+ const transition_1 = require("./transition");
10
+ const MAX_SCALE = 5;
11
+ const DOUBLE_TAP_SCALE = 2.5;
12
+ const DRAG_DISMISS_DISTANCE = 72;
13
+ const DRAG_DISMISS_MIN_FLING_DISTANCE = 24;
14
+ const DRAG_DISMISS_VELOCITY = 520;
15
+ const SWIPE_DISTANCE = 52;
16
+ const SWIPE_MIN_FLING_DISTANCE = 22;
17
+ const SWIPE_VELOCITY = 480;
18
+ const MODE_LOCK_DISTANCE = 8;
19
+ class NativeImagePreviewWebViewer {
20
+ constructor() {
21
+ this.root = null;
22
+ this.backdrop = null;
23
+ this.viewport = null;
24
+ this.track = null;
25
+ this.closeButton = null;
26
+ this.counter = null;
27
+ this.mediaElements = [];
28
+ this.slideStates = [];
29
+ this.items = [];
30
+ this.options = null;
31
+ this.index = 0;
32
+ this.openState = false;
33
+ this.closing = false;
34
+ this.sourceVisibility = 'hidden';
35
+ this.sourceElement = null;
36
+ this.sourcePreviousVisibility = '';
37
+ this.pointers = new Map();
38
+ this.mode = 'none';
39
+ this.downX = 0;
40
+ this.downY = 0;
41
+ this.lastX = 0;
42
+ this.lastY = 0;
43
+ this.downAt = 0;
44
+ this.dragX = 0;
45
+ this.dragY = 0;
46
+ this.pinchStartDistance = 1;
47
+ this.pinchStartScale = 1;
48
+ this.pinchStartTranslateX = 0;
49
+ this.pinchStartTranslateY = 0;
50
+ this.pinchStartCenterX = 0;
51
+ this.pinchStartCenterY = 0;
52
+ this.previousBodyOverflow = '';
53
+ this.previousHtmlOverflow = '';
54
+ this.viewportWidth = 1;
55
+ this.viewportHeight = 1;
56
+ this.handlePointerDown = (event) => {
57
+ if (this.closing || !this.root) {
58
+ return;
59
+ }
60
+ const target = event.target;
61
+ if (this.shouldIgnorePointerStart(target, event)) {
62
+ return;
63
+ }
64
+ this.root.setPointerCapture(event.pointerId);
65
+ this.pointers.set(event.pointerId, { id: event.pointerId, x: event.clientX, y: event.clientY });
66
+ this.downX = event.clientX;
67
+ this.downY = event.clientY;
68
+ this.lastX = event.clientX;
69
+ this.lastY = event.clientY;
70
+ this.downAt = performance.now();
71
+ this.dragX = 0;
72
+ this.dragY = 0;
73
+ this.mode = this.pointers.size >= 2 ? 'pinch' : 'none';
74
+ this.preparePinchIfNeeded();
75
+ };
76
+ this.handlePointerMove = (event) => {
77
+ if (this.closing || !this.pointers.has(event.pointerId)) {
78
+ return;
79
+ }
80
+ event.preventDefault();
81
+ this.pointers.set(event.pointerId, { id: event.pointerId, x: event.clientX, y: event.clientY });
82
+ if (this.pointers.size >= 2) {
83
+ this.mode = 'pinch';
84
+ this.applyPinch();
85
+ return;
86
+ }
87
+ const state = this.activeState();
88
+ const dx = event.clientX - this.downX;
89
+ const dy = event.clientY - this.downY;
90
+ const frameDx = event.clientX - this.lastX;
91
+ const frameDy = event.clientY - this.lastY;
92
+ this.lastX = event.clientX;
93
+ this.lastY = event.clientY;
94
+ if (this.mode === 'none') {
95
+ if (Math.hypot(dx, dy) < MODE_LOCK_DISTANCE) {
96
+ return;
97
+ }
98
+ if (state.scale > 1.02) {
99
+ this.mode = 'pan';
100
+ }
101
+ else if (Math.abs(dy) > Math.abs(dx) * 1.15) {
102
+ this.mode = 'dismiss';
103
+ }
104
+ else {
105
+ this.mode = 'swipe';
106
+ }
107
+ }
108
+ if (this.mode === 'pan') {
109
+ state.translateX += frameDx;
110
+ state.translateY += frameDy;
111
+ this.clampActiveState();
112
+ this.applyActiveMediaTransform(false);
113
+ return;
114
+ }
115
+ if (this.mode === 'dismiss') {
116
+ this.applyDismissDrag(dy);
117
+ return;
118
+ }
119
+ if (this.mode === 'swipe') {
120
+ this.dragX = dx;
121
+ this.updateTrack();
122
+ }
123
+ };
124
+ this.handlePointerUp = (event) => {
125
+ var _a;
126
+ if (!this.pointers.has(event.pointerId)) {
127
+ return;
128
+ }
129
+ const finalDx = event.clientX - this.downX;
130
+ const finalDy = event.clientY - this.downY;
131
+ this.pointers.delete(event.pointerId);
132
+ if ((_a = this.root) === null || _a === void 0 ? void 0 : _a.hasPointerCapture(event.pointerId)) {
133
+ this.root.releasePointerCapture(event.pointerId);
134
+ }
135
+ if (this.pointers.size >= 2) {
136
+ this.preparePinchIfNeeded();
137
+ return;
138
+ }
139
+ if (this.pointers.size === 1) {
140
+ const point = [...this.pointers.values()][0];
141
+ this.downX = point.x;
142
+ this.downY = point.y;
143
+ this.lastX = point.x;
144
+ this.lastY = point.y;
145
+ this.mode = 'none';
146
+ return;
147
+ }
148
+ const elapsed = Math.max(1, performance.now() - this.downAt);
149
+ const velocityX = (event.clientX - this.downX) / elapsed * 1000;
150
+ const velocityY = (event.clientY - this.downY) / elapsed * 1000;
151
+ if (this.mode === 'dismiss') {
152
+ this.applyDismissDrag(finalDy);
153
+ const shouldClose = Math.abs(this.dragY) > DRAG_DISMISS_DISTANCE
154
+ || (Math.abs(this.dragY) > DRAG_DISMISS_MIN_FLING_DISTANCE
155
+ && Math.abs(velocityY) > DRAG_DISMISS_VELOCITY);
156
+ if (shouldClose) {
157
+ void this.close('drag');
158
+ }
159
+ else {
160
+ this.resetDismissDrag();
161
+ }
162
+ }
163
+ else if (this.mode === 'swipe') {
164
+ this.dragX = finalDx;
165
+ this.finishSwipe(velocityX);
166
+ }
167
+ else if (this.mode === 'none') {
168
+ this.handleTap(event);
169
+ }
170
+ this.mode = 'none';
171
+ this.dragX = 0;
172
+ this.dragY = 0;
173
+ };
174
+ this.handleWheel = (event) => {
175
+ if (!event.ctrlKey && Math.abs(event.deltaY) < Math.abs(event.deltaX)) {
176
+ return;
177
+ }
178
+ event.preventDefault();
179
+ const state = this.activeState();
180
+ const nextScale = (0, geometry_1.clamp)(state.scale * (event.deltaY > 0 ? 0.92 : 1.08), 1, MAX_SCALE);
181
+ state.scale = nextScale;
182
+ if (nextScale <= 1.01) {
183
+ state.translateX = 0;
184
+ state.translateY = 0;
185
+ }
186
+ this.clampActiveState();
187
+ this.applyActiveMediaTransform(true);
188
+ };
189
+ this.handleRootClick = (event) => {
190
+ if (event.target === this.backdrop) {
191
+ void this.close('backdrop');
192
+ }
193
+ };
194
+ this.handleCloseClick = () => {
195
+ void this.close('button');
196
+ };
197
+ this.handleKeyDown = (event) => {
198
+ if (!this.openState) {
199
+ return;
200
+ }
201
+ if (event.key === 'Escape') {
202
+ void this.close('escape');
203
+ }
204
+ if (event.key === 'ArrowLeft') {
205
+ this.goTo(this.index - 1, 'api');
206
+ }
207
+ if (event.key === 'ArrowRight') {
208
+ this.goTo(this.index + 1, 'api');
209
+ }
210
+ };
211
+ this.handleResize = () => {
212
+ this.captureViewportSize();
213
+ this.applyViewportStyle();
214
+ void this.layoutMediaElements().then(() => {
215
+ this.clampActiveState();
216
+ this.updateTrack();
217
+ this.applyActiveMediaTransform(false);
218
+ });
219
+ };
220
+ this.handleWindowBlur = () => {
221
+ this.cancelActiveGesture();
222
+ };
223
+ this.handleVisibilityChange = () => {
224
+ if (document.visibilityState !== 'visible') {
225
+ this.cancelActiveGesture();
226
+ }
227
+ };
228
+ this.handleLostPointerCapture = (event) => {
229
+ const pointerId = event.pointerId;
230
+ window.setTimeout(() => {
231
+ if (this.pointers.has(pointerId)) {
232
+ this.cancelActiveGesture();
233
+ }
234
+ }, 80);
235
+ };
236
+ }
237
+ async open(options) {
238
+ var _a, _b;
239
+ if (this.openState) {
240
+ await this.close('replace');
241
+ }
242
+ const items = (0, media_1.normalizeItems)(options.items);
243
+ if (!items.length) {
244
+ return;
245
+ }
246
+ this.options = options;
247
+ this.items = items;
248
+ this.index = (0, geometry_1.clamp)(Math.trunc((_a = options.index) !== null && _a !== void 0 ? _a : 0), 0, items.length - 1);
249
+ this.sourceVisibility = (_b = options.sourceVisibility) !== null && _b !== void 0 ? _b : 'hidden';
250
+ this.sourceElement = this.resolveSourceElement(this.index);
251
+ this.slideStates = items.map(() => this.createSlideState());
252
+ this.openState = true;
253
+ this.closing = false;
254
+ this.captureViewportSize();
255
+ this.lockDocumentScroll();
256
+ this.mount();
257
+ await this.layoutMediaElements();
258
+ this.updateCounter();
259
+ this.updateTrack();
260
+ await this.nextFrame();
261
+ await this.performOpenTransition();
262
+ this.showContent();
263
+ }
264
+ async close(reason = 'api') {
265
+ if (!this.openState || this.closing) {
266
+ return;
267
+ }
268
+ this.closing = true;
269
+ await this.performCloseTransition(reason);
270
+ this.finishClose(reason);
271
+ }
272
+ destroy() {
273
+ if (!this.openState) {
274
+ this.unmount();
275
+ return;
276
+ }
277
+ this.finishClose('api');
278
+ }
279
+ getIndex() {
280
+ return this.index;
281
+ }
282
+ isOpen() {
283
+ return this.openState;
284
+ }
285
+ mount() {
286
+ var _a, _b, _c, _d;
287
+ const root = document.createElement('div');
288
+ root.className = `native-image-preview-root native-image-preview-theme-${(_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.theme) !== null && _b !== void 0 ? _b : 'dark'}`;
289
+ root.tabIndex = -1;
290
+ this.applyViewportStyle(root);
291
+ const backdrop = document.createElement('div');
292
+ backdrop.className = 'native-image-preview-backdrop';
293
+ backdrop.style.opacity = '0';
294
+ root.append(backdrop);
295
+ const viewport = document.createElement('div');
296
+ viewport.className = 'native-image-preview-viewport';
297
+ const track = document.createElement('div');
298
+ track.className = 'native-image-preview-track';
299
+ track.style.opacity = '0';
300
+ this.items.forEach((item, index) => {
301
+ const slide = document.createElement('div');
302
+ slide.className = 'native-image-preview-slide';
303
+ slide.dataset.index = String(index);
304
+ const media = this.createMediaElement(item);
305
+ this.mediaElements.push(media);
306
+ slide.append(media);
307
+ track.append(slide);
308
+ });
309
+ viewport.append(track);
310
+ root.append(viewport);
311
+ const closeButton = document.createElement('button');
312
+ closeButton.className = 'native-image-preview-close';
313
+ closeButton.type = 'button';
314
+ closeButton.textContent = '×';
315
+ closeButton.setAttribute('aria-label', 'Close image preview');
316
+ closeButton.hidden = ((_c = this.options) === null || _c === void 0 ? void 0 : _c.closeButton) === false;
317
+ closeButton.style.opacity = '0';
318
+ root.append(closeButton);
319
+ const counter = document.createElement('div');
320
+ counter.className = 'native-image-preview-counter';
321
+ counter.hidden = ((_d = this.options) === null || _d === void 0 ? void 0 : _d.counter) === false || this.items.length <= 1;
322
+ counter.style.opacity = '0';
323
+ root.append(counter);
324
+ this.root = root;
325
+ this.backdrop = backdrop;
326
+ this.viewport = viewport;
327
+ this.track = track;
328
+ this.closeButton = closeButton;
329
+ this.counter = counter;
330
+ root.addEventListener('pointerdown', this.handlePointerDown);
331
+ root.addEventListener('pointermove', this.handlePointerMove);
332
+ root.addEventListener('pointerup', this.handlePointerUp);
333
+ root.addEventListener('pointercancel', this.handlePointerUp);
334
+ root.addEventListener('lostpointercapture', this.handleLostPointerCapture);
335
+ root.addEventListener('wheel', this.handleWheel, { passive: false });
336
+ root.addEventListener('click', this.handleRootClick);
337
+ closeButton.addEventListener('click', this.handleCloseClick);
338
+ window.addEventListener('keydown', this.handleKeyDown);
339
+ window.addEventListener('resize', this.handleResize);
340
+ window.addEventListener('blur', this.handleWindowBlur);
341
+ window.addEventListener('pagehide', this.handleWindowBlur);
342
+ document.addEventListener('visibilitychange', this.handleVisibilityChange);
343
+ document.body.append(root);
344
+ root.focus({ preventScroll: true });
345
+ }
346
+ createMediaElement(item) {
347
+ var _a;
348
+ if (item.type === 'video') {
349
+ const video = document.createElement('video');
350
+ video.className = 'native-image-preview-media native-image-preview-video';
351
+ video.src = item.src;
352
+ video.poster = (_a = item.poster) !== null && _a !== void 0 ? _a : '';
353
+ if (item.width && item.height) {
354
+ video.width = item.width;
355
+ video.height = item.height;
356
+ }
357
+ video.controls = true;
358
+ video.playsInline = true;
359
+ video.preload = 'metadata';
360
+ return video;
361
+ }
362
+ const image = document.createElement('img');
363
+ image.className = 'native-image-preview-media native-image-preview-image';
364
+ image.src = item.src;
365
+ image.alt = item.alt;
366
+ if (item.width && item.height) {
367
+ image.width = item.width;
368
+ image.height = item.height;
369
+ }
370
+ image.draggable = false;
371
+ image.decoding = 'async';
372
+ return image;
373
+ }
374
+ async performOpenTransition() {
375
+ const root = this.root;
376
+ const backdrop = this.backdrop;
377
+ const track = this.track;
378
+ const item = this.items[this.index];
379
+ if (!root || !backdrop || !track || !item) {
380
+ return;
381
+ }
382
+ track.style.opacity = '0';
383
+ backdrop.style.opacity = '0';
384
+ const sourceGeometry = await this.resolveSourceGeometry(item);
385
+ const targetGeometry = await this.resolveTargetGeometry(item);
386
+ if (!sourceGeometry || !targetGeometry) {
387
+ backdrop.style.opacity = '1';
388
+ return;
389
+ }
390
+ await (0, transition_1.animateSharedElement)({
391
+ item,
392
+ from: sourceGeometry,
393
+ to: targetGeometry,
394
+ parent: root,
395
+ beforeStart: () => {
396
+ this.hideSourceElement();
397
+ },
398
+ onProgress: (progress) => {
399
+ backdrop.style.opacity = String(progress);
400
+ },
401
+ beforeRemove: () => {
402
+ this.showContent();
403
+ },
404
+ });
405
+ }
406
+ async performCloseTransition(reason) {
407
+ const root = this.root;
408
+ const backdrop = this.backdrop;
409
+ const track = this.track;
410
+ const item = this.items[this.index];
411
+ const media = this.mediaElements[this.index];
412
+ if (!root || !backdrop || !track || !item || !media) {
413
+ return;
414
+ }
415
+ this.pauseInactiveVideos(-1);
416
+ this.hideControls();
417
+ const from = await this.resolveCurrentMediaGeometry(item, media);
418
+ const to = await this.resolveReturnGeometry(item);
419
+ if (!from || !to) {
420
+ root.classList.add('native-image-preview-fading-out');
421
+ await this.delay(180);
422
+ return;
423
+ }
424
+ const backdropStartOpacity = this.resolveBackdropCloseStartOpacity(backdrop, reason);
425
+ await (0, transition_1.animateSharedElement)({
426
+ item,
427
+ from,
428
+ to,
429
+ parent: root,
430
+ duration: reason === 'drag' ? 260 : 300,
431
+ beforeStart: () => {
432
+ track.style.opacity = '0';
433
+ },
434
+ onProgress: (progress) => {
435
+ backdrop.style.opacity = String(backdropStartOpacity * (1 - progress));
436
+ },
437
+ beforeRemove: () => {
438
+ this.restoreSourceElement();
439
+ },
440
+ });
441
+ }
442
+ resolveBackdropCloseStartOpacity(backdrop, reason) {
443
+ if (reason !== 'drag') {
444
+ return 1;
445
+ }
446
+ const opacity = Number.parseFloat(getComputedStyle(backdrop).opacity);
447
+ return Number.isFinite(opacity) ? (0, geometry_1.clamp)(opacity, 0, 1) : 1;
448
+ }
449
+ showContent() {
450
+ if (!this.track || !this.backdrop) {
451
+ return;
452
+ }
453
+ this.backdrop.style.opacity = '1';
454
+ this.track.style.opacity = '1';
455
+ this.showControls();
456
+ this.applyActiveMediaTransform(false);
457
+ }
458
+ showControls() {
459
+ if (this.closeButton && !this.closeButton.hidden) {
460
+ this.closeButton.style.opacity = '1';
461
+ }
462
+ if (this.counter && !this.counter.hidden) {
463
+ this.counter.style.opacity = '1';
464
+ }
465
+ }
466
+ hideControls() {
467
+ if (this.closeButton) {
468
+ this.closeButton.style.opacity = '0';
469
+ }
470
+ if (this.counter) {
471
+ this.counter.style.opacity = '0';
472
+ }
473
+ }
474
+ finishClose(reason) {
475
+ var _a, _b, _c;
476
+ const item = (_a = this.items[this.index]) !== null && _a !== void 0 ? _a : null;
477
+ const index = this.index;
478
+ this.restoreSourceElement();
479
+ this.unlockDocumentScroll();
480
+ this.unmount();
481
+ (_c = (_b = this.options) === null || _b === void 0 ? void 0 : _b.onClose) === null || _c === void 0 ? void 0 : _c.call(_b, { reason, index, item });
482
+ this.openState = false;
483
+ this.closing = false;
484
+ this.items = [];
485
+ this.options = null;
486
+ this.mediaElements = [];
487
+ this.slideStates = [];
488
+ this.pointers.clear();
489
+ this.mode = 'none';
490
+ }
491
+ unmount() {
492
+ const root = this.root;
493
+ const closeButton = this.closeButton;
494
+ if (!root) {
495
+ return;
496
+ }
497
+ root.removeEventListener('pointerdown', this.handlePointerDown);
498
+ root.removeEventListener('pointermove', this.handlePointerMove);
499
+ root.removeEventListener('pointerup', this.handlePointerUp);
500
+ root.removeEventListener('pointercancel', this.handlePointerUp);
501
+ root.removeEventListener('lostpointercapture', this.handleLostPointerCapture);
502
+ root.removeEventListener('wheel', this.handleWheel);
503
+ root.removeEventListener('click', this.handleRootClick);
504
+ closeButton === null || closeButton === void 0 ? void 0 : closeButton.removeEventListener('click', this.handleCloseClick);
505
+ window.removeEventListener('keydown', this.handleKeyDown);
506
+ window.removeEventListener('resize', this.handleResize);
507
+ window.removeEventListener('blur', this.handleWindowBlur);
508
+ window.removeEventListener('pagehide', this.handleWindowBlur);
509
+ document.removeEventListener('visibilitychange', this.handleVisibilityChange);
510
+ root.remove();
511
+ this.root = null;
512
+ this.backdrop = null;
513
+ this.viewport = null;
514
+ this.track = null;
515
+ this.closeButton = null;
516
+ this.counter = null;
517
+ }
518
+ captureViewportSize() {
519
+ var _a, _b;
520
+ const visualViewport = window.visualViewport;
521
+ this.viewportWidth = Math.max(1, (_a = visualViewport === null || visualViewport === void 0 ? void 0 : visualViewport.width) !== null && _a !== void 0 ? _a : window.innerWidth);
522
+ this.viewportHeight = Math.max(1, (_b = visualViewport === null || visualViewport === void 0 ? void 0 : visualViewport.height) !== null && _b !== void 0 ? _b : window.innerHeight);
523
+ }
524
+ applyViewportStyle(root = this.root) {
525
+ if (!root) {
526
+ return;
527
+ }
528
+ root.style.setProperty('--native-image-preview-vw', `${this.viewportWidth}px`);
529
+ root.style.setProperty('--native-image-preview-vh', `${this.viewportHeight}px`);
530
+ }
531
+ viewportRect() {
532
+ return (0, geometry_1.normalizeRect)({
533
+ left: 0,
534
+ top: 0,
535
+ width: this.viewportWidth,
536
+ height: this.viewportHeight,
537
+ });
538
+ }
539
+ async resolveSourceGeometry(item) {
540
+ var _a;
541
+ const element = this.sourceElement;
542
+ const rect = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.sourceRect)
543
+ ? (0, geometry_1.rectFromDOMRect)(this.options.sourceRect)
544
+ : element ? (0, geometry_1.rectFromElement)(element) : null;
545
+ if (!(0, geometry_1.isUsableRect)(rect)) {
546
+ return null;
547
+ }
548
+ await (0, media_1.waitForElementImage)(element);
549
+ const size = await this.resolveSharedMediaSize(item);
550
+ return (0, geometry_1.sharedGeometryFor)(rect, size, this.resolveSourceObjectFit(element));
551
+ }
552
+ async resolveReturnGeometry(item) {
553
+ const element = this.resolveSourceElement(this.index);
554
+ if (!element || !element.isConnected) {
555
+ return null;
556
+ }
557
+ await (0, media_1.waitForElementImage)(element);
558
+ const rect = (0, geometry_1.rectFromElement)(element);
559
+ if (!(0, geometry_1.isUsableRect)(rect)) {
560
+ return null;
561
+ }
562
+ const size = await this.resolveSharedMediaSize(item);
563
+ return (0, geometry_1.sharedGeometryFor)(rect, size, this.resolveSourceObjectFit(element));
564
+ }
565
+ async resolveTargetGeometry(item) {
566
+ const size = await this.resolveSharedMediaSize(item);
567
+ return (0, geometry_1.targetGeometryFor)(size, this.viewportRect());
568
+ }
569
+ async resolveCurrentMediaGeometry(item, media) {
570
+ const rect = (0, geometry_1.rectFromElement)(media);
571
+ if (!(0, geometry_1.isUsableRect)(rect)) {
572
+ return null;
573
+ }
574
+ const size = await this.resolveSharedMediaSize(item);
575
+ return (0, geometry_1.sharedGeometryFor)(rect, size, 'fill');
576
+ }
577
+ async resolveSharedMediaSize(item) {
578
+ var _a;
579
+ return (0, media_1.resolveMediaSize)(item, (_a = this.options) === null || _a === void 0 ? void 0 : _a.sourceImageSize);
580
+ }
581
+ resolveSourceObjectFit(element) {
582
+ var _a, _b, _c;
583
+ const optionFit = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.sourceObjectFit) !== null && _b !== void 0 ? _b : (_c = this.options) === null || _c === void 0 ? void 0 : _c.objectFit;
584
+ if (optionFit) {
585
+ return optionFit;
586
+ }
587
+ if (!element) {
588
+ return 'cover';
589
+ }
590
+ const fit = getComputedStyle(element).objectFit;
591
+ if (fit === 'contain' || fit === 'fill') {
592
+ return fit;
593
+ }
594
+ return 'cover';
595
+ }
596
+ resolveSourceElement(index) {
597
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
598
+ const item = this.items[index];
599
+ if (!item) {
600
+ return null;
601
+ }
602
+ return (_f = (_c = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.getSourceElement) === null || _b === void 0 ? void 0 : _b.call(_a, index, item)) !== null && _c !== void 0 ? _c : (_e = (_d = this.options) === null || _d === void 0 ? void 0 : _d.sourceElements) === null || _e === void 0 ? void 0 : _e[index]) !== null && _f !== void 0 ? _f : (index === ((_h = (_g = this.options) === null || _g === void 0 ? void 0 : _g.index) !== null && _h !== void 0 ? _h : 0) ? (_k = (_j = this.options) === null || _j === void 0 ? void 0 : _j.sourceElement) !== null && _k !== void 0 ? _k : null : null);
603
+ }
604
+ hideSourceElement() {
605
+ if (this.sourceVisibility !== 'hidden' || !this.sourceElement) {
606
+ return;
607
+ }
608
+ this.sourcePreviousVisibility = this.sourceElement.style.visibility;
609
+ this.sourceElement.style.visibility = 'hidden';
610
+ }
611
+ restoreSourceElement() {
612
+ if (!this.sourceElement) {
613
+ return;
614
+ }
615
+ this.sourceElement.style.visibility = this.sourcePreviousVisibility;
616
+ this.sourceElement = null;
617
+ this.sourcePreviousVisibility = '';
618
+ }
619
+ syncHiddenSourceElement(index) {
620
+ this.restoreSourceElement();
621
+ this.sourceElement = this.resolveSourceElement(index);
622
+ this.hideSourceElement();
623
+ }
624
+ shouldIgnorePointerStart(target, event) {
625
+ if (!target) {
626
+ return false;
627
+ }
628
+ if (target.closest('button')) {
629
+ return true;
630
+ }
631
+ const video = target.closest('video');
632
+ if (!video) {
633
+ return false;
634
+ }
635
+ return this.isPointerInVideoControls(video, event);
636
+ }
637
+ isPointerInVideoControls(video, event) {
638
+ if (!video.controls) {
639
+ return false;
640
+ }
641
+ const rect = video.getBoundingClientRect();
642
+ if (!(0, geometry_1.isUsableRect)((0, geometry_1.rectFromDOMRect)(rect))) {
643
+ return false;
644
+ }
645
+ const controlsHeight = Math.min(72, Math.max(44, rect.height * 0.24));
646
+ return event.clientY >= rect.bottom - controlsHeight;
647
+ }
648
+ handleTap(event) {
649
+ const state = this.activeState();
650
+ const now = performance.now();
651
+ if (now - state.lastTapAt < 280) {
652
+ state.scale = state.scale > 1.02 ? 1 : DOUBLE_TAP_SCALE;
653
+ state.translateX = 0;
654
+ state.translateY = 0;
655
+ this.applyActiveMediaTransform(true);
656
+ state.lastTapAt = 0;
657
+ event.preventDefault();
658
+ return;
659
+ }
660
+ state.lastTapAt = now;
661
+ }
662
+ preparePinchIfNeeded() {
663
+ if (this.pointers.size < 2) {
664
+ return;
665
+ }
666
+ const [first, second] = [...this.pointers.values()];
667
+ const state = this.activeState();
668
+ this.pinchStartDistance = Math.max(1, distance(first, second));
669
+ this.pinchStartScale = state.scale;
670
+ this.pinchStartTranslateX = state.translateX;
671
+ this.pinchStartTranslateY = state.translateY;
672
+ this.pinchStartCenterX = (first.x + second.x) / 2;
673
+ this.pinchStartCenterY = (first.y + second.y) / 2;
674
+ }
675
+ applyPinch() {
676
+ if (this.pointers.size < 2) {
677
+ return;
678
+ }
679
+ const [first, second] = [...this.pointers.values()];
680
+ const state = this.activeState();
681
+ const nextDistance = Math.max(1, distance(first, second));
682
+ const centerX = (first.x + second.x) / 2;
683
+ const centerY = (first.y + second.y) / 2;
684
+ state.scale = (0, geometry_1.clamp)(this.pinchStartScale * nextDistance / this.pinchStartDistance, 1, MAX_SCALE);
685
+ state.translateX = this.pinchStartTranslateX + centerX - this.pinchStartCenterX;
686
+ state.translateY = this.pinchStartTranslateY + centerY - this.pinchStartCenterY;
687
+ if (state.scale <= 1.01) {
688
+ state.translateX = 0;
689
+ state.translateY = 0;
690
+ }
691
+ this.clampActiveState();
692
+ this.applyActiveMediaTransform(false);
693
+ }
694
+ finishSwipe(velocityX) {
695
+ const previous = this.index;
696
+ const shouldGoNext = this.dragX < -SWIPE_DISTANCE
697
+ || (this.dragX < -SWIPE_MIN_FLING_DISTANCE && velocityX < -SWIPE_VELOCITY);
698
+ const shouldGoPrevious = this.dragX > SWIPE_DISTANCE
699
+ || (this.dragX > SWIPE_MIN_FLING_DISTANCE && velocityX > SWIPE_VELOCITY);
700
+ if (shouldGoNext) {
701
+ this.goTo(this.index + 1, 'swipe');
702
+ }
703
+ else if (shouldGoPrevious) {
704
+ this.goTo(this.index - 1, 'swipe');
705
+ }
706
+ if (previous === this.index) {
707
+ this.updateTrack(true);
708
+ }
709
+ }
710
+ goTo(nextIndex, reason) {
711
+ var _a, _b;
712
+ const clamped = (0, geometry_1.clamp)(nextIndex, 0, this.items.length - 1);
713
+ const previousIndex = this.index;
714
+ this.index = clamped;
715
+ this.dragX = 0;
716
+ this.resetSlideState(clamped);
717
+ this.updateTrack(true);
718
+ this.updateCounter();
719
+ this.pauseInactiveVideos(clamped);
720
+ if (previousIndex !== clamped) {
721
+ this.syncHiddenSourceElement(clamped);
722
+ const item = this.items[clamped];
723
+ const meta = { reason, previousIndex, index: clamped, item };
724
+ (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.onChange) === null || _b === void 0 ? void 0 : _b.call(_a, clamped, meta);
725
+ }
726
+ }
727
+ resetDismissDrag() {
728
+ this.dragX = 0;
729
+ this.dragY = 0;
730
+ this.updateTrack(true);
731
+ this.applyActiveMediaTransform(true);
732
+ if (this.backdrop) {
733
+ this.backdrop.style.transition = 'opacity 220ms cubic-bezier(0.22, 1, 0.36, 1)';
734
+ this.backdrop.style.opacity = '1';
735
+ window.setTimeout(() => {
736
+ if (this.backdrop) {
737
+ this.backdrop.style.transition = '';
738
+ }
739
+ }, 240);
740
+ }
741
+ }
742
+ applyDismissDrag(dy) {
743
+ const media = this.mediaElements[this.index];
744
+ this.dragX = 0;
745
+ this.dragY = dy;
746
+ this.updateTrack(false);
747
+ const progress = Math.min(1, Math.abs(dy) / Math.max(1, this.viewportHeight));
748
+ const scale = Math.max(0.84, 1 - progress * 0.16);
749
+ if (media) {
750
+ media.style.transition = 'none';
751
+ media.style.transform = `translate3d(0px, ${dy}px, 0) scale(${scale})`;
752
+ }
753
+ if (this.backdrop) {
754
+ this.backdrop.style.opacity = String(1 - Math.min(0.9, progress * 1.35));
755
+ }
756
+ }
757
+ cancelActiveGesture() {
758
+ if (!this.pointers.size && this.mode === 'none' && !this.dragX && !this.dragY) {
759
+ return;
760
+ }
761
+ const mode = this.mode;
762
+ const pointers = [...this.pointers.values()];
763
+ this.pointers.clear();
764
+ this.mode = 'none';
765
+ this.dragX = 0;
766
+ this.dragY = 0;
767
+ this.releasePointerCaptures(pointers);
768
+ if (!this.openState || this.closing) {
769
+ return;
770
+ }
771
+ if (mode === 'dismiss') {
772
+ this.resetDismissDrag();
773
+ return;
774
+ }
775
+ if (mode === 'swipe') {
776
+ this.updateTrack(true);
777
+ }
778
+ }
779
+ releasePointerCaptures(pointers) {
780
+ const root = this.root;
781
+ if (!root) {
782
+ return;
783
+ }
784
+ pointers.forEach((pointer) => {
785
+ if (root.hasPointerCapture(pointer.id)) {
786
+ root.releasePointerCapture(pointer.id);
787
+ }
788
+ });
789
+ }
790
+ updateTrack(animated = false) {
791
+ if (!this.track) {
792
+ return;
793
+ }
794
+ this.track.style.transition = animated ? 'transform 260ms cubic-bezier(0.22, 1, 0.36, 1)' : 'none';
795
+ const x = -this.index * this.viewportWidth + this.dragX;
796
+ this.track.style.transform = `translate3d(${x}px, 0, 0)`;
797
+ }
798
+ applyActiveMediaTransform(animated) {
799
+ const media = this.mediaElements[this.index];
800
+ const state = this.activeState();
801
+ if (!media) {
802
+ return;
803
+ }
804
+ media.style.transition = animated ? 'transform 220ms cubic-bezier(0.22, 1, 0.36, 1)' : 'none';
805
+ media.style.transform = `translate3d(${state.translateX}px, ${state.translateY}px, 0) scale(${state.scale})`;
806
+ }
807
+ clampActiveState() {
808
+ const state = this.activeState();
809
+ const media = this.mediaElements[this.index];
810
+ if (!media || state.scale <= 1.01) {
811
+ if (state.scale <= 1.01) {
812
+ state.scale = 1;
813
+ state.translateX = 0;
814
+ state.translateY = 0;
815
+ }
816
+ return;
817
+ }
818
+ const rect = media.getBoundingClientRect();
819
+ const baseWidth = rect.width / state.scale;
820
+ const baseHeight = rect.height / state.scale;
821
+ const maxX = Math.max(0, (baseWidth * state.scale - this.viewportWidth) / 2);
822
+ const maxY = Math.max(0, (baseHeight * state.scale - this.viewportHeight) / 2);
823
+ state.translateX = (0, geometry_1.clamp)(state.translateX, -maxX, maxX);
824
+ state.translateY = (0, geometry_1.clamp)(state.translateY, -maxY, maxY);
825
+ }
826
+ activeState() {
827
+ var _a;
828
+ return (_a = this.slideStates[this.index]) !== null && _a !== void 0 ? _a : this.createSlideState();
829
+ }
830
+ resetSlideState(index) {
831
+ this.slideStates[index] = this.createSlideState();
832
+ const media = this.mediaElements[index];
833
+ if (media) {
834
+ media.style.transition = 'none';
835
+ media.style.transform = '';
836
+ }
837
+ }
838
+ createSlideState() {
839
+ return { scale: 1, translateX: 0, translateY: 0, lastTapAt: 0 };
840
+ }
841
+ updateCounter() {
842
+ if (!this.counter) {
843
+ return;
844
+ }
845
+ this.counter.textContent = `${this.index + 1} / ${this.items.length}`;
846
+ }
847
+ pauseInactiveVideos(activeIndex) {
848
+ this.mediaElements.forEach((element, index) => {
849
+ if (index !== activeIndex && element instanceof HTMLVideoElement) {
850
+ element.pause();
851
+ }
852
+ });
853
+ }
854
+ async layoutMediaElements() {
855
+ const viewport = this.viewportRect();
856
+ await Promise.all(this.items.map(async (item, index) => {
857
+ var _a;
858
+ const media = this.mediaElements[index];
859
+ if (!media) {
860
+ return;
861
+ }
862
+ const size = await (0, media_1.resolveMediaSize)(item, index === this.index ? (_a = this.options) === null || _a === void 0 ? void 0 : _a.sourceImageSize : null);
863
+ const frame = (0, geometry_1.targetGeometryFor)(size, viewport).visibleFrame;
864
+ media.style.width = `${frame.width}px`;
865
+ media.style.height = `${frame.height}px`;
866
+ }));
867
+ }
868
+ lockDocumentScroll() {
869
+ this.previousBodyOverflow = document.body.style.overflow;
870
+ this.previousHtmlOverflow = document.documentElement.style.overflow;
871
+ document.body.style.overflow = 'hidden';
872
+ document.documentElement.style.overflow = 'hidden';
873
+ }
874
+ unlockDocumentScroll() {
875
+ document.body.style.overflow = this.previousBodyOverflow;
876
+ document.documentElement.style.overflow = this.previousHtmlOverflow;
877
+ }
878
+ nextFrame() {
879
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
880
+ }
881
+ delay(ms) {
882
+ return new Promise((resolve) => window.setTimeout(resolve, ms));
883
+ }
884
+ }
885
+ exports.NativeImagePreviewWebViewer = NativeImagePreviewWebViewer;
886
+ function createNativeImagePreviewWeb() {
887
+ return new NativeImagePreviewWebViewer();
888
+ }
889
+ function distance(first, second) {
890
+ return Math.hypot(first.x - second.x, first.y - second.y);
891
+ }
892
+ function sourceRectFromElement(element) {
893
+ return (0, geometry_1.rectFromDOMRect)(element.getBoundingClientRect());
894
+ }