@michaelyagi/shoji 0.1.0-beta.14 → 0.1.0-beta.16

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.
@@ -542,14 +542,34 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
542
542
  }
543
543
  return origin.getBoundingClientRect();
544
544
  }
545
- function computeTransform(origin, target, aspectRatio, naturalSize) {
545
+ const ASPECT_MISMATCH_THRESHOLD = 2;
546
+ function hasRealTargetContent(target) {
547
+ const child = target.firstElementChild;
548
+ const isPlaceholder = child instanceof HTMLElement && (child.classList.contains("shoji-slide-spinner") || child.classList.contains("shoji-slide-open-placeholder"));
549
+ if (!(child instanceof HTMLElement) || isPlaceholder) return false;
550
+ const rect = child.getBoundingClientRect();
551
+ return rect.width > 0 && rect.height > 0;
552
+ }
553
+ function computeTransformOutcome(origin, target, aspectRatio, naturalSize) {
546
554
  const originRect = effectiveOriginBox(origin);
547
555
  const targetRect = effectiveTargetBox(target, aspectRatio, naturalSize);
548
556
  if (targetRect.width === 0 || targetRect.height === 0 || originRect.width === 0 || originRect.height === 0) {
549
- return null;
557
+ return { kind: "none" };
558
+ }
559
+ const transform = transformBetween(targetRect, originRect);
560
+ const unknownTargetSize = !naturalSize && !hasRealTargetContent(target);
561
+ if (unknownTargetSize) {
562
+ return { kind: "fade", transform };
563
+ }
564
+ const originRatio = originRect.width / originRect.height;
565
+ const targetRatio = targetRect.width / targetRect.height;
566
+ const ratioOfRatios = Math.max(originRatio, targetRatio) / Math.min(originRatio, targetRatio);
567
+ if (ratioOfRatios > ASPECT_MISMATCH_THRESHOLD) {
568
+ return { kind: "fade", transform };
550
569
  }
551
- return transformBetween(targetRect, originRect);
570
+ return { kind: "zoom", transform };
552
571
  }
572
+ const FADE_TRANSITION = "transform var(--shoji-duration) var(--shoji-easing), opacity var(--shoji-duration) var(--shoji-easing)";
553
573
  function waitForTransitionEnd(target, cb, property = "transform") {
554
574
  const durationMs = parseCssTime$1(getComputedStyle(target).transitionDuration);
555
575
  let done = false;
@@ -574,15 +594,19 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
574
594
  }
575
595
  function zoomIn({ origin, target, aspectRatio, naturalSize }) {
576
596
  if (prefersReducedMotion$1()) return;
577
- const transform = computeTransform(origin, target, aspectRatio, naturalSize);
578
- if (!transform) return;
597
+ const outcome = computeTransformOutcome(origin, target, aspectRatio, naturalSize);
598
+ if (outcome.kind === "none") return;
599
+ const { transform } = outcome;
600
+ const isFade = outcome.kind === "fade";
579
601
  target.style.transition = "none";
580
602
  target.style.transformOrigin = "center";
581
- target.style.willChange = "transform";
603
+ target.style.willChange = isFade ? "transform, opacity" : "transform";
582
604
  target.style.transform = transform;
605
+ if (isFade) target.style.opacity = "0";
583
606
  void target.offsetHeight;
584
- target.style.transition = "transform var(--shoji-duration) var(--shoji-easing)";
607
+ target.style.transition = isFade ? FADE_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
585
608
  target.style.transform = "none";
609
+ if (isFade) target.style.opacity = "1";
586
610
  waitForTransitionEnd(target, () => clearInlineTransform$1(target, "none"));
587
611
  }
588
612
  function zoomOut({ origin, target, aspectRatio, naturalSize, dragStart, zoomStart }, onComplete) {
@@ -590,13 +614,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
590
614
  onComplete();
591
615
  return;
592
616
  }
593
- const transform = computeTransform(origin, target, aspectRatio, naturalSize);
594
- if (!transform) {
617
+ const outcome = computeTransformOutcome(origin, target, aspectRatio, naturalSize);
618
+ if (outcome.kind === "none") {
595
619
  onComplete();
596
620
  return;
597
621
  }
622
+ const { transform } = outcome;
623
+ const isFade = outcome.kind === "fade";
598
624
  target.style.transformOrigin = "center";
599
- target.style.willChange = "transform";
625
+ target.style.willChange = isFade ? "transform, opacity" : "transform";
600
626
  if (dragStart) {
601
627
  target.style.transition = "none";
602
628
  target.style.transform = `translate3d(0px, ${dragStart.translateY}px, 0px) scale3d(${dragStart.scale}, ${dragStart.scale}, 1)`;
@@ -612,9 +638,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
612
638
  void target.offsetHeight;
613
639
  }
614
640
  }
615
- target.style.transition = "transform var(--shoji-duration) var(--shoji-easing)";
641
+ target.style.transition = isFade ? FADE_TRANSITION : "transform var(--shoji-duration) var(--shoji-easing)";
616
642
  void target.offsetHeight;
617
643
  target.style.transform = transform;
644
+ if (isFade) target.style.opacity = "0";
618
645
  const appliedTransform = target.style.transform;
619
646
  waitForTransitionEnd(target, () => {
620
647
  clearInlineTransform$1(target, appliedTransform);
@@ -831,7 +858,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
831
858
  const n = Number(raw);
832
859
  return Number.isFinite(n) ? n : void 0;
833
860
  }
834
- const NO_OWN_DATA_FIELD = /* @__PURE__ */ new Set(["no-drag", "video-id", "video-provider"]);
861
+ const NO_OWN_DATA_FIELD = /* @__PURE__ */ new Set([
862
+ "no-drag",
863
+ "video-id",
864
+ "video-provider",
865
+ "thumbnail-width",
866
+ "thumbnail-height"
867
+ ]);
835
868
  function applyCommon(element, item, src) {
836
869
  const caption = attr(element, "data-shoji-caption");
837
870
  if (caption) item.caption = caption;
@@ -857,6 +890,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
857
890
  if (width !== void 0) item.width = width;
858
891
  const height = numAttr(element, "data-shoji-height");
859
892
  if (height !== void 0) item.height = height;
893
+ const thumbnailWidth = numAttr(element, "data-shoji-thumbnail-width");
894
+ if (thumbnailWidth !== void 0) item.thumbnailWidth = thumbnailWidth;
895
+ const thumbnailHeight = numAttr(element, "data-shoji-thumbnail-height");
896
+ if (thumbnailHeight !== void 0) item.thumbnailHeight = thumbnailHeight;
860
897
  const srcset = attr(element, "data-shoji-srcset");
861
898
  if (srcset) item.srcset = srcset;
862
899
  const sizes = attr(element, "data-shoji-sizes");
@@ -2943,7 +2980,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
2943
2980
  this.focusTrap.activate(this.dom.dialog);
2944
2981
  this.scheduleAutoHide();
2945
2982
  const media = (_b = this.slides) == null ? void 0 : _b.getActiveMedia();
2946
- if (media && origin && naturalSize) {
2983
+ if (media && origin) {
2947
2984
  zoomIn({
2948
2985
  origin,
2949
2986
  target: media,
@@ -3127,9 +3164,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3127
3164
  (_a = this.dom) == null ? void 0 : _a.dialog.classList.toggle("shoji-controls-hidden-for-drag", hidden);
3128
3165
  this.bus.emit("dragCloseThreshold", { hidden });
3129
3166
  }
3130
- /** `item.width`/`height`, else origin's `naturalWidth`/`naturalHeight` (accurate when `item.thumb` is unset). Feeds `computeTransform`'s letterbox-aware sizing. */
3167
+ /** `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
3168
  resolveAspectRatio(index, origin) {
3132
3169
  const item = this.itemList[index];
3170
+ if ((item == null ? void 0 : item.thumbnailWidth) && item.thumbnailHeight) {
3171
+ return item.thumbnailWidth / item.thumbnailHeight;
3172
+ }
3133
3173
  if ((item == null ? void 0 : item.width) && item.height) return item.width / item.height;
3134
3174
  const thumbImg = origin == null ? void 0 : origin.querySelector("img");
3135
3175
  if ((thumbImg == null ? void 0 : thumbImg.naturalWidth) && thumbImg.naturalHeight) {
@@ -3137,18 +3177,14 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
3137
3177
  }
3138
3178
  return void 0;
3139
3179
  }
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
- */
3180
+ /** `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
3181
  resolveNaturalSize(index) {
3150
3182
  const item = this.itemList[index];
3151
- return (item == null ? void 0 : item.width) && item.height ? { width: item.width, height: item.height } : void 0;
3183
+ if ((item == null ? void 0 : item.width) && item.height) return { width: item.width, height: item.height };
3184
+ if ((item == null ? void 0 : item.thumbnailWidth) && item.thumbnailHeight) {
3185
+ return { width: item.thumbnailWidth, height: item.thumbnailHeight };
3186
+ }
3187
+ return void 0;
3152
3188
  }
3153
3189
  /**
3154
3190
  * Idempotent on purpose: a pending zoom-out's `transitionend`/fallback