@michaelyagi/shoji 0.1.0-beta.16 → 0.1.0-beta.18
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 +97 -43
- 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 +1 -0
- package/dist/esm/core/SlideManager.d.ts +11 -2
- package/dist/esm/core/index.js +67 -26
- package/dist/esm/core/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/plugins/rotateFlip/index.js +1 -1
- package/dist/esm/plugins/zoom/index.js +1 -1
- package/dist/esm/{zoomTransition-C_xGL1Hq.js → zoomTransition-UoldKrgd.js} +34 -21
- package/dist/esm/zoomTransition-UoldKrgd.js.map +1 -0
- 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 +98 -44
- 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-C_xGL1Hq.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");
|
|
@@ -550,26 +563,26 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
550
563
|
const rect = child.getBoundingClientRect();
|
|
551
564
|
return rect.width > 0 && rect.height > 0;
|
|
552
565
|
}
|
|
553
|
-
function computeTransformOutcome(origin, target, aspectRatio, naturalSize) {
|
|
566
|
+
function computeTransformOutcome(origin, target, direction, aspectRatio, naturalSize) {
|
|
554
567
|
const originRect = effectiveOriginBox(origin);
|
|
555
568
|
const targetRect = effectiveTargetBox(target, aspectRatio, naturalSize);
|
|
569
|
+
const pivotRect = target.getBoundingClientRect();
|
|
556
570
|
if (targetRect.width === 0 || targetRect.height === 0 || originRect.width === 0 || originRect.height === 0) {
|
|
557
571
|
return { kind: "none" };
|
|
558
572
|
}
|
|
559
|
-
const transform = transformBetween(targetRect, originRect);
|
|
560
573
|
const unknownTargetSize = !naturalSize && !hasRealTargetContent(target);
|
|
561
574
|
if (unknownTargetSize) {
|
|
562
|
-
return { kind: "fade", transform };
|
|
575
|
+
return { kind: "fade", transform: transformBetween(pivotRect, targetRect, originRect) };
|
|
563
576
|
}
|
|
564
577
|
const originRatio = originRect.width / originRect.height;
|
|
565
578
|
const targetRatio = targetRect.width / targetRect.height;
|
|
566
579
|
const ratioOfRatios = Math.max(originRatio, targetRatio) / Math.min(originRatio, targetRatio);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
return { kind: "zoom", transform };
|
|
580
|
+
const isMismatch = direction === "out" && ratioOfRatios > ASPECT_MISMATCH_THRESHOLD;
|
|
581
|
+
const transform = direction === "out" ? transformBetweenStretch(pivotRect, targetRect, originRect) : transformBetween(pivotRect, targetRect, originRect);
|
|
582
|
+
return isMismatch ? { kind: "fade", transform } : { kind: "zoom", transform };
|
|
571
583
|
}
|
|
572
|
-
const
|
|
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))";
|
|
573
586
|
function waitForTransitionEnd(target, cb, property = "transform") {
|
|
574
587
|
const durationMs = parseCssTime$1(getComputedStyle(target).transitionDuration);
|
|
575
588
|
let done = false;
|
|
@@ -594,7 +607,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
594
607
|
}
|
|
595
608
|
function zoomIn({ origin, target, aspectRatio, naturalSize }) {
|
|
596
609
|
if (prefersReducedMotion$1()) return;
|
|
597
|
-
const outcome = computeTransformOutcome(origin, target, aspectRatio, naturalSize);
|
|
610
|
+
const outcome = computeTransformOutcome(origin, target, "in", aspectRatio, naturalSize);
|
|
598
611
|
if (outcome.kind === "none") return;
|
|
599
612
|
const { transform } = outcome;
|
|
600
613
|
const isFade = outcome.kind === "fade";
|
|
@@ -604,7 +617,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
604
617
|
target.style.transform = transform;
|
|
605
618
|
if (isFade) target.style.opacity = "0";
|
|
606
619
|
void target.offsetHeight;
|
|
607
|
-
target.style.transition = isFade ?
|
|
620
|
+
target.style.transition = isFade ? FADE_IN_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
|
|
608
621
|
target.style.transform = "none";
|
|
609
622
|
if (isFade) target.style.opacity = "1";
|
|
610
623
|
waitForTransitionEnd(target, () => clearInlineTransform$1(target, "none"));
|
|
@@ -614,7 +627,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
614
627
|
onComplete();
|
|
615
628
|
return;
|
|
616
629
|
}
|
|
617
|
-
const outcome = computeTransformOutcome(origin, target, aspectRatio, naturalSize);
|
|
630
|
+
const outcome = computeTransformOutcome(origin, target, "out", aspectRatio, naturalSize);
|
|
618
631
|
if (outcome.kind === "none") {
|
|
619
632
|
onComplete();
|
|
620
633
|
return;
|
|
@@ -632,13 +645,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
632
645
|
const targetRect = effectiveTargetBox(target, aspectRatio, naturalSize);
|
|
633
646
|
if (targetRect.width > 0 && targetRect.height > 0) {
|
|
634
647
|
const existing = target.style.transform;
|
|
635
|
-
const jump = transformBetween(targetRect, zoomStart);
|
|
648
|
+
const jump = transformBetween(target.getBoundingClientRect(), targetRect, zoomStart);
|
|
636
649
|
target.style.transition = "none";
|
|
637
650
|
target.style.transform = existing && existing !== "none" ? `${existing} ${jump}` : jump;
|
|
638
651
|
void target.offsetHeight;
|
|
639
652
|
}
|
|
640
653
|
}
|
|
641
|
-
target.style.transition = isFade ?
|
|
654
|
+
target.style.transition = isFade ? FADE_OUT_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
|
|
642
655
|
void target.offsetHeight;
|
|
643
656
|
target.style.transform = transform;
|
|
644
657
|
if (isFade) target.style.opacity = "0";
|
|
@@ -1149,8 +1162,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1149
1162
|
slot.root.style.transform = `translateX(calc(${slot.offset * 100}% + ${this.dragOffsetPx}px))`;
|
|
1150
1163
|
}
|
|
1151
1164
|
}
|
|
1152
|
-
/** 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. */
|
|
1153
|
-
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) {
|
|
1154
1167
|
const canWrap = loop && items.length > this.slots.length;
|
|
1155
1168
|
const targets = this.slots.map((_, i) => {
|
|
1156
1169
|
const offset = i - this.preload;
|
|
@@ -1200,7 +1213,16 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1200
1213
|
this.applyAspect(slot, item);
|
|
1201
1214
|
if (index === centerIndex && openPlaceholderSrc) {
|
|
1202
1215
|
const naturalSize = item.width && item.height ? { width: item.width, height: item.height } : void 0;
|
|
1203
|
-
|
|
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
|
+
);
|
|
1204
1226
|
}
|
|
1205
1227
|
if (((_a = item.video) == null ? void 0 : _a.provider) === "html5") {
|
|
1206
1228
|
this.renderVideo(item, slot, index, onLoad);
|
|
@@ -1301,25 +1323,41 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
1301
1323
|
* (`.shoji-slide`) fixes it: same box, only ever `translateX`'d for
|
|
1302
1324
|
* pool-offset positioning, never scaled — a stable read regardless of
|
|
1303
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).
|
|
1304
1335
|
*/
|
|
1305
|
-
revealOpenPlaceholder(src, slot, index, naturalSize) {
|
|
1336
|
+
revealOpenPlaceholder(src, slot, index, naturalSize, aspectRatio, onReady, alreadyVisible = false) {
|
|
1306
1337
|
const img = createOpenPlaceholder(src);
|
|
1307
1338
|
const reveal = () => {
|
|
1308
|
-
if (slot.assignedIndex !== index
|
|
1339
|
+
if (slot.assignedIndex !== index) return;
|
|
1340
|
+
if (slot.ready) {
|
|
1341
|
+
onReady == null ? void 0 : onReady();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1309
1344
|
slot.media.querySelectorAll(".shoji-slide-spinner, .shoji-slide-provider-poster").forEach((el) => el.remove());
|
|
1310
1345
|
slot.media.appendChild(img);
|
|
1311
|
-
if (
|
|
1346
|
+
if (aspectRatio) {
|
|
1312
1347
|
const containerRect = slot.root.getBoundingClientRect();
|
|
1313
1348
|
const box = containedBox(
|
|
1314
1349
|
{ left: 0, top: 0, width: containerRect.width, height: containerRect.height },
|
|
1315
|
-
|
|
1350
|
+
aspectRatio,
|
|
1316
1351
|
naturalSize
|
|
1317
1352
|
);
|
|
1318
1353
|
img.style.width = `${box.width}px`;
|
|
1319
1354
|
img.style.height = `${box.height}px`;
|
|
1320
1355
|
}
|
|
1356
|
+
onReady == null ? void 0 : onReady();
|
|
1321
1357
|
};
|
|
1322
|
-
if (
|
|
1358
|
+
if (alreadyVisible) {
|
|
1359
|
+
queueMicrotask(reveal);
|
|
1360
|
+
} else if (typeof img.decode === "function") {
|
|
1323
1361
|
img.decode().then(reveal, reveal);
|
|
1324
1362
|
} else {
|
|
1325
1363
|
img.addEventListener("load", reveal, { once: true });
|
|
@@ -2927,7 +2965,8 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2927
2965
|
this.onActivity();
|
|
2928
2966
|
this.bus.emit("captionModalChange", { open: false });
|
|
2929
2967
|
}
|
|
2930
|
-
|
|
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) {
|
|
2931
2970
|
if (!this.slides || !this.dom) return;
|
|
2932
2971
|
const dom = this.dom;
|
|
2933
2972
|
this.slides.render(
|
|
@@ -2938,10 +2977,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2938
2977
|
this.bus.emit("slideItemLoad", { index: loadedIndex });
|
|
2939
2978
|
this.setSlideLoading(false);
|
|
2940
2979
|
if (!this.captionFadePending) this.updateCaptionVisibility();
|
|
2980
|
+
onCenterContentVisible == null ? void 0 : onCenterContentVisible();
|
|
2941
2981
|
}
|
|
2942
2982
|
},
|
|
2943
2983
|
openPlaceholderSrc,
|
|
2944
|
-
this.loop
|
|
2984
|
+
this.loop,
|
|
2985
|
+
onCenterContentVisible,
|
|
2986
|
+
openPlaceholderAlreadyVisible
|
|
2945
2987
|
);
|
|
2946
2988
|
this.setSlideLoading(!this.slides.isActiveReady());
|
|
2947
2989
|
const item = this.itemList[this.activeIndex];
|
|
@@ -2972,27 +3014,39 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
2972
3014
|
this.captionFadePending = true;
|
|
2973
3015
|
this.dom.caption.style.opacity = "0";
|
|
2974
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
|
+
};
|
|
2975
3040
|
this.renderCurrentSlide(
|
|
2976
|
-
|
|
3041
|
+
openPlaceholderSrc,
|
|
3042
|
+
openPlaceholderSrc ? beginZoom : void 0,
|
|
3043
|
+
openPlaceholderAlreadyVisible
|
|
2977
3044
|
);
|
|
2978
3045
|
this.dom.outer.classList.add("shoji-open");
|
|
2979
3046
|
document.addEventListener("keydown", this.onKeydown);
|
|
2980
3047
|
this.focusTrap.activate(this.dom.dialog);
|
|
2981
3048
|
this.scheduleAutoHide();
|
|
2982
|
-
|
|
2983
|
-
if (media && origin) {
|
|
2984
|
-
zoomIn({
|
|
2985
|
-
origin,
|
|
2986
|
-
target: media,
|
|
2987
|
-
aspectRatio: this.resolveAspectRatio(index, origin),
|
|
2988
|
-
naturalSize
|
|
2989
|
-
});
|
|
2990
|
-
}
|
|
2991
|
-
if (fadeInOnOpen && this.dom) {
|
|
2992
|
-
this.captionFadePending = false;
|
|
2993
|
-
this.updateCaptionVisibility();
|
|
2994
|
-
this.dom.caption.style.opacity = "";
|
|
2995
|
-
}
|
|
3049
|
+
if (!openPlaceholderSrc || ((_b = this.slides) == null ? void 0 : _b.isActiveReady())) beginZoom();
|
|
2996
3050
|
this.bus.emit("open", { index });
|
|
2997
3051
|
this.bus.emit("afterOpen", { index });
|
|
2998
3052
|
}
|