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