feat: Introduce configurable icon and card sizes for document views and desktop workspace.

This commit is contained in:
2025-12-01 11:45:58 +01:00
parent fea1bf68c5
commit 033026f57c
9 changed files with 39 additions and 27 deletions
+2
View File
@@ -4,6 +4,8 @@ export const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-
export const TAG_TEXT_MIME_TYPE = 'text/plain'; export const TAG_TEXT_MIME_TYPE = 'text/plain';
export const DEFAULT_GRID_ICON_SIZE = 144; export const DEFAULT_GRID_ICON_SIZE = 144;
export const DEFAULT_LIST_ICON_SIZE = 48;
export const DEFAULT_DESKTOP_CARD_SIZE = 300;
export const SORT_OPTIONS = [ export const SORT_OPTIONS = [
{ value: 'title', label: 'Title' }, { value: 'title', label: 'Title' },
+11 -7
View File
@@ -38,8 +38,9 @@ interface DocumentSizeInfo {
} }
// Fallback size computation // Fallback size computation
const computeFallbackCardSize = (_doc: DeskDocument): DocumentSizeInfo => { const computeFallbackCardSize = (_doc: DeskDocument, defaultSize: number = 200): DocumentSizeInfo => {
return { width: 200, height: 200, source: 'fallback' }; const size = Math.round(defaultSize * (1 / Math.SQRT2));
return { width: size, height: size, source: 'fallback' };
}; };
export interface DesktopWorkspaceProps { export interface DesktopWorkspaceProps {
@@ -52,6 +53,7 @@ export interface DesktopWorkspaceProps {
onDocumentTagDetach?: (docId: Identifier, tagId: Identifier) => void; onDocumentTagDetach?: (docId: Identifier, tagId: Identifier) => void;
tenantId?: Identifier | null; tenantId?: Identifier | null;
viewId?: string | null; viewId?: string | null;
defaultCardSize?: number;
} }
// Wrapper to handle hooks per card // Wrapper to handle hooks per card
@@ -86,6 +88,7 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
onDocumentTagDetach, onDocumentTagDetach,
tenantId, tenantId,
viewId, viewId,
defaultCardSize = 200,
}) => { }) => {
const { openPreview } = usePreviewContext(); const { openPreview } = usePreviewContext();
const { addPointer, removePointer } = usePointerTracking(); const { addPointer, removePointer } = usePointerTracking();
@@ -179,8 +182,8 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
return { width: meta.width, height: meta.height, source: 'metadata' }; return { width: meta.width, height: meta.height, source: 'metadata' };
} }
return computeFallbackCardSize(doc); return computeFallbackCardSize(doc, defaultCardSize);
}, [metadataMap]); }, [metadataMap, defaultCardSize]);
// Sync LayoutStore to layoutRef // Sync LayoutStore to layoutRef
useEffect(() => { useEffect(() => {
@@ -371,9 +374,10 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
const pageCount = metadata?.page_count ?? 1; const pageCount = metadata?.page_count ?? 1;
const layoutCard = layoutStore.initialize(docId, null, { const layoutCard = layoutStore.initialize(docId, null, {
width: Number.isFinite(size.width) && size.width > 0 ? size.width : 200, width: size.width,
height: Number.isFinite(size.height) && size.height > 0 ? size.height : 200, height: size.height,
pageCount pageCount,
maxSize: defaultCardSize
}); });
return ( return (
+2 -1
View File
@@ -355,13 +355,14 @@ export class LayoutStore {
width: number; width: number;
height: number; height: number;
pageCount: number; pageCount: number;
maxSize?: number;
}) { }) {
let card = this.items.get(id); let card = this.items.get(id);
const { width, height } = constrainDimensions( const { width, height } = constrainDimensions(
config.width, config.width,
config.height, config.height,
320 config.maxSize || 320
); );
if (!card) { if (!card) {
+4 -3
View File
@@ -112,23 +112,24 @@ const AbstractDocumentsView = <CProps extends { clearSelection: () => void; chil
); );
}; };
export const DocumentsList: React.FC<DocumentsViewProps> = (props) => { export const DocumentsList: React.FC<DocumentsViewProps & { iconSize?: number }> = (props) => {
return ( return (
<AbstractDocumentsView <AbstractDocumentsView
ContainerComponent={DocumentsListContainer} ContainerComponent={DocumentsListContainer}
ItemComponent={DocumentsListRow} ItemComponent={DocumentsListRow}
viewMode="list" viewMode="list"
containerProps={{ iconSize: props.iconSize }}
{...props} {...props}
/> />
); );
}; };
export const DocumentsGrid: React.FC<DocumentsViewProps & { gridIconSize?: number }> = (props) => { export const DocumentsGrid: React.FC<DocumentsViewProps & { iconSize?: number }> = (props) => {
return ( return (
<AbstractDocumentsView <AbstractDocumentsView
ContainerComponent={DocumentsGridContainer} ContainerComponent={DocumentsGridContainer}
ItemComponent={DocumentsGridCard} ItemComponent={DocumentsGridCard}
containerProps={{ gridIconSize: props.gridIconSize }} containerProps={{ iconSize: props.iconSize }}
viewMode="grid" viewMode="grid"
{...props} {...props}
/> />
@@ -14,11 +14,11 @@ import DocumentEntry from './DocumentEntry';
interface DocumentsGridCardProps extends DocumentsViewProps { interface DocumentsGridCardProps extends DocumentsViewProps {
entry: DocumentsListEntry; entry: DocumentsListEntry;
viewLogic: DocumentViewLogic; viewLogic: DocumentViewLogic;
gridIconSize?: number; iconSize?: number;
} }
const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => { const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
const { entry, gridIconSize } = props; const { entry, iconSize } = props;
if (entry.type === 'folder') { if (entry.type === 'folder') {
const folder = entry.folder; const folder = entry.folder;
@@ -35,7 +35,7 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
{(logic) => ( {(logic) => (
<> <>
<div className="folder-card__icon"> <div className="folder-card__icon">
<FolderIcon className="folder-card__icon-svg" size={gridIconSize} /> <FolderIcon className="folder-card__icon-svg" size={iconSize} />
</div> </div>
<div className="folder-card__meta"> <div className="folder-card__meta">
<div className="folder-card__label-row"> <div className="folder-card__label-row">
@@ -82,7 +82,7 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
ensureAssetUrl={props.ensureAssetUrl} ensureAssetUrl={props.ensureAssetUrl}
getDocumentAsset={props.getDocumentAsset} getDocumentAsset={props.getDocumentAsset}
alt={`Thumbnail for ${doc.title}`} alt={`Thumbnail for ${doc.title}`}
maxSize={gridIconSize} maxSize={iconSize}
scrollRootRef={props.scrollRef} scrollRootRef={props.scrollRef}
/> />
<div className="document-card__meta"> <div className="document-card__meta">
@@ -3,14 +3,14 @@ import React from 'react';
interface DocumentsGridContainerProps { interface DocumentsGridContainerProps {
children: React.ReactNode; children: React.ReactNode;
clearSelection: () => void; clearSelection: () => void;
gridIconSize?: number; iconSize?: number;
[key: string]: any; [key: string]: any;
} }
const DocumentsGridContainer: React.FC<DocumentsGridContainerProps> = ({ const DocumentsGridContainer: React.FC<DocumentsGridContainerProps> = ({
children, children,
clearSelection, clearSelection,
gridIconSize, iconSize,
...props ...props
}) => { }) => {
return ( return (
@@ -19,8 +19,8 @@ const DocumentsGridContainer: React.FC<DocumentsGridContainerProps> = ({
role="list" role="list"
{...props} {...props}
style={ style={
gridIconSize iconSize
? ({ '--documents-grid-icon-size': `${gridIconSize}px` } as React.CSSProperties) ? ({ '--documents-grid-icon-size': `${iconSize}px` } as React.CSSProperties)
: undefined : undefined
} }
onClick={(event) => { onClick={(event) => {
@@ -15,10 +15,11 @@ import DocumentEntry from './DocumentEntry';
interface DocumentsListRowProps extends DocumentsViewProps { interface DocumentsListRowProps extends DocumentsViewProps {
entry: DocumentsListEntry; entry: DocumentsListEntry;
viewLogic: DocumentViewLogic; viewLogic: DocumentViewLogic;
iconSize?: number;
} }
const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => { const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
const { entry } = props; const { entry, iconSize } = props;
if (entry.type === 'folder') { if (entry.type === 'folder') {
const folder = entry.folder; const folder = entry.folder;
@@ -35,7 +36,7 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
<> <>
<td className="thumb-cell"> <td className="thumb-cell">
<div className="thumb-icon"> <div className="thumb-icon">
<FolderIcon className="thumb-icon__image" size={32} /> <FolderIcon className="thumb-icon__image" size={iconSize || 32} />
</div> </div>
</td> </td>
<td className="doc-list__name"> <td className="doc-list__name">
@@ -92,6 +93,7 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
getDocumentAsset={props.getDocumentAsset} getDocumentAsset={props.getDocumentAsset}
alt={`Thumbnail for ${doc.title}`} alt={`Thumbnail for ${doc.title}`}
scrollRootRef={props.scrollRef} scrollRootRef={props.scrollRef}
maxSize={props.iconSize}
/> />
</td> </td>
<td className="doc-list__name"> <td className="doc-list__name">
@@ -24,7 +24,11 @@ import DocumentsPanelHeader, {
import { SelectionFloatingPanel } from '../SelectionFloatingActions'; import { SelectionFloatingPanel } from '../SelectionFloatingActions';
import { createDocumentsTableHeaderActions } from './DocumentsToolbar'; import { createDocumentsTableHeaderActions } from './DocumentsToolbar';
import { useDocumentsFilter } from '../context/DocumentsFilterContext'; import { useDocumentsFilter } from '../context/DocumentsFilterContext';
import { DEFAULT_GRID_ICON_SIZE } from '../../constants/documents'; import {
DEFAULT_GRID_ICON_SIZE,
DEFAULT_LIST_ICON_SIZE,
DEFAULT_DESKTOP_CARD_SIZE,
} from '../../constants/documents';
import type { Identifier } from '../../types/identifiers'; import type { Identifier } from '../../types/identifiers';
const EntryType = { const EntryType = {
@@ -490,12 +494,12 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
switch (viewMode) { switch (viewMode) {
case 'desk': case 'desk':
return <DesktopWorkspace {...viewProps} />; return <DesktopWorkspace {...viewProps} defaultCardSize={DEFAULT_DESKTOP_CARD_SIZE} />;
case 'grid': case 'grid':
return <DocumentsGrid {...viewProps} gridIconSize={gridIconSize} />; return <DocumentsGrid {...viewProps} iconSize={gridIconSize} />;
case 'list': case 'list':
default: default:
return <DocumentsList {...viewProps} />; return <DocumentsList {...viewProps} iconSize={DEFAULT_LIST_ICON_SIZE} />;
} }
}; };
@@ -346,8 +346,6 @@
} }
.thumb-icon__image { .thumb-icon__image {
width: 32px;
height: 32px;
display: block; display: block;
} }