From fe80ccc046fcfe7400a4727ec97326315fc2d031 Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Tue, 25 Nov 2025 10:13:46 +0100 Subject: [PATCH] refactor: unify document panel empty state rendering and view mode logic, adjust related styles --- frontend/src/desktop/DesktopWorkspace.tsx | 6 +- .../src/documents/panel/DocumentsPanel.tsx | 174 ++++++++---------- frontend/src/styles/documents/listing.css | 47 ++--- .../src/styles/workspace/workspace-layout.css | 13 +- 4 files changed, 94 insertions(+), 146 deletions(-) diff --git a/frontend/src/desktop/DesktopWorkspace.tsx b/frontend/src/desktop/DesktopWorkspace.tsx index 9d9ad6f..9d4c60a 100644 --- a/frontend/src/desktop/DesktopWorkspace.tsx +++ b/frontend/src/desktop/DesktopWorkspace.tsx @@ -1023,13 +1023,9 @@ function DesktopWorkspaceView({ }} > {!allSizesReady ? ( -
+

Loading previews…

- ) : items.length === 0 ? ( -
-

No documents to show here yet. Drop files to make this space come alive.

-
) : ( items.map((doc, index) => { const sizeInfo = ensureDocumentSize(doc); diff --git a/frontend/src/documents/panel/DocumentsPanel.tsx b/frontend/src/documents/panel/DocumentsPanel.tsx index 6ef3b24..c762fce 100644 --- a/frontend/src/documents/panel/DocumentsPanel.tsx +++ b/frontend/src/documents/panel/DocumentsPanel.tsx @@ -40,6 +40,37 @@ const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null; export type DocumentLinkLike = { url?: string | null; mimeType?: string | null }; +export interface DocumentsListProps { + entries: Array<{ type: string; id: Identifier; key: string; folder?: any; document?: any }>; + draggingDocumentIdsSet: Set; + draggedFolderId: Identifier | 'root' | null; + onFolderClick: (folder: any, event: React.MouseEvent) => void; + onFolderSelect?: (id: Identifier) => void; + onFolderDragOver?: (event: React.DragEvent, folder: any) => void; + onFolderDragLeave?: (event: React.DragEvent) => void; + onFolderDrop?: (event: React.DragEvent, folder: any) => void; + onFolderDragStart?: (event: React.DragEvent, folder: any) => void; + onFolderDragEnd?: (event: React.DragEvent) => void; + onDocumentClick: (doc: any, event: React.MouseEvent) => void; + onDocumentActivate: (doc: any, event?: React.MouseEvent | KeyboardEvent) => void; + onDocumentDragStart?: (event: React.DragEvent, doc: any) => void; + onDocumentDragEnd?: (event: React.DragEvent) => void; + onDocumentTagDragOver?: (event: React.DragEvent) => void; + onDocumentTagDragLeave?: (event: React.DragEvent) => void; + onDocumentTagDrop?: (event: React.DragEvent, docId: Identifier) => void; + ensureAssetUrl?: any; + getDocumentAsset?: any; + tagLookupById?: any; + onTagClick?: (tagId: Identifier) => void; + scrollRef?: React.RefObject; + onCorrespondentClick?: (correspondentId: Identifier) => void; + activeCorrespondentIdSet?: Set; + onDocumentRename?: (id: Identifier, name: string) => void; + onFolderRename?: (id: Identifier, name: string) => void; + focusedRowKey?: string | null; + gridIconSize?: number; +} + const DocumentsPanelInner: React.FC = ({ headerLeading = null, onBreadcrumbNavigate, @@ -706,116 +737,69 @@ const DocumentsPanelInner: React.FC = ({ () => entries.some((entry) => entry.type === EntryType.document), [entries], ); - const showTableRows = entries.length > 0; - const showDefaultEmptyState = !showingSearchResults && !isFilterActive && entries.length === 0; - const showListSearchEmptyState = showingSearchResults && !hasDocumentEntries && !isGridView && !isSearchLoading; - const showGridSearchEmptyState = isGridView && showingSearchResults && !hasDocumentEntries && !isSearchLoading; - const renderBody = () => { - if (isDeskView) { - return deskWorkspaceProps ? ( - - ) : ( -
- Desk view is unavailable. -
- ); - } - if (showDefaultEmptyState) { + const renderBody = () => { + const hasEntries = entries.length > 0; + const isSearchEmpty = (showingSearchResults || isFilterActive) && !hasDocumentEntries && !isSearchLoading; + + if (isSearchEmpty) { return (
- Drop files anywhere or onto a folder to upload documents. -
- ); - } - - if (showGridSearchEmptyState) { - return ( -
No documents match the current filters.
); } - if (showListSearchEmptyState) { - return ( -
No documents match the current filters.
- ); + if (!hasEntries) { +
+ No documents to show here yet. Drop files to make this space come alive. +
} - if (!showTableRows) { - return null; - } + const listProps: DocumentsListProps = { + entries, + draggingDocumentIdsSet: draggingSet, + draggedFolderId, + onFolderClick: handleFolderClick, + onFolderSelect, + onFolderDragOver, + onFolderDragLeave, + onFolderDrop, + onFolderDragStart, + onFolderDragEnd, + onDocumentClick: handleDocumentClick, + onDocumentActivate: handleDocumentActivate, + onDocumentDragStart: handleDocumentDragStartLocal, + onDocumentDragEnd: handleDocumentDragEndLocal, + onDocumentTagDragOver: handleDocumentTagDragOver, + onDocumentTagDragLeave: handleDocumentTagDragLeave, + onDocumentTagDrop: handleDocumentTagDrop, + onDocumentRename, + onFolderRename, + ensureAssetUrl, + getDocumentAsset, + tagLookupById, + onTagClick: toggleTagFilter, + onCorrespondentClick: toggleCorrespondentFilter, + activeCorrespondentIdSet: activeCorrespondentIdSet, + scrollRef, + focusedRowKey, + gridIconSize, + }; - if (isGridView) { - return ( - - ); + switch (viewMode) { + case 'desk': + return ; + case 'grid': + return ; + case 'list': + default: + return ; } - - return ( - - ); }; const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list'; - const shouldHandlePanelInteractions = !isDeskView && showTableRows; + const shouldHandlePanelInteractions = !isDeskView && entries.length > 0; const handleSectionFocus = useCallback((event: React.FocusEvent) => { if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) { diff --git a/frontend/src/styles/documents/listing.css b/frontend/src/styles/documents/listing.css index 66cab75..bdb2e88 100644 --- a/frontend/src/styles/documents/listing.css +++ b/frontend/src/styles/documents/listing.css @@ -467,8 +467,8 @@ cursor: pointer; } -.documents-panel tbody tr.document > *, -.documents-panel tbody tr.folder > * { +.documents-panel tbody tr.document>*, +.documents-panel tbody tr.folder>* { height: var(--documents-list-row-height); } @@ -762,28 +762,11 @@ padding: 0.2rem 0.4rem; font-weight: 600; border: none; - background: oklch( - min(1, calc(var(--tag-chip-resolved-l) + (1 - var(--tag-chip-resolved-l)) * var(--tag-chip-bg-lighten))) - calc(var(--tag-chip-resolved-c) * var(--tag-chip-bg-chroma-scale)) - var(--tag-chip-resolved-h) - / 0.9 - ); - --tag-chip-outline-color: var( - --tag-chip-outline, - oklch( - min(1, calc(var(--tag-chip-resolved-l) + (1 - var(--tag-chip-resolved-l)) * var(--tag-chip-outline-lighten))) - min(1, calc(var(--tag-chip-resolved-c) * var(--tag-chip-outline-chroma-scale))) - var(--tag-chip-resolved-h) - ) - ); - --tag-chip-text-color: var( - --tag-chip-text, - oklch( - clamp(0, calc(var(--tag-chip-resolved-l) + var(--tag-chip-text-lighten)), 1) - clamp(0, calc(var(--tag-chip-resolved-c) * var(--tag-chip-text-chroma-scale)), 1) - var(--tag-chip-resolved-h) - ) - ); + background: oklch(min(1, calc(var(--tag-chip-resolved-l) + (1 - var(--tag-chip-resolved-l)) * var(--tag-chip-bg-lighten))) calc(var(--tag-chip-resolved-c) * var(--tag-chip-bg-chroma-scale)) var(--tag-chip-resolved-h) / 0.9); + --tag-chip-outline-color: var(--tag-chip-outline, + oklch(min(1, calc(var(--tag-chip-resolved-l) + (1 - var(--tag-chip-resolved-l)) * var(--tag-chip-outline-lighten))) min(1, calc(var(--tag-chip-resolved-c) * var(--tag-chip-outline-chroma-scale))) var(--tag-chip-resolved-h))); + --tag-chip-text-color: var(--tag-chip-text, + oklch(clamp(0, calc(var(--tag-chip-resolved-l) + var(--tag-chip-text-lighten)), 1) clamp(0, calc(var(--tag-chip-resolved-c) * var(--tag-chip-text-chroma-scale)), 1) var(--tag-chip-resolved-h))); color: var(--tag-chip-text-color, currentColor); } @@ -822,15 +805,11 @@ } .empty-state { - border: 1px dashed var(--border); - border-radius: 0; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: 1rem; text-align: center; - padding: 1.1rem 0.9rem; color: var(--muted); - margin-top: 0.75rem; - background: var(--surface-subtle); -} - -.empty-state--global { - border-style: solid; -} +} \ No newline at end of file diff --git a/frontend/src/styles/workspace/workspace-layout.css b/frontend/src/styles/workspace/workspace-layout.css index 223e693..4fcfd23 100644 --- a/frontend/src/styles/workspace/workspace-layout.css +++ b/frontend/src/styles/workspace/workspace-layout.css @@ -28,18 +28,7 @@ outline: none; } -.desk-empty { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: 3rem; - text-align: center; - color: var(--muted); - font-size: 0.95rem; -} - body.desk-cursor-remove, body.desk-cursor-remove * { cursor: not-allowed !important; -} +} \ No newline at end of file