frontend: papercorner for multi-page documents
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 128 128" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<defs>
|
||||
<filter id="cornerShadow" x="-20%" y="-20%" width="200%" height="200%">
|
||||
<feDropShadow dx="-4" dy="4" stdDeviation="8" flood-opacity="0.24"/>
|
||||
</filter>
|
||||
<linearGradient id="cornerGradient" gradientUnits="userSpaceOnUse" x1="32" y1="0" x2="0" y2="64">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="100%" stop-color="#f3f3f3"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g filter="url(#cornerShadow)">
|
||||
<g transform="matrix(-1.81626,-1.81626,0.460023,-0.460023,180.663,203.387)">
|
||||
<path d="M70.488,10.11L89.954,86.966L51.022,86.966L70.488,10.11Z" fill="url(#cornerGradient)"/>
|
||||
</g>
|
||||
<g transform="matrix(1.81626,1.81626,-0.460023,0.460023,4.6259,-132.676)">
|
||||
<path d="M70.488,10.11L89.954,86.966L51.022,86.966L70.488,10.11Z" fill="#fdfdfd"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,11 +1,36 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { resolveDocumentAssetUrl } from '../asset_manager';
|
||||
import {getAssetFromVersion, resolveDocumentAssetUrl} from '../asset_manager';
|
||||
import { getTagColorStyle } from '../utils/colors';
|
||||
import { DownloadIcon, EditIcon, ViewListIcon, ViewGridIcon, FolderIcon } from '../ui/icons';
|
||||
|
||||
const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];
|
||||
|
||||
const DocumentThumbnailImage = ({ document, ensureAssetUrl, getDocumentAsset, alt }) => {
|
||||
const getPageCount = (doc) =>
|
||||
Number.isFinite(doc?.current_version?.metadata?.page_count)
|
||||
? doc.current_version.metadata.page_count
|
||||
: null;
|
||||
|
||||
const DocumentThumbnailImage = ({ document, ensureAssetUrl, getDocumentAsset, alt, maxSize = 48 }) => {
|
||||
const resolvedMaxSize = Math.max(1, Math.round(maxSize || 1));
|
||||
const thumbnailAsset = useMemo(() => getAssetFromVersion(document?.current_version, 'thumbnail'), [document?.current_version]);
|
||||
const assetWidth = Number(thumbnailAsset?.metadata?.width);
|
||||
const assetHeight = Number(thumbnailAsset?.metadata?.height);
|
||||
|
||||
const dimensions = useMemo(() => {
|
||||
if (!Number.isFinite(assetWidth) || assetWidth <= 0 || !Number.isFinite(assetHeight) || assetHeight <= 0) {
|
||||
return { width: resolvedMaxSize, height: resolvedMaxSize };
|
||||
}
|
||||
const scale = Math.min(1, resolvedMaxSize / assetWidth, resolvedMaxSize / assetHeight);
|
||||
return {
|
||||
width: Math.max(1, Math.round(assetWidth * scale)),
|
||||
height: Math.max(1, Math.round(assetHeight * scale)),
|
||||
};
|
||||
}, [assetWidth, assetHeight, resolvedMaxSize]);
|
||||
|
||||
const innerStyle = useMemo(
|
||||
() => ({ width: `${dimensions.width}px`, height: `${dimensions.height}px` }),
|
||||
[dimensions.height, dimensions.width],
|
||||
);
|
||||
const url = useMemo(
|
||||
() =>
|
||||
resolveDocumentAssetUrl(document, 'thumbnail', {
|
||||
@@ -15,19 +40,28 @@ const DocumentThumbnailImage = ({ document, ensureAssetUrl, getDocumentAsset, al
|
||||
[document, ensureAssetUrl, getDocumentAsset],
|
||||
);
|
||||
|
||||
const pageCount = getPageCount(document);
|
||||
const showMultiPageBadge = Number.isFinite(pageCount) && pageCount > 1;
|
||||
const innerClasses = ['document-thumbnail-inner'];
|
||||
if (showMultiPageBadge) {
|
||||
innerClasses.push('document-thumbnail-inner--multipage');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="document-thumbnail-wrapper">
|
||||
{url ? (
|
||||
<img
|
||||
src={url}
|
||||
alt={alt || ''}
|
||||
className="document-thumbnail"
|
||||
draggable={false}
|
||||
onDragStart={(event) => event.preventDefault()}
|
||||
/>
|
||||
) : (
|
||||
<div className="thumb-placeholder">DOC</div>
|
||||
)}
|
||||
<div className={innerClasses.join(' ')} style={innerStyle}>
|
||||
{url ? (
|
||||
<img
|
||||
src={url}
|
||||
alt={alt || ''}
|
||||
className="document-thumbnail"
|
||||
draggable={false}
|
||||
onDragStart={(event) => event.preventDefault()}
|
||||
/>
|
||||
) : (
|
||||
<div className="thumb-placeholder">DOC</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -413,6 +447,7 @@ const DocumentsTable = ({
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
alt={`Thumbnail for ${doc.title || doc.original_name}`}
|
||||
maxSize={128}
|
||||
/>
|
||||
<div className="document-card__meta">
|
||||
<div
|
||||
|
||||
+28
-13
@@ -845,6 +845,7 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
color: inherit;
|
||||
transition: background 0.12s ease, color 0.12s ease;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.folder-row span.name {
|
||||
@@ -1176,29 +1177,43 @@ button.icon-button.ghost:hover:not([disabled]) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.document-thumbnail-wrapper {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
.documents-panel--view-grid .document-thumbnail-wrapper {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
.document-thumbnail-inner {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.documents-panel--view-grid .document-thumbnail-wrapper {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
.document-thumbnail-inner--multipage::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-image: url('./assets/papercorner.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.documents-panel--view-grid .document-thumbnail-inner--multipage::after {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.document-thumbnail {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
box-shadow: 0 1px 6px var(--shadow-medium);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user