This commit is contained in:
2025-11-16 11:36:11 +01:00
parent 7e0768a09a
commit dae66703f2
17 changed files with 1110 additions and 160 deletions
@@ -2,6 +2,11 @@ import { useMemo } from 'react';
type Identifier = string | number;
interface PreviewEntryLike {
url?: string | null;
contentType?: string | null;
}
export interface Breadcrumb {
id?: Identifier;
name?: string;
@@ -65,6 +70,8 @@ export interface UseDocumentsPanelPropsArgs {
handleBulkSelectionReanalyze?: (...args: unknown[]) => void;
folderOptions?: unknown[];
moveDocumentsToFolder?: (...args: unknown[]) => void;
previewEntries?: Map<Identifier, PreviewEntryLike>;
ensureDownloadUrl?: (documentId: Identifier, options?: { force?: boolean }) => Promise<PreviewEntryLike | null>;
}
export type DocumentsPanelProps = ReturnType<typeof useDocumentsPanelProps>;
@@ -93,8 +100,8 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
searchLoading,
tagLookupById,
activeCorrespondentFilters,
ensureAssetUrl,
getDocumentAsset,
ensureAssetUrl,
getDocumentAsset,
toggleTagFilter,
toggleCorrespondentFilter,
handleDocumentTagDrop,
@@ -119,6 +126,8 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
handleBulkSelectionReanalyze,
folderOptions,
moveDocumentsToFolder,
previewEntries,
ensureDownloadUrl,
} = props;
return useMemo(
@@ -173,6 +182,8 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
onBulkReanalyze: handleBulkSelectionReanalyze,
folderOptions,
onMoveDocumentsToFolder: moveDocumentsToFolder,
previewEntries,
ensureDownloadUrl,
}),
[
activeCorrespondentFilters,
@@ -223,6 +234,8 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
ensureAssetUrl,
getDocumentAsset,
folderOptions,
previewEntries,
ensureDownloadUrl,
],
);
};
+70 -33
View File
@@ -5,7 +5,6 @@ import DocumentsGrid from '../DocumentsGrid';
import DocumentsList from '../DocumentsList';
import { isTagTransferEvent } from '../tagTransfer';
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
import { useAssetNavigator } from '../../hooks/useAssetNavigator';
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
@@ -22,6 +21,8 @@ interface DocumentsPanelProps {
const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;
export type PreviewEntryLike = { url?: string | null; contentType?: string | null };
const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
currentFolderName,
breadcrumbs,
@@ -55,6 +56,8 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
viewMode = 'list',
onViewModeChange,
showHeader = true,
previewEntries,
ensureDownloadUrl,
}) => {
const {
selectedEntries,
@@ -65,6 +68,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
} = useWorkspaceSelectionContext();
const showingSearchResults = searchResults !== null;
const rows = showingSearchResults ? searchResults : documents;
const previewEntryMap = previewEntries instanceof Map ? previewEntries : null;
const currentFolderId = useMemo(() => {
if (showingSearchResults) {
@@ -138,7 +142,10 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
const isGridView = viewMode === 'grid';
const isDeskView = viewMode === 'desk';
const [previewDocId, setPreviewDocId] = useState(null);
type Identifier = string | number;
type ZoomSource = { url: string; alt?: string | null; contentType?: string | null };
const [previewDocId, setPreviewDocId] = useState<Identifier | null>(null);
const previewDoc = useMemo(() => {
if (!previewDocId) {
@@ -153,38 +160,69 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
}
}, [previewDocId, previewDoc]);
const previewNavigator = useAssetNavigator({
document: previewDoc,
assetType: 'preview',
ensureAssetUrl,
getAsset: getDocumentAsset,
prefetch: 3,
});
const [previewZoomSource, setPreviewZoomSource] = useState<ZoomSource | null>(null);
const zoomDisplay = previewZoomSource;
const {
currentUrl: previewUrl,
canGoPrev: previewCanGoPrev,
canGoNext: previewCanGoNext,
goPrev: previewGoPrev,
goNext: previewGoNext,
} = previewNavigator;
const previewDisplay = useMemo(() => {
if (!previewDoc || !previewUrl) {
return null;
useEffect(() => {
let cancelled = false;
if (!previewDocId || !previewDoc) {
setPreviewZoomSource(null);
return () => {
cancelled = true;
};
}
return {
url: previewUrl,
alt: previewDoc.title,
canGoPrev: Boolean(previewCanGoPrev),
canGoNext: Boolean(previewCanGoNext),
goPrev: previewGoPrev,
goNext: previewGoNext,
const docContentType = previewDoc.content_type;
const versionContentType = previewDoc.current_version?.version?.content_type;
const contentFallback = docContentType || versionContentType || null;
const applyEntry = (entry?: PreviewEntryLike | null) => {
if (!entry?.url) {
setPreviewZoomSource(null);
return;
}
setPreviewZoomSource({
url: entry.url,
alt: previewDoc.title,
contentType: entry.contentType || contentFallback || undefined,
});
};
}, [previewDoc, previewUrl, previewCanGoPrev, previewCanGoNext, previewGoPrev, previewGoNext]);
const cachedEntry = previewEntryMap?.get(previewDocId) || null;
if (cachedEntry?.url) {
applyEntry(cachedEntry);
return () => {
cancelled = true;
};
}
if (!ensureDownloadUrl) {
setPreviewZoomSource(null);
return () => {
cancelled = true;
};
}
ensureDownloadUrl(previewDocId)
.then((entry) => {
if (cancelled) {
return;
}
applyEntry(entry);
})
.catch(() => {
if (!cancelled) {
setPreviewZoomSource(null);
}
});
return () => {
cancelled = true;
};
}, [previewDocId, previewDoc, previewEntryMap, ensureDownloadUrl]);
const closePreviewOverlay = useCallback(() => {
setPreviewDocId(null);
setPreviewZoomSource(null);
}, []);
const handleDocumentPreviewZoom = useCallback(
@@ -192,13 +230,12 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
if (!doc || !doc.id) {
return;
}
const previewAsset = getDocumentAsset(doc, 'preview');
if (!previewAsset) {
if (!ensureDownloadUrl && !(previewEntryMap?.get(doc.id)?.url)) {
return;
}
setPreviewDocId(doc.id);
},
[getDocumentAsset],
[ensureDownloadUrl, previewEntryMap],
);
const handleDocumentActivate = useCallback(
@@ -724,8 +761,8 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
)}
</section>
<PreviewZoomOverlay
open={Boolean(previewDocId)}
display={previewDisplay}
open={Boolean(zoomDisplay?.url)}
display={zoomDisplay}
onClose={closePreviewOverlay}
/>
</>