refactor: unify document panel empty state rendering and view mode logic, adjust related styles

This commit is contained in:
2025-11-25 10:13:46 +01:00
parent ccc22beda2
commit fe80ccc046
4 changed files with 94 additions and 146 deletions
+1 -5
View File
@@ -1023,13 +1023,9 @@ function DesktopWorkspaceView({
}} }}
> >
{!allSizesReady ? ( {!allSizesReady ? (
<div className="desk-empty"> <div className="empty-state">
<p>Loading previews</p> <p>Loading previews</p>
</div> </div>
) : items.length === 0 ? (
<div className="desk-empty">
<p>No documents to show here yet. Drop files to make this space come alive.</p>
</div>
) : ( ) : (
items.map((doc, index) => { items.map((doc, index) => {
const sizeInfo = ensureDocumentSize(doc); const sizeInfo = ensureDocumentSize(doc);
+79 -95
View File
@@ -40,6 +40,37 @@ const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;
export type DocumentLinkLike = { url?: string | null; mimeType?: 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<Identifier>;
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<HTMLElement>;
onCorrespondentClick?: (correspondentId: Identifier) => void;
activeCorrespondentIdSet?: Set<Identifier>;
onDocumentRename?: (id: Identifier, name: string) => void;
onFolderRename?: (id: Identifier, name: string) => void;
focusedRowKey?: string | null;
gridIconSize?: number;
}
const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
headerLeading = null, headerLeading = null,
onBreadcrumbNavigate, onBreadcrumbNavigate,
@@ -706,116 +737,69 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
() => entries.some((entry) => entry.type === EntryType.document), () => entries.some((entry) => entry.type === EntryType.document),
[entries], [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 ? (
<DesktopWorkspace {...deskWorkspaceProps} />
) : (
<div className="empty-state empty-state--global">
Desk view is unavailable.
</div>
);
}
if (showDefaultEmptyState) { const renderBody = () => {
const hasEntries = entries.length > 0;
const isSearchEmpty = (showingSearchResults || isFilterActive) && !hasDocumentEntries && !isSearchLoading;
if (isSearchEmpty) {
return ( return (
<div className="empty-state"> <div className="empty-state">
Drop files anywhere or onto a folder to upload documents.
</div>
);
}
if (showGridSearchEmptyState) {
return (
<div className="empty-state empty-state--global">
No documents match the current filters. No documents match the current filters.
</div> </div>
); );
} }
if (showListSearchEmptyState) { if (!hasEntries) {
return ( <div className="empty-state">
<div className="empty-state">No documents match the current filters.</div> No documents to show here yet. Drop files to make this space come alive.
); </div>
} }
if (!showTableRows) { const listProps: DocumentsListProps = {
return null; 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) { switch (viewMode) {
return ( case 'desk':
<DocumentsGrid return <DesktopWorkspace {...deskWorkspaceProps} />;
entries={entries} case 'grid':
draggingDocumentIdsSet={draggingSet} return <DocumentsGrid {...listProps} />;
draggedFolderId={draggedFolderId} case 'list':
onFolderClick={handleFolderClick} default:
onFolderSelect={onFolderSelect} return <DocumentsList {...listProps} />;
onFolderDragOver={onFolderDragOver}
onFolderDragLeave={onFolderDragLeave}
onFolderDrop={onFolderDrop}
onFolderDragStart={onFolderDragStart}
onFolderDragEnd={onFolderDragEnd}
onDocumentClick={handleDocumentClick}
onDocumentActivate={handleDocumentActivate}
onDocumentDragStart={handleDocumentDragStartLocal}
onDocumentDragEnd={handleDocumentDragEndLocal}
onDocumentTagDragOver={handleDocumentTagDragOver}
onDocumentTagDragLeave={handleDocumentTagDragLeave}
onDocumentTagDrop={onDocumentTagDrop}
ensureAssetUrl={ensureAssetUrl}
getDocumentAsset={getDocumentAsset}
gridIconSize={gridIconSize}
tagLookupById={tagLookupById}
onTagClick={toggleTagFilter}
scrollRef={scrollRef}
onCorrespondentClick={toggleCorrespondentFilter}
activeCorrespondentIdSet={activeCorrespondentIdSet}
onDocumentRename={onDocumentRename}
onFolderRename={onFolderRename}
/>
);
} }
return (
<DocumentsList
entries={entries}
focusedRowKey={focusedRowKey}
draggingDocumentIdsSet={draggingSet}
draggedFolderId={draggedFolderId}
ensureAssetUrl={ensureAssetUrl}
getDocumentAsset={getDocumentAsset}
onFolderClick={handleFolderClick}
onFolderSelect={onFolderSelect}
onFolderDragOver={onFolderDragOver}
onFolderDragLeave={onFolderDragLeave}
onFolderDrop={onFolderDrop}
onFolderDragStart={onFolderDragStart}
onFolderDragEnd={onFolderDragEnd}
onFolderRename={onFolderRename}
onDocumentClick={handleDocumentClick}
onDocumentActivate={handleDocumentActivate}
onDocumentDragStart={handleDocumentDragStartLocal}
onDocumentDragEnd={handleDocumentDragEndLocal}
onDocumentTagDragOver={handleDocumentTagDragOver}
onDocumentTagDragLeave={handleDocumentTagDragLeave}
onDocumentTagDrop={handleDocumentTagDrop}
onDocumentRename={onDocumentRename}
tagLookupById={tagLookupById}
onTagClick={toggleTagFilter}
onCorrespondentClick={toggleCorrespondentFilter}
activeCorrespondentIdSet={activeCorrespondentIdSet}
scrollRef={scrollRef}
/>
);
}; };
const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list'; const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list';
const shouldHandlePanelInteractions = !isDeskView && showTableRows; const shouldHandlePanelInteractions = !isDeskView && entries.length > 0;
const handleSectionFocus = useCallback((event: React.FocusEvent<HTMLElement>) => { const handleSectionFocus = useCallback((event: React.FocusEvent<HTMLElement>) => {
if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) { if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) {
+12 -33
View File
@@ -467,8 +467,8 @@
cursor: pointer; cursor: pointer;
} }
.documents-panel tbody tr.document > *, .documents-panel tbody tr.document>*,
.documents-panel tbody tr.folder > * { .documents-panel tbody tr.folder>* {
height: var(--documents-list-row-height); height: var(--documents-list-row-height);
} }
@@ -762,28 +762,11 @@
padding: 0.2rem 0.4rem; padding: 0.2rem 0.4rem;
font-weight: 600; font-weight: 600;
border: none; border: none;
background: oklch( 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);
min(1, calc(var(--tag-chip-resolved-l) + (1 - var(--tag-chip-resolved-l)) * var(--tag-chip-bg-lighten))) --tag-chip-outline-color: var(--tag-chip-outline,
calc(var(--tag-chip-resolved-c) * var(--tag-chip-bg-chroma-scale)) 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)));
var(--tag-chip-resolved-h) --tag-chip-text-color: var(--tag-chip-text,
/ 0.9 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)));
);
--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); color: var(--tag-chip-text-color, currentColor);
} }
@@ -822,15 +805,11 @@
} }
.empty-state { .empty-state {
border: 1px dashed var(--border); height: 100%;
border-radius: 0; display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
text-align: center; text-align: center;
padding: 1.1rem 0.9rem;
color: var(--muted); color: var(--muted);
margin-top: 0.75rem;
background: var(--surface-subtle);
}
.empty-state--global {
border-style: solid;
} }
@@ -28,17 +28,6 @@
outline: none; 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,
body.desk-cursor-remove * { body.desk-cursor-remove * {
cursor: not-allowed !important; cursor: not-allowed !important;