From f523e5651b2bffa2be92b81bdfbdd9834629987a Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Tue, 4 Nov 2025 00:48:37 +0100 Subject: [PATCH] fix --- frontend/src/app/AppLayout.jsx | 169 +--------------------- frontend/src/desktop/useDocumentDrag.js | 22 ++- frontend/src/documents/DocumentsPanel.jsx | 157 +++++++++++++++++++- 3 files changed, 166 insertions(+), 182 deletions(-) diff --git a/frontend/src/app/AppLayout.jsx b/frontend/src/app/AppLayout.jsx index 8687183..ad805d4 100644 --- a/frontend/src/app/AppLayout.jsx +++ b/frontend/src/app/AppLayout.jsx @@ -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(() => { if (!previewDocumentId) return; const handleKeyDown = (event) => { @@ -4728,8 +4569,7 @@ const AppLayout = () => { isSearchLoading: searchLoading, tagLookupById, activeCorrespondentIds: activeCorrespondentFilters, - onDocumentListFocus: handleDocumentListFocus, - onDocumentListKeyDown: handleDocumentListKeyDown, + selectedEntries, onFocusedRowChange: setFocusedRowKey, ensureAssetUrl, getDocumentAsset, @@ -4741,6 +4581,7 @@ const AppLayout = () => { onClearSelection: clearDocumentSelection, onDeleteSelection: handleDeleteSelection, onEntryPointer: handleEntryPointer, + onEntrySelection: handleEntrySelection, tags, correspondents, documentLookup, @@ -4764,14 +4605,14 @@ const AppLayout = () => { folderClickHandlers, handleDocumentDragEnd, handleDocumentDragStart, - handleDocumentListFocus, - handleDocumentListKeyDown, handleDocumentTagDrop, handleDocumentTitleUpdate, handleDocumentsViewModeChange, handleFolderDragEnd, handleFolderDragStart, handleFolderRename, + handleEntryPointer, + handleEntrySelection, handleDeleteSelection, isFilterActive, openDocumentPreview, @@ -4780,12 +4621,12 @@ const AppLayout = () => { searchResults, selectFolder, selectedDocumentIds, + selectedEntries, selectedFolderIds, setFocusedRowKey, tagLookupById, toggleCorrespondentFilter, toggleTagFilter, - handleEntryPointer, ensureAssetUrl, getDocumentAsset, tags, diff --git a/frontend/src/desktop/useDocumentDrag.js b/frontend/src/desktop/useDocumentDrag.js index 38f1775..f8b53ed 100644 --- a/frontend/src/desktop/useDocumentDrag.js +++ b/frontend/src/desktop/useDocumentDrag.js @@ -158,24 +158,22 @@ const useDocumentDrag = () => { : []; 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; if (!stackDocIdsOption && metaOrCtrl && !selectionIds.includes(docKey)) { selectionIds = [...selectionIds, docKey]; } - let groupDocIds = []; - if (stackDocIdsOption && stackDocIdsOption.length) { - groupDocIds = stackDocIdsOption.filter((id, index, array) => { - 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)); - } + let groupDocIds = selectionIds + .map((id) => String(id)) + .filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id)); if (!groupDocIds.includes(docKey)) { groupDocIds.unshift(docKey); } diff --git a/frontend/src/documents/DocumentsPanel.jsx b/frontend/src/documents/DocumentsPanel.jsx index 4f97bff..df85732 100644 --- a/frontend/src/documents/DocumentsPanel.jsx +++ b/frontend/src/documents/DocumentsPanel.jsx @@ -49,10 +49,9 @@ const DocumentsPanel = ({ onDocumentDragEnd, onDocumentRename, onEntryPointer = null, + onEntrySelection = null, tagLookupById, activeCorrespondentIds = [], - onDocumentListFocus, - onDocumentListKeyDown, onFocusedRowChange, ensureAssetUrl = null, getDocumentAsset = () => null, @@ -63,6 +62,7 @@ const DocumentsPanel = ({ viewMode = 'list', onViewModeChange, onClearSelection, + selectedEntries = [], showHeader = true, }) => { const showingSearchResults = searchResults !== null; @@ -198,6 +198,153 @@ const DocumentsPanel = ({ [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 gridIconSize = DEFAULT_GRID_ICON_SIZE; const handleSetViewMode = useCallback( @@ -482,16 +629,14 @@ const DocumentsPanel = ({ tabIndex={0} onFocus={(event) => { if (event.target === scrollRef.current) { - onDocumentListFocus?.(); + handlePanelFocus(); } }} onKeyDown={(event) => { if (event.target !== scrollRef.current) { return; } - if (onDocumentListKeyDown) { - onDocumentListKeyDown(event); - } + handlePanelKeyDown(event); }} onClick={(event) => { if (event.target === event.currentTarget) {