detailspanel, selection handling, single document details panel
This commit is contained in:
@@ -4358,17 +4358,12 @@ const AppLayout = () => {
|
||||
|
||||
const {
|
||||
detailPanelOpen,
|
||||
detailPanelSelectedDocuments,
|
||||
detailPanelDocument,
|
||||
openDetailPanel,
|
||||
closeDetailPanel,
|
||||
} = useDetailPanel({
|
||||
selectedDocumentIds,
|
||||
documentLookup,
|
||||
orderedSelectedDocuments,
|
||||
selectionOrder,
|
||||
documentsViewMode,
|
||||
getRowId,
|
||||
isDocumentRowKey,
|
||||
});
|
||||
|
||||
detailPanelControlRef.current = {
|
||||
@@ -4900,17 +4895,13 @@ const AppLayout = () => {
|
||||
|
||||
const detailPanelProps = useMemo(
|
||||
() => ({
|
||||
selectedDocuments: detailPanelSelectedDocuments,
|
||||
document: detailPanelDocument,
|
||||
tags,
|
||||
tagLookupById,
|
||||
onTagAdd: handleTagAdd,
|
||||
onTagRemove: handleTagRemove,
|
||||
previewEntry: selectedPreviewEntry,
|
||||
onOpenPreview: openDocumentPreview,
|
||||
onBulkTagAdd: handleBulkTagAddFromDetail,
|
||||
onBulkTagRemove: handleBulkTagRemoveFromDetail,
|
||||
onBulkCorrespondentAdd: handleBulkCorrespondentAdd,
|
||||
onBulkCorrespondentRemove: handleBulkCorrespondentRemove,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
activePreviewId,
|
||||
onUpdateTitle: handleDocumentTitleUpdate,
|
||||
@@ -4929,14 +4920,10 @@ const AppLayout = () => {
|
||||
[
|
||||
activePreviewId,
|
||||
correspondents,
|
||||
detailPanelSelectedDocuments,
|
||||
detailPanelDocument,
|
||||
ensureAssetUrl,
|
||||
ensurePreviewData,
|
||||
getDocumentAsset,
|
||||
handleBulkCorrespondentAdd,
|
||||
handleBulkCorrespondentRemove,
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
handleCorrespondentAdd,
|
||||
handleCorrespondentRemove,
|
||||
handleDetailPanelClose,
|
||||
|
||||
@@ -3,147 +3,74 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
export const useDetailPanel = ({
|
||||
documentLookup,
|
||||
orderedSelectedDocuments,
|
||||
selectionOrder,
|
||||
documentsViewMode,
|
||||
getRowId,
|
||||
isDocumentRowKey,
|
||||
}) => {
|
||||
const [detailPanelOpen, setDetailPanelOpen] = useState(false);
|
||||
const [detailPanelDocIds, setDetailPanelDocIds] = useState([]);
|
||||
const [detailPanelDocs, setDetailPanelDocs] = useState([]);
|
||||
const lastScrolledDetailDocRef = useRef(null);
|
||||
const [detailPanelDocId, setDetailPanelDocId] = useState(null);
|
||||
const [detailPanelDocument, setDetailPanelDocument] = useState(null);
|
||||
const latestOrderedDocsRef = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
latestOrderedDocsRef.current = orderedSelectedDocuments;
|
||||
if (detailPanelOpen && orderedSelectedDocuments.length) {
|
||||
const snapshotIds = orderedSelectedDocuments
|
||||
.map((doc) => doc?.id)
|
||||
.filter((id) => typeof id === 'string' || typeof id === 'number');
|
||||
if (snapshotIds.length) {
|
||||
setDetailPanelDocIds(snapshotIds);
|
||||
const nextDoc = orderedSelectedDocuments[orderedSelectedDocuments.length - 1];
|
||||
if (nextDoc?.id) {
|
||||
setDetailPanelDocId(nextDoc.id);
|
||||
setDetailPanelDocument(nextDoc);
|
||||
}
|
||||
}
|
||||
}, [orderedSelectedDocuments, detailPanelOpen]);
|
||||
|
||||
const resolveDocsForIds = useCallback(
|
||||
(ids, fallbackDocs = []) => {
|
||||
if (!ids?.length) {
|
||||
return [];
|
||||
}
|
||||
const fallbackMap = new Map((fallbackDocs || []).map((doc) => [doc?.id, doc]));
|
||||
return ids
|
||||
.map((id) => documentLookup.get(id) || fallbackMap.get(id) || null)
|
||||
.filter(Boolean);
|
||||
},
|
||||
[documentLookup],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!detailPanelDocIds.length) {
|
||||
setDetailPanelDocs((prev) => (prev.length ? [] : prev));
|
||||
return;
|
||||
}
|
||||
|
||||
setDetailPanelDocs((prevDocs) => {
|
||||
const resolved = resolveDocsForIds(detailPanelDocIds, prevDocs);
|
||||
if (resolved.length === prevDocs.length && resolved.every((doc, index) => doc === prevDocs[index])) {
|
||||
return prevDocs;
|
||||
if (!detailPanelDocId) {
|
||||
if (!detailPanelOpen) {
|
||||
setDetailPanelDocument(null);
|
||||
}
|
||||
return resolved;
|
||||
});
|
||||
}, [detailPanelDocIds, resolveDocsForIds]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!detailPanelOpen) {
|
||||
lastScrolledDetailDocRef.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!orderedSelectedDocuments.length) {
|
||||
lastScrolledDetailDocRef.current = null;
|
||||
return;
|
||||
const resolved = documentLookup.get(detailPanelDocId);
|
||||
if (resolved && resolved !== detailPanelDocument) {
|
||||
setDetailPanelDocument(resolved);
|
||||
}
|
||||
|
||||
let lastSelectedId = null;
|
||||
for (let index = selectionOrder.length - 1; index >= 0; index -= 1) {
|
||||
const key = selectionOrder[index];
|
||||
if (isDocumentRowKey(key)) {
|
||||
lastSelectedId = getRowId(key);
|
||||
if (lastSelectedId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastSelectedId && orderedSelectedDocuments.length) {
|
||||
const fallbackDoc = orderedSelectedDocuments[orderedSelectedDocuments.length - 1];
|
||||
lastSelectedId = fallbackDoc?.id || null;
|
||||
}
|
||||
|
||||
if (!lastSelectedId || lastScrolledDetailDocRef.current === lastSelectedId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetElement =
|
||||
document.getElementById(`document-row-${lastSelectedId}`)
|
||||
|| document.getElementById(`document-card-${lastSelectedId}`);
|
||||
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastScrolledDetailDocRef.current = lastSelectedId;
|
||||
requestAnimationFrame(() => {
|
||||
targetElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
||||
});
|
||||
}, [
|
||||
detailPanelOpen,
|
||||
orderedSelectedDocuments,
|
||||
selectionOrder,
|
||||
documentsViewMode,
|
||||
getRowId,
|
||||
isDocumentRowKey,
|
||||
]);
|
||||
|
||||
const detailPanelSelectedDocuments = detailPanelDocs;
|
||||
}, [detailPanelDocId, documentLookup, detailPanelDocument, detailPanelOpen]);
|
||||
|
||||
const openDetailPanel = useCallback(
|
||||
({ documentIds: explicitIds, documents: explicitDocs } = {}) => {
|
||||
let sourceDocs = Array.isArray(explicitDocs) ? explicitDocs : null;
|
||||
let snapshotIds = Array.isArray(explicitIds)
|
||||
? explicitIds.filter((id) => typeof id === 'string' || typeof id === 'number')
|
||||
: null;
|
||||
({ documentId, document, documentIds, documents } = {}) => {
|
||||
let targetDoc = document || null;
|
||||
let targetId = documentId ?? document?.id ?? null;
|
||||
|
||||
if (!snapshotIds?.length) {
|
||||
if (!sourceDocs || !sourceDocs.length) {
|
||||
sourceDocs = latestOrderedDocsRef.current;
|
||||
}
|
||||
snapshotIds = Array.isArray(sourceDocs)
|
||||
? sourceDocs
|
||||
.map((doc) => doc?.id)
|
||||
.filter((id) => typeof id === 'string' || typeof id === 'number')
|
||||
: [];
|
||||
if (!targetDoc && Array.isArray(documents) && documents.length) {
|
||||
targetDoc = documents[documents.length - 1];
|
||||
targetId = targetDoc?.id ?? targetId;
|
||||
}
|
||||
|
||||
const uniqueIds = [];
|
||||
snapshotIds.forEach((id) => {
|
||||
if (!uniqueIds.includes(id)) {
|
||||
uniqueIds.push(id);
|
||||
if (!targetDoc && Array.isArray(documentIds) && documentIds.length) {
|
||||
targetId = documentIds[documentIds.length - 1];
|
||||
}
|
||||
|
||||
if (!targetDoc && targetId != null) {
|
||||
targetDoc = documentLookup.get(String(targetId)) || null;
|
||||
}
|
||||
|
||||
if (!targetDoc) {
|
||||
const fallbackDocs = latestOrderedDocsRef.current;
|
||||
const fallbackDoc = Array.isArray(fallbackDocs) && fallbackDocs.length
|
||||
? fallbackDocs[fallbackDocs.length - 1]
|
||||
: null;
|
||||
if (fallbackDoc) {
|
||||
targetDoc = fallbackDoc;
|
||||
targetId = fallbackDoc.id;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const resolvedDocs = resolveDocsForIds(uniqueIds, sourceDocs || latestOrderedDocsRef.current);
|
||||
if (!targetDoc && targetId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDetailPanelDocIds(uniqueIds);
|
||||
setDetailPanelDocs(resolvedDocs);
|
||||
setDetailPanelOpen(true);
|
||||
setDetailPanelDocId(targetDoc?.id || targetId || null);
|
||||
setDetailPanelDocument(targetDoc || null);
|
||||
setDetailPanelOpen(Boolean(targetDoc || targetId));
|
||||
},
|
||||
[resolveDocsForIds],
|
||||
[documentLookup],
|
||||
);
|
||||
|
||||
const closeDetailPanel = useCallback(() => {
|
||||
@@ -152,7 +79,7 @@ export const useDetailPanel = ({
|
||||
|
||||
return {
|
||||
detailPanelOpen,
|
||||
detailPanelSelectedDocuments,
|
||||
detailPanelDocument,
|
||||
openDetailPanel,
|
||||
closeDetailPanel,
|
||||
setDetailPanelOpen,
|
||||
|
||||
Reference in New Issue
Block a user