smartphoto 1.6.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +401 -0
  2. package/css/smartphoto.css +78 -81
  3. package/css/smartphoto.css.map +1 -0
  4. package/css/smartphoto.min.css +1 -1
  5. package/js/jquery-smartphoto.js +1706 -3809
  6. package/js/jquery-smartphoto.min.js +5 -55
  7. package/js/smartphoto.js +1722 -3794
  8. package/js/smartphoto.min.js +6 -55
  9. package/lib/smartphoto.js +1823 -0
  10. package/lib/smartphoto.mjs +1799 -0
  11. package/lib/types/adaptor/jquery.d.ts +5 -0
  12. package/lib/types/core/gestures.d.ts +12 -0
  13. package/lib/types/core/index.d.ts +68 -0
  14. package/lib/types/core/state.d.ts +20 -0
  15. package/lib/types/core/types.d.ts +147 -0
  16. package/lib/types/core/view.d.ts +48 -0
  17. package/lib/types/index.d.ts +2 -0
  18. package/lib/types/lib/util.d.ts +11 -0
  19. package/package.json +60 -52
  20. package/.babelrc +0 -9
  21. package/.csscomb.json +0 -314
  22. package/.editorconfig +0 -24
  23. package/.eslintrc +0 -24
  24. package/.git +0 -1
  25. package/.node-version +0 -1
  26. package/circle.yml +0 -15
  27. package/examples/assets/bear.jpg +0 -0
  28. package/examples/assets/camel.jpg +0 -0
  29. package/examples/assets/castle.jpg +0 -0
  30. package/examples/assets/hippo.jpg +0 -0
  31. package/examples/assets/koala.jpg +0 -0
  32. package/examples/assets/large-bear.jpg +0 -0
  33. package/examples/assets/large-camel.jpg +0 -0
  34. package/examples/assets/large-hippo.jpg +0 -0
  35. package/examples/assets/large-koala.jpg +0 -0
  36. package/examples/assets/large-lion.jpg +0 -0
  37. package/examples/assets/large-rhino.jpg +0 -0
  38. package/examples/assets/lion.jpg +0 -0
  39. package/examples/assets/rhino.jpg +0 -0
  40. package/examples/assets/sample.png +0 -0
  41. package/examples/assets/style.css +0 -98
  42. package/examples/assets/test.png +0 -0
  43. package/examples/event.html +0 -78
  44. package/examples/jquery.html +0 -80
  45. package/examples/lazy.html +0 -81
  46. package/examples/multiple.html +0 -91
  47. package/examples/noimage.html +0 -22
  48. package/examples/resizeFit.html +0 -56
  49. package/examples/vanilla.html +0 -57
  50. package/images/icon_arrow_next.svg +0 -1
  51. package/images/icon_arrow_prev.svg +0 -1
  52. package/images/icon_close.svg +0 -1
  53. package/index.d.ts +0 -64
  54. package/lib/adaptor/jquery.js +0 -25
  55. package/lib/core/index.js +0 -1573
  56. package/lib/index.js +0 -3
  57. package/lib/lib/util.js +0 -183
  58. package/readme.md +0 -210
  59. package/scss/smartphoto.scss +0 -442
  60. package/src/adaptor/jquery.js +0 -24
  61. package/src/core/index.js +0 -1202
  62. package/src/core/viwer.html +0 -49
  63. package/src/index.js +0 -3
  64. package/src/lib/util.js +0 -141
  65. package/test/preload.js +0 -47
  66. package/test/test.js +0 -49
  67. package/tools/index.js +0 -34
