This commit is contained in:
2025-11-04 00:36:44 +01:00
parent 9ad704f50a
commit 01fd774b18
8 changed files with 551 additions and 267 deletions
+119 -53
View File
@@ -14,6 +14,9 @@ import DocumentsGrid from './DocumentsGrid';
import DocumentsList from './DocumentsList';
import { isTagTransferEvent } from './tagTransfer';
import SelectionFloatingActions from './SelectionFloatingActions';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
import { useAssetNavigator } from '../hooks/useAssetNavigator';
import { isPointerModifierEvent, isPrimaryPointerEvent } from './useEntryPointer';
const DEFAULT_GRID_ICON_SIZE = 144;
@@ -39,15 +42,13 @@ const DocumentsPanel = ({
draggedFolderId,
onFolderRename,
selectedFolderIds = [],
onDocumentOpen,
selectedDocumentIds = [],
focusedRowKey,
draggingDocumentIds = [],
onDocumentDragStart,
onDocumentDragEnd,
onDocumentRename,
onEntrySelection = null,
onOpenDetailPanel = null,
onEntryPointer = null,
tagLookupById,
activeCorrespondentIds = [],
onDocumentListFocus,
@@ -118,6 +119,85 @@ const DocumentsPanel = ({
}, []);
const isGridView = viewMode === 'grid';
const isDeskView = viewMode === 'desk';
const [previewDocId, setPreviewDocId] = useState(null);
const previewDoc = useMemo(() => {
if (!previewDocId) {
return null;
}
return rows.find((doc) => doc?.id === previewDocId) || null;
}, [previewDocId, rows]);
useEffect(() => {
if (previewDocId && !previewDoc) {
setPreviewDocId(null);
}
}, [previewDocId, previewDoc]);
const previewNavigator = useAssetNavigator({
document: previewDoc,
assetType: 'preview',
ensureAssetUrl,
getAsset: getDocumentAsset,
prefetch: 3,
});
const {
currentUrl: previewUrl,
canGoPrev: previewCanGoPrev,
canGoNext: previewCanGoNext,
goPrev: previewGoPrev,
goNext: previewGoNext,
} = previewNavigator;
const previewDisplay = useMemo(() => {
if (!previewDoc || !previewUrl) {
return null;
}
return {
url: previewUrl,
alt: previewDoc.title,
canGoPrev: Boolean(previewCanGoPrev),
canGoNext: Boolean(previewCanGoNext),
goPrev: previewGoPrev,
goNext: previewGoNext,
};
}, [previewDoc, previewUrl, previewCanGoPrev, previewCanGoNext, previewGoPrev, previewGoNext]);
const closePreviewOverlay = useCallback(() => {
setPreviewDocId(null);
}, []);
const handleDocumentPreviewZoom = useCallback(
(doc) => {
if (!doc || !doc.id) {
return;
}
const previewAsset = typeof getDocumentAsset === 'function' ? getDocumentAsset(doc, 'preview') : null;
if (!previewAsset) {
return;
}
setPreviewDocId(doc.id);
},
[getDocumentAsset],
);
const handleDocumentActivate = useCallback(
(doc, event) => {
if (event) {
if (typeof event.preventDefault === 'function') {
event.preventDefault();
}
if (typeof event.stopPropagation === 'function') {
event.stopPropagation();
}
}
handleDocumentPreviewZoom(doc);
},
[handleDocumentPreviewZoom],
);
const isListView = viewMode === 'list';
const gridIconSize = DEFAULT_GRID_ICON_SIZE;
const handleSetViewMode = useCallback(
@@ -240,56 +320,20 @@ const DocumentsPanel = ({
[isTagDragEvent, onDocumentTagDrop],
);
const handleEntryClick = useCallback(
(entry, event) => {
if (!entry || !entry.id) {
return;
}
if (entry.type === EntryType.document && suppressDocumentClickRef.current) {
return;
}
const rowKey = entry.type === EntryType.document ? `document:${entry.id}` : `folder:${entry.id}`;
if (rowKey && typeof onEntrySelection === 'function') {
onEntrySelection(rowKey, event);
}
if (entry.type === EntryType.document) {
const hasModifier = Boolean(
event && (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey),
);
if (!hasModifier && typeof onOpenDetailPanel === 'function') {
onOpenDetailPanel();
}
return;
}
if (entry.type === EntryType.folder) {
const hasModifier = Boolean(
event && (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey),
);
const isPrimaryClick = Boolean(event && event.type === 'click' && event.button === 0);
if (!hasModifier && isPrimaryClick && typeof onFolderSelect === 'function') {
onFolderSelect(entry.id);
}
if (scrollRef.current) {
scrollRef.current.focus({ preventScroll: true });
}
onFocusedRowChange?.(rowKey);
}
},
[onEntrySelection, onOpenDetailPanel, onFocusedRowChange, onFolderSelect],
);
const handleDocumentClick = useCallback(
(doc, event) => {
if (!doc) {
if (!doc || suppressDocumentClickRef.current) {
return;
}
handleEntryClick({ type: EntryType.document, id: doc.id, document: doc }, event);
if (typeof onEntryPointer === 'function') {
onEntryPointer(
{ type: EntryType.document, id: doc.id, key: `document:${doc.id}`, document: doc },
event,
);
}
},
[handleEntryClick],
[onEntryPointer],
);
const handleFolderClick = useCallback(
@@ -297,9 +341,24 @@ const DocumentsPanel = ({
if (!folder) {
return;
}
handleEntryClick({ type: EntryType.folder, id: folder.id, folder }, event);
if (typeof onEntryPointer === 'function') {
onEntryPointer(
{ type: EntryType.folder, id: folder.id, key: `folder:${folder.id}`, folder },
event,
);
}
if (
!isPointerModifierEvent(event)
&& isPrimaryPointerEvent(event)
&& scrollRef.current
) {
scrollRef.current.focus({ preventScroll: true });
onFocusedRowChange?.(`folder:${folder.id}`);
}
},
[handleEntryClick],
[onEntryPointer, onFocusedRowChange],
);
const handleDocumentDragStartLocal = useCallback(
@@ -345,7 +404,8 @@ const DocumentsPanel = ({
}, [breadcrumbEntries, currentFolderName, onFolderSelect]);
return (
<section
<>
<section
className={`documents-panel documents-panel--view-${isGridView ? 'grid' : 'list'}`}
>
{showHeader ? (
@@ -455,7 +515,7 @@ const DocumentsPanel = ({
onFolderDragStart={onFolderDragStart}
onFolderDragEnd={onFolderDragEnd}
onDocumentClick={handleDocumentClick}
onDocumentOpen={onDocumentOpen}
onDocumentActivate={handleDocumentActivate}
onDocumentDragStart={handleDocumentDragStartLocal}
onDocumentDragEnd={handleDocumentDragEndLocal}
onDocumentTagDragOver={handleDocumentTagDragOver}
@@ -492,7 +552,7 @@ const DocumentsPanel = ({
onFolderDragEnd={onFolderDragEnd}
onFolderRename={onFolderRename}
onDocumentClick={handleDocumentClick}
onDocumentOpen={onDocumentOpen}
onDocumentActivate={handleDocumentActivate}
onDocumentDragStart={handleDocumentDragStartLocal}
onDocumentDragEnd={handleDocumentDragEndLocal}
onDocumentTagDragOver={handleDocumentTagDragOver}
@@ -516,6 +576,12 @@ const DocumentsPanel = ({
</div>
)}
</section>
<PreviewZoomOverlay
open={Boolean(previewDocId)}
display={previewDisplay}
onClose={closePreviewOverlay}
/>
</>
);
};