feat: add keyboard navigation for document views, externalize document preview handling, and include physics model documentation.

This commit is contained in:
2025-11-27 01:06:31 +01:00
parent 3588fde3a2
commit 9ba581f64e
6 changed files with 306 additions and 392 deletions
+18 -15
View File
@@ -6,7 +6,6 @@ import React, {
useState,
} from 'react';
import { LayoutStore, LayoutCard } from './LayoutSystem';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
import DesktopDocumentCard from './DesktopDocumentCard';
import usePreviewMetadata from './hooks/usePreviewMetadata';
import useDeskTagInteractions from './tags/useDeskTagInteractions';
@@ -31,12 +30,6 @@ export interface DeskDocument {
[key: string]: unknown;
}
interface OverlayDisplay {
url: string;
alt?: string | null;
mimeType?: string | null;
}
interface DocumentSizeInfo {
width: number;
height: number;
@@ -57,6 +50,7 @@ export interface DesktopWorkspaceProps {
onDocumentTagDrop?: (docId: Identifier, tag: any) => void;
tenantId?: Identifier | null;
viewId?: string | null;
onPreview?: (doc: DeskDocument) => void;
}
// Wrapper to handle hooks per card
@@ -90,6 +84,7 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
onDocumentTagDrop,
tenantId,
viewId,
onPreview,
}) => {
const { addPointer, removePointer } = usePointerTracking();
const containerRef = useRef<HTMLDivElement>(null);
@@ -206,9 +201,22 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
requestCanvasFocus: focusShell,
});
// Overlay State
const [overlayDisplay, setOverlayDisplay] = useState<OverlayDisplay | null>(null);
const closeOverlay = useCallback(() => setOverlayDisplay(null), []);
useEffect(() => {
const handleWindowKeyDown = (e: KeyboardEvent) => {
if (e.code === 'Space' && selectedDocumentIds.length > 0) {
// Preview the last selected document
const lastId = selectedDocumentIds[selectedDocumentIds.length - 1];
const doc = items.find(i => String(i.id) === lastId);
if (doc && onPreview) {
e.preventDefault();
onPreview(doc);
}
}
};
window.addEventListener('keydown', handleWindowKeyDown);
return () => window.removeEventListener('keydown', handleWindowKeyDown);
}, [selectedDocumentIds, items, onPreview]);
return (
<>
@@ -305,11 +313,6 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
})}
</div>
</div>
<PreviewZoomOverlay
open={Boolean(overlayDisplay?.url)}
onClose={closeOverlay}
document={null}
/>
</>
);
};
+49 -3
View File
@@ -1,5 +1,7 @@
import React from 'react';
import React, { useEffect, useCallback } from 'react';
import { useDocumentViewLogic, DocumentViewLogic } from './hooks/useDocumentViewLogic';
import { useDocumentsNavigation } from './hooks/useDocumentsNavigation';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
import DocumentsListRow from './components/DocumentsListRow';
import DocumentsGridCard from './components/DocumentsGridCard';
import DocumentsListContainer from './components/DocumentsListContainer';
@@ -19,15 +21,59 @@ const AbstractDocumentsView = <CProps extends { clearSelection: () => void; chil
containerProps,
...props
}: AbstractDocumentsViewProps<CProps>) => {
const { entries, onDocumentRename, onFolderRename } = props;
const { entries, onDocumentRename, onFolderRename, onFolderSelect, onPreview, scrollRef, viewId } = props;
const viewLogic = useDocumentViewLogic({
onDocumentRename,
onFolderRename,
});
const { handleKeyDown, handleFocus } = useDocumentsNavigation({
entries,
onFolderSelect,
onPreview,
});
const { clearSelection } = viewLogic;
useEffect(() => {
if (scrollRef?.current) {
scrollRef.current.scrollTop = 0;
}
}, [scrollRef, viewId]);
const { focusedEntryKey } = useWorkspaceSelectionContext();
const ensureFocusedEntryVisible = useCallback(() => {
if (!focusedEntryKey) return;
const container = scrollRef?.current;
if (!container) return;
let selector = null;
if (focusedEntryKey.startsWith('document:')) {
selector = `#document-${focusedEntryKey.slice('document:'.length)}`;
} else if (focusedEntryKey.startsWith('folder:')) {
selector = `#folder-${focusedEntryKey.slice('folder:'.length)}`;
}
if (!selector) {
return;
}
const entry = container.querySelector(selector) as HTMLElement;
if (!entry || !container.contains(entry)) {
return;
}
entry.scrollIntoView({ block: 'nearest' });
}, [focusedEntryKey, scrollRef]);
useEffect(() => {
ensureFocusedEntryVisible();
}, [ensureFocusedEntryVisible]);
return (
<ContainerComponent clearSelection={clearSelection} {...(containerProps as any)}>
<ContainerComponent
clearSelection={clearSelection}
onKeyDown={handleKeyDown}
onFocus={handleFocus}
tabIndex={0}
{...(containerProps as any)}
>
{entries.map((entry) => (
<ItemComponent
key={entry.key}
@@ -4,17 +4,20 @@ interface DocumentsGridContainerProps {
children: React.ReactNode;
clearSelection: () => void;
gridIconSize?: number;
[key: string]: any;
}
const DocumentsGridContainer: React.FC<DocumentsGridContainerProps> = ({
children,
clearSelection,
gridIconSize,
...props
}) => {
return (
<div
className="documents-grid"
role="list"
{...props}
style={
gridIconSize
? ({ '--documents-grid-icon-size': `${gridIconSize}px` } as React.CSSProperties)
@@ -3,14 +3,16 @@ import React from 'react';
interface DocumentsListContainerProps {
children: React.ReactNode;
clearSelection: () => void;
[key: string]: any;
}
const DocumentsListContainer: React.FC<DocumentsListContainerProps> = ({
children,
clearSelection,
...props
}) => {
return (
<table aria-multiselectable="true">
<table aria-multiselectable="true" {...props}>
<thead
onClick={() => {
clearSelection();
@@ -0,0 +1,171 @@
import React, { useCallback, useMemo } from 'react';
import type { DocumentsListEntry } from '../../types/documents';
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
interface UseDocumentsNavigationProps {
entries: DocumentsListEntry[];
onFolderSelect?: (folderId: string) => void;
onPreview?: (doc: any) => void;
}
export const useDocumentsNavigation = ({
entries,
onFolderSelect,
onPreview,
}: UseDocumentsNavigationProps) => {
const {
selectedEntries,
focusedEntryKey,
setFocusedEntryKey,
handleEntrySelection,
} = useWorkspaceSelectionContext();
const navigableRows = useMemo(
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
[entries],
);
const navigableEntryKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);
const getEntryByKey = useCallback(
(entryKey: string) => entries.find((entry) => entry.key === entryKey) || null,
[entries],
);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent) => {
const { key, shiftKey } = event;
const triggers = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', ' ', 'Space', 'Spacebar'];
if (!triggers.includes(key)) {
return;
}
if (!navigableRows.length) {
return;
}
event.preventDefault();
let activeKey =
focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)
? focusedEntryKey
: null;
if (!activeKey) {
if (selectedEntries.length) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableEntryKeys.includes(candidate)) {
activeKey = candidate;
break;
}
}
}
if (!activeKey) {
activeKey = key === 'ArrowUp' ? navigableEntryKeys[navigableEntryKeys.length - 1] : navigableEntryKeys[0];
}
}
const currentIndex = navigableEntryKeys.indexOf(activeKey);
const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
if (activeRow) {
handleEntrySelection(activeRow.key, event);
if (activeRow.type === 'folder') {
onFolderSelect?.(activeRow.id as string);
} else {
const entry = getEntryByKey(activeRow.key);
// @ts-ignore
if (entry?.document) {
// @ts-ignore
onPreview?.(entry.document);
}
}
}
return;
}
let nextIndex = currentIndex;
if (key === 'ArrowDown') {
nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, navigableRows.length - 1);
} else if (key === 'ArrowUp') {
nextIndex = currentIndex === -1 ? navigableRows.length - 1 : Math.max(currentIndex - 1, 0);
} else if (key === 'Home') {
nextIndex = 0;
} else if (key === 'End') {
nextIndex = navigableRows.length - 1;
}
if (nextIndex === -1 || nextIndex >= navigableRows.length) {
return;
}
const targetRow = navigableRows[nextIndex];
if (!targetRow) {
return;
}
setFocusedEntryKey(targetRow.key);
handleEntrySelection(targetRow.key, {
shiftKey,
preventDefault: () => { },
});
},
[
focusedEntryKey,
getEntryByKey,
navigableEntryKeys,
navigableRows,
onFolderSelect,
selectedEntries,
onPreview,
handleEntrySelection,
setFocusedEntryKey,
],
);
const handleFocus = useCallback(() => {
let resolvedKey = null;
if (focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)) {
resolvedKey = focusedEntryKey;
}
if (!resolvedKey) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableEntryKeys.includes(candidate)) {
resolvedKey = candidate;
break;
}
}
}
if (!resolvedKey) {
if (!selectedEntries.length) {
return;
}
if (navigableRows.length) {
resolvedKey = navigableRows[0].key;
}
}
if (!resolvedKey) {
return;
}
setFocusedEntryKey(resolvedKey);
}, [
focusedEntryKey,
navigableEntryKeys,
navigableRows,
setFocusedEntryKey,
selectedEntries,
]);
return {
handleKeyDown,
handleFocus,
};
};
+62 -373
View File
@@ -22,7 +22,6 @@ import DocumentsPanelHeader, {
DocumentsHeaderBreadcrumb,
} from './DocumentsPanelHeader';
import { SelectionFloatingPanel } from '../SelectionFloatingActions';
import { createDocumentEntryKey } from '../../app/entryKey';
import { createDocumentsTableHeaderActions } from './DocumentsToolbar';
import { useDocumentsFilter } from '../context/DocumentsFilterContext';
import { DEFAULT_GRID_ICON_SIZE } from '../../constants/documents';
@@ -77,10 +76,7 @@ export interface DocumentsViewProps {
// Desk specific (optional for now or handled via intersection)
tenantId?: Identifier | null;
viewId?: string | null;
documentLinks?: Map<Identifier, any> | null;
ensureDownloadUrl?: (docId: Identifier, options?: { force?: boolean }) => Promise<any>;
onDocumentStackSelect?: (docIds: Identifier[], event?: any) => void;
onPromoteSelection?: (docId: Identifier | null) => void;
activeTagFilters?: Array<Identifier | null>;
}
@@ -113,7 +109,6 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
isSearchLoading = false,
viewMode = 'list',
onViewModeChange,
documentLinks,
ensureDownloadUrl,
onRefresh = () => { },
sortField,
@@ -135,18 +130,12 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
activeTagFilters = [],
activeCorrespondentFilters = [],
selectedFolder = null,
promoteSelectionOrder,
onDocumentTagDrop,
currentTenantId,
}): ReactNode => {
const {
selectedEntries,
focusedEntryKey,
setFocusedEntryKey,
handleEntrySelection,
clearSelection,
selectionAnchorRef,
applySelection,
} = useWorkspaceSelectionContext();
const {
isActive: isFilterActive,
@@ -167,43 +156,10 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
const showingSearchResults = Array.isArray(searchResultIds);
const rows = showingSearchResults && searchDocuments ? searchDocuments : documents;
const documentLinkMap = documentLinks instanceof Map ? documentLinks : null;
const searchResultCount = Array.isArray(searchResultIds) ? searchResultIds.length : 0;
const handleDeskDocumentStackSelect = useCallback(
(docIds: Array<Identifier | string>) => {
if (!Array.isArray(docIds) || docIds.length === 0) {
return;
}
const entryKeys = docIds
.map((id) => createDocumentEntryKey(id as Identifier))
.filter((value): value is string => typeof value === 'string');
if (!entryKeys.length) {
return;
}
const nextKeys = [...selectedEntries];
entryKeys.forEach((key) => {
if (!nextKeys.includes(key)) {
nextKeys.push(key);
}
});
const anchor = (entryKeys[0]
|| selectionAnchorRef.current
|| nextKeys[nextKeys.length - 1]) as string | null;
applySelection(nextKeys, {
anchor,
interactedKeys: entryKeys,
});
},
[applySelection, selectedEntries, selectionAnchorRef],
);
const deskViewId = useMemo(() => {
const viewId = useMemo(() => {
if (showingSearchResults) {
const trimmedQuery = searchQuery.trim();
const tagsKey = [...activeTagFilters].sort().join(',');
@@ -361,89 +317,59 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
const isDeskView = viewMode === 'desk';
const gridIconSize = DEFAULT_GRID_ICON_SIZE;
type ZoomSource = { url: string; alt?: string | null; mimeType?: string | null };
const [previewDocId, setPreviewDocId] = useState<Identifier | null>(null);
const previewDoc = useMemo(() => {
if (!previewDocId) {
return null;
}
return rows.find((doc) => doc?.id === previewDocId) || null;
}, [previewDocId, rows]);
const [previewDoc, setPreviewDoc] = useState<Document | null>(null);
useEffect(() => {
if (previewDocId && !previewDoc) {
setPreviewDocId(null);
if (previewDoc && !rows.find(d => d.id === previewDoc.id)) {
setPreviewDoc(null);
}
}, [previewDocId, previewDoc]);
}, [previewDoc, rows]);
const [previewZoomSource, setPreviewZoomSource] = useState<ZoomSource | null>(null);
const zoomDisplay = previewZoomSource;
const overlayDocument = useMemo(() => (
previewDoc && zoomDisplay?.url
? { ...previewDoc, documentLink: zoomDisplay }
: previewDoc
), [previewDoc, zoomDisplay]);
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
if (!previewDocId || !previewDoc) {
setPreviewZoomSource(null);
return () => {
cancelled = true;
};
const overlayDocument = useMemo(() => {
if (!previewDoc || !previewUrl) {
return previewDoc;
}
const documentMimeType = previewDoc.mime_type;
const applyEntry = (entry?: DocumentLinkLike | null) => {
if (!entry?.url) {
setPreviewZoomSource(null);
return;
}
setPreviewZoomSource({
url: entry.url,
return {
...previewDoc,
documentLink: {
url: previewUrl,
alt: previewDoc.title,
mimeType: documentMimeType,
});
mimeType: previewDoc.mime_type,
},
};
}, [previewDoc, previewUrl]);
const cachedEntry = documentLinkMap?.get(previewDocId) || null;
if (cachedEntry?.url) {
applyEntry(cachedEntry);
return () => {
cancelled = true;
};
useEffect(() => {
if (!previewDoc) {
setPreviewUrl(null);
return;
}
if (!ensureDownloadUrl) {
setPreviewZoomSource(null);
return () => {
cancelled = true;
};
let cancelled = false;
if (ensureDownloadUrl) {
ensureDownloadUrl(previewDoc.id)
.then((entry) => {
if (!cancelled && entry?.url) {
setPreviewUrl(entry.url);
}
})
.catch(() => {
if (!cancelled) {
setPreviewUrl(null);
}
});
}
ensureDownloadUrl(previewDocId)
.then((entry) => {
if (cancelled) {
return;
}
applyEntry(entry);
})
.catch(() => {
if (!cancelled) {
setPreviewZoomSource(null);
}
});
return () => {
cancelled = true;
};
}, [previewDocId, previewDoc, documentLinkMap, ensureDownloadUrl]);
}, [previewDoc, ensureDownloadUrl]);
const closePreviewOverlay = useCallback(() => {
setPreviewDocId(null);
setPreviewZoomSource(null);
setPreviewDoc(null);
setPreviewUrl(null);
}, []);
const handleDocumentPreviewZoom = useCallback(
@@ -451,251 +377,15 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
if (!doc || !doc.id) {
return;
}
if (!ensureDownloadUrl && !(documentLinkMap?.get(doc.id)?.url)) {
if (!ensureDownloadUrl) {
return;
}
setPreviewDocId(doc.id);
setPreviewDoc(doc);
},
[ensureDownloadUrl, documentLinkMap],
[ensureDownloadUrl],
);
const handleDocumentActivate = useCallback(
(doc, event?: React.MouseEvent | KeyboardEvent | null) => {
if (!doc) {
return;
}
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (event?.altKey) {
handleDocumentPreviewZoom(doc);
return;
}
onDocumentActivate?.(doc.id);
},
[handleDocumentPreviewZoom, onDocumentActivate],
);
const navigableRows = useMemo(
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
[entries],
);
const navigableEntryKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);
const getEntryByKey = useCallback(
(entryKey) => entries.find((entry) => entry.key === entryKey) || null,
[entries],
);
const handlePanelFocus = useCallback(() => {
let resolvedKey = null;
if (focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)) {
resolvedKey = focusedEntryKey;
}
if (!resolvedKey) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableEntryKeys.includes(candidate)) {
resolvedKey = candidate;
break;
}
}
}
if (!resolvedKey) {
if (!selectedEntries.length) {
return;
}
if (navigableRows.length) {
resolvedKey = navigableRows[0].key;
}
}
if (!resolvedKey) {
return;
}
setFocusedEntryKey(resolvedKey);
}, [
focusedEntryKey,
navigableEntryKeys,
navigableRows,
setFocusedEntryKey,
selectedEntries,
]);
const handlePanelKeyDown = useCallback(
(event) => {
const { key, shiftKey } = event;
const triggers = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', ' ', 'Space', 'Spacebar'];
if (!triggers.includes(key)) {
return;
}
if (!navigableRows.length) {
return;
}
event.preventDefault();
let activeKey =
focusedEntryKey && navigableEntryKeys.includes(focusedEntryKey)
? focusedEntryKey
: null;
if (!activeKey) {
if (selectedEntries.length) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableEntryKeys.includes(candidate)) {
activeKey = candidate;
break;
}
}
}
if (!activeKey) {
activeKey = key === 'ArrowUp' ? navigableEntryKeys[navigableEntryKeys.length - 1] : navigableEntryKeys[0];
}
}
const currentIndex = navigableEntryKeys.indexOf(activeKey);
const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
if (activeRow) {
handleEntrySelection(activeRow.key, event);
if (activeRow.type === EntryType.folder) {
onFolderSelect?.(activeRow.id);
} else {
const entry = getEntryByKey(activeRow.key);
if (entry?.document) {
handleDocumentPreviewZoom(entry.document);
}
}
}
return;
}
let nextIndex = currentIndex;
if (key === 'ArrowDown') {
nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, navigableRows.length - 1);
} else if (key === 'ArrowUp') {
nextIndex = currentIndex === -1 ? navigableRows.length - 1 : Math.max(currentIndex - 1, 0);
} else if (key === 'Home') {
nextIndex = 0;
} else if (key === 'End') {
nextIndex = navigableRows.length - 1;
}
if (nextIndex === -1 || nextIndex >= navigableRows.length) {
return;
}
const targetRow = navigableRows[nextIndex];
if (!targetRow) {
return;
}
setFocusedEntryKey(targetRow.key);
handleEntrySelection(targetRow.key, {
shiftKey,
preventDefault: () => { },
});
},
[
focusedEntryKey,
getEntryByKey,
navigableEntryKeys,
navigableRows,
onFolderSelect,
selectedEntries,
handleDocumentPreviewZoom,
handleEntrySelection,
setFocusedEntryKey,
],
);
const scrollToTop = useCallback(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = 0;
}
}, []);
const searchKey = useMemo(
() => (Array.isArray(searchResultIds) ? searchResultIds.join(':') : 'none'),
[searchResultIds],
);
const breadcrumbKey = useMemo(
() => (Array.isArray(breadcrumbs) ? breadcrumbs.map((crumb) => crumb?.id ?? '').join(':') : 'none'),
[breadcrumbs],
);
useEffect(() => {
scrollToTop();
}, [
scrollToTop,
viewMode,
showingSearchResults,
searchKey,
breadcrumbKey,
]);
const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);
const ensureFocusedRowVisible = useCallback(() => {
if (!focusedEntryKey) return;
const container = scrollRef.current;
if (!container) return;
let selector = null;
if (focusedEntryKey.startsWith('document:')) {
selector = `#document-${focusedEntryKey.slice('document:'.length)}`;
} else if (focusedEntryKey.startsWith('folder:')) {
selector = `#folder-${focusedEntryKey.slice('folder:'.length)}`;
}
if (!selector) {
return;
}
const row = container.querySelector(selector);
if (!row || !container.contains(row)) {
return;
}
const header = container.querySelector('thead');
const headerHeight = header ? header.getBoundingClientRect().height : 0;
const rowTop = row.offsetTop;
const rowBottom = rowTop + row.offsetHeight;
const visibleTop = container.scrollTop + headerHeight;
const visibleBottom = container.scrollTop + container.clientHeight;
if (rowTop < visibleTop) {
container.scrollTop = Math.max(rowTop - headerHeight, 0);
return;
}
if (rowBottom > visibleBottom) {
const nextScrollTop = rowBottom - container.clientHeight;
container.scrollTop = Math.max(nextScrollTop, 0);
}
}, [focusedEntryKey]);
useEffect(() => {
ensureFocusedRowVisible();
}, [ensureFocusedRowVisible]);
const activeDescendantId = useMemo(() => {
if (!focusedEntryKey) return undefined;
if (focusedEntryKey.startsWith('document:')) {
return `document-${focusedEntryKey.slice('document:'.length)}`;
}
if (focusedEntryKey.startsWith('folder:')) {
return `folder-${focusedEntryKey.slice('folder:'.length)}`;
}
return undefined;
}, [focusedEntryKey]);
const handleDocumentTagDragOver = useCallback(
(event) => {
@@ -736,6 +426,24 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
[onEntryPointer],
);
const handleDocumentActivate = useCallback(
(doc, event?: React.MouseEvent | KeyboardEvent | null) => {
if (!doc) {
return;
}
if (event) {
event.preventDefault();
event.stopPropagation();
}
if (event?.altKey) {
handleDocumentPreviewZoom(doc);
return;
}
onDocumentActivate?.(doc.id);
},
[handleDocumentPreviewZoom, onDocumentActivate],
);
const handleFolderClick = useCallback(
(folder, event) => {
if (!folder) {
@@ -812,9 +520,8 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
activeCorrespondentIdSet: activeCorrespondentIdSet,
onCorrespondentClick: toggleCorrespondentFilter,
tenantId: currentTenantId,
viewId: deskViewId,
onDocumentStackSelect: handleDeskDocumentStackSelect,
onPromoteSelection: promoteSelectionOrder,
viewId,
onPreview: handleDocumentPreviewZoom,
};
const renderBody = () => {
@@ -839,7 +546,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
switch (viewMode) {
case 'desk':
return <DesktopWorkspace {...viewProps} tenantId={currentTenantId} viewId={deskViewId} />;
return <DesktopWorkspace {...viewProps} />;
case 'grid':
return <DocumentsGrid {...viewProps} gridIconSize={gridIconSize} />;
case 'list':
@@ -851,20 +558,6 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list';
const shouldHandlePanelInteractions = !isDeskView && entries.length > 0;
const handleSectionFocus = useCallback((event: React.FocusEvent<HTMLElement>) => {
if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) {
return;
}
handlePanelFocus();
}, [shouldHandlePanelInteractions, handlePanelFocus]);
const handleSectionKeyDown = useCallback((event: React.KeyboardEvent<HTMLElement>) => {
if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) {
return;
}
handlePanelKeyDown(event);
}, [shouldHandlePanelInteractions, handlePanelKeyDown]);
const handleSectionClick = useCallback((event: React.MouseEvent<HTMLElement>) => {
if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) {
return;
@@ -881,16 +574,12 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
<section
ref={scrollRef}
className={`documents-panel documents-panel--view-${panelVariant}`}
tabIndex={shouldHandlePanelInteractions ? 0 : undefined}
onFocus={handleSectionFocus}
onKeyDown={handleSectionKeyDown}
onClick={handleSectionClick}
aria-activedescendant={shouldHandlePanelInteractions && !isGridView ? activeDescendantId : undefined}
>
{renderBody()}
</section>
<PreviewZoomOverlay
open={Boolean(zoomDisplay?.url)}
open={Boolean(previewUrl)}
onClose={closePreviewOverlay}
document={overlayDocument}
/>