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 React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
import { resolveDocumentAssetUrl } from '../asset_manager';
|
import {getAssetFromVersion, resolveDocumentAssetUrl} from '../asset_manager';
|
||||||
import { getTagColorStyle } from '../utils/colors';
|
import { getTagColorStyle } from '../utils/colors';
|
||||||
import { DownloadIcon, EditIcon, ViewListIcon, ViewGridIcon, FolderIcon } from '../ui/icons';
|
import { DownloadIcon, EditIcon, ViewListIcon, ViewGridIcon, FolderIcon } from '../ui/icons';
|
||||||
|
|
||||||
const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];
|
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(
|
const url = useMemo(
|
||||||
() =>
|
() =>
|
||||||
resolveDocumentAssetUrl(document, 'thumbnail', {
|
resolveDocumentAssetUrl(document, 'thumbnail', {
|
||||||
@@ -15,19 +40,28 @@ const DocumentThumbnailImage = ({ document, ensureAssetUrl, getDocumentAsset, al
|
|||||||
[document, ensureAssetUrl, getDocumentAsset],
|
[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 (
|
return (
|
||||||
<div className="document-thumbnail-wrapper">
|
<div className="document-thumbnail-wrapper">
|
||||||
{url ? (
|
<div className={innerClasses.join(' ')} style={innerStyle}>
|
||||||
<img
|
{url ? (
|
||||||
src={url}
|
<img
|
||||||
alt={alt || ''}
|
src={url}
|
||||||
className="document-thumbnail"
|
alt={alt || ''}
|
||||||
draggable={false}
|
className="document-thumbnail"
|
||||||
onDragStart={(event) => event.preventDefault()}
|
draggable={false}
|
||||||
/>
|
onDragStart={(event) => event.preventDefault()}
|
||||||
) : (
|
/>
|
||||||
<div className="thumb-placeholder">DOC</div>
|
) : (
|
||||||
)}
|
<div className="thumb-placeholder">DOC</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -413,6 +447,7 @@ const DocumentsTable = ({
|
|||||||
ensureAssetUrl={ensureAssetUrl}
|
ensureAssetUrl={ensureAssetUrl}
|
||||||
getDocumentAsset={getDocumentAsset}
|
getDocumentAsset={getDocumentAsset}
|
||||||
alt={`Thumbnail for ${doc.title || doc.original_name}`}
|
alt={`Thumbnail for ${doc.title || doc.original_name}`}
|
||||||
|
maxSize={128}
|
||||||
/>
|
/>
|
||||||
<div className="document-card__meta">
|
<div className="document-card__meta">
|
||||||
<div
|
<div
|
||||||
|
|||||||
+28
-13
@@ -845,6 +845,7 @@ button.icon-button.ghost:hover:not([disabled]) {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
transition: background 0.12s ease, color 0.12s ease;
|
transition: background 0.12s ease, color 0.12s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-row span.name {
|
.folder-row span.name {
|
||||||
@@ -1176,29 +1177,43 @@ button.icon-button.ghost:hover:not([disabled]) {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-thumbnail-wrapper {
|
.documents-panel--view-grid .document-thumbnail-wrapper {
|
||||||
width: 48px;
|
width: 128px;
|
||||||
height: 48px;
|
height: 128px;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.document-thumbnail-inner {
|
||||||
|
position: relative;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.documents-panel--view-grid .document-thumbnail-wrapper {
|
.document-thumbnail-inner--multipage::after {
|
||||||
width: 128px;
|
content: '';
|
||||||
height: 128px;
|
position: absolute;
|
||||||
padding: 8px;
|
top: 0;
|
||||||
display: flex;
|
right: 0;
|
||||||
align-items: center;
|
width: 24px;
|
||||||
justify-content: center;
|
height: 24px;
|
||||||
border-radius: 10px;
|
background-image: url('./assets/papercorner.svg');
|
||||||
overflow: hidden;
|
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 {
|
.document-thumbnail {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
object-fit: contain;
|
|
||||||
box-shadow: 0 1px 6px var(--shadow-medium);
|
box-shadow: 0 1px 6px var(--shadow-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user