@michaelyagi/shoji 0.1.0-beta.15 → 0.1.0-beta.17
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/dist/core/shoji-core.js +144 -54
- package/dist/core/shoji-core.js.map +1 -1
- package/dist/core/shoji-core.min.js +1 -1
- package/dist/core/shoji-core.min.js.map +1 -1
- package/dist/esm/core/Gallery.d.ts +3 -10
- package/dist/esm/core/SlideManager.d.ts +11 -2
- package/dist/esm/core/index.js +88 -38
- package/dist/esm/core/index.js.map +1 -1
- package/dist/esm/core/types.d.ts +18 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/plugins/layout/index.js +3 -0
- package/dist/esm/plugins/layout/index.js.map +1 -1
- package/dist/esm/plugins/rotateFlip/index.js +1 -1
- package/dist/esm/plugins/zoom/index.js +1 -1
- package/dist/esm/{zoomTransition-CQG__VzO.js → zoomTransition-DZlhPOl8.js} +60 -20
- package/dist/esm/zoomTransition-DZlhPOl8.js.map +1 -0
- package/dist/plugins/layout.js +3 -0
- package/dist/plugins/layout.js.map +1 -1
- package/dist/plugins/layout.min.js +1 -1
- package/dist/plugins/layout.min.js.map +1 -1
- package/dist/plugins/rotateFlip.js.map +1 -1
- package/dist/plugins/rotateFlip.min.js.map +1 -1
- package/dist/plugins/zoom.js.map +1 -1
- package/dist/plugins/zoom.min.js.map +1 -1
- package/dist/shoji.js +149 -56
- package/dist/shoji.js.map +1 -1
- package/dist/shoji.min.js +1 -1
- package/dist/shoji.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/esm/zoomTransition-CQG__VzO.js.map +0 -1
package/dist/core/shoji-core.js
CHANGED
|
@@ -519,7 +519,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
519
519
|
const child = target.firstElementChild;
|
|
520
520
|
const isPlaceholder = child instanceof HTMLElement && (child.classList.contains("shoji-slide-spinner") || child.classList.contains("shoji-slide-open-placeholder"));
|
|
521
521
|
if (child instanceof HTMLElement && !isPlaceholder) {
|
|
522
|
-
const
|
|
522
|
+
const measureTarget = child.classList.contains("shoji-slide-provider-video") ? child.querySelector(".shoji-video-mount, iframe") ?? child : child;
|
|
523
|
+
const rect = measureTarget.getBoundingClientRect();
|
|
523
524
|
if (rect.width > 0 && rect.height > 0) return rect;
|
|
524
525
|
}
|
|
525
526
|
const containerRect = target.getBoundingClientRect();
|
|
@@ -528,11 +529,23 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
528
529
|
}
|
|
529
530
|
return containerRect;
|
|
530
531
|
}
|
|
531
|
-
function transformBetween(from, to) {
|
|
532
|
+
function transformBetween(pivot, from, to) {
|
|
532
533
|
const scale = Math.min(to.width / from.width, to.height / from.height);
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
534
|
+
return transformString(pivot, from, to, scale, scale);
|
|
535
|
+
}
|
|
536
|
+
function transformBetweenStretch(pivot, from, to) {
|
|
537
|
+
return transformString(pivot, from, to, to.width / from.width, to.height / from.height);
|
|
538
|
+
}
|
|
539
|
+
function transformString(pivot, from, to, scaleX, scaleY) {
|
|
540
|
+
const pivotX = pivot.left + pivot.width / 2;
|
|
541
|
+
const pivotY = pivot.top + pivot.height / 2;
|
|
542
|
+
const fromX = from.left + from.width / 2;
|
|
543
|
+
const fromY = from.top + from.height / 2;
|
|
544
|
+
const toX = to.left + to.width / 2;
|
|
545
|
+
const toY = to.top + to.height / 2;
|
|
546
|
+
const translateX = toX - pivotX - scaleX * (fromX - pivotX);
|
|
547
|
+
const translateY = toY - pivotY - scaleY * (fromY - pivotY);
|
|
548
|
+
return `translate3d(${translateX}px, ${translateY}px, 0) scale3d(${scaleX}, ${scaleY}, 1)`;
|
|
536
549
|
}
|
|
537
550
|
function effectiveOriginBox(origin) {
|
|
538
551
|
const img = origin.querySelector("img");
|
|
@@ -542,14 +555,34 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
542
555
|
}
|
|
543
556
|
return origin.getBoundingClientRect();
|
|
544
557
|
}
|
|
545
|
-
|
|
558
|
+
const ASPECT_MISMATCH_THRESHOLD = 2;
|
|
559
|
+
function hasRealTargetContent(target) {
|
|
560
|
+
const child = target.firstElementChild;
|
|
561
|
+
const isPlaceholder = child instanceof HTMLElement && (child.classList.contains("shoji-slide-spinner") || child.classList.contains("shoji-slide-open-placeholder"));
|
|
562
|
+
if (!(child instanceof HTMLElement) || isPlaceholder) return false;
|
|
563
|
+
const rect = child.getBoundingClientRect();
|
|
564
|
+
return rect.width > 0 && rect.height > 0;
|
|
565
|
+
}
|
|
566
|
+
function computeTransformOutcome(origin, target, direction, aspectRatio, naturalSize) {
|
|
546
567
|
const originRect = effectiveOriginBox(origin);
|
|
547
568
|
const targetRect = effectiveTargetBox(target, aspectRatio, naturalSize);
|
|
569
|
+
const pivotRect = target.getBoundingClientRect();
|
|
548
570
|
if (targetRect.width === 0 || targetRect.height === 0 || originRect.width === 0 || originRect.height === 0) {
|
|
549
|
-
return
|
|
571
|
+
return { kind: "none" };
|
|
572
|
+
}
|
|
573
|
+
const unknownTargetSize = !naturalSize && !hasRealTargetContent(target);
|
|
574
|
+
if (unknownTargetSize) {
|
|
575
|
+
return { kind: "fade", transform: transformBetween(pivotRect, targetRect, originRect) };
|
|
550
576
|
}
|
|
551
|
-
|
|
577
|
+
const originRatio = originRect.width / originRect.height;
|
|
578
|
+
const targetRatio = targetRect.width / targetRect.height;
|
|
579
|
+
const ratioOfRatios = Math.max(originRatio, targetRatio) / Math.min(originRatio, targetRatio);
|
|
580
|
+
const isMismatch = direction === "out" && ratioOfRatios > ASPECT_MISMATCH_THRESHOLD;
|
|
581
|
+
const transform = isMismatch ? transformBetweenStretch(pivotRect, targetRect, originRect) : transformBetween(pivotRect, targetRect, originRect);
|
|
582
|
+
return isMismatch ? { kind: "fade", transform } : { kind: "zoom", transform };
|
|
552
583
|
}
|
|
584
|
+
const FADE_IN_TRANSITION = "transform var(--shoji-duration) var(--shoji-easing), opacity var(--shoji-fade-duration, 120ms) var(--shoji-easing)";
|
|
585
|
+
const FADE_OUT_TRANSITION = "transform var(--shoji-duration) var(--shoji-easing), opacity var(--shoji-fade-duration, 120ms) var(--shoji-easing) calc(var(--shoji-duration) - var(--shoji-fade-duration, 120ms))";
|
|
553
586
|
function waitForTransitionEnd(target, cb, property = "transform") {
|
|
554
587
|
const durationMs = parseCssTime$1(getComputedStyle(target).transitionDuration);
|
|
555
588
|
let done = false;
|
|
@@ -574,15 +607,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
574
607
|
}
|
|
575
608
|
function zoomIn({ origin, target, aspectRatio, naturalSize }) {
|
|
576
609
|
if (prefersReducedMotion$1()) return;
|
|
577
|
-
const
|
|
578
|
-
if (
|
|
610
|
+
const outcome = computeTransformOutcome(origin, target, "in", aspectRatio, naturalSize);
|
|
611
|
+
if (outcome.kind === "none") return;
|
|
612
|
+
const { transform } = outcome;
|
|
613
|
+
const isFade = outcome.kind === "fade";
|
|
579
614
|
target.style.transition = "none";
|
|
580
615
|
target.style.transformOrigin = "center";
|
|
581
|
-
target.style.willChange = "transform";
|
|
616
|
+
target.style.willChange = isFade ? "transform, opacity" : "transform";
|
|
582
617
|
target.style.transform = transform;
|
|
618
|
+
if (isFade) target.style.opacity = "0";
|
|
583
619
|
void target.offsetHeight;
|
|
584
|
-
target.style.transition = "transform var(--shoji-duration) var(--shoji-easing)";
|
|
620
|
+
target.style.transition = isFade ? FADE_IN_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
|
|
585
621
|
target.style.transform = "none";
|
|
622
|
+
if (isFade) target.style.opacity = "1";
|
|
586
623
|
waitForTransitionEnd(target, () => clearInlineTransform$1(target, "none"));
|
|
587
624
|
}
|
|
588
625
|
function zoomOut({ origin, target, aspectRatio, naturalSize, dragStart, zoomStart }, onComplete) {
|
|
@@ -590,13 +627,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
590
627
|
onComplete();
|
|
591
628
|
return;
|
|
592
629
|
}
|
|
593
|
-
const
|
|
594
|
-
if (
|
|
630
|
+
const outcome = computeTransformOutcome(origin, target, "out", aspectRatio, naturalSize);
|
|
631
|
+
if (outcome.kind === "none") {
|
|
595
632
|
onComplete();
|
|
596
633
|
return;
|
|
597
634
|
}
|
|
635
|
+
const { transform } = outcome;
|
|
636
|
+
const isFade = outcome.kind === "fade";
|
|
598
637
|
target.style.transformOrigin = "center";
|
|
599
|
-
target.style.willChange = "transform";
|
|
638
|
+
target.style.willChange = isFade ? "transform, opacity" : "transform";
|
|
600
639
|
if (dragStart) {
|
|
601
640
|
target.style.transition = "none";
|
|
602
641
|
target.style.transform = `translate3d(0px, ${dragStart.translateY}px, 0px) scale3d(${dragStart.scale}, ${dragStart.scale}, 1)`;
|
|
@@ -606,15 +645,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
606
645
|
const targetRect = effectiveTargetBox(target, aspectRatio, naturalSize);
|
|
607
646
|
if (targetRect.width > 0 && targetRect.height > 0) {
|
|
608
647
|
const existing = target.style.transform;
|
|
609
|
-
const jump = transformBetween(targetRect, zoomStart);
|
|
648
|
+
const jump = transformBetween(target.getBoundingClientRect(), targetRect, zoomStart);
|
|
610
649
|
target.style.transition = "none";
|
|
611
650
|
target.style.transform = existing && existing !== "none" ? `${existing} ${jump}` : jump;
|
|
612
651
|
void target.offsetHeight;
|
|
613
652
|
}
|
|
614
653
|
}
|
|
615
|
-
target.style.transition = "transform var(--shoji-duration) var(--shoji-easing)";
|
|
654
|
+
target.style.transition = isFade ? FADE_OUT_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
|
|
616
655
|
void target.offsetHeight;
|
|
617
656
|
target.style.transform = transform;
|
|
657
|
+
if (isFade) target.style.opacity = "0";
|
|
618
658
|
const appliedTransform = target.style.transform;
|
|
619
659
|
waitForTransitionEnd(target, () => {
|
|
620
660
|
clearInlineTransform$1(target, appliedTransform);
|
|
@@ -831,7 +871,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
831
871
|
const n = Number(raw);
|
|
832
872
|
return Number.isFinite(n) ? n : void 0;
|
|
833
873
|
}
|
|
834
|
-
const NO_OWN_DATA_FIELD = /* @__PURE__ */ new Set([
|
|
874
|
+
const NO_OWN_DATA_FIELD = /* @__PURE__ */ new Set([
|
|
875
|
+
"no-drag",
|
|
876
|
+
"video-id",
|
|
877
|
+
"video-provider",
|
|
878
|
+
"thumbnail-width",
|
|
879
|
+
"thumbnail-height"
|
|
880
|
+
]);
|
|
835
881
|
function applyCommon(element, item, src) {
|
|
836
882
|
const caption = attr(element, "data-shoji-caption");
|
|
837
883
|
if (caption) item.caption = caption;
|
|
@@ -857,6 +903,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
857
903
|
if (width !== void 0) item.width = width;
|
|
858
904
|
const height = numAttr(element, "data-shoji-height");
|
|
859
905
|
if (height !== void 0) item.height = height;
|
|
906
|
+
const thumbnailWidth = numAttr(element, "data-shoji-thumbnail-width");
|
|
907
|
+
if (thumbnailWidth !== void 0) item.thumbnailWidth = thumbnailWidth;
|
|
908
|
+
const thumbnailHeight = numAttr(element, "data-shoji-thumbnail-height");
|
|
909
|
+
if (thumbnailHeight !== void 0) item.thumbnailHeight = thumbnailHeight;
|
|
860
910
|
const srcset = attr(element, "data-shoji-srcset");
|
|
861
911
|
if (srcset) item.srcset = srcset;
|
|
862
912
|
const sizes = attr(element, "data-shoji-sizes");
|
|
@@ -1112,8 +1162,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1112
1162
|
slot.root.style.transform = `translateX(calc(${slot.offset * 100}% + ${this.dragOffsetPx}px))`;
|
|
1113
1163
|
}
|
|
1114
1164
|
}
|
|
1115
|
-
/** Re-renders whichever slots need a different item; `onLoad` fires per index once its media settles. `openPlaceholderSrc` (only from `Gallery.open()`) swaps the centerIndex slot's spinner for a low-res placeholder once it decodes, if not already ready. `loop` matches `Gallery.ts`'s own `nextIndex()`/`prevIndex()` wrap-around — without it, the pool slot just past the last (or before the first) item has no item to preload at all, so a boundary drag reveals nothing even though the completed navigation itself already wraps correctly. */
|
|
1116
|
-
render(items, centerIndex, onLoad, openPlaceholderSrc, loop = false) {
|
|
1165
|
+
/** Re-renders whichever slots need a different item; `onLoad` fires per index once its media settles. `openPlaceholderSrc` (only from `Gallery.open()`) swaps the centerIndex slot's spinner for a low-res placeholder once it decodes, if not already ready; `onOpenPlaceholderReady` fires once that swap happens (`Gallery.ts`'s `beginZoom` doc comment explains why). `openPlaceholderAlreadyVisible` (`Gallery.ts`'s own doc comment on it) skips that decode wait entirely when it's not actually needed. `loop` matches `Gallery.ts`'s own `nextIndex()`/`prevIndex()` wrap-around — without it, the pool slot just past the last (or before the first) item has no item to preload at all, so a boundary drag reveals nothing even though the completed navigation itself already wraps correctly. */
|
|
1166
|
+
render(items, centerIndex, onLoad, openPlaceholderSrc, loop = false, onOpenPlaceholderReady, openPlaceholderAlreadyVisible = false) {
|
|
1117
1167
|
const canWrap = loop && items.length > this.slots.length;
|
|
1118
1168
|
const targets = this.slots.map((_, i) => {
|
|
1119
1169
|
const offset = i - this.preload;
|
|
@@ -1163,7 +1213,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1163
1213
|
this.applyAspect(slot, item);
|
|
1164
1214
|
if (index === centerIndex && openPlaceholderSrc) {
|
|
1165
1215
|
const naturalSize = item.width && item.height ? { width: item.width, height: item.height } : void 0;
|
|
1166
|
-
|
|
1216
|
+
const aspectRatio = item.thumbnailWidth && item.thumbnailHeight ? item.thumbnailWidth / item.thumbnailHeight : naturalSize && naturalSize.width / naturalSize.height;
|
|
1217
|
+
this.revealOpenPlaceholder(
|
|
1218
|
+
openPlaceholderSrc,
|
|
1219
|
+
slot,
|
|
1220
|
+
index,
|
|
1221
|
+
naturalSize,
|
|
1222
|
+
aspectRatio,
|
|
1223
|
+
onOpenPlaceholderReady,
|
|
1224
|
+
openPlaceholderAlreadyVisible
|
|
1225
|
+
);
|
|
1167
1226
|
}
|
|
1168
1227
|
if (((_a = item.video) == null ? void 0 : _a.provider) === "html5") {
|
|
1169
1228
|
this.renderVideo(item, slot, index, onLoad);
|
|
@@ -1264,25 +1323,41 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1264
1323
|
* (`.shoji-slide`) fixes it: same box, only ever `translateX`'d for
|
|
1265
1324
|
* pool-offset positioning, never scaled — a stable read regardless of
|
|
1266
1325
|
* what its child is mid-animation through.
|
|
1326
|
+
*
|
|
1327
|
+
* A third real bug: `aspectRatio` (the box's *shape*) must come from the
|
|
1328
|
+
* thumbnail's own aspect (`item.thumbnailWidth`/`thumbnailHeight` when
|
|
1329
|
+
* declared, the caller's own doc comment on this call explains the
|
|
1330
|
+
* priority) — this image's actual pixels are the thumbnail's, and sizing
|
|
1331
|
+
* the box to `naturalSize`'s aspect (the real photo's) instead left most
|
|
1332
|
+
* of it transparent whenever the two differ, `object-fit: contain`
|
|
1333
|
+
* letterboxing the real thumbnail content down to a thin strip inside a
|
|
1334
|
+
* wrongly-shaped box (DESIGN.md §2.3b).
|
|
1267
1335
|
*/
|
|
1268
|
-
revealOpenPlaceholder(src, slot, index, naturalSize) {
|
|
1336
|
+
revealOpenPlaceholder(src, slot, index, naturalSize, aspectRatio, onReady, alreadyVisible = false) {
|
|
1269
1337
|
const img = createOpenPlaceholder(src);
|
|
1270
1338
|
const reveal = () => {
|
|
1271
|
-
if (slot.assignedIndex !== index
|
|
1339
|
+
if (slot.assignedIndex !== index) return;
|
|
1340
|
+
if (slot.ready) {
|
|
1341
|
+
onReady == null ? void 0 : onReady();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1272
1344
|
slot.media.querySelectorAll(".shoji-slide-spinner, .shoji-slide-provider-poster").forEach((el) => el.remove());
|
|
1273
1345
|
slot.media.appendChild(img);
|
|
1274
|
-
if (
|
|
1346
|
+
if (aspectRatio) {
|
|
1275
1347
|
const containerRect = slot.root.getBoundingClientRect();
|
|
1276
1348
|
const box = containedBox(
|
|
1277
1349
|
{ left: 0, top: 0, width: containerRect.width, height: containerRect.height },
|
|
1278
|
-
|
|
1350
|
+
aspectRatio,
|
|
1279
1351
|
naturalSize
|
|
1280
1352
|
);
|
|
1281
1353
|
img.style.width = `${box.width}px`;
|
|
1282
1354
|
img.style.height = `${box.height}px`;
|
|
1283
1355
|
}
|
|
1356
|
+
onReady == null ? void 0 : onReady();
|
|
1284
1357
|
};
|
|
1285
|
-
if (
|
|
1358
|
+
if (alreadyVisible) {
|
|
1359
|
+
queueMicrotask(reveal);
|
|
1360
|
+
} else if (typeof img.decode === "function") {
|
|
1286
1361
|
img.decode().then(reveal, reveal);
|
|
1287
1362
|
} else {
|
|
1288
1363
|
img.addEventListener("load", reveal, { once: true });
|
|
@@ -2890,7 +2965,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2890
2965
|
this.onActivity();
|
|
2891
2966
|
this.bus.emit("captionModalChange", { open: false });
|
|
2892
2967
|
}
|
|
2893
|
-
|
|
2968
|
+
/** `onCenterContentVisible` — only `open()` passes this, to start the zoom-in once something real is actually visible (its own `beginZoom` doc comment). */
|
|
2969
|
+
renderCurrentSlide(openPlaceholderSrc, onCenterContentVisible, openPlaceholderAlreadyVisible = false) {
|
|
2894
2970
|
if (!this.slides || !this.dom) return;
|
|
2895
2971
|
const dom = this.dom;
|
|
2896
2972
|
this.slides.render(
|
|
@@ -2901,10 +2977,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2901
2977
|
this.bus.emit("slideItemLoad", { index: loadedIndex });
|
|
2902
2978
|
this.setSlideLoading(false);
|
|
2903
2979
|
if (!this.captionFadePending) this.updateCaptionVisibility();
|
|
2980
|
+
onCenterContentVisible == null ? void 0 : onCenterContentVisible();
|
|
2904
2981
|
}
|
|
2905
2982
|
},
|
|
2906
2983
|
openPlaceholderSrc,
|
|
2907
|
-
this.loop
|
|
2984
|
+
this.loop,
|
|
2985
|
+
onCenterContentVisible,
|
|
2986
|
+
openPlaceholderAlreadyVisible
|
|
2908
2987
|
);
|
|
2909
2988
|
this.setSlideLoading(!this.slides.isActiveReady());
|
|
2910
2989
|
const item = this.itemList[this.activeIndex];
|
|
@@ -2935,27 +3014,39 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2935
3014
|
this.captionFadePending = true;
|
|
2936
3015
|
this.dom.caption.style.opacity = "0";
|
|
2937
3016
|
}
|
|
3017
|
+
const openPlaceholderSrc = naturalSize && !((_a = this.itemList[index]) == null ? void 0 : _a.video) ? this.resolveOpenPlaceholderSrc(this.itemList[index], origin) : void 0;
|
|
3018
|
+
const originImg = origin == null ? void 0 : origin.querySelector("img");
|
|
3019
|
+
const openPlaceholderAlreadyVisible = !!openPlaceholderSrc && !!originImg && (originImg.currentSrc || originImg.src) === openPlaceholderSrc;
|
|
3020
|
+
let zoomStarted = false;
|
|
3021
|
+
const beginZoom = () => {
|
|
3022
|
+
var _a2;
|
|
3023
|
+
if (zoomStarted) return;
|
|
3024
|
+
zoomStarted = true;
|
|
3025
|
+
const media = (_a2 = this.slides) == null ? void 0 : _a2.getActiveMedia();
|
|
3026
|
+
if (media && origin) {
|
|
3027
|
+
zoomIn({
|
|
3028
|
+
origin,
|
|
3029
|
+
target: media,
|
|
3030
|
+
aspectRatio: this.resolveAspectRatio(index, origin),
|
|
3031
|
+
naturalSize
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
if (fadeInOnOpen && this.dom) {
|
|
3035
|
+
this.captionFadePending = false;
|
|
3036
|
+
this.updateCaptionVisibility();
|
|
3037
|
+
this.dom.caption.style.opacity = "";
|
|
3038
|
+
}
|
|
3039
|
+
};
|
|
2938
3040
|
this.renderCurrentSlide(
|
|
2939
|
-
|
|
3041
|
+
openPlaceholderSrc,
|
|
3042
|
+
openPlaceholderSrc ? beginZoom : void 0,
|
|
3043
|
+
openPlaceholderAlreadyVisible
|
|
2940
3044
|
);
|
|
2941
3045
|
this.dom.outer.classList.add("shoji-open");
|
|
2942
3046
|
document.addEventListener("keydown", this.onKeydown);
|
|
2943
3047
|
this.focusTrap.activate(this.dom.dialog);
|
|
2944
3048
|
this.scheduleAutoHide();
|
|
2945
|
-
|
|
2946
|
-
if (media && origin && naturalSize) {
|
|
2947
|
-
zoomIn({
|
|
2948
|
-
origin,
|
|
2949
|
-
target: media,
|
|
2950
|
-
aspectRatio: this.resolveAspectRatio(index, origin),
|
|
2951
|
-
naturalSize
|
|
2952
|
-
});
|
|
2953
|
-
}
|
|
2954
|
-
if (fadeInOnOpen && this.dom) {
|
|
2955
|
-
this.captionFadePending = false;
|
|
2956
|
-
this.updateCaptionVisibility();
|
|
2957
|
-
this.dom.caption.style.opacity = "";
|
|
2958
|
-
}
|
|
3049
|
+
if (!openPlaceholderSrc || ((_b = this.slides) == null ? void 0 : _b.isActiveReady())) beginZoom();
|
|
2959
3050
|
this.bus.emit("open", { index });
|
|
2960
3051
|
this.bus.emit("afterOpen", { index });
|
|
2961
3052
|
}
|
|
@@ -3127,9 +3218,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3127
3218
|
(_a = this.dom) == null ? void 0 : _a.dialog.classList.toggle("shoji-controls-hidden-for-drag", hidden);
|
|
3128
3219
|
this.bus.emit("dragCloseThreshold", { hidden });
|
|
3129
3220
|
}
|
|
3130
|
-
/** `item.width`/`height
|
|
3221
|
+
/** `item.thumbnailWidth`/`thumbnailHeight` (the thumbnail's own true shape) first, else `item.width`/`height` (the photo's), else origin's live `naturalWidth`/`naturalHeight`. Shape only — see `resolveNaturalSize` for why the size cap below uses the opposite order. DESIGN.md §2.3b (sixteenth/seventeenth entries). */
|
|
3131
3222
|
resolveAspectRatio(index, origin) {
|
|
3132
3223
|
const item = this.itemList[index];
|
|
3224
|
+
if ((item == null ? void 0 : item.thumbnailWidth) && item.thumbnailHeight) {
|
|
3225
|
+
return item.thumbnailWidth / item.thumbnailHeight;
|
|
3226
|
+
}
|
|
3133
3227
|
if ((item == null ? void 0 : item.width) && item.height) return item.width / item.height;
|
|
3134
3228
|
const thumbImg = origin == null ? void 0 : origin.querySelector("img");
|
|
3135
3229
|
if ((thumbImg == null ? void 0 : thumbImg.naturalWidth) && thumbImg.naturalHeight) {
|
|
@@ -3137,18 +3231,14 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
3137
3231
|
}
|
|
3138
3232
|
return void 0;
|
|
3139
3233
|
}
|
|
3140
|
-
/**
|
|
3141
|
-
* `item.width`/`height` only — deliberately never a thumbnail's own
|
|
3142
|
-
* `naturalWidth`/`naturalHeight` the way `resolveAspectRatio` above will:
|
|
3143
|
-
* that's a fine stand-in for *shape*, but using it as the real photo's
|
|
3144
|
-
* true pixel size would under-cap a genuinely large photo down to
|
|
3145
|
-
* thumbnail resolution. `undefined` here just means "genuinely unknown,"
|
|
3146
|
-
* not "assume small" — `zoomTransition.ts`'s `containedBox` already
|
|
3147
|
-
* treats it that way (no cap applied at all).
|
|
3148
|
-
*/
|
|
3234
|
+
/** `item.width`/`height` (the real photo) first, `item.thumbnailWidth`/`thumbnailHeight` only once that's unknown too — opposite priority from `resolveAspectRatio` on purpose: this only caps the placeholder's *size*, never its shape, so preferring the real (usually larger) photo size is never a distortion risk, and is what lets the placeholder grow to a properly large size instead of the thumbnail's own tiny one. DESIGN.md §2.3b (seventeenth entry) has the real bug this fixed. */
|
|
3149
3235
|
resolveNaturalSize(index) {
|
|
3150
3236
|
const item = this.itemList[index];
|
|
3151
|
-
|
|
3237
|
+
if ((item == null ? void 0 : item.width) && item.height) return { width: item.width, height: item.height };
|
|
3238
|
+
if ((item == null ? void 0 : item.thumbnailWidth) && item.thumbnailHeight) {
|
|
3239
|
+
return { width: item.thumbnailWidth, height: item.thumbnailHeight };
|
|
3240
|
+
}
|
|
3241
|
+
return void 0;
|
|
3152
3242
|
}
|
|
3153
3243
|
/**
|
|
3154
3244
|
* Idempotent on purpose: a pending zoom-out's `transitionend`/fallback
|