feat: Introduce configurable icon and card sizes for document views and desktop workspace.
This commit is contained in:
@@ -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' },
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user