@@ -0,0 +1,1823 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ default: () => src_default
24
+ });
25
+ module.exports = __toCommonJS(src_exports);
26
+
27
+ // src/lib/util.ts
28
+ var isSmartPhone = () => {
29
+ const agent = navigator.userAgent;
30
+ return agent.indexOf("iPhone") > 0 || agent.indexOf("iPad") > 0 || agent.indexOf("ipod") > 0 || agent.indexOf("Android") > 0;
31
+ };
32
+ function deepExtend(out, ...args) {
33
+ out = out || {};
34
+ for (let i = 0; i < args.length; i++) {
35
+ const obj = args[i];
36
+ if (!obj) {
37
+ continue;
38
+ }
39
+ for (const key in obj) {
40
+ if (Object.hasOwn(obj, key)) {
41
+ const value = obj[key];
42
+ if (value && typeof value === "object") {
43
+ out[key] = deepExtend(
44
+ out[key] ?? {},
45
+ value
46
+ );
47
+ } else {
48
+ out[key] = value;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ return out;
54
+ }
55
+ var extend = deepExtend;
56
+ var triggerEvent = (el, eventName, options) => {
57
+ let event;
58
+ if (window.CustomEvent) {
59
+ event = new CustomEvent(eventName, { cancelable: true });
60
+ } else {
61
+ event = document.createEvent("CustomEvent");
62
+ event.initCustomEvent(eventName, false, false, options);
63
+ }
64
+ el.dispatchEvent(event);
65
+ };
66
+ var parseQuery = (query) => {
67
+ const data = {};
68
+ for (const pair of query.split("&")) {
69
+ const param = pair.split("=");
70
+ const key = param[0];
71
+ const value = param.length > 1 ? param.slice(1).join("=") : key;
72
+ data[key] = decodeURIComponent(value);
73
+ }
74
+ return data;
75
+ };
76
+ var getViewPos = (element) => {
77
+ return {
78
+ left: element.getBoundingClientRect().left,
79
+ top: element.getBoundingClientRect().top
80
+ };
81
+ };
82
+
83
+ // src/core/state.ts
84
+ var defaults = {
85
+ classNames: {
86
+ smartPhoto: "smartphoto",
87
+ smartPhotoClose: "smartphoto-close",
88
+ smartPhotoBody: "smartphoto-body",
89
+ smartPhotoInner: "smartphoto-inner",
90
+ smartPhotoContent: "smartphoto-content",
91
+ smartPhotoImg: "smartphoto-img",
92
+ smartPhotoImgOnMove: "smartphoto-img-onmove",
93
+ smartPhotoImgElasticMove: "smartphoto-img-elasticmove",
94
+ smartPhotoImgWrap: "smartphoto-img-wrap",
95
+ smartPhotoArrows: "smartphoto-arrows",
96
+ smartPhotoNav: "smartphoto-nav",
97
+ smartPhotoArrowRight: "smartphoto-arrow-right",
98
+ smartPhotoArrowLeft: "smartphoto-arrow-left",
99
+ smartPhotoArrowHideIcon: "smartphoto-arrow-hide",
100
+ smartPhotoImgLeft: "smartphoto-img-left",
101
+ smartPhotoImgRight: "smartphoto-img-right",
102
+ smartPhotoList: "smartphoto-list",
103
+ smartPhotoListOnMove: "smartphoto-list-onmove",
104
+ smartPhotoHeader: "smartphoto-header",
105
+ smartPhotoCount: "smartphoto-count",
106
+ smartPhotoCaption: "smartphoto-caption",
107
+ smartPhotoDismiss: "smartphoto-dismiss",
108
+ smartPhotoLoader: "smartphoto-loader",
109
+ smartPhotoLoaderWrap: "smartphoto-loader-wrap",
110
+ smartPhotoImgClone: "smartphoto-img-clone"
111
+ },
112
+ message: {
113
+ gotoNextImage: "go to the next image",
114
+ gotoPrevImage: "go to the previous image",
115
+ closeDialog: "close the image dialog",
116
+ carouselLabel: "Images"
117
+ },
118
+ arrows: true,
119
+ nav: true,
120
+ showAnimation: true,
121
+ verticalGravity: false,
122
+ useOrientationApi: false,
123
+ useHistoryApi: true,
124
+ swipeTopToClose: false,
125
+ swipeBottomToClose: true,
126
+ swipeOffset: 100,
127
+ headerHeight: 60,
128
+ footerHeight: 60,
129
+ forceInterval: 10,
130
+ registance: 0.5,
131
+ loadOffset: 2,
132
+ resizeStyle: "fit",
133
+ lazyAttribute: "data-src",
134
+ animationSpeed: 300
135
+ };
136
+ function deepFreeze(obj) {
137
+ Object.keys(obj).forEach((key) => {
138
+ const value = obj[key];
139
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
140
+ deepFreeze(value);
141
+ }
142
+ });
143
+ return Object.freeze(obj);
144
+ }
145
+ function createState(settings) {
146
+ return {
147
+ options: deepFreeze(
148
+ extend({}, defaults, settings)
149
+ ),
150
+ viewer: {
151
+ isOpen: false,
152
+ currentGroup: null,
153
+ currentIndex: 0,
154
+ oldIndex: 0,
155
+ total: 0,
156
+ translateX: 0,
157
+ translateY: 0,
158
+ photoPosX: 0,
159
+ photoPosY: 0,
160
+ scaleSize: 1,
161
+ scale: false,
162
+ elastic: false,
163
+ hideUi: false,
164
+ onMove: false,
165
+ appear: false,
166
+ appearEffect: null,
167
+ prev: -1,
168
+ next: -1,
169
+ showPrevArrow: false,
170
+ showNextArrow: false
171
+ },
172
+ groups: /* @__PURE__ */ new Map()
173
+ };
174
+ }
175
+ function groupIdFromElement(element) {
176
+ return element.getAttribute("data-group") || "nogroup";
177
+ }
178
+ function groupIdFromSlide(slide) {
179
+ return slide.group || "nogroup";
180
+ }
181
+ function itemFromElement(element, options, index, winWidth) {
182
+ const groupId = groupIdFromElement(element);
183
+ const src = element.getAttribute("href");
184
+ const img = element.querySelector("img");
185
+ let thumb = src;
186
+ if (img) {
187
+ if (img.getAttribute(options.lazyAttribute)) {
188
+ thumb = img.getAttribute(options.lazyAttribute);
189
+ } else if (img.currentSrc) {
190
+ thumb = img.currentSrc;
191
+ } else {
192
+ thumb = img.src;
193
+ }
194
+ }
195
+ let alt = "";
196
+ if (img?.getAttribute("alt")) {
197
+ alt = img.getAttribute("alt");
198
+ } else if (element.getAttribute("data-caption")) {
199
+ alt = element.getAttribute("data-caption");
200
+ } else {
201
+ alt = src ?? "";
202
+ }
203
+ const dataId = element.getAttribute("data-id");
204
+ return {
205
+ src,
206
+ thumb,
207
+ caption: element.getAttribute("data-caption"),
208
+ alt,
209
+ groupId,
210
+ translateX: winWidth * index,
211
+ translateY: 0,
212
+ index,
213
+ width: 50,
214
+ height: 50,
215
+ scale: 1,
216
+ x: 0,
217
+ y: 0,
218
+ id: dataId || index,
219
+ loaded: false,
220
+ processed: false,
221
+ element
222
+ };
223
+ }
224
+ function itemFromSlide(slide, index, winWidth) {
225
+ const groupId = groupIdFromSlide(slide);
226
+ const src = slide.src;
227
+ const thumb = slide.thumb ?? src;
228
+ const caption = slide.caption ?? null;
229
+ const alt = slide.alt ?? caption ?? src;
230
+ const hasSize = typeof slide.width === "number" && typeof slide.height === "number";
231
+ return {
232
+ src,
233
+ thumb,
234
+ caption,
235
+ alt,
236
+ groupId,
237
+ translateX: winWidth * index,
238
+ translateY: 0,
239
+ index,
240
+ width: hasSize ? slide.width : 50,
241
+ height: hasSize ? slide.height : 50,
242
+ scale: 1,
243
+ x: 0,
244
+ y: 0,
245
+ id: slide.id ?? index,
246
+ loaded: hasSize,
247
+ processed: false,
248
+ element: null
249
+ };
250
+ }
251
+ function addItemToGroup(state, item) {
252
+ if (!state.groups.has(item.groupId)) {
253
+ state.groups.set(item.groupId, []);
254
+ }
255
+ state.groups.get(item.groupId).push(item);
256
+ state.viewer.currentGroup = item.groupId;
257
+ }
258
+ function currentItems(state) {
259
+ if (state.viewer.currentGroup === null) {
260
+ return null;
261
+ }
262
+ return state.groups.get(state.viewer.currentGroup) ?? null;
263
+ }
264
+ function currentItem(state) {
265
+ const items = currentItems(state);
266
+ return items ? items[state.viewer.currentIndex] ?? null : null;
267
+ }
268
+ function setArrow(state) {
269
+ const items = currentItems(state);
270
+ if (!items) {
271
+ return;
272
+ }
273
+ const length = items.length;
274
+ const next = state.viewer.currentIndex + 1;
275
+ const prev = state.viewer.currentIndex - 1;
276
+ state.viewer.showNextArrow = false;
277
+ state.viewer.showPrevArrow = false;
278
+ if (next !== length) {
279
+ state.viewer.next = next;
280
+ state.viewer.showNextArrow = true;
281
+ }
282
+ if (prev !== -1) {
283
+ state.viewer.prev = prev;
284
+ state.viewer.showPrevArrow = true;
285
+ }
286
+ }
287
+ function resetTranslate(items, winWidth) {
288
+ items.forEach((item, index) => {
289
+ item.translateX = winWidth * index;
290
+ });
291
+ }
292
+ function round(val, precision) {
293
+ const digit = 10 ** precision;
294
+ return Math.round(val * digit) / digit;
295
+ }
296
+ function scaleBorder(item, winWidth, winHeight, isSmartPhone2) {
297
+ if (!isSmartPhone2) {
298
+ return 1 / item.scale;
299
+ }
300
+ if (item.width > item.height) {
301
+ return winHeight / (item.height * item.scale);
302
+ }
303
+ return winWidth / (item.width * item.scale);
304
+ }
305
+ function makeBound(item, viewer, winWidth, winHeight) {
306
+ const width = item.width * item.scale * viewer.scaleSize;
307
+ const height = item.height * item.scale * viewer.scaleSize;
308
+ let minX;
309
+ let minY;
310
+ let maxX;
311
+ let maxY;
312
+ if (winWidth > width) {
313
+ maxX = (winWidth - width) / 2;
314
+ minX = -1 * maxX;
315
+ } else {
316
+ maxX = (width - winWidth) / 2;
317
+ minX = -1 * maxX;
318
+ }
319
+ if (winHeight > height) {
320
+ maxY = (winHeight - height) / 2;
321
+ minY = -1 * maxY;
322
+ } else {
323
+ maxY = (height - winHeight) / 2;
324
+ minY = -1 * maxY;
325
+ }
326
+ return {
327
+ minX: round(minX, 6) * viewer.scaleSize,
328
+ minY: round(minY, 6) * viewer.scaleSize,
329
+ maxX: round(maxX, 6) * viewer.scaleSize,
330
+ maxY: round(maxY, 6) * viewer.scaleSize
331
+ };
332
+ }
333
+ function sizeItems(items, winWidth, winHeight, headerHeight, footerHeight) {
334
+ const screenY = winHeight - (headerHeight + footerHeight);
335
+ items.forEach((item) => {
336
+ if (!item.loaded) {
337
+ return;
338
+ }
339
+ item.processed = true;
340
+ item.scale = screenY / item.height;
341
+ if (item.height < screenY) {
342
+ item.scale = 1;
343
+ }
344
+ item.x = (item.scale - 1) / 2 * item.width + (winWidth - item.width * item.scale) / 2;
345
+ item.y = (item.scale - 1) / 2 * item.height + (winHeight - item.height * item.scale) / 2;
346
+ if (item.width * item.scale > winWidth) {
347
+ item.scale = winWidth / item.width;
348
+ item.x = (item.scale - 1) / 2 * item.width;
349
+ }
350
+ });
351
+ }
352
+ function buildHash(state) {
353
+ const item = currentItem(state);
354
+ if (!item) {
355
+ return "";
356
+ }
357
+ return `group=${state.viewer.currentGroup}&photo=${item.id}`;
358
+ }
359
+ function findItemByHash(state, hashObj) {
360
+ let found = null;
361
+ state.groups.forEach((items) => {
362
+ items.forEach((item) => {
363
+ if (hashObj.group === item.groupId && hashObj.photo === item.id) {
364
+ found = item;
365
+ }
366
+ });
367
+ });
368
+ return found;
369
+ }
370
+
371
+ // src/core/gestures.ts
372
+ function round2(val, precision) {
373
+ const digit = 10 ** precision;
374
+ return Math.round(val * digit) / digit;
375
+ }
376
+ function getPos(e) {
377
+ return { x: e.pageX, y: e.pageY };
378
+ }
379
+ function distance(p1, p2) {
380
+ const x = p1.x - p2.x;
381
+ const y = p1.y - p2.y;
382
+ return Math.sqrt(x * x + y * y);
383
+ }
384
+ function getForceAndTheta(x, y) {
385
+ return { force: Math.sqrt(x * x + y * y), theta: Math.atan2(y, x) };
386
+ }
387
+ function windowSize() {
388
+ return {
389
+ width: document.documentElement.clientWidth,
390
+ height: document.documentElement.clientHeight
391
+ };
392
+ }
393
+ function createGestures({ state, callbacks }, { signal }) {
394
+ const activePointers = /* @__PURE__ */ new Map();
395
+ let tapSecond = Date.now();
396
+ let swiping = false;
397
+ let dragStart = false;
398
+ let firstPos = null;
399
+ let oldPos = null;
400
+ let moveDir = null;
401
+ let photoSwipable = false;
402
+ let firstPhotoPos = null;
403
+ let oldPhotoPos = null;
404
+ let photoVX = 0;
405
+ let photoVY = 0;
406
+ let pinching = false;
407
+ let oldDistance = 0;
408
+ let vx = 0;
409
+ let vy = 0;
410
+ function isSmartPhone2() {
411
+ return isSmartPhone();
412
+ }
413
+ function boundOf(item) {
414
+ const { width, height } = windowSize();
415
+ return makeBound(item, state.viewer, width, height);
416
+ }
417
+ function borderOf(item) {
418
+ const { width, height } = windowSize();
419
+ return scaleBorder(item, width, height, isSmartPhone2());
420
+ }
421
+ function registerElasticForce(flagX, flagY) {
422
+ const item = currentItem(state);
423
+ const bound = boundOf(item);
424
+ state.viewer.elastic = true;
425
+ if (flagX === 1) {
426
+ state.viewer.photoPosX = bound.minX;
427
+ } else if (flagX === -1) {
428
+ state.viewer.photoPosX = bound.maxX;
429
+ }
430
+ if (flagY === 1) {
431
+ state.viewer.photoPosY = bound.minY;
432
+ } else if (flagY === -1) {
433
+ state.viewer.photoPosY = bound.maxY;
434
+ }
435
+ callbacks.onPhotoDragMove();
436
+ setTimeout(() => {
437
+ state.viewer.elastic = false;
438
+ callbacks.onPhotoDragMove();
439
+ }, 300);
440
+ }
441
+ const interval = setInterval(() => {
442
+ if (pinching || swiping || photoSwipable || state.viewer.elastic || !state.viewer.scale) {
443
+ return;
444
+ }
445
+ state.viewer.photoPosX += vx;
446
+ state.viewer.photoPosY += vy;
447
+ const item = currentItem(state);
448
+ if (!item) {
449
+ return;
450
+ }
451
+ const bound = boundOf(item);
452
+ if (state.viewer.photoPosX < bound.minX) {
453
+ state.viewer.photoPosX = bound.minX;
454
+ vx *= -0.2;
455
+ } else if (state.viewer.photoPosX > bound.maxX) {
456
+ state.viewer.photoPosX = bound.maxX;
457
+ vx *= -0.2;
458
+ }
459
+ if (state.viewer.photoPosY < bound.minY) {
460
+ state.viewer.photoPosY = bound.minY;
461
+ vy *= -0.2;
462
+ } else if (state.viewer.photoPosY > bound.maxY) {
463
+ state.viewer.photoPosY = bound.maxY;
464
+ vy *= -0.2;
465
+ }
466
+ const power = getForceAndTheta(vx, vy);
467
+ const force = power.force - state.options.registance;
468
+ if (Math.abs(force) < 0.5) {
469
+ return;
470
+ }
471
+ vx = Math.cos(power.theta) * force;
472
+ vy = Math.sin(power.theta) * force;
473
+ callbacks.onPhotoDragMove();
474
+ }, state.options.forceInterval);
475
+ function calcGravity(gamma, beta) {
476
+ if (gamma > 5 || gamma < -5) {
477
+ vx += gamma * 0.05;
478
+ }
479
+ if (!state.options.verticalGravity) {
480
+ return;
481
+ }
482
+ if (beta > 5 || beta < -5) {
483
+ vy += beta * 0.05;
484
+ }
485
+ }
486
+ function handleOrientationEvent(e) {
487
+ if (!e?.gamma || state.viewer.appearEffect) {
488
+ return;
489
+ }
490
+ if (pinching || swiping || photoSwipable || state.viewer.elastic || !state.viewer.scale) {
491
+ return;
492
+ }
493
+ const { orientation } = window;
494
+ if (orientation === 0) {
495
+ calcGravity(e.gamma, e.beta);
496
+ } else if (orientation === 90) {
497
+ calcGravity(e.beta, e.gamma);
498
+ } else if (orientation === -90) {
499
+ calcGravity(-e.beta, -e.gamma);
500
+ } else if (orientation === 180) {
501
+ calcGravity(-e.gamma, -e.beta);
502
+ }
503
+ }
504
+ if (state.options.useOrientationApi) {
505
+ window.addEventListener(
506
+ "deviceorientation",
507
+ handleOrientationEvent,
508
+ { signal }
509
+ );
510
+ }
511
+ function startPinch() {
512
+ pinching = true;
513
+ swiping = false;
514
+ photoSwipable = false;
515
+ const points = Array.from(activePointers.values());
516
+ oldDistance = distance(
517
+ points[0],
518
+ points[1]
519
+ );
520
+ state.viewer.scale = true;
521
+ callbacks.onGestureStart();
522
+ }
523
+ function startSwipe(e) {
524
+ const pos = getPos(e);
525
+ swiping = true;
526
+ dragStart = true;
527
+ firstPos = pos;
528
+ oldPos = pos;
529
+ }
530
+ function startPhotoDrag(e) {
531
+ photoSwipable = true;
532
+ const pos = getPos(e);
533
+ oldPhotoPos = pos;
534
+ firstPhotoPos = pos;
535
+ }
536
+ function onPointerDown(e) {
537
+ try {
538
+ e.currentTarget.setPointerCapture?.(e.pointerId);
539
+ } catch {
540
+ }
541
+ activePointers.set(e.pointerId, getPos(e));
542
+ if (activePointers.size > 1) {
543
+ startPinch();
544
+ return;
545
+ }
546
+ if (state.viewer.scale) {
547
+ startPhotoDrag(e);
548
+ return;
549
+ }
550
+ startSwipe(e);
551
+ }
552
+ function movePinch() {
553
+ const points = Array.from(activePointers.values());
554
+ const dist = distance(
555
+ points[0],
556
+ points[1]
557
+ );
558
+ const size = (dist - oldDistance) / 100;
559
+ const oldScaleSize = state.viewer.scaleSize;
560
+ const posX = state.viewer.photoPosX;
561
+ const posY = state.viewer.photoPosY;
562
+ state.viewer.scaleSize += round2(size, 6);
563
+ if (state.viewer.scaleSize < 0.2) {
564
+ state.viewer.scaleSize = 0.2;
565
+ }
566
+ if (state.viewer.scaleSize < oldScaleSize) {
567
+ state.viewer.photoPosX = (1 + state.viewer.scaleSize - oldScaleSize) * posX;
568
+ state.viewer.photoPosY = (1 + state.viewer.scaleSize - oldScaleSize) * posY;
569
+ }
570
+ const item = currentItem(state);
571
+ if (item) {
572
+ const border = borderOf(item);
573
+ state.viewer.hideUi = state.viewer.scaleSize < 1 || state.viewer.scaleSize > border;
574
+ }
575
+ oldDistance = dist;
576
+ callbacks.onGestureMove();
577
+ }
578
+ function moveSwipe(e) {
579
+ const pos = getPos(e);
580
+ const x = pos.x - oldPos.x;
581
+ const y = pos.y - firstPos.y;
582
+ if (dragStart) {
583
+ callbacks.onSwipeStart();
584
+ dragStart = false;
585
+ moveDir = Math.abs(x) > Math.abs(y) ? "horizontal" : "vertical";
586
+ }
587
+ if (moveDir === "horizontal") {
588
+ state.viewer.translateX += x;
589
+ } else {
590
+ state.viewer.translateY = y;
591
+ }
592
+ oldPos = pos;
593
+ callbacks.onSwipeMove();
594
+ }
595
+ function movePhotoDrag(e) {
596
+ const pos = getPos(e);
597
+ const x = pos.x - oldPhotoPos.x;
598
+ const y = pos.y - oldPhotoPos.y;
599
+ const moveX = round2(state.viewer.scaleSize * x, 6);
600
+ const moveY = round2(state.viewer.scaleSize * y, 6);
601
+ state.viewer.photoPosX += moveX;
602
+ photoVX = moveX;
603
+ state.viewer.photoPosY += moveY;
604
+ photoVY = moveY;
605
+ oldPhotoPos = pos;
606
+ callbacks.onPhotoDragMove();
607
+ }
608
+ function onPointerMove(e) {
609
+ if (!activePointers.has(e.pointerId)) {
610
+ return;
611
+ }
612
+ activePointers.set(e.pointerId, getPos(e));
613
+ if (pinching) {
614
+ movePinch();
615
+ return;
616
+ }
617
+ if (photoSwipable) {
618
+ movePhotoDrag(e);
619
+ return;
620
+ }
621
+ moveSwipe(e);
622
+ }
623
+ function endPinch() {
624
+ pinching = false;
625
+ const item = currentItem(state);
626
+ if (!item) {
627
+ return;
628
+ }
629
+ const border = borderOf(item);
630
+ if (state.viewer.scaleSize > border) {
631
+ return;
632
+ }
633
+ state.viewer.photoPosX = 0;
634
+ state.viewer.photoPosY = 0;
635
+ state.viewer.scale = false;
636
+ state.viewer.scaleSize = 1;
637
+ state.viewer.hideUi = false;
638
+ callbacks.onGestureEnd();
639
+ }
640
+ function endSwipe() {
641
+ swiping = false;
642
+ const first = firstPos;
643
+ const last = oldPos;
644
+ const now = Date.now();
645
+ const offset = tapSecond - now;
646
+ const swipeWidth = last.x - first.x;
647
+ const swipeHeight = last.y - first.y;
648
+ const noMove = swipeWidth === 0 && swipeHeight === 0;
649
+ if (!isSmartPhone2() && noMove) {
650
+ callbacks.onTap();
651
+ return;
652
+ }
653
+ if (Math.abs(offset) <= 500 && noMove) {
654
+ callbacks.onTap();
655
+ return;
656
+ }
657
+ tapSecond = now;
658
+ const items = currentItems(state) ?? [];
659
+ if (moveDir === "horizontal") {
660
+ let result = "stay";
661
+ if (swipeWidth >= state.options.swipeOffset && state.viewer.currentIndex !== 0) {
662
+ result = "prev";
663
+ } else if (swipeWidth <= -state.options.swipeOffset && state.viewer.currentIndex !== items.length - 1) {
664
+ result = "next";
665
+ }
666
+ callbacks.onSwipeEnd(result);
667
+ } else {
668
+ let result = "stay";
669
+ if (state.options.swipeBottomToClose && swipeHeight >= state.options.swipeOffset) {
670
+ result = "close-bottom";
671
+ } else if (state.options.swipeTopToClose && swipeHeight <= -state.options.swipeOffset) {
672
+ result = "close-top";
673
+ }
674
+ callbacks.onSwipeEnd(result);
675
+ }
676
+ }
677
+ function endPhotoDrag() {
678
+ photoSwipable = false;
679
+ const oldPos_ = oldPhotoPos;
680
+ const firstPos_ = firstPhotoPos;
681
+ if (oldPos_.x === firstPos_.x) {
682
+ callbacks.onPhotoDragEnd("zoom-out");
683
+ return;
684
+ }
685
+ const item = currentItem(state);
686
+ if (!item) {
687
+ callbacks.onPhotoDragEnd(null);
688
+ return;
689
+ }
690
+ const bound = boundOf(item);
691
+ const offset = state.options.swipeOffset * state.viewer.scaleSize;
692
+ let flagX = 0;
693
+ let flagY = 0;
694
+ if (state.viewer.photoPosX > bound.maxX) {
695
+ flagX = -1;
696
+ } else if (state.viewer.photoPosX < bound.minX) {
697
+ flagX = 1;
698
+ }
699
+ if (state.viewer.photoPosY > bound.maxY) {
700
+ flagY = -1;
701
+ } else if (state.viewer.photoPosY < bound.minY) {
702
+ flagY = 1;
703
+ }
704
+ if (state.viewer.photoPosX - bound.maxX > offset && state.viewer.currentIndex !== 0) {
705
+ callbacks.onPhotoDragEnd("prev");
706
+ return;
707
+ }
708
+ if (bound.minX - state.viewer.photoPosX > offset && state.viewer.currentIndex + 1 !== state.viewer.total) {
709
+ callbacks.onPhotoDragEnd("next");
710
+ return;
711
+ }
712
+ if (flagX === 0 && flagY === 0) {
713
+ vx = photoVX / 5;
714
+ vy = photoVY / 5;
715
+ } else {
716
+ registerElasticForce(flagX, flagY);
717
+ }
718
+ callbacks.onPhotoDragEnd(null);
719
+ }
720
+ function onPointerUp(e) {
721
+ activePointers.delete(e.pointerId);
722
+ if (pinching) {
723
+ if (activePointers.size < 2) {
724
+ endPinch();
725
+ }
726
+ return;
727
+ }
728
+ if (photoSwipable) {
729
+ endPhotoDrag();
730
+ return;
731
+ }
732
+ if (swiping) {
733
+ endSwipe();
734
+ }
735
+ }
736
+ function attach(...targets) {
737
+ for (const target of targets) {
738
+ target.addEventListener("pointerdown", onPointerDown, {
739
+ signal
740
+ });
741
+ target.addEventListener("pointermove", onPointerMove, {
742
+ signal
743
+ });
744
+ target.addEventListener("pointerup", onPointerUp, {
745
+ signal
746
+ });
747
+ target.addEventListener("pointercancel", onPointerUp, {
748
+ signal
749
+ });
750
+ }
751
+ }
752
+ function detach() {
753
+ clearInterval(interval);
754
+ }
755
+ return { attach, detach };
756
+ }
757
+
758
+ // src/core/view.ts
759
+ function srOnly(text) {
760
+ const span = document.createElement("span");
761
+ span.className = "smartphoto-sr-only";
762
+ span.textContent = text;
763
+ return span;
764
+ }
765
+ function buildButton() {
766
+ const button = document.createElement("button");
767
+ button.type = "button";
768
+ return button;
769
+ }
770
+ function escapeCssUrl(value) {
771
+ return value.replace(/"/g, '\\"');
772
+ }
773
+ function createView({ id, options }, handlers, { signal }) {
774
+ const { classNames, message } = options;
775
+ const root = document.createElement("div");
776
+ root.setAttribute("data-id", id);
777
+ const dialog = document.createElement("dialog");
778
+ dialog.className = classNames.smartPhoto;
779
+ dialog.setAttribute("aria-labelledby", `smartphoto-${id}-title`);
780
+ dialog.style.setProperty(
781
+ "--smartphoto-animation-speed",
782
+ `${options.animationSpeed}ms`
783
+ );
784
+ const body = document.createElement("div");
785
+ body.className = classNames.smartPhotoBody;
786
+ const inner = document.createElement("div");
787
+ inner.className = classNames.smartPhotoInner;
788
+ const header = document.createElement("div");
789
+ header.className = classNames.smartPhotoHeader;
790
+ const count = document.createElement("span");
791
+ count.className = classNames.smartPhotoCount;
792
+ const caption = document.createElement("h1");
793
+ caption.id = `smartphoto-${id}-title`;
794
+ caption.className = classNames.smartPhotoCaption;
795
+ caption.setAttribute("tabindex", "-1");
796
+ const dismiss = document.createElement("button");
797
+ dismiss.className = classNames.smartPhotoDismiss;
798
+ dismiss.appendChild(srOnly(message.closeDialog));
799
+ dismiss.addEventListener("click", () => handlers.onDismiss(), { signal });
800
+ header.append(count, caption, dismiss);
801
+ const content = document.createElement("div");
802
+ content.className = classNames.smartPhotoContent;
803
+ content.addEventListener(
804
+ "click",
805
+ (e) => {
806
+ if (e.target === content) {
807
+ handlers.onBackdropClick();
808
+ }
809
+ },
810
+ { signal }
811
+ );
812
+ const list = document.createElement("ul");
813
+ list.className = classNames.smartPhotoList;
814
+ list.setAttribute("role", "region");
815
+ list.setAttribute("aria-roledescription", "carousel");
816
+ list.setAttribute("aria-label", message.carouselLabel);
817
+ list.setAttribute("aria-live", "polite");
818
+ list.setAttribute("aria-atomic", "false");
819
+ inner.append(header, content, list);
820
+ let arrows = null;
821
+ let arrowLeft = null;
822
+ let arrowRight = null;
823
+ if (options.arrows) {
824
+ arrows = document.createElement("ul");
825
+ arrows.className = classNames.smartPhotoArrows;
826
+ arrowLeft = document.createElement("li");
827
+ arrowLeft.className = classNames.smartPhotoArrowLeft;
828
+ const prevButton = buildButton();
829
+ prevButton.appendChild(srOnly(message.gotoPrevImage));
830
+ prevButton.addEventListener("click", () => handlers.onPrev(), { signal });
831
+ arrowLeft.appendChild(prevButton);
832
+ arrowRight = document.createElement("li");
833
+ arrowRight.className = classNames.smartPhotoArrowRight;
834
+ const nextButton = buildButton();
835
+ nextButton.appendChild(srOnly(message.gotoNextImage));
836
+ nextButton.addEventListener("click", () => handlers.onNext(), { signal });
837
+ arrowRight.appendChild(nextButton);
838
+ arrows.append(arrowLeft, arrowRight);
839
+ inner.appendChild(arrows);
840
+ }
841
+ let nav = null;
842
+ let navList = null;
843
+ if (options.nav) {
844
+ nav = document.createElement("nav");
845
+ nav.className = classNames.smartPhotoNav;
846
+ nav.setAttribute("aria-label", "Choose slide to display");
847
+ navList = document.createElement("ul");
848
+ nav.appendChild(navList);
849
+ inner.appendChild(nav);
850
+ }
851
+ body.appendChild(inner);
852
+ dialog.appendChild(body);
853
+ root.appendChild(dialog);
854
+ const refs = {
855
+ dialog,
856
+ count,
857
+ caption,
858
+ dismiss,
859
+ content,
860
+ list,
861
+ arrows,
862
+ arrowLeft,
863
+ arrowRight,
864
+ nav,
865
+ navList,
866
+ slides: /* @__PURE__ */ new Map(),
867
+ imgClone: null
868
+ };
869
+ function buildLoaderWrap() {
870
+ const loaderWrap = document.createElement("div");
871
+ loaderWrap.className = classNames.smartPhotoLoaderWrap;
872
+ const loader = document.createElement("span");
873
+ loader.className = classNames.smartPhotoLoader;
874
+ loaderWrap.appendChild(loader);
875
+ return loaderWrap;
876
+ }
877
+ function buildImgWrap(item) {
878
+ const imgWrap = document.createElement("div");
879
+ imgWrap.className = classNames.smartPhotoImgWrap;
880
+ const img = document.createElement("img");
881
+ img.className = classNames.smartPhotoImg;
882
+ img.src = item.src ?? "";
883
+ img.alt = item.alt ?? "";
884
+ img.addEventListener("dragstart", (e) => e.preventDefault(), { signal });
885
+ imgWrap.appendChild(img);
886
+ return { imgWrap, img };
887
+ }
888
+ function syncSlides(items, state) {
889
+ refs.list.replaceChildren();
890
+ refs.navList?.replaceChildren();
891
+ refs.slides = /* @__PURE__ */ new Map();
892
+ items.forEach((item) => {
893
+ const li = document.createElement("li");
894
+ li.setAttribute("role", "group");
895
+ li.setAttribute("aria-roledescription", "slide");
896
+ li.setAttribute("aria-label", `${item.index + 1} of ${items.length}`);
897
+ const slideRefs = {
898
+ li,
899
+ loaderWrap: null,
900
+ imgWrap: null,
901
+ img: null,
902
+ navLink: null
903
+ };
904
+ if (item.processed) {
905
+ const { imgWrap, img } = buildImgWrap(item);
906
+ li.appendChild(imgWrap);
907
+ slideRefs.imgWrap = imgWrap;
908
+ slideRefs.img = img;
909
+ } else {
910
+ const loaderWrap = buildLoaderWrap();
911
+ li.appendChild(loaderWrap);
912
+ slideRefs.loaderWrap = loaderWrap;
913
+ }
914
+ refs.list.appendChild(li);
915
+ refs.slides.set(item, slideRefs);
916
+ if (refs.navList) {
917
+ const navLi = document.createElement("li");
918
+ const navLink = buildButton();
919
+ navLink.style.backgroundImage = `url("${escapeCssUrl(item.thumb ?? "")}")`;
920
+ const index = item.index;
921
+ navLink.addEventListener("click", () => handlers.onNavigate(index), {
922
+ signal
923
+ });
924
+ navLink.appendChild(srOnly(`go to ${item.caption ?? ""}`));
925
+ navLi.appendChild(navLink);
926
+ refs.navList.appendChild(navLi);
927
+ slideRefs.navLink = navLink;
928
+ }
929
+ });
930
+ render(state);
931
+ }
932
+ function upgradeIfProcessed(item, slideRefs) {
933
+ if (slideRefs.imgWrap || !item.processed) {
934
+ return slideRefs;
935
+ }
936
+ const { imgWrap, img } = buildImgWrap(item);
937
+ slideRefs.loaderWrap?.replaceWith(imgWrap);
938
+ slideRefs.loaderWrap = null;
939
+ slideRefs.imgWrap = imgWrap;
940
+ slideRefs.img = img;
941
+ return slideRefs;
942
+ }
943
+ function hideFromA11yIfFocused(el) {
944
+ if (el && document.activeElement && el.contains(document.activeElement)) {
945
+ refs.caption.focus();
946
+ }
947
+ }
948
+ function render(state) {
949
+ const { viewer } = state;
950
+ refs.count.textContent = `${viewer.currentIndex + 1}/${viewer.total}`;
951
+ refs.slides.forEach((rawSlideRefs, item) => {
952
+ const slideRefs = upgradeIfProcessed(item, rawSlideRefs);
953
+ const isCurrent = item.index === viewer.currentIndex;
954
+ slideRefs.li.style.transform = `translate(${item.translateX}px,${item.translateY}px)`;
955
+ slideRefs.li.classList.toggle("current", isCurrent);
956
+ if (isCurrent) {
957
+ slideRefs.li.removeAttribute("aria-hidden");
958
+ } else {
959
+ slideRefs.li.setAttribute("aria-hidden", "true");
960
+ }
961
+ if (isCurrent) {
962
+ refs.caption.textContent = item.caption ?? "";
963
+ }
964
+ if (slideRefs.imgWrap && slideRefs.img) {
965
+ slideRefs.imgWrap.style.transform = `translate(${item.x}px,${item.y}px) scale(${item.scale})`;
966
+ slideRefs.img.style.width = `${item.width}px`;
967
+ slideRefs.img.classList.toggle("active", viewer.appear);
968
+ slideRefs.img.classList.toggle(
969
+ classNames.smartPhotoImgOnMove,
970
+ viewer.scale
971
+ );
972
+ slideRefs.img.classList.toggle(
973
+ classNames.smartPhotoImgElasticMove,
974
+ viewer.elastic
975
+ );
976
+ }
977
+ if (slideRefs.navLink) {
978
+ slideRefs.navLink.classList.toggle("current", isCurrent);
979
+ if (isCurrent) {
980
+ slideRefs.navLink.setAttribute("aria-current", "true");
981
+ } else {
982
+ slideRefs.navLink.removeAttribute("aria-current");
983
+ }
984
+ }
985
+ });
986
+ if (refs.arrowLeft) {
987
+ if (viewer.showPrevArrow) {
988
+ refs.arrowLeft.removeAttribute("aria-hidden");
989
+ } else {
990
+ hideFromA11yIfFocused(refs.arrowLeft);
991
+ refs.arrowLeft.setAttribute("aria-hidden", "true");
992
+ }
993
+ }
994
+ if (refs.arrowRight) {
995
+ if (viewer.showNextArrow) {
996
+ refs.arrowRight.removeAttribute("aria-hidden");
997
+ } else {
998
+ hideFromA11yIfFocused(refs.arrowRight);
999
+ refs.arrowRight.setAttribute("aria-hidden", "true");
1000
+ }
1001
+ }
1002
+ if (refs.arrows) {
1003
+ if (viewer.hideUi) {
1004
+ hideFromA11yIfFocused(refs.arrows);
1005
+ }
1006
+ refs.arrows.setAttribute("aria-hidden", viewer.hideUi ? "true" : "false");
1007
+ }
1008
+ if (refs.nav) {
1009
+ if (viewer.hideUi) {
1010
+ hideFromA11yIfFocused(refs.nav);
1011
+ }
1012
+ refs.nav.setAttribute("aria-hidden", viewer.hideUi ? "true" : "false");
1013
+ }
1014
+ }
1015
+ function findCurrentSlideRefs(state) {
1016
+ for (const [item, slideRefs] of refs.slides) {
1017
+ if (item.index === state.viewer.currentIndex) {
1018
+ return slideRefs;
1019
+ }
1020
+ }
1021
+ return null;
1022
+ }
1023
+ function updatePhotoTransform(state) {
1024
+ const { viewer } = state;
1025
+ const slideRefs = findCurrentSlideRefs(state);
1026
+ const img = slideRefs?.img;
1027
+ if (img) {
1028
+ img.style.transform = `translate(${viewer.photoPosX}px,${viewer.photoPosY}px) scale(${viewer.scaleSize})`;
1029
+ img.classList.toggle(classNames.smartPhotoImgOnMove, viewer.scale);
1030
+ img.classList.toggle(classNames.smartPhotoImgElasticMove, viewer.elastic);
1031
+ }
1032
+ if (refs.nav) {
1033
+ if (viewer.hideUi) {
1034
+ hideFromA11yIfFocused(refs.nav);
1035
+ }
1036
+ refs.nav.setAttribute("aria-hidden", viewer.hideUi ? "true" : "false");
1037
+ }
1038
+ if (refs.arrows) {
1039
+ if (viewer.hideUi) {
1040
+ hideFromA11yIfFocused(refs.arrows);
1041
+ }
1042
+ refs.arrows.setAttribute("aria-hidden", viewer.hideUi ? "true" : "false");
1043
+ }
1044
+ }
1045
+ function updateListTransform(state) {
1046
+ const { viewer } = state;
1047
+ refs.list.style.transform = `translate(${viewer.translateX}px,${viewer.translateY}px)`;
1048
+ refs.list.classList.toggle(classNames.smartPhotoListOnMove, viewer.onMove);
1049
+ }
1050
+ function showAppearEffect(effect) {
1051
+ const clone = document.createElement("img");
1052
+ clone.className = classNames.smartPhotoImgClone;
1053
+ clone.src = effect.img;
1054
+ clone.style.width = `${effect.width}px`;
1055
+ clone.style.height = `${effect.height}px`;
1056
+ clone.style.transform = `translate(${effect.left}px,${effect.top}px) scale(1)`;
1057
+ body.appendChild(clone);
1058
+ refs.imgClone = clone;
1059
+ }
1060
+ function removeAppearEffect() {
1061
+ refs.imgClone?.remove();
1062
+ refs.imgClone = null;
1063
+ }
1064
+ function destroy() {
1065
+ root.remove();
1066
+ }
1067
+ return {
1068
+ root,
1069
+ refs,
1070
+ render,
1071
+ syncSlides,
1072
+ updatePhotoTransform,
1073
+ updateListTransform,
1074
+ showAppearEffect,
1075
+ removeAppearEffect,
1076
+ destroy
1077
+ };
1078
+ }
1079
+
1080
+ // src/core/index.ts
1081
+ function getWindowWidth() {
1082
+ return document.documentElement.clientWidth;
1083
+ }
1084
+ function getWindowHeight() {
1085
+ return document.documentElement.clientHeight;
1086
+ }
1087
+ function getUniqId() {
1088
+ return (Date.now().toString(36) + Math.random().toString(36).substring(2, 7)).toUpperCase();
1089
+ }
1090
+ function getScroll() {
1091
+ return {
1092
+ x: window.pageXOffset !== void 0 ? window.pageXOffset : document.documentElement.scrollLeft,
1093
+ y: window.pageYOffset !== void 0 ? window.pageYOffset : document.documentElement.scrollTop
1094
+ };
1095
+ }
1096
+ var SmartPhoto = class {
1097
+ constructor(source, settings) {
1098
+ this.id = getUniqId();
1099
+ this.abortController = new AbortController();
1100
+ this.isSmartPhoneFlag = isSmartPhone();
1101
+ this.lastTriggerElement = null;
1102
+ // fireEvent("close") が dialog に発火する公開イベントは、ネイティブの close
1103
+ // イベントと同じ "close" という型名を共有する(§8)。閉じるアニメーション完了後に
1104
+ // 遅延発火するこの公開イベントを、ネイティブ close 同期用リスナー(下記)が
1105
+ // 誤って拾わないよう、発火中であることを示すフラグで区別する
1106
+ this.isFiringPublicCloseEvent = false;
1107
+ // doHideEffect() の後始末(画像に設定した translateY の除去と promise 解決)を
1108
+ // transitionend を待たずに即時実行するための関数。後始末が済むと null に戻る
1109
+ this.finishHideEffect = null;
1110
+ this.timeouts = [];
1111
+ this.loadAllFired = /* @__PURE__ */ new Set();
1112
+ this.syncedGroupId = null;
1113
+ // ---- 内部: window イベント ----
1114
+ this.handleResize = () => {
1115
+ if (!currentItems(this.state)) {
1116
+ return;
1117
+ }
1118
+ this.resetTranslateCurrent();
1119
+ this.setPosByCurrentIndex();
1120
+ this.setSizeByScreen();
1121
+ this.commit();
1122
+ };
1123
+ this.handleKeydown = (e) => {
1124
+ if (!this.state.viewer.isOpen) {
1125
+ return;
1126
+ }
1127
+ const code = e.keyCode || e.which;
1128
+ if (code === 37) {
1129
+ this.gotoSlide(this.state.viewer.prev);
1130
+ } else if (code === 39) {
1131
+ this.gotoSlide(this.state.viewer.next);
1132
+ } else if (code === 27) {
1133
+ this.hidePhoto();
1134
+ }
1135
+ };
1136
+ this.handleOrientationChange = () => {
1137
+ if (!currentItems(this.state)) {
1138
+ return;
1139
+ }
1140
+ this.resetTranslateCurrent();
1141
+ this.setPosByCurrentIndex();
1142
+ this.setHashByCurrentIndex();
1143
+ this.setSizeByScreen();
1144
+ this.commit();
1145
+ const prevWidth = getWindowWidth();
1146
+ const timeout = 500;
1147
+ const poll = (time) => {
1148
+ this.scheduleTimeout(() => {
1149
+ if (prevWidth !== getWindowWidth()) {
1150
+ this.resetTranslateCurrent();
1151
+ this.setPosByCurrentIndex();
1152
+ this.setHashByCurrentIndex();
1153
+ this.setSizeByScreen();
1154
+ this.commit();
1155
+ } else if (time <= timeout) {
1156
+ poll(time + 25);
1157
+ }
1158
+ }, 25);
1159
+ };
1160
+ poll(0);
1161
+ };
1162
+ this.state = createState(settings ?? {});
1163
+ this.view = createView(
1164
+ { id: this.id, options: this.state.options },
1165
+ this.buildViewHandlers(),
1166
+ { signal: this.abortController.signal }
1167
+ );
1168
+ document.body.appendChild(this.view.root);
1169
+ this.gestures = createGestures(
1170
+ { state: this.state, callbacks: this.buildGestureCallbacks() },
1171
+ { signal: this.abortController.signal }
1172
+ );
1173
+ this.gestures.attach(this.view.refs.content, this.view.refs.list);
1174
+ this.view.refs.dialog.addEventListener(
1175
+ "close",
1176
+ () => {
1177
+ if (!this.isFiringPublicCloseEvent && this.state.viewer.isOpen) {
1178
+ this.hidePhoto();
1179
+ }
1180
+ },
1181
+ { signal: this.abortController.signal }
1182
+ );
1183
+ this.ingestSource(source);
1184
+ this.syncCurrentGroupView();
1185
+ const restored = this.restoreFromHash();
1186
+ if (restored) {
1187
+ if (restored.element) {
1188
+ triggerEvent(restored.element, "click");
1189
+ } else {
1190
+ this.openPhoto(restored, null);
1191
+ }
1192
+ }
1193
+ if (!this.isSmartPhoneFlag) {
1194
+ window.addEventListener("resize", this.handleResize, {
1195
+ signal: this.abortController.signal
1196
+ });
1197
+ window.addEventListener("keydown", this.handleKeydown, {
1198
+ signal: this.abortController.signal
1199
+ });
1200
+ return;
1201
+ }
1202
+ window.addEventListener("orientationchange", this.handleOrientationChange, {
1203
+ signal: this.abortController.signal
1204
+ });
1205
+ }
1206
+ // ---- 公開 API: 既存契約 ----
1207
+ on(event, fn) {
1208
+ const dialog = this.view.refs.dialog;
1209
+ const handler = (e) => fn.call(dialog, e);
1210
+ dialog.addEventListener(event, handler, {
1211
+ signal: this.abortController.signal
1212
+ });
1213
+ }
1214
+ destroy() {
1215
+ this.state.viewer.isOpen = false;
1216
+ if (this.view.refs.dialog.open) {
1217
+ this.view.refs.dialog.close();
1218
+ }
1219
+ this.abortController.abort();
1220
+ this.timeouts.forEach((id) => {
1221
+ clearTimeout(id);
1222
+ });
1223
+ this.timeouts = [];
1224
+ this.gestures.detach();
1225
+ this.view.destroy();
1226
+ }
1227
+ gotoSlide(index) {
1228
+ this.state.viewer.currentIndex = Number.parseInt(String(index), 10);
1229
+ if (!this.state.viewer.currentIndex) {
1230
+ this.state.viewer.currentIndex = 0;
1231
+ }
1232
+ this.slideList();
1233
+ }
1234
+ hidePhoto(dir = "bottom") {
1235
+ if (!this.state.viewer.isOpen) {
1236
+ return;
1237
+ }
1238
+ this.state.viewer.isOpen = false;
1239
+ this.state.viewer.appear = false;
1240
+ this.state.viewer.appearEffect = null;
1241
+ this.view.removeAppearEffect();
1242
+ this.state.viewer.hideUi = false;
1243
+ this.state.viewer.scale = false;
1244
+ this.state.viewer.scaleSize = 1;
1245
+ const scroll = getScroll();
1246
+ if (location.hash) {
1247
+ this.setHash("");
1248
+ }
1249
+ window.scroll(scroll.x, scroll.y);
1250
+ this.syncDialog();
1251
+ if (this.lastTriggerElement?.isConnected) {
1252
+ this.lastTriggerElement.focus();
1253
+ }
1254
+ this.lastTriggerElement = null;
1255
+ this.doHideEffect(dir).then(() => {
1256
+ this.view.render(this.state);
1257
+ this.isFiringPublicCloseEvent = true;
1258
+ this.fireEvent("close");
1259
+ this.isFiringPublicCloseEvent = false;
1260
+ });
1261
+ }
1262
+ zoomPhoto() {
1263
+ const item = currentItem(this.state);
1264
+ if (!item) {
1265
+ return;
1266
+ }
1267
+ this.state.viewer.hideUi = true;
1268
+ this.state.viewer.scaleSize = scaleBorder(
1269
+ item,
1270
+ getWindowWidth(),
1271
+ getWindowHeight(),
1272
+ this.isSmartPhoneFlag
1273
+ );
1274
+ if (this.state.viewer.scaleSize <= 1) {
1275
+ return;
1276
+ }
1277
+ this.state.viewer.photoPosX = 0;
1278
+ this.state.viewer.photoPosY = 0;
1279
+ this.view.updatePhotoTransform(this.state);
1280
+ this.scheduleTimeout(() => {
1281
+ this.state.viewer.scale = true;
1282
+ this.view.updatePhotoTransform(this.state);
1283
+ this.fireEvent("zoomin");
1284
+ }, 300);
1285
+ }
1286
+ zoomOutPhoto() {
1287
+ this.state.viewer.scaleSize = 1;
1288
+ this.state.viewer.hideUi = false;
1289
+ this.state.viewer.scale = false;
1290
+ this.state.viewer.photoPosX = 0;
1291
+ this.state.viewer.photoPosY = 0;
1292
+ this.view.updatePhotoTransform(this.state);
1293
+ this.fireEvent("zoomout");
1294
+ }
1295
+ addNewItem(element) {
1296
+ return this.addItem(element);
1297
+ }
1298
+ // ---- 公開 API: v2 新設(§3.3) ----
1299
+ show(indexOrId = 0, opts = {}) {
1300
+ const groupId = opts.group ?? this.state.viewer.currentGroup;
1301
+ if (groupId === null) {
1302
+ return;
1303
+ }
1304
+ const items = this.state.groups.get(groupId);
1305
+ if (!items?.length) {
1306
+ return;
1307
+ }
1308
+ const item = typeof indexOrId === "number" ? items[indexOrId] : items.find((i) => i.id === indexOrId);
1309
+ if (!item) {
1310
+ return;
1311
+ }
1312
+ const activeElement = document.activeElement instanceof HTMLElement ? document.activeElement : null;
1313
+ const trigger = opts.trigger ?? item.element ?? activeElement;
1314
+ this.openPhoto(item, trigger);
1315
+ }
1316
+ hide() {
1317
+ this.hidePhoto();
1318
+ }
1319
+ next() {
1320
+ if (!this.state.viewer.showNextArrow) {
1321
+ return;
1322
+ }
1323
+ this.gotoSlide(this.state.viewer.next);
1324
+ }
1325
+ prev() {
1326
+ if (!this.state.viewer.showPrevArrow) {
1327
+ return;
1328
+ }
1329
+ this.gotoSlide(this.state.viewer.prev);
1330
+ }
1331
+ addItem(slideOrElement) {
1332
+ const item = slideOrElement instanceof Element ? this.addElementItem(slideOrElement) : this.addSlideItem(slideOrElement);
1333
+ this.syncCurrentGroupView();
1334
+ return item;
1335
+ }
1336
+ get currentIndex() {
1337
+ return this.state.viewer.currentIndex;
1338
+ }
1339
+ // ---- 内部: ソース取り込み ----
1340
+ ingestSource(source) {
1341
+ if (Array.isArray(source)) {
1342
+ source.forEach((slide) => {
1343
+ this.addSlideItem(slide);
1344
+ });
1345
+ return;
1346
+ }
1347
+ const elements = typeof source === "string" ? Array.from(document.querySelectorAll(source)) : Array.from(source);
1348
+ elements.forEach((el) => {
1349
+ this.addElementItem(el);
1350
+ });
1351
+ }
1352
+ addElementItem(element) {
1353
+ const groupId = groupIdFromElement(element);
1354
+ const index = this.state.groups.get(groupId)?.length ?? 0;
1355
+ const item = itemFromElement(
1356
+ element,
1357
+ this.state.options,
1358
+ index,
1359
+ getWindowWidth()
1360
+ );
1361
+ addItemToGroup(this.state, item);
1362
+ this.loadAllFired.delete(groupId);
1363
+ this.bindThumbnailClick(element, item);
1364
+ return item;
1365
+ }
1366
+ addSlideItem(slide) {
1367
+ const groupId = groupIdFromSlide(slide);
1368
+ const index = this.state.groups.get(groupId)?.length ?? 0;
1369
+ const item = itemFromSlide(slide, index, getWindowWidth());
1370
+ addItemToGroup(this.state, item);
1371
+ this.loadAllFired.delete(groupId);
1372
+ return item;
1373
+ }
1374
+ bindThumbnailClick(element, item) {
1375
+ element.addEventListener(
1376
+ "click",
1377
+ (e) => {
1378
+ e.preventDefault();
1379
+ this.openPhoto(item, element);
1380
+ },
1381
+ { signal: this.abortController.signal }
1382
+ );
1383
+ }
1384
+ syncCurrentGroupView() {
1385
+ const items = currentItems(this.state);
1386
+ if (items) {
1387
+ this.view.syncSlides(items, this.state);
1388
+ this.syncedGroupId = this.state.viewer.currentGroup;
1389
+ }
1390
+ }
1391
+ // ---- 内部: ハッシュ ----
1392
+ setHash(hash) {
1393
+ if (!window.history?.pushState || !this.state.options.useHistoryApi) {
1394
+ return;
1395
+ }
1396
+ const path = `${location.pathname}${location.search}`;
1397
+ window.history.replaceState(null, "", hash ? `${path}#${hash}` : path);
1398
+ }
1399
+ setHashByCurrentIndex() {
1400
+ const scroll = getScroll();
1401
+ this.setHash(buildHash(this.state));
1402
+ window.scroll(scroll.x, scroll.y);
1403
+ }
1404
+ restoreFromHash() {
1405
+ const hash = location.hash.substring(1);
1406
+ if (!hash) {
1407
+ return null;
1408
+ }
1409
+ return findItemByHash(this.state, parseQuery(hash));
1410
+ }
1411
+ // ---- 内部: レイアウト ----
1412
+ setPosByCurrentIndex() {
1413
+ const item = currentItem(this.state);
1414
+ if (!item) {
1415
+ return;
1416
+ }
1417
+ this.state.viewer.translateX = -item.translateX;
1418
+ this.state.viewer.translateY = 0;
1419
+ this.view.updateListTransform(this.state);
1420
+ }
1421
+ setSizeByScreen() {
1422
+ const items = currentItems(this.state);
1423
+ if (!items) {
1424
+ return;
1425
+ }
1426
+ sizeItems(
1427
+ items,
1428
+ getWindowWidth(),
1429
+ getWindowHeight(),
1430
+ this.state.options.headerHeight,
1431
+ this.state.options.footerHeight
1432
+ );
1433
+ }
1434
+ // resize/orientationchange ハンドラは呼び出し前に currentItems の存在を確認済み
1435
+ resetTranslateCurrent() {
1436
+ resetTranslate(currentItems(this.state), getWindowWidth());
1437
+ }
1438
+ currentImgElement() {
1439
+ for (const [item, slideRefs] of this.view.refs.slides) {
1440
+ if (item.index === this.state.viewer.currentIndex) {
1441
+ return slideRefs.img;
1442
+ }
1443
+ }
1444
+ return null;
1445
+ }
1446
+ // ---- 内部: dialog 制御(§8) ----
1447
+ syncDialog() {
1448
+ const { dialog, caption } = this.view.refs;
1449
+ if (this.state.viewer.isOpen && !dialog.open) {
1450
+ dialog.showModal();
1451
+ caption.focus();
1452
+ } else if (!this.state.viewer.isOpen && dialog.open) {
1453
+ dialog.close();
1454
+ }
1455
+ }
1456
+ commit() {
1457
+ this.view.render(this.state);
1458
+ this.syncDialog();
1459
+ }
1460
+ // ---- 内部: 開く演出 ----
1461
+ // initPhoto() は必ず有効なアイテムが存在するグループを開いた直後に呼ばれる
1462
+ // (doOpen/loadNeighborItems/slideList のいずれも呼び出し前に確認済み)
1463
+ initPhoto() {
1464
+ this.finishHideEffect?.();
1465
+ this.view.refs.dialog.style.opacity = "";
1466
+ const items = currentItems(this.state);
1467
+ this.state.viewer.total = items.length;
1468
+ this.state.viewer.isOpen = true;
1469
+ this.state.viewer.photoPosX = 0;
1470
+ this.state.viewer.photoPosY = 0;
1471
+ this.setPosByCurrentIndex();
1472
+ this.setSizeByScreen();
1473
+ setArrow(this.state);
1474
+ if (this.state.options.resizeStyle === "fill" && this.isSmartPhoneFlag) {
1475
+ const item = currentItem(this.state);
1476
+ this.state.viewer.scale = true;
1477
+ this.state.viewer.hideUi = true;
1478
+ this.state.viewer.scaleSize = scaleBorder(
1479
+ item,
1480
+ getWindowWidth(),
1481
+ getWindowHeight(),
1482
+ this.isSmartPhoneFlag
1483
+ );
1484
+ }
1485
+ }
1486
+ supportsViewTransition() {
1487
+ return typeof document.startViewTransition === "function";
1488
+ }
1489
+ openPhotoWithViewTransition(trigger) {
1490
+ const transitionName = "smartphoto-hero";
1491
+ const thumbImg = trigger?.querySelector("img") ?? null;
1492
+ if (thumbImg) {
1493
+ thumbImg.style.viewTransitionName = transitionName;
1494
+ }
1495
+ const clearNames = () => {
1496
+ if (thumbImg) {
1497
+ thumbImg.style.viewTransitionName = "";
1498
+ }
1499
+ const img = this.currentImgElement();
1500
+ if (img) {
1501
+ img.style.viewTransitionName = "";
1502
+ }
1503
+ };
1504
+ const transition = document.startViewTransition?.(() => {
1505
+ this.initPhoto();
1506
+ this.state.viewer.appear = true;
1507
+ this.commit();
1508
+ if (thumbImg) {
1509
+ thumbImg.style.viewTransitionName = "";
1510
+ }
1511
+ this.currentImgElement().style.viewTransitionName = transitionName;
1512
+ });
1513
+ transition?.ready.catch(() => {
1514
+ clearNames();
1515
+ });
1516
+ transition?.finished.then(clearNames, clearNames);
1517
+ }
1518
+ // doOpen() の呼び出し元でこの分岐に来る時点で showAnimation !== false が保証されている
1519
+ addAppearEffect(trigger, item) {
1520
+ const img = trigger?.querySelector("img") ?? null;
1521
+ if (!img) {
1522
+ this.state.viewer.appear = true;
1523
+ return;
1524
+ }
1525
+ const pos = getViewPos(img);
1526
+ const width = img.offsetWidth;
1527
+ const height = img.offsetHeight;
1528
+ const toX = getWindowWidth();
1529
+ const toY = getWindowHeight();
1530
+ const screenY = toY - this.state.options.headerHeight - this.state.options.footerHeight;
1531
+ let scale = 1;
1532
+ if (this.state.options.resizeStyle === "fill" && this.isSmartPhoneFlag) {
1533
+ if (width > height) {
1534
+ scale = toY / height;
1535
+ } else {
1536
+ scale = toX / width;
1537
+ }
1538
+ } else {
1539
+ if (width >= height) {
1540
+ if (item.height < screenY) {
1541
+ scale = item.width / width;
1542
+ } else {
1543
+ scale = screenY / height;
1544
+ }
1545
+ } else {
1546
+ if (item.height < screenY) {
1547
+ scale = item.height / height;
1548
+ } else {
1549
+ scale = screenY / height;
1550
+ }
1551
+ }
1552
+ if (width * scale > toX) {
1553
+ scale = toX / width;
1554
+ }
1555
+ }
1556
+ const x = (scale - 1) / 2 * width + (toX - width * scale) / 2;
1557
+ const y = (scale - 1) / 2 * height + (toY - height * scale) / 2;
1558
+ const lazyImg = img.getAttribute(this.state.options.lazyAttribute);
1559
+ this.state.viewer.appearEffect = {
1560
+ width,
1561
+ height,
1562
+ top: pos.top,
1563
+ left: pos.left,
1564
+ once: true,
1565
+ img: lazyImg || item.src || "",
1566
+ afterX: x,
1567
+ afterY: y,
1568
+ scale
1569
+ };
1570
+ }
1571
+ // showAppearEffect() が直前で refs.imgClone を必ず生成するため non-null が保証される
1572
+ runAppearEffect(effect) {
1573
+ this.view.showAppearEffect(effect);
1574
+ const clone = this.view.refs.imgClone;
1575
+ return new Promise((resolve) => {
1576
+ const handler = () => {
1577
+ clone.removeEventListener("transitionend", handler, true);
1578
+ resolve();
1579
+ };
1580
+ clone.addEventListener("transitionend", handler, true);
1581
+ this.scheduleTimeout(() => {
1582
+ clone.style.transform = `translate(${effect.afterX}px, ${effect.afterY}px) scale(${effect.scale})`;
1583
+ }, 10);
1584
+ });
1585
+ }
1586
+ doOpen(trigger, item) {
1587
+ if (this.state.options.showAnimation !== false && this.supportsViewTransition()) {
1588
+ this.openPhotoWithViewTransition(trigger);
1589
+ } else if (this.state.options.showAnimation === false) {
1590
+ this.initPhoto();
1591
+ this.state.viewer.appear = true;
1592
+ this.commit();
1593
+ } else {
1594
+ this.initPhoto();
1595
+ this.addAppearEffect(trigger, item);
1596
+ this.commit();
1597
+ const effect = this.state.viewer.appearEffect;
1598
+ if (effect) {
1599
+ this.runAppearEffect(effect).then(() => {
1600
+ this.state.viewer.appearEffect = null;
1601
+ this.view.removeAppearEffect();
1602
+ this.state.viewer.appear = true;
1603
+ this.commit();
1604
+ });
1605
+ }
1606
+ }
1607
+ this.fireEvent("open");
1608
+ this.resyncSizeAfterOpen();
1609
+ }
1610
+ // initPhoto() 内の setSizeByScreen() は dialog がまだ showModal() 前(§: :root:has()
1611
+ // による overflow:hidden 未適用)の時点の getWindowWidth/Height() を使っている。
1612
+ // ページに縦スクロールバーがある環境ではその分だけ幅が狭く計測され、実際に
1613
+ // dialog が開いてスクロールバーが消えた後の幅とズレる。次の描画フレームで
1614
+ // 開いた後の正しいサイズを使って再計算し、必要なら再描画する
1615
+ resyncSizeAfterOpen() {
1616
+ const winWidthBefore = getWindowWidth();
1617
+ const winHeightBefore = getWindowHeight();
1618
+ requestAnimationFrame(() => {
1619
+ if (!this.state.viewer.isOpen) {
1620
+ return;
1621
+ }
1622
+ if (getWindowWidth() === winWidthBefore && getWindowHeight() === winHeightBefore) {
1623
+ return;
1624
+ }
1625
+ this.resetTranslateCurrent();
1626
+ this.setPosByCurrentIndex();
1627
+ this.setSizeByScreen();
1628
+ this.view.render(this.state);
1629
+ });
1630
+ }
1631
+ openPhoto(item, trigger) {
1632
+ this.lastTriggerElement = trigger;
1633
+ this.state.viewer.currentGroup = item.groupId;
1634
+ this.state.viewer.currentIndex = item.index;
1635
+ if (this.syncedGroupId !== item.groupId) {
1636
+ this.syncCurrentGroupView();
1637
+ }
1638
+ this.setHashByCurrentIndex();
1639
+ if (item.loaded) {
1640
+ this.doOpen(trigger, item);
1641
+ } else {
1642
+ this.loadItem(item).then(() => {
1643
+ this.doOpen(trigger, item);
1644
+ });
1645
+ }
1646
+ }
1647
+ doHideEffect(dir) {
1648
+ return new Promise((resolve) => {
1649
+ const dialog = this.view.refs.dialog;
1650
+ const img = this.currentImgElement();
1651
+ const height = getWindowHeight();
1652
+ const applied = dir === "top" ? `translateY(-${height}px)` : `translateY(${height}px)`;
1653
+ const finish = () => {
1654
+ if (this.finishHideEffect !== finish) {
1655
+ return;
1656
+ }
1657
+ this.finishHideEffect = null;
1658
+ dialog.removeEventListener("transitionend", finish, true);
1659
+ if (img && img.style.transform === applied) {
1660
+ img.style.transform = "";
1661
+ }
1662
+ resolve();
1663
+ };
1664
+ this.finishHideEffect = finish;
1665
+ if (img) {
1666
+ img.style.transform = applied;
1667
+ }
1668
+ dialog.addEventListener("transitionend", finish, true);
1669
+ this.scheduleTimeout(finish, this.state.options.animationSpeed + 100);
1670
+ });
1671
+ }
1672
+ // ---- 内部: ロード ----
1673
+ loadItem(item) {
1674
+ return new Promise((resolve) => {
1675
+ const img = new Image();
1676
+ img.onload = () => {
1677
+ item.width = img.width;
1678
+ item.height = img.height;
1679
+ item.loaded = true;
1680
+ this.checkLoadAll(item.groupId);
1681
+ resolve();
1682
+ };
1683
+ img.onerror = () => resolve();
1684
+ img.src = item.src ?? "";
1685
+ });
1686
+ }
1687
+ checkLoadAll(groupId) {
1688
+ if (this.loadAllFired.has(groupId)) {
1689
+ return;
1690
+ }
1691
+ const items = this.state.groups.get(groupId);
1692
+ if (items?.length && items.every((i) => i.loaded)) {
1693
+ this.loadAllFired.add(groupId);
1694
+ this.fireEvent("loadall");
1695
+ }
1696
+ }
1697
+ loadNeighborItems() {
1698
+ const items = currentItems(this.state);
1699
+ if (!items) {
1700
+ return;
1701
+ }
1702
+ const { currentIndex } = this.state.viewer;
1703
+ const { loadOffset } = this.state.options;
1704
+ const promises = [];
1705
+ for (let i = currentIndex - loadOffset; i < currentIndex + loadOffset; i++) {
1706
+ const item = items[i];
1707
+ if (item && !item.loaded) {
1708
+ promises.push(this.loadItem(item));
1709
+ }
1710
+ }
1711
+ if (promises.length) {
1712
+ Promise.all(promises).then(() => {
1713
+ this.initPhoto();
1714
+ this.commit();
1715
+ });
1716
+ }
1717
+ }
1718
+ // ---- 内部: ナビゲーション ----
1719
+ slideList() {
1720
+ this.state.viewer.scaleSize = 1;
1721
+ this.state.viewer.hideUi = false;
1722
+ this.state.viewer.scale = false;
1723
+ this.state.viewer.photoPosX = 0;
1724
+ this.state.viewer.photoPosY = 0;
1725
+ this.state.viewer.onMove = true;
1726
+ this.setPosByCurrentIndex();
1727
+ this.setHashByCurrentIndex();
1728
+ this.setSizeByScreen();
1729
+ this.scheduleTimeout(() => {
1730
+ const item = currentItem(this.state);
1731
+ this.state.viewer.onMove = false;
1732
+ setArrow(this.state);
1733
+ this.commit();
1734
+ if (this.state.viewer.oldIndex !== this.state.viewer.currentIndex) {
1735
+ this.fireEvent("change");
1736
+ }
1737
+ this.state.viewer.oldIndex = this.state.viewer.currentIndex;
1738
+ this.loadNeighborItems();
1739
+ if (item && !item.loaded) {
1740
+ this.loadItem(item).then(() => {
1741
+ this.initPhoto();
1742
+ this.commit();
1743
+ });
1744
+ }
1745
+ }, 200);
1746
+ }
1747
+ // ---- 内部: タイマー(destroy 時に一括破棄) ----
1748
+ scheduleTimeout(fn, delay) {
1749
+ const id = window.setTimeout(() => {
1750
+ this.timeouts = this.timeouts.filter((t) => t !== id);
1751
+ fn();
1752
+ }, delay);
1753
+ this.timeouts.push(id);
1754
+ return id;
1755
+ }
1756
+ // ---- 内部: イベント発火 ----
1757
+ fireEvent(eventName) {
1758
+ triggerEvent(this.view.refs.dialog, eventName);
1759
+ }
1760
+ // ---- 内部: 結線 ----
1761
+ buildViewHandlers() {
1762
+ return {
1763
+ onDismiss: () => this.hidePhoto(),
1764
+ onPrev: () => this.prev(),
1765
+ onNext: () => this.next(),
1766
+ onNavigate: (index) => this.gotoSlide(index),
1767
+ onBackdropClick: () => this.hidePhoto()
1768
+ };
1769
+ }
1770
+ buildGestureCallbacks() {
1771
+ return {
1772
+ onSwipeStart: () => this.fireEvent("swipestart"),
1773
+ onSwipeMove: () => this.view.updateListTransform(this.state),
1774
+ onSwipeEnd: (result) => {
1775
+ this.fireEvent("swipeend");
1776
+ if (result === "close-bottom") {
1777
+ this.hidePhoto("bottom");
1778
+ return;
1779
+ }
1780
+ if (result === "close-top") {
1781
+ this.hidePhoto("top");
1782
+ return;
1783
+ }
1784
+ if (result === "prev") {
1785
+ this.state.viewer.currentIndex -= 1;
1786
+ } else if (result === "next") {
1787
+ this.state.viewer.currentIndex += 1;
1788
+ }
1789
+ this.slideList();
1790
+ },
1791
+ onTap: () => this.zoomPhoto(),
1792
+ onGestureStart: () => {
1793
+ this.fireEvent("gesturestart");
1794
+ this.view.updatePhotoTransform(this.state);
1795
+ },
1796
+ onGestureMove: () => this.view.updatePhotoTransform(this.state),
1797
+ onGestureEnd: () => {
1798
+ this.fireEvent("gestureend");
1799
+ this.view.updatePhotoTransform(this.state);
1800
+ },
1801
+ onPhotoDragMove: () => this.view.updatePhotoTransform(this.state),
1802
+ onPhotoDragEnd: (result) => {
1803
+ if (result === "zoom-out") {
1804
+ this.zoomOutPhoto();
1805
+ return;
1806
+ }
1807
+ if (result === "prev") {
1808
+ this.gotoSlide(this.state.viewer.prev);
1809
+ return;
1810
+ }
1811
+ if (result === "next") {
1812
+ this.gotoSlide(this.state.viewer.next);
1813
+ return;
1814
+ }
1815
+ this.view.updatePhotoTransform(this.state);
1816
+ }
1817
+ };
1818
+ }
1819
+ };
1820
+
1821
+ // src/index.ts
1822
+ var src_default = SmartPhoto;
1823
+ module.exports = module.exports.default;