skeuo aspect ratio
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import {
|
||||
DownloadIcon,
|
||||
EditIcon,
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
transition: width 0.28s ease, height 0.28s ease;
|
||||
}
|
||||
|
||||
.skeuo-item:focus-visible {
|
||||
|
||||
@@ -60,9 +60,12 @@ const SkeuoPreviewCard = ({
|
||||
prefetch,
|
||||
});
|
||||
|
||||
const { currentUrl, cardinality, canGoPrev, canGoNext } = navigator;
|
||||
const { currentUrl, cardinality, canGoPrev, canGoNext, currentMetadata, ordinal } = navigator;
|
||||
const docId = doc?.id ?? null;
|
||||
|
||||
const metadataWidth = Number(currentMetadata?.width);
|
||||
const metadataHeight = Number(currentMetadata?.height);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onNavigatorSnapshot || !docId) {
|
||||
return undefined;
|
||||
@@ -74,6 +77,9 @@ const SkeuoPreviewCard = ({
|
||||
canGoNext,
|
||||
goPrev: navigator.goPrev,
|
||||
goNext: navigator.goNext,
|
||||
ordinal,
|
||||
width: Number.isFinite(metadataWidth) && metadataWidth > 0 ? metadataWidth : null,
|
||||
height: Number.isFinite(metadataHeight) && metadataHeight > 0 ? metadataHeight : null,
|
||||
};
|
||||
onNavigatorSnapshot(docId, snapshot);
|
||||
return () => onNavigatorSnapshot(docId, null);
|
||||
@@ -83,6 +89,9 @@ const SkeuoPreviewCard = ({
|
||||
title,
|
||||
canGoPrev,
|
||||
canGoNext,
|
||||
ordinal,
|
||||
metadataWidth,
|
||||
metadataHeight,
|
||||
navigator.goPrev,
|
||||
navigator.goNext,
|
||||
onNavigatorSnapshot,
|
||||
@@ -555,6 +564,37 @@ const clamp = (value, min, max) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const clampCardDimensions = (width, height) => {
|
||||
let nextWidth = Number(width);
|
||||
let nextHeight = Number(height);
|
||||
if (!Number.isFinite(nextWidth) || nextWidth <= 0 || !Number.isFinite(nextHeight) || nextHeight <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scaleDown = Math.min(1, CARD_MAX / nextWidth, CARD_MAX / nextHeight);
|
||||
nextWidth *= scaleDown;
|
||||
nextHeight *= scaleDown;
|
||||
|
||||
const minDim = Math.min(nextWidth, nextHeight);
|
||||
if (minDim > 0 && minDim < CARD_MIN) {
|
||||
const scaleUp = CARD_MIN / minDim;
|
||||
nextWidth *= scaleUp;
|
||||
nextHeight *= scaleUp;
|
||||
|
||||
const adjust = Math.min(1, CARD_MAX / nextWidth, CARD_MAX / nextHeight);
|
||||
nextWidth *= adjust;
|
||||
nextHeight *= adjust;
|
||||
}
|
||||
|
||||
nextWidth = clamp(nextWidth, CARD_MIN, CARD_MAX);
|
||||
nextHeight = clamp(nextHeight, CARD_MIN, CARD_MAX);
|
||||
|
||||
return {
|
||||
width: nextWidth,
|
||||
height: nextHeight,
|
||||
};
|
||||
};
|
||||
|
||||
const formatTransform = (x, y, rotation = 0, scale = 1) =>
|
||||
`translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`;
|
||||
|
||||
@@ -602,43 +642,90 @@ const SkeuomorphicWorkspace = ({
|
||||
const [overlayOriginRect, setOverlayOriginRect] = useState(null);
|
||||
const [overlayOriginTransform, setOverlayOriginTransform] = useState(null);
|
||||
const [previewSnapshots, setPreviewSnapshots] = useState(() => new Map());
|
||||
const [docSizeVersion, setDocSizeVersion] = useState(0);
|
||||
const [tagDropTargetId, setTagDropTargetId] = useState(null);
|
||||
const [pendingTagDocId, setPendingTagDocId] = useState(null);
|
||||
const [pendingRemovalTag, setPendingRemovalTag] = useState(null);
|
||||
const draggingTagRef = useRef(null);
|
||||
const pendingDocTagDragRef = useRef(null);
|
||||
const docSizeMapRef = useRef(new Map());
|
||||
const documentLookupRef = useRef(new Map());
|
||||
const removalCursorActiveRef = useRef(false);
|
||||
const handleNavigatorSnapshot = useCallback((docId, snapshot) => {
|
||||
if (!docId) {
|
||||
return;
|
||||
}
|
||||
setPreviewSnapshots((previous) => {
|
||||
const prevSnapshot = previous.get(docId);
|
||||
if (!snapshot) {
|
||||
if (!previous.has(docId)) {
|
||||
const applySnapshotDimensions = useCallback(
|
||||
(docId, snapshot) => {
|
||||
if (!docId || !snapshot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = documentLookupRef.current.get(docId);
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
const width = Number(snapshot.width);
|
||||
const height = Number(snapshot.height);
|
||||
if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = clampCardDimensions(width, height);
|
||||
if (!normalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = resolveSizeKey(doc);
|
||||
const existing = docSizeMapRef.current.get(key);
|
||||
if (existing && existing.width === normalized.width && existing.height === normalized.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
docSizeMapRef.current.set(key, normalized);
|
||||
setDocSizeVersion((value) => value + 1);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const handleNavigatorSnapshot = useCallback(
|
||||
(docId, snapshot) => {
|
||||
if (!docId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPreviewSnapshots((previous) => {
|
||||
const prevSnapshot = previous.get(docId);
|
||||
if (!snapshot) {
|
||||
if (!previous.has(docId)) {
|
||||
return previous;
|
||||
}
|
||||
const next = new Map(previous);
|
||||
next.delete(docId);
|
||||
return next;
|
||||
}
|
||||
|
||||
const next = new Map(previous);
|
||||
const sameSnapshot =
|
||||
prevSnapshot &&
|
||||
prevSnapshot.url === snapshot.url &&
|
||||
prevSnapshot.alt === snapshot.alt &&
|
||||
prevSnapshot.canGoPrev === snapshot.canGoPrev &&
|
||||
prevSnapshot.canGoNext === snapshot.canGoNext &&
|
||||
prevSnapshot.goPrev === snapshot.goPrev &&
|
||||
prevSnapshot.goNext === snapshot.goNext &&
|
||||
prevSnapshot.ordinal === snapshot.ordinal &&
|
||||
prevSnapshot.width === snapshot.width &&
|
||||
prevSnapshot.height === snapshot.height;
|
||||
if (sameSnapshot) {
|
||||
return previous;
|
||||
}
|
||||
const next = new Map(previous);
|
||||
next.delete(docId);
|
||||
next.set(docId, snapshot);
|
||||
return next;
|
||||
});
|
||||
|
||||
if (snapshot) {
|
||||
applySnapshotDimensions(docId, snapshot);
|
||||
}
|
||||
const next = new Map(previous);
|
||||
const sameSnapshot =
|
||||
prevSnapshot &&
|
||||
prevSnapshot.url === snapshot.url &&
|
||||
prevSnapshot.alt === snapshot.alt &&
|
||||
prevSnapshot.canGoPrev === snapshot.canGoPrev &&
|
||||
prevSnapshot.canGoNext === snapshot.canGoNext &&
|
||||
prevSnapshot.goPrev === snapshot.goPrev &&
|
||||
prevSnapshot.goNext === snapshot.goNext;
|
||||
if (sameSnapshot) {
|
||||
return previous;
|
||||
}
|
||||
next.set(docId, snapshot);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
},
|
||||
[applySnapshotDimensions],
|
||||
);
|
||||
const activeTagSet = useMemo(() => {
|
||||
if (!Array.isArray(activeTagIds) || activeTagIds.length === 0) {
|
||||
return new Set();
|
||||
@@ -663,6 +750,15 @@ const SkeuomorphicWorkspace = ({
|
||||
const resolvePreviewDimensions = useCallback(
|
||||
(doc) => {
|
||||
if (!doc) return null;
|
||||
|
||||
const snapshot = previewSnapshots.get(doc.id);
|
||||
if (snapshot && snapshot.width && snapshot.height) {
|
||||
return {
|
||||
width: snapshot.width,
|
||||
height: snapshot.height,
|
||||
};
|
||||
}
|
||||
|
||||
const asset = resolvePreviewAsset(doc);
|
||||
const view = createAssetView(asset);
|
||||
const metadata = view.getPrimaryMetadata() || {};
|
||||
@@ -673,7 +769,7 @@ const SkeuomorphicWorkspace = ({
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[resolvePreviewAsset],
|
||||
[previewSnapshots, resolvePreviewAsset],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -780,48 +876,26 @@ const SkeuomorphicWorkspace = ({
|
||||
if (cache) {
|
||||
return cache;
|
||||
}
|
||||
let width;
|
||||
let height;
|
||||
|
||||
const intrinsic = resolvePreviewDimensions(doc);
|
||||
let normalized = null;
|
||||
if (intrinsic?.width && intrinsic?.height) {
|
||||
width = intrinsic.width;
|
||||
height = intrinsic.height;
|
||||
} else {
|
||||
normalized = clampCardDimensions(intrinsic.width, intrinsic.height);
|
||||
}
|
||||
|
||||
if (!normalized) {
|
||||
const seed = seededRandom(`${key}:size`);
|
||||
width = CARD_MIN + seed * (Math.min(CARD_MAX, CARD_MAX / EMPTY_CARD_ASPECT) - CARD_MIN);
|
||||
let width = CARD_MIN + seed * (Math.min(CARD_MAX, CARD_MAX / EMPTY_CARD_ASPECT) - CARD_MIN);
|
||||
width = clamp(width, CARD_MIN, Math.min(CARD_MAX, CARD_MAX / EMPTY_CARD_ASPECT));
|
||||
height = width * EMPTY_CARD_ASPECT;
|
||||
const height = width * EMPTY_CARD_ASPECT;
|
||||
normalized = clampCardDimensions(width, height);
|
||||
}
|
||||
|
||||
if (!Number.isFinite(width) || width <= 0) {
|
||||
width = CARD_MIN;
|
||||
}
|
||||
if (!Number.isFinite(height) || height <= 0) {
|
||||
height = CARD_MIN;
|
||||
if (!normalized) {
|
||||
normalized = { width: CARD_MIN, height: CARD_MIN };
|
||||
}
|
||||
|
||||
const scaleDown = Math.min(1, CARD_MAX / width, CARD_MAX / height);
|
||||
width *= scaleDown;
|
||||
height *= scaleDown;
|
||||
|
||||
const minDim = Math.min(width, height);
|
||||
if (minDim < CARD_MIN) {
|
||||
const scaleUp = CARD_MIN / minDim;
|
||||
width *= scaleUp;
|
||||
height *= scaleUp;
|
||||
|
||||
const adjust = Math.min(1, CARD_MAX / width, CARD_MAX / height);
|
||||
width *= adjust;
|
||||
height *= adjust;
|
||||
}
|
||||
|
||||
width = clamp(width, CARD_MIN, CARD_MAX);
|
||||
height = clamp(height, CARD_MIN, CARD_MAX);
|
||||
|
||||
const size = { width, height };
|
||||
docSizeMapRef.current.set(key, size);
|
||||
return size;
|
||||
docSizeMapRef.current.set(key, normalized);
|
||||
return normalized;
|
||||
}, [resolvePreviewDimensions]);
|
||||
|
||||
const documentLookup = useMemo(() => {
|
||||
@@ -834,8 +908,13 @@ const SkeuomorphicWorkspace = ({
|
||||
return map;
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
documentLookupRef.current = documentLookup;
|
||||
}, [documentLookup]);
|
||||
|
||||
useEffect(() => {
|
||||
docSizeMapRef.current = new Map();
|
||||
setDocSizeVersion((value) => value + 1);
|
||||
}, [items]);
|
||||
|
||||
const overlayDisplay = useMemo(() => {
|
||||
@@ -1020,6 +1099,7 @@ const SkeuomorphicWorkspace = ({
|
||||
items,
|
||||
canvasSize.width,
|
||||
canvasSize.height,
|
||||
docSizeVersion,
|
||||
ensureDocumentSize,
|
||||
syncLayoutSnapshot,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user