desk view

This commit is contained in:
2025-10-14 10:27:13 +02:00
parent 9aa43d2753
commit 4d5f7b4ccd
2 changed files with 84 additions and 57 deletions
+46 -55
View File
@@ -22,13 +22,10 @@ const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];
const resolveSizeKey = (doc) =>
doc?.id || doc?.document_id || doc?.uuid || doc?.original_name || doc?.title || 'doc';
const CARD_WIDTH_MIN = 200;
const CARD_WIDTH_MAX = 260;
const CARD_HEIGHT_MIN = 240;
const CARD_HEIGHT_MAX = 340;
const CARD_ASPECT_MIN = 0.8; // height / width
const CARD_ASPECT_MAX = 1.35;
const ZOOM_FILL_RATIO = 0.97;
const CARD_MIN = 240;
const CARD_MAX = 340;
const EMPTY_CARD_ASPECT = 1.4;
const ZOOM_FILL_RATIO = 0.99;
const ZOOM_MIN_SCALE = 1.05;
const ZOOM_MAX_SCALE = 5;
const TAG_REMOVE_DISTANCE = 160;
@@ -554,54 +551,40 @@ const SkeuomorphicWorkspace = ({
const intrinsic = resolvePreviewDimensions(doc);
if (intrinsic?.width && intrinsic?.height) {
const ratio = intrinsic.height / intrinsic.width || 1;
width = intrinsic.width;
height = intrinsic.height;
// scale down to fit within max bounds
if (width > CARD_WIDTH_MAX || height > CARD_HEIGHT_MAX) {
const scale = Math.min(CARD_WIDTH_MAX / width, CARD_HEIGHT_MAX / height);
width *= scale;
height *= scale;
}
// scale up to satisfy minimum bounds while respecting ratio
if (width < CARD_WIDTH_MIN || height < CARD_HEIGHT_MIN) {
const scale = Math.max(CARD_WIDTH_MIN / width, CARD_HEIGHT_MIN / height);
width *= scale;
height *= scale;
if (width > CARD_WIDTH_MAX || height > CARD_HEIGHT_MAX) {
const downScale = Math.min(CARD_WIDTH_MAX / width, CARD_HEIGHT_MAX / height);
width *= downScale;
height *= downScale;
}
}
width = clamp(width, CARD_WIDTH_MIN, CARD_WIDTH_MAX);
height = clamp(height, CARD_HEIGHT_MIN, CARD_HEIGHT_MAX);
} else {
const widthSeed = seededRandom(`${key}:width`);
const ratioSeed = seededRandom(`${key}:ratio`);
const ratio = CARD_ASPECT_MIN + ratioSeed * (CARD_ASPECT_MAX - CARD_ASPECT_MIN);
width = CARD_WIDTH_MIN + widthSeed * (CARD_WIDTH_MAX - CARD_WIDTH_MIN);
height = width * ratio;
if (width > CARD_WIDTH_MAX || height > CARD_HEIGHT_MAX) {
const scale = Math.min(CARD_WIDTH_MAX / width, CARD_HEIGHT_MAX / height);
width *= scale;
height *= scale;
}
if (width < CARD_WIDTH_MIN || height < CARD_HEIGHT_MIN) {
const scale = Math.max(CARD_WIDTH_MIN / width, CARD_HEIGHT_MIN / height);
width *= scale;
height *= scale;
}
width = clamp(width, CARD_WIDTH_MIN, CARD_WIDTH_MAX);
height = clamp(height, CARD_HEIGHT_MIN, CARD_HEIGHT_MAX);
const seed = seededRandom(`${key}:size`);
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;
}
if (!Number.isFinite(width) || width <= 0) {
width = CARD_MIN;
}
if (!Number.isFinite(height) || height <= 0) {
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;
@@ -636,8 +619,8 @@ const SkeuomorphicWorkspace = ({
(doc, cardWidth, cardHeight) => {
const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
const canvasHeight = canvasSize.height || DEFAULT_CANVAS_HEIGHT;
const safeCardWidth = cardWidth || CARD_WIDTH_MIN;
const safeCardHeight = cardHeight || CARD_HEIGHT_MIN;
const safeCardWidth = cardWidth || CARD_MIN;
const safeCardHeight = cardHeight || CARD_MIN;
const usableWidth = Math.max(canvasWidth - CANVAS_PADDING * 2, safeCardWidth);
const usableHeight = Math.max(canvasHeight - CANVAS_PADDING * 2, safeCardHeight);
const viewportTargetWidth = usableWidth * ZOOM_FILL_RATIO;
@@ -1406,6 +1389,7 @@ const SkeuomorphicWorkspace = ({
const tagsStyle = { '--tag-scale': inverseTagScale };
const previewUrl = resolvePreviewUrl(doc);
const imageUrl = previewUrl;
const hasPreview = Boolean(imageUrl);
const title = doc.title || doc.original_name || 'Document';
const dragging = draggingId === doc.id;
const tags = Array.isArray(doc.tags) ? doc.tags : [];
@@ -1422,6 +1406,8 @@ const SkeuomorphicWorkspace = ({
if (dropActive) itemClasses.push('is-tag-target');
if (dropPending) itemClasses.push('is-tag-pending');
if (!matchesFilter) itemClasses.push('is-filtered-out');
const cardClasses = ['skeuo-item__card'];
if (!hasPreview) cardClasses.push('skeuo-item__card--empty');
const docTagTokens = docTagKeys.join(' ');
return (
<div
@@ -1458,11 +1444,16 @@ const SkeuomorphicWorkspace = ({
}}
>
<div className="skeuo-item__body" style={bodyStyle}>
<div className="skeuo-item__card">
{imageUrl ? (
<div className={cardClasses.join(' ')}>
{hasPreview ? (
<img src={imageUrl} alt={title} />
) : (
<div className="skeuo-item__placeholder">DOC</div>
<div className="skeuo-item__empty">
<div className="skeuo-item__placeholder">DOC</div>
<div className="skeuo-item__title" title={title}>
{title}
</div>
</div>
)}
</div>
{tags.length > 0 && (