smartphoto 2.1.4 → 2.1.6
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.
- package/README.md +13 -5
- package/css/smartphoto.css +18 -18
- package/css/smartphoto.min.css +1 -1
- package/js/jquery-smartphoto.js +126 -20
- package/js/jquery-smartphoto.min.js +2 -2
- package/js/smartphoto.js +126 -20
- package/js/smartphoto.min.js +2 -2
- package/lib/smartphoto.js +124 -19
- package/lib/smartphoto.mjs +124 -19
- package/lib/types/core/index.d.ts +9 -5
- package/lib/types/core/state.d.ts +6 -6
- package/lib/types/core/types.d.ts +13 -11
- package/lib/types/core/view.d.ts +2 -2
- package/lib/types/index.d.ts +1 -0
- package/package.json +1 -1
package/lib/smartphoto.mjs
CHANGED
|
@@ -106,7 +106,7 @@ var defaults = {
|
|
|
106
106
|
loadOffset: 2,
|
|
107
107
|
resizeStyle: "fit",
|
|
108
108
|
lazyAttribute: "data-src",
|
|
109
|
-
animationSpeed:
|
|
109
|
+
animationSpeed: 450
|
|
110
110
|
};
|
|
111
111
|
function deepFreeze(obj) {
|
|
112
112
|
Object.keys(obj).forEach((key) => {
|
|
@@ -120,7 +120,11 @@ function deepFreeze(obj) {
|
|
|
120
120
|
function createState(settings) {
|
|
121
121
|
return {
|
|
122
122
|
options: deepFreeze(
|
|
123
|
-
extend(
|
|
123
|
+
extend(
|
|
124
|
+
{},
|
|
125
|
+
defaults,
|
|
126
|
+
settings
|
|
127
|
+
)
|
|
124
128
|
),
|
|
125
129
|
viewer: {
|
|
126
130
|
isOpen: false,
|
|
@@ -1120,6 +1124,44 @@ var SmartPhoto = class {
|
|
|
1120
1124
|
this.timeouts = [];
|
|
1121
1125
|
this.loadAllFired = /* @__PURE__ */ new Set();
|
|
1122
1126
|
this.syncedGroupId = null;
|
|
1127
|
+
// 文字列セレクタで構築された場合のみ設定される。Ajax等で後から追加された
|
|
1128
|
+
// 要素をクリック時に動的検出するための再スキャン起点(§discoverGroupElements)
|
|
1129
|
+
this.rootSelector = null;
|
|
1130
|
+
// クリックされた要素(またはその祖先)から登録済み Item を逆引きするための
|
|
1131
|
+
// キャッシュ。documentへのイベントデリゲーションで祖先チェーンを辿る際に使う
|
|
1132
|
+
this.itemsByElement = /* @__PURE__ */ new Map();
|
|
1133
|
+
// サムネイルクリックはこの1本の document デリゲーションリスナーだけで処理する
|
|
1134
|
+
// (個別バインドは廃止)。同じクリックイベントを複数の SmartPhoto インスタンスが
|
|
1135
|
+
// 二重処理しないよう、処理済みマーカーをイベント自体に立てて後続インスタンスに
|
|
1136
|
+
// 早期returnさせる(セレクタが重複するインスタンスが同時に存在するケース)
|
|
1137
|
+
this.handleDocumentClick = (e) => {
|
|
1138
|
+
if (!(e.target instanceof Element)) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const marker = e;
|
|
1142
|
+
if (marker.__smartphotoClaimed) {
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
let matched = this.findRegisteredAncestor(e.target);
|
|
1146
|
+
if (!matched && this.rootSelector) {
|
|
1147
|
+
matched = e.target.closest(this.rootSelector);
|
|
1148
|
+
}
|
|
1149
|
+
if (!matched) {
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
e.preventDefault();
|
|
1153
|
+
marker.__smartphotoClaimed = true;
|
|
1154
|
+
if (!this.itemsByElement.has(matched) && this.rootSelector) {
|
|
1155
|
+
const groupId = groupIdFromElement(matched);
|
|
1156
|
+
if (this.resyncGroupFromDom(groupId)) {
|
|
1157
|
+
this.syncCurrentGroupView();
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
const item = this.itemsByElement.get(matched);
|
|
1161
|
+
if (item) {
|
|
1162
|
+
this.openPhoto(item, matched);
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1123
1165
|
// ---- 内部: window イベント ----
|
|
1124
1166
|
this.updateViewportHeight = () => {
|
|
1125
1167
|
this.view.refs.dialog.style.setProperty(
|
|
@@ -1131,6 +1173,7 @@ var SmartPhoto = class {
|
|
|
1131
1173
|
if (!currentItems(this.state)) {
|
|
1132
1174
|
return;
|
|
1133
1175
|
}
|
|
1176
|
+
this.updateViewportHeight();
|
|
1134
1177
|
this.resetTranslateCurrent();
|
|
1135
1178
|
this.setPosByCurrentIndex();
|
|
1136
1179
|
this.setSizeByScreen();
|
|
@@ -1153,6 +1196,7 @@ var SmartPhoto = class {
|
|
|
1153
1196
|
if (!currentItems(this.state)) {
|
|
1154
1197
|
return;
|
|
1155
1198
|
}
|
|
1199
|
+
this.updateViewportHeight();
|
|
1156
1200
|
this.resetTranslateCurrent();
|
|
1157
1201
|
this.setPosByCurrentIndex();
|
|
1158
1202
|
this.setHashByCurrentIndex();
|
|
@@ -1163,6 +1207,7 @@ var SmartPhoto = class {
|
|
|
1163
1207
|
const poll = (time) => {
|
|
1164
1208
|
this.scheduleTimeout(() => {
|
|
1165
1209
|
if (prevWidth !== getWindowWidth()) {
|
|
1210
|
+
this.updateViewportHeight();
|
|
1166
1211
|
this.resetTranslateCurrent();
|
|
1167
1212
|
this.setPosByCurrentIndex();
|
|
1168
1213
|
this.setHashByCurrentIndex();
|
|
@@ -1175,6 +1220,7 @@ var SmartPhoto = class {
|
|
|
1175
1220
|
};
|
|
1176
1221
|
poll(0);
|
|
1177
1222
|
};
|
|
1223
|
+
this.rootSelector = typeof source === "string" ? source : null;
|
|
1178
1224
|
this.state = createState(settings ?? {});
|
|
1179
1225
|
this.view = createView(
|
|
1180
1226
|
{ id: this.id, options: this.state.options },
|
|
@@ -1196,15 +1242,14 @@ var SmartPhoto = class {
|
|
|
1196
1242
|
},
|
|
1197
1243
|
{ signal: this.abortController.signal }
|
|
1198
1244
|
);
|
|
1245
|
+
document.addEventListener("click", this.handleDocumentClick, {
|
|
1246
|
+
signal: this.abortController.signal
|
|
1247
|
+
});
|
|
1199
1248
|
this.ingestSource(source);
|
|
1200
1249
|
this.syncCurrentGroupView();
|
|
1201
1250
|
const restored = this.restoreFromHash();
|
|
1202
1251
|
if (restored) {
|
|
1203
|
-
|
|
1204
|
-
triggerEvent(restored.element, "click");
|
|
1205
|
-
} else {
|
|
1206
|
-
this.openPhoto(restored, null);
|
|
1207
|
-
}
|
|
1252
|
+
this.openPhoto(restored, restored.element);
|
|
1208
1253
|
}
|
|
1209
1254
|
this.updateViewportHeight();
|
|
1210
1255
|
if (window.visualViewport) {
|
|
@@ -1249,6 +1294,7 @@ var SmartPhoto = class {
|
|
|
1249
1294
|
clearTimeout(id);
|
|
1250
1295
|
});
|
|
1251
1296
|
this.timeouts = [];
|
|
1297
|
+
this.itemsByElement.clear();
|
|
1252
1298
|
this.gestures.detach();
|
|
1253
1299
|
this.view.destroy();
|
|
1254
1300
|
}
|
|
@@ -1361,7 +1407,7 @@ var SmartPhoto = class {
|
|
|
1361
1407
|
this.gotoSlide(this.state.viewer.prev);
|
|
1362
1408
|
}
|
|
1363
1409
|
addItem(slideOrElement) {
|
|
1364
|
-
const item = slideOrElement instanceof
|
|
1410
|
+
const item = slideOrElement instanceof HTMLElement ? this.addElementItem(slideOrElement) : this.addSlideItem(slideOrElement);
|
|
1365
1411
|
this.syncCurrentGroupView();
|
|
1366
1412
|
return item;
|
|
1367
1413
|
}
|
|
@@ -1392,7 +1438,7 @@ var SmartPhoto = class {
|
|
|
1392
1438
|
);
|
|
1393
1439
|
addItemToGroup(this.state, item);
|
|
1394
1440
|
this.loadAllFired.delete(groupId);
|
|
1395
|
-
this.
|
|
1441
|
+
this.itemsByElement.set(element, item);
|
|
1396
1442
|
return item;
|
|
1397
1443
|
}
|
|
1398
1444
|
addSlideItem(slide) {
|
|
@@ -1403,15 +1449,72 @@ var SmartPhoto = class {
|
|
|
1403
1449
|
this.loadAllFired.delete(groupId);
|
|
1404
1450
|
return item;
|
|
1405
1451
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1452
|
+
// クリックされた要素(またはその祖先。<a><img></a> の img がクリックされる
|
|
1453
|
+
// ケースを含む)から、登録済み Item を持つ要素まで祖先チェーンを遡って探す
|
|
1454
|
+
findRegisteredAncestor(target) {
|
|
1455
|
+
let node = target;
|
|
1456
|
+
while (node) {
|
|
1457
|
+
if (this.itemsByElement.has(node)) {
|
|
1458
|
+
return node;
|
|
1459
|
+
}
|
|
1460
|
+
node = node.parentElement;
|
|
1461
|
+
}
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
// rootSelector(文字列セレクタで構築した場合のみ設定される)を再スキャンし、
|
|
1465
|
+
// 指定グループを現在のDOM状態に合わせて丸ごと再構築する(追加・削除・並び順の
|
|
1466
|
+
// 変化を一度に反映)。既知要素は既存の Item オブジェクトをそのまま再利用する
|
|
1467
|
+
// ため loaded/width/height は保持され、DOM から消えた要素だけ itemsByElement
|
|
1468
|
+
// からも除去する。index/translateX は resetTranslate() で再計算する。
|
|
1469
|
+
// 呼び出し元(openPhoto)がこの結果を使い、変化があった場合のみ view を
|
|
1470
|
+
// 再同期する(§ダイアログを開く瞬間に整合を取る。開いている間の next()/prev()
|
|
1471
|
+
// では呼ばれないため、この間の追加/削除は次に開き直すまで反映されない)
|
|
1472
|
+
resyncGroupFromDom(groupId) {
|
|
1473
|
+
if (!this.rootSelector) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
const domElements = Array.from(
|
|
1477
|
+
document.querySelectorAll(this.rootSelector)
|
|
1478
|
+
).filter((el) => groupIdFromElement(el) === groupId);
|
|
1479
|
+
const domElementSet = new Set(domElements);
|
|
1480
|
+
const previous = this.state.groups.get(groupId) ?? [];
|
|
1481
|
+
const previousByElement = /* @__PURE__ */ new Map();
|
|
1482
|
+
previous.forEach((it) => {
|
|
1483
|
+
if (it.element) {
|
|
1484
|
+
previousByElement.set(it.element, it);
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
let changed = domElements.length !== previous.length;
|
|
1488
|
+
const rebuilt = domElements.map((el, index) => {
|
|
1489
|
+
const existing = previousByElement.get(el);
|
|
1490
|
+
if (existing) {
|
|
1491
|
+
if (existing.index !== index) {
|
|
1492
|
+
changed = true;
|
|
1493
|
+
existing.index = index;
|
|
1494
|
+
}
|
|
1495
|
+
return existing;
|
|
1496
|
+
}
|
|
1497
|
+
changed = true;
|
|
1498
|
+
const item = itemFromElement(
|
|
1499
|
+
el,
|
|
1500
|
+
this.state.options,
|
|
1501
|
+
index,
|
|
1502
|
+
getWindowWidth()
|
|
1503
|
+
);
|
|
1504
|
+
this.itemsByElement.set(el, item);
|
|
1505
|
+
return item;
|
|
1506
|
+
});
|
|
1507
|
+
previous.forEach((it) => {
|
|
1508
|
+
if (it.element && !domElementSet.has(it.element)) {
|
|
1509
|
+
this.itemsByElement.delete(it.element);
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
resetTranslate(rebuilt, getWindowWidth());
|
|
1513
|
+
this.state.groups.set(groupId, rebuilt);
|
|
1514
|
+
if (changed) {
|
|
1515
|
+
this.loadAllFired.delete(groupId);
|
|
1516
|
+
}
|
|
1517
|
+
return changed;
|
|
1415
1518
|
}
|
|
1416
1519
|
syncCurrentGroupView() {
|
|
1417
1520
|
const items = currentItems(this.state);
|
|
@@ -1665,10 +1768,11 @@ var SmartPhoto = class {
|
|
|
1665
1768
|
});
|
|
1666
1769
|
}
|
|
1667
1770
|
openPhoto(item, trigger) {
|
|
1771
|
+
const groupChanged = this.rootSelector ? this.resyncGroupFromDom(item.groupId) : false;
|
|
1668
1772
|
this.lastTriggerElement = trigger;
|
|
1669
1773
|
this.state.viewer.currentGroup = item.groupId;
|
|
1670
1774
|
this.state.viewer.currentIndex = item.index;
|
|
1671
|
-
if (this.syncedGroupId !== item.groupId) {
|
|
1775
|
+
if (this.syncedGroupId !== item.groupId || groupChanged) {
|
|
1672
1776
|
this.syncCurrentGroupView();
|
|
1673
1777
|
}
|
|
1674
1778
|
this.setHashByCurrentIndex();
|
|
@@ -1759,6 +1863,7 @@ var SmartPhoto = class {
|
|
|
1759
1863
|
this.state.viewer.photoPosX = 0;
|
|
1760
1864
|
this.state.viewer.photoPosY = 0;
|
|
1761
1865
|
this.state.viewer.onMove = true;
|
|
1866
|
+
this.updateViewportHeight();
|
|
1762
1867
|
this.setPosByCurrentIndex();
|
|
1763
1868
|
this.setHashByCurrentIndex();
|
|
1764
1869
|
this.setSizeByScreen();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ItemId, SlideData, SmartPhotoEvent, SmartPhotoItem, SmartPhotoOptions, SmartPhotoSource } from "./types";
|
|
2
2
|
interface ShowOptions {
|
|
3
3
|
group?: string;
|
|
4
4
|
trigger?: HTMLElement;
|
|
@@ -16,7 +16,9 @@ export default class SmartPhoto {
|
|
|
16
16
|
private timeouts;
|
|
17
17
|
private loadAllFired;
|
|
18
18
|
private syncedGroupId;
|
|
19
|
-
|
|
19
|
+
private rootSelector;
|
|
20
|
+
private readonly itemsByElement;
|
|
21
|
+
constructor(source: SmartPhotoSource, settings?: SmartPhotoOptions);
|
|
20
22
|
on(event: SmartPhotoEvent, fn: (this: HTMLDialogElement, ev: Event) => void): void;
|
|
21
23
|
destroy(): void;
|
|
22
24
|
[Symbol.dispose](): void;
|
|
@@ -24,17 +26,19 @@ export default class SmartPhoto {
|
|
|
24
26
|
hidePhoto(dir?: "top" | "bottom"): void;
|
|
25
27
|
zoomPhoto(): void;
|
|
26
28
|
zoomOutPhoto(): void;
|
|
27
|
-
addNewItem(element: HTMLElement):
|
|
29
|
+
addNewItem(element: HTMLElement): SmartPhotoItem;
|
|
28
30
|
show(indexOrId?: ItemId, opts?: ShowOptions): void;
|
|
29
31
|
hide(): void;
|
|
30
32
|
next(): void;
|
|
31
33
|
prev(): void;
|
|
32
|
-
addItem(slideOrElement:
|
|
34
|
+
addItem(slideOrElement: SlideData | HTMLElement): SmartPhotoItem;
|
|
33
35
|
get currentIndex(): number;
|
|
34
36
|
private ingestSource;
|
|
35
37
|
private addElementItem;
|
|
36
38
|
private addSlideItem;
|
|
37
|
-
private
|
|
39
|
+
private findRegisteredAncestor;
|
|
40
|
+
private resyncGroupFromDom;
|
|
41
|
+
private handleDocumentClick;
|
|
38
42
|
private syncCurrentGroupView;
|
|
39
43
|
private setHash;
|
|
40
44
|
private setHashByCurrentIndex;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { Bound, Item,
|
|
2
|
-
export declare const defaults:
|
|
3
|
-
export declare function createState(settings:
|
|
1
|
+
import type { Bound, Item, ResolvedSmartPhotoOptions, SlideData, SmartPhotoOptions, State, ViewerState } from "./types";
|
|
2
|
+
export declare const defaults: ResolvedSmartPhotoOptions;
|
|
3
|
+
export declare function createState(settings: SmartPhotoOptions): State;
|
|
4
4
|
export declare function groupIdFromElement(element: Element): string;
|
|
5
|
-
export declare function groupIdFromSlide(slide:
|
|
6
|
-
export declare function itemFromElement(element: Element, options: Pick<
|
|
7
|
-
export declare function itemFromSlide(slide:
|
|
5
|
+
export declare function groupIdFromSlide(slide: SlideData): string;
|
|
6
|
+
export declare function itemFromElement(element: Element, options: Pick<ResolvedSmartPhotoOptions, "lazyAttribute">, index: number, winWidth: number): Item;
|
|
7
|
+
export declare function itemFromSlide(slide: SlideData, index: number, winWidth: number): Item;
|
|
8
8
|
export declare function addItemToGroup(state: State, item: Item): void;
|
|
9
9
|
export declare function currentItems(state: State): Item[] | null;
|
|
10
10
|
export declare function currentItem(state: State): Item | null;
|
|
@@ -31,7 +31,7 @@ export interface Messages {
|
|
|
31
31
|
closeDialog: string;
|
|
32
32
|
carouselLabel: string;
|
|
33
33
|
}
|
|
34
|
-
export interface
|
|
34
|
+
export interface ResolvedSmartPhotoOptions {
|
|
35
35
|
classNames: ClassNames;
|
|
36
36
|
message: Messages;
|
|
37
37
|
arrows: boolean;
|
|
@@ -53,29 +53,31 @@ export interface SmartPhotoOptions {
|
|
|
53
53
|
lazyAttribute: string;
|
|
54
54
|
animationSpeed: number;
|
|
55
55
|
}
|
|
56
|
-
export type
|
|
56
|
+
export type SmartPhotoOptions = Partial<Omit<ResolvedSmartPhotoOptions, "classNames" | "message">> & {
|
|
57
57
|
classNames?: Partial<ClassNames>;
|
|
58
58
|
message?: Partial<Messages>;
|
|
59
59
|
};
|
|
60
60
|
export type ItemId = string | number;
|
|
61
|
-
export interface
|
|
61
|
+
export interface SmartPhotoItem {
|
|
62
62
|
src: string | null;
|
|
63
63
|
thumb: string | null;
|
|
64
64
|
caption: string | null;
|
|
65
65
|
alt: string | null;
|
|
66
66
|
groupId: string;
|
|
67
|
-
translateX: number;
|
|
68
|
-
translateY: number;
|
|
69
67
|
index: number;
|
|
70
68
|
width: number;
|
|
71
69
|
height: number;
|
|
70
|
+
id: ItemId;
|
|
71
|
+
loaded: boolean;
|
|
72
|
+
element: HTMLElement | null;
|
|
73
|
+
}
|
|
74
|
+
export interface Item extends SmartPhotoItem {
|
|
75
|
+
translateX: number;
|
|
76
|
+
translateY: number;
|
|
72
77
|
scale: number;
|
|
73
78
|
x: number;
|
|
74
79
|
y: number;
|
|
75
|
-
id: ItemId;
|
|
76
|
-
loaded: boolean;
|
|
77
80
|
processed: boolean;
|
|
78
|
-
element: HTMLElement | null;
|
|
79
81
|
}
|
|
80
82
|
export interface AppearEffect {
|
|
81
83
|
width: number;
|
|
@@ -111,7 +113,7 @@ export interface ViewerState {
|
|
|
111
113
|
showNextArrow: boolean;
|
|
112
114
|
}
|
|
113
115
|
export interface State {
|
|
114
|
-
options:
|
|
116
|
+
options: ResolvedSmartPhotoOptions;
|
|
115
117
|
viewer: ViewerState;
|
|
116
118
|
groups: Map<string, Item[]>;
|
|
117
119
|
}
|
|
@@ -121,7 +123,7 @@ export interface Bound {
|
|
|
121
123
|
maxX: number;
|
|
122
124
|
maxY: number;
|
|
123
125
|
}
|
|
124
|
-
export interface
|
|
126
|
+
export interface SlideData {
|
|
125
127
|
src: string;
|
|
126
128
|
thumb?: string;
|
|
127
129
|
caption?: string;
|
|
@@ -131,7 +133,7 @@ export interface Slide {
|
|
|
131
133
|
width?: number;
|
|
132
134
|
height?: number;
|
|
133
135
|
}
|
|
134
|
-
export type SmartPhotoSource = string | NodeListOf<Element> | Element[] |
|
|
136
|
+
export type SmartPhotoSource = string | NodeListOf<Element> | Element[] | SlideData[];
|
|
135
137
|
export type SmartPhotoEvent = "open" | "close" | "change" | "swipestart" | "swipeend" | "zoomin" | "zoomout" | "gesturestart" | "gestureend" | "loadall";
|
|
136
138
|
export type SwipeEndResult = "prev" | "next" | "close-top" | "close-bottom" | "stay";
|
|
137
139
|
export type PhotoDragEndResult = "prev" | "next" | "zoom-out" | null;
|
package/lib/types/core/view.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppearEffect, Item,
|
|
1
|
+
import type { AppearEffect, Item, ResolvedSmartPhotoOptions, State } from "./types";
|
|
2
2
|
interface SlideRefs {
|
|
3
3
|
li: HTMLLIElement;
|
|
4
4
|
loaderWrap: HTMLDivElement | null;
|
|
@@ -41,7 +41,7 @@ export interface View {
|
|
|
41
41
|
}
|
|
42
42
|
export declare function createView({ id, options }: {
|
|
43
43
|
id: string;
|
|
44
|
-
options:
|
|
44
|
+
options: ResolvedSmartPhotoOptions;
|
|
45
45
|
}, handlers: ViewHandlers, { signal }: {
|
|
46
46
|
signal: AbortSignal;
|
|
47
47
|
}): View;
|
package/lib/types/index.d.ts
CHANGED