This commit is contained in:
2025-11-04 00:48:37 +01:00
parent 01fd774b18
commit f523e5651b
3 changed files with 166 additions and 182 deletions
+5 -164
View File
@@ -3133,165 +3133,6 @@ const AppLayout = () => {
], ],
); );
const handleDocumentListFocus = useCallback(() => {
if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
return;
}
let resolvedKey = null;
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableRowKeys.includes(candidate)) {
resolvedKey = candidate;
break;
}
}
if (!resolvedKey && navigableRows.length) {
resolvedKey = navigableRows[0].key;
}
if (!resolvedKey) {
return;
}
if (selectedEntries.length === 0) {
return;
}
setFocusedRowKey(resolvedKey);
if (!selectedEntries.includes(resolvedKey) && selectedEntries.length > 0) {
applySelection([resolvedKey], { anchor: resolvedKey, interactedKeys: [resolvedKey] });
}
}, [
focusedRowKey,
navigableRowKeys,
selectedEntries,
navigableRows,
applySelection,
setFocusedRowKey,
]);
const handleDocumentListKeyDown = 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 =
focusedRowKey && navigableRowKeys.includes(focusedRowKey)
? focusedRowKey
: null;
let initializedFromEmptyState = false;
if (!activeKey) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableRowKeys.includes(candidate)) {
activeKey = candidate;
break;
}
}
}
if (!activeKey) {
if (key === 'ArrowUp') {
const lastKey = navigableRowKeys[navigableRowKeys.length - 1];
if (!lastKey) {
return;
}
activeKey = lastKey;
} else {
activeKey = navigableRowKeys[0];
}
setFocusedRowKey(activeKey);
initializedFromEmptyState = true;
}
let currentIndex = navigableRowKeys.indexOf(activeKey);
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
const row = currentIndex === -1 ? navigableRows[0] : navigableRows[currentIndex];
if (!row) {
return;
}
handleEntrySelection(row.key, event);
if (row.type === 'folder') {
selectFolder(row.id);
} else if (row.type === 'document') {
openDocumentPreview(row.id);
}
return;
}
let nextIndex = currentIndex;
if (key === 'ArrowDown') {
if (initializedFromEmptyState && selectedEntries.length === 0) {
handleEntrySelection(activeKey, {
shiftKey,
preventDefault: () => {},
});
return;
}
nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, navigableRows.length - 1);
} else if (key === 'ArrowUp') {
if (initializedFromEmptyState && selectedEntries.length === 0) {
handleEntrySelection(activeKey, {
shiftKey,
preventDefault: () => {},
});
return;
}
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;
}
if (nextIndex === currentIndex && key !== 'Home' && key !== 'End') {
return;
}
const targetRow = navigableRows[nextIndex];
if (!targetRow) {
return;
}
setFocusedRowKey(targetRow.key);
handleEntrySelection(targetRow.key, {
shiftKey,
preventDefault: () => {},
});
},
[
navigableRows,
navigableRowKeys,
focusedRowKey,
selectedEntries,
handleEntrySelection,
selectFolder,
openDocumentPreview,
setFocusedRowKey,
],
);
useEffect(() => { useEffect(() => {
if (!previewDocumentId) return; if (!previewDocumentId) return;
const handleKeyDown = (event) => { const handleKeyDown = (event) => {
@@ -4728,8 +4569,7 @@ const AppLayout = () => {
isSearchLoading: searchLoading, isSearchLoading: searchLoading,
tagLookupById, tagLookupById,
activeCorrespondentIds: activeCorrespondentFilters, activeCorrespondentIds: activeCorrespondentFilters,
onDocumentListFocus: handleDocumentListFocus, selectedEntries,
onDocumentListKeyDown: handleDocumentListKeyDown,
onFocusedRowChange: setFocusedRowKey, onFocusedRowChange: setFocusedRowKey,
ensureAssetUrl, ensureAssetUrl,
getDocumentAsset, getDocumentAsset,
@@ -4741,6 +4581,7 @@ const AppLayout = () => {
onClearSelection: clearDocumentSelection, onClearSelection: clearDocumentSelection,
onDeleteSelection: handleDeleteSelection, onDeleteSelection: handleDeleteSelection,
onEntryPointer: handleEntryPointer, onEntryPointer: handleEntryPointer,
onEntrySelection: handleEntrySelection,
tags, tags,
correspondents, correspondents,
documentLookup, documentLookup,
@@ -4764,14 +4605,14 @@ const AppLayout = () => {
folderClickHandlers, folderClickHandlers,
handleDocumentDragEnd, handleDocumentDragEnd,
handleDocumentDragStart, handleDocumentDragStart,
handleDocumentListFocus,
handleDocumentListKeyDown,
handleDocumentTagDrop, handleDocumentTagDrop,
handleDocumentTitleUpdate, handleDocumentTitleUpdate,
handleDocumentsViewModeChange, handleDocumentsViewModeChange,
handleFolderDragEnd, handleFolderDragEnd,
handleFolderDragStart, handleFolderDragStart,
handleFolderRename, handleFolderRename,
handleEntryPointer,
handleEntrySelection,
handleDeleteSelection, handleDeleteSelection,
isFilterActive, isFilterActive,
openDocumentPreview, openDocumentPreview,
@@ -4780,12 +4621,12 @@ const AppLayout = () => {
searchResults, searchResults,
selectFolder, selectFolder,
selectedDocumentIds, selectedDocumentIds,
selectedEntries,
selectedFolderIds, selectedFolderIds,
setFocusedRowKey, setFocusedRowKey,
tagLookupById, tagLookupById,
toggleCorrespondentFilter, toggleCorrespondentFilter,
toggleTagFilter, toggleTagFilter,
handleEntryPointer,
ensureAssetUrl, ensureAssetUrl,
getDocumentAsset, getDocumentAsset,
tags, tags,
+10 -12
View File
@@ -158,24 +158,22 @@ const useDocumentDrag = () => {
: []; : [];
if (stackDocIdsOption && stackDocIdsOption.length) { if (stackDocIdsOption && stackDocIdsOption.length) {
selectionIds = stackDocIdsOption; const selectionSet = new Set(selectionIds);
stackDocIdsOption.forEach((value) => {
if (value != null) {
selectionSet.add(String(value));
}
});
selectionIds = Array.from(selectionSet);
} }
const metaOrCtrl = event.metaKey || event.ctrlKey; const metaOrCtrl = event.metaKey || event.ctrlKey;
if (!stackDocIdsOption && metaOrCtrl && !selectionIds.includes(docKey)) { if (!stackDocIdsOption && metaOrCtrl && !selectionIds.includes(docKey)) {
selectionIds = [...selectionIds, docKey]; selectionIds = [...selectionIds, docKey];
} }
let groupDocIds = []; let groupDocIds = selectionIds
if (stackDocIdsOption && stackDocIdsOption.length) { .map((id) => String(id))
groupDocIds = stackDocIdsOption.filter((id, index, array) => { .filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id));
const unique = array.indexOf(id) === index;
return unique && documentLookup.has(id);
});
} else if (selectionIds.includes(docKey) && selectionIds.length > 1) {
groupDocIds = selectionIds
.map((id) => String(id))
.filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id));
}
if (!groupDocIds.includes(docKey)) { if (!groupDocIds.includes(docKey)) {
groupDocIds.unshift(docKey); groupDocIds.unshift(docKey);
} }
+151 -6
View File
@@ -49,10 +49,9 @@ const DocumentsPanel = ({
onDocumentDragEnd, onDocumentDragEnd,
onDocumentRename, onDocumentRename,
onEntryPointer = null, onEntryPointer = null,
onEntrySelection = null,
tagLookupById, tagLookupById,
activeCorrespondentIds = [], activeCorrespondentIds = [],
onDocumentListFocus,
onDocumentListKeyDown,
onFocusedRowChange, onFocusedRowChange,
ensureAssetUrl = null, ensureAssetUrl = null,
getDocumentAsset = () => null, getDocumentAsset = () => null,
@@ -63,6 +62,7 @@ const DocumentsPanel = ({
viewMode = 'list', viewMode = 'list',
onViewModeChange, onViewModeChange,
onClearSelection, onClearSelection,
selectedEntries = [],
showHeader = true, showHeader = true,
}) => { }) => {
const showingSearchResults = searchResults !== null; const showingSearchResults = searchResults !== null;
@@ -198,6 +198,153 @@ const DocumentsPanel = ({
[handleDocumentPreviewZoom], [handleDocumentPreviewZoom],
); );
const selectedRowKeySet = useMemo(() => new Set(selectedEntries || []), [selectedEntries]);
const navigableRows = useMemo(
() => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
[entries],
);
const navigableRowKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);
const getEntryByKey = useCallback(
(rowKey) => entries.find((entry) => entry.key === rowKey) || null,
[entries],
);
const handlePanelFocus = useCallback(() => {
let resolvedKey = null;
if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
resolvedKey = focusedRowKey;
}
if (!resolvedKey) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableRowKeys.includes(candidate)) {
resolvedKey = candidate;
break;
}
}
}
if (!resolvedKey && navigableRows.length) {
resolvedKey = navigableRows[0].key;
}
if (!resolvedKey) {
return;
}
onFocusedRowChange?.(resolvedKey);
if (!selectedRowKeySet.has(resolvedKey) && typeof onEntrySelection === 'function') {
onEntrySelection(resolvedKey, {
shiftKey: false,
preventDefault: () => {},
});
}
}, [
focusedRowKey,
navigableRowKeys,
navigableRows,
onEntrySelection,
onFocusedRowChange,
selectedEntries,
selectedRowKeySet,
]);
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 =
focusedRowKey && navigableRowKeys.includes(focusedRowKey)
? focusedRowKey
: null;
if (!activeKey) {
if (selectedEntries.length) {
for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
const candidate = selectedEntries[index];
if (navigableRowKeys.includes(candidate)) {
activeKey = candidate;
break;
}
}
}
if (!activeKey) {
activeKey = key === 'ArrowUp' ? navigableRowKeys[navigableRowKeys.length - 1] : navigableRowKeys[0];
}
}
const currentIndex = navigableRowKeys.indexOf(activeKey);
const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
if (activeRow) {
onEntrySelection?.(activeRow.key, event);
if (activeRow.type === EntryType.folder) {
onFolderSelect?.(activeRow.id);
} else {
const entry = getEntryByKey(activeRow.key);
if (entry?.document) {
handleDocumentActivate(entry.document, event);
}
}
}
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;
}
onFocusedRowChange?.(targetRow.key);
onEntrySelection?.(targetRow.key, {
shiftKey,
preventDefault: () => {},
});
},
[
focusedRowKey,
getEntryByKey,
handleDocumentActivate,
navigableRowKeys,
navigableRows,
onEntrySelection,
onFocusedRowChange,
onFolderSelect,
selectedEntries,
],
);
const isListView = viewMode === 'list'; const isListView = viewMode === 'list';
const gridIconSize = DEFAULT_GRID_ICON_SIZE; const gridIconSize = DEFAULT_GRID_ICON_SIZE;
const handleSetViewMode = useCallback( const handleSetViewMode = useCallback(
@@ -482,16 +629,14 @@ const DocumentsPanel = ({
tabIndex={0} tabIndex={0}
onFocus={(event) => { onFocus={(event) => {
if (event.target === scrollRef.current) { if (event.target === scrollRef.current) {
onDocumentListFocus?.(); handlePanelFocus();
} }
}} }}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.target !== scrollRef.current) { if (event.target !== scrollRef.current) {
return; return;
} }
if (onDocumentListKeyDown) { handlePanelKeyDown(event);
onDocumentListKeyDown(event);
}
}} }}
onClick={(event) => { onClick={(event) => {
if (event.target === event.currentTarget) { if (event.target === event.currentTarget) {