This commit is contained in:
2025-11-11 12:01:22 +01:00
parent 2dd786ce14
commit 10f9f3be2f
6 changed files with 452 additions and 172 deletions
+105 -29
View File
@@ -1,8 +1,9 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
DownloadIcon,
DetailPanelCollapseIcon,
WindowMaximizeIcon,
IconZoomInArea,
} from '../ui/icons';
import PanelHeader from '../ui/PanelHeader';
import { resolveDocumentAssetUrl } from '../asset_manager';
@@ -12,6 +13,8 @@ import { sortCorrespondents, buildCorrespondentOptions } from '../documents/Docu
import BreadcrumbTrail from '../ui/BreadcrumbTrail';
import { extractDocumentMetadataPayload } from '../documents/documentMetadata';
import DocumentViewerLayout from '../preview/DocumentViewerLayout';
import PreviewZoomOverlay from './PreviewZoomOverlay';
import { useAssetNavigator } from '../hooks/useAssetNavigator';
const DetailPanel = ({
document = null,
@@ -35,6 +38,19 @@ const DetailPanel = ({
onClose = () => {},
}) => {
const singleDoc = document || null;
const [zoomOverlayOpen, setZoomOverlayOpen] = useState(false);
const previewNavigator = useAssetNavigator({
document: singleDoc,
assetType: 'preview',
ensureAssetUrl,
getAsset: getDocumentAsset,
prefetch: 3,
});
const navigatorUrl = previewNavigator?.currentUrl;
const navigatorCanGoPrev = Boolean(previewNavigator?.canGoPrev);
const navigatorCanGoNext = Boolean(previewNavigator?.canGoNext);
const navigatorGoPrev = previewNavigator?.goPrev;
const navigatorGoNext = previewNavigator?.goNext;
const { downloadHref: singleDownloadHref } = useMemo(
() =>
@@ -199,15 +215,19 @@ const DetailPanel = ({
}
return (
<section className="document-viewer document-viewer--stacked">
<DocumentViewerLayout
document={singleDoc}
previewEntry={previewEntry}
summaryProps={singleSummaryProps}
metadataPayload={singleMetadataPayload}
contentTabConfig={singleContentConfig}
previewLoadingMessage="Loading preview…"
/>
<section className="document-viewer-panel document-viewer-panel--stacked">
<div className="document-viewer-panel__body">
<section className="document-viewer document-viewer--stacked">
<DocumentViewerLayout
document={singleDoc}
previewEntry={previewEntry}
summaryProps={singleSummaryProps}
metadataPayload={singleMetadataPayload}
contentTabConfig={singleContentConfig}
previewLoadingMessage="Loading preview…"
/>
</section>
</div>
</section>
);
};
@@ -264,27 +284,83 @@ const DetailPanel = ({
);
}
const zoomDisplay = useMemo(() => {
if (navigatorUrl && singleDoc) {
return {
url: navigatorUrl,
alt: singleDoc.title,
canGoPrev: navigatorCanGoPrev,
canGoNext: navigatorCanGoNext,
goPrev: navigatorCanGoPrev ? navigatorGoPrev : undefined,
goNext: navigatorCanGoNext ? navigatorGoNext : undefined,
};
}
if (singleDoc && previewEntry?.url) {
return {
url: previewEntry.url,
alt: singleDoc.title,
canGoPrev: false,
canGoNext: false,
};
}
return null;
}, [
navigatorUrl,
navigatorCanGoPrev,
navigatorCanGoNext,
navigatorGoPrev,
navigatorGoNext,
singleDoc,
previewEntry?.url,
]);
if (singleDoc && zoomDisplay) {
headerActions.push(
<button
key="zoom"
type="button"
className="icon-button"
onClick={() => setZoomOverlayOpen(true)}
aria-label="Open zoom preview"
title="Open zoom preview"
>
<IconZoomInArea />
</button>,
);
}
useEffect(() => {
setZoomOverlayOpen(false);
}, [previewEntry?.url, singleDoc?.id]);
return (
<aside className="detail-panel panel">
<PanelHeader
leading={headerLeading}
title={
headerBreadcrumbs ? (
<BreadcrumbTrail
entries={headerBreadcrumbs}
separator="/"
className="panel-header__breadcrumbs"
truncateFromStart
/>
) : (
headerTitle
)
}
titleTag="h3"
actions={headerActions.length ? headerActions : null}
<>
<aside className="detail-panel panel">
<PanelHeader
leading={headerLeading}
title={
headerBreadcrumbs ? (
<BreadcrumbTrail
entries={headerBreadcrumbs}
separator="/"
className="panel-header__breadcrumbs"
truncateFromStart
/>
) : (
headerTitle
)
}
titleTag="h3"
actions={headerActions.length ? headerActions : null}
/>
<div className="panel-body detail-panel__content">{renderContent()}</div>
</aside>
<PreviewZoomOverlay
open={Boolean(zoomOverlayOpen && zoomDisplay)}
display={zoomDisplay}
onClose={() => setZoomOverlayOpen(false)}
/>
<div className="panel-body">{renderContent()}</div>
</aside>
</>
);
};
+6 -15
View File
@@ -10,7 +10,6 @@ import {
const useDetailWorkspace = ({
documents,
searchResults,
focusedDocumentId,
selectionOrder,
selectedDocumentIds,
documentLookup,
@@ -37,14 +36,6 @@ const useDetailWorkspace = ({
tags,
tagLookupById,
}) => {
const selectedDocument = useMemo(() => {
if (!focusedDocumentId) {
return null;
}
const pool = searchResults ?? documents;
return pool.find((doc) => doc.id === focusedDocumentId) || null;
}, [focusedDocumentId, searchResults, documents]);
const orderedSelectedDocuments = useMemo(() => {
const ordered = [];
const seen = new Set();
@@ -185,12 +176,12 @@ const useDetailWorkspace = ({
[folderNodes],
);
const selectedPreviewEntry = useMemo(() => {
if (!selectedDocument) {
const detailPanelPreviewEntry = useMemo(() => {
if (!detailPanelDocument) {
return null;
}
return previewEntries.get(selectedDocument.id) || null;
}, [selectedDocument, previewEntries]);
return previewEntries.get(detailPanelDocument.id) || null;
}, [detailPanelDocument, previewEntries]);
const previewWorkspaceEntry = useMemo(() => {
if (!previewDocumentId) {
@@ -239,7 +230,7 @@ const useDetailWorkspace = ({
tagLookupById,
onTagAdd: handleDocumentTagAdd,
onTagRemove: handleTagRemove,
previewEntry: selectedPreviewEntry,
previewEntry: detailPanelPreviewEntry,
onOpenPreview: openDocumentPreview,
activePreviewId,
onUpdateTitle: handleDocumentTitleUpdate,
@@ -273,7 +264,7 @@ const useDetailWorkspace = ({
resolveApiPath,
resolveFolderPath,
selectFolder,
selectedPreviewEntry,
detailPanelPreviewEntry,
tags,
tagLookupById,
],