This commit is contained in:
2025-11-13 14:07:31 +01:00
parent 18558ab087
commit 62e44b5375
10 changed files with 152 additions and 132 deletions
+8 -4
View File
@@ -6,6 +6,7 @@ import UploadQueueOverlay from './UploadQueueOverlay';
import useDocumentsWorkspace from '../hooks/documents/useDocumentsWorkspace'; import useDocumentsWorkspace from '../hooks/documents/useDocumentsWorkspace';
import { useDocumentsPreferences } from './useDocumentsPreferences'; import { useDocumentsPreferences } from './useDocumentsPreferences';
import SettingsRoute from './SettingsRoute'; import SettingsRoute from './SettingsRoute';
import { WorkspaceSelectionProvider } from './WorkspaceSelectionContext';
const AppLayout: React.FC = () => { const AppLayout: React.FC = () => {
const documentsPreferences = useDocumentsPreferences(); const documentsPreferences = useDocumentsPreferences();
@@ -16,6 +17,7 @@ const AppLayout: React.FC = () => {
dropOverlayState, dropOverlayState,
managementModals, managementModals,
contextValue, contextValue,
workspaceSelection,
settingsOpen, settingsOpen,
closeSettings, closeSettings,
} = useDocumentsWorkspace({ } = useDocumentsWorkspace({
@@ -50,8 +52,9 @@ const AppLayout: React.FC = () => {
} }
return ( return (
<AppShellContext.Provider value={contextValue}> <WorkspaceSelectionProvider value={workspaceSelection}>
<div className="app-shell" ref={shellRef}> <AppShellContext.Provider value={contextValue}>
<div className="app-shell" ref={shellRef}>
<DropOverlay <DropOverlay
active={dropOverlayState.active} active={dropOverlayState.active}
folderName={dropOverlayState.folderName} folderName={dropOverlayState.folderName}
@@ -65,8 +68,9 @@ const AppLayout: React.FC = () => {
{settingsOpen ? ( {settingsOpen ? (
<SettingsRoute open onClose={closeSettings} /> <SettingsRoute open onClose={closeSettings} />
) : null} ) : null}
</div> </div>
</AppShellContext.Provider> </AppShellContext.Provider>
</WorkspaceSelectionProvider>
); );
}; };
@@ -0,0 +1,23 @@
import React, { createContext, useContext } from 'react';
import type useWorkspaceSelection from './useWorkspaceSelection';
type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection> | null;
const WorkspaceSelectionContext = createContext<WorkspaceSelectionValue>(null);
export const WorkspaceSelectionProvider: React.FC<{ value: NonNullable<WorkspaceSelectionValue>; children: React.ReactNode }>
= ({ value, children }) => (
<WorkspaceSelectionContext.Provider value={value}>
{children}
</WorkspaceSelectionContext.Provider>
);
export const useWorkspaceSelectionContext = () => {
const context = useContext(WorkspaceSelectionContext);
if (!context) {
throw new Error('useWorkspaceSelectionContext must be used within a WorkspaceSelectionProvider');
}
return context;
};
export default WorkspaceSelectionContext;
+38 -27
View File
@@ -4,6 +4,27 @@ import { createDocumentsTableHeaderActions } from '../documents/DocumentsPanel';
import createWorkspaceSurfaceConfig from '../documents/workspaceHeader'; import createWorkspaceSurfaceConfig from '../documents/workspaceHeader';
import DocumentViewerPanel from '../preview/DocumentViewerPanel'; import DocumentViewerPanel from '../preview/DocumentViewerPanel';
import DesktopWorkspace from './DesktopWorkspace'; import DesktopWorkspace from './DesktopWorkspace';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
type SelectionFloatingActionsBaseProps = Omit<React.ComponentProps<typeof SelectionFloatingActions>, 'selectionCount' | 'selectedDocumentIds' | 'selectedFolderIds'>;
const SelectionFloatingActionsWithSelection: React.FC<SelectionFloatingActionsBaseProps> = (props) => {
const { selectedDocumentIds, selectedFolderIds } = useWorkspaceSelectionContext();
const documentIds = Array.isArray(selectedDocumentIds) ? selectedDocumentIds : [];
const folderIds = Array.isArray(selectedFolderIds) ? selectedFolderIds : [];
const selectionCount = documentIds.length + folderIds.length;
if (selectionCount === 0) {
return null;
}
return (
<SelectionFloatingActions
selectionCount={selectionCount}
selectedDocumentIds={documentIds}
selectedFolderIds={folderIds}
{...props}
/>
);
};
type WorkspaceProps = Record<string, any>; type WorkspaceProps = Record<string, any>;
@@ -36,8 +57,6 @@ const createDesktopSurface = ({
onRefresh, onRefresh,
viewMode, viewMode,
onViewModeChange, onViewModeChange,
selectedDocumentIds,
selectedFolderIds,
onDeleteSelection, onDeleteSelection,
onClearSelection, onClearSelection,
tags, tags,
@@ -60,9 +79,6 @@ const createDesktopSurface = ({
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}` ? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
: null; : null;
const documentSelectionCount = Array.isArray(selectedDocumentIds) ? selectedDocumentIds.length : 0;
const folderSelectionCount = Array.isArray(selectedFolderIds) ? selectedFolderIds.length : 0;
const selectionCount = documentSelectionCount + folderSelectionCount;
const actions = createDocumentsTableHeaderActions({ const actions = createDocumentsTableHeaderActions({
viewMode: viewMode || 'desk', viewMode: viewMode || 'desk',
onViewModeChange, onViewModeChange,
@@ -71,28 +87,23 @@ const createDesktopSurface = ({
onToggleIncludeDescendants: onToggleSearchIncludeDescendants, onToggleIncludeDescendants: onToggleSearchIncludeDescendants,
}); });
const floatingActions = selectionCount > 0 const floatingActions = (
? ( <SelectionFloatingActionsWithSelection
<SelectionFloatingActions documentLookup={documentLookup}
selectionCount={selectionCount} tags={tags}
selectedDocumentIds={selectedDocumentIds} tagLookupById={tagLookupById}
selectedFolderIds={selectedFolderIds} correspondents={correspondents}
documentLookup={documentLookup} onBulkTagAdd={onBulkTagAdd}
tags={tags} onBulkTagRemove={onBulkTagRemove}
tagLookupById={tagLookupById} onBulkCorrespondentAdd={onBulkCorrespondentAdd}
correspondents={correspondents} onBulkCorrespondentRemove={onBulkCorrespondentRemove}
onBulkTagAdd={onBulkTagAdd} onBulkReanalyze={onBulkReanalyze}
onBulkTagRemove={onBulkTagRemove} onDeleteSelection={onDeleteSelection}
onBulkCorrespondentAdd={onBulkCorrespondentAdd} onClearSelection={onClearSelection}
onBulkCorrespondentRemove={onBulkCorrespondentRemove} folderOptions={folderOptions}
onBulkReanalyze={onBulkReanalyze} onMoveDocumentsToFolder={onMoveDocumentsToFolder}
onDeleteSelection={onDeleteSelection} />
onClearSelection={onClearSelection} );
folderOptions={folderOptions}
onMoveDocumentsToFolder={onMoveDocumentsToFolder}
/>
)
: null;
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
const detail = detailOpen && detailProps const detail = detailOpen && detailProps
+10 -8
View File
@@ -1,4 +1,4 @@
import React from 'react'; import React, { useMemo } from 'react';
import type { DragEvent, MouseEvent, RefObject } from 'react'; import type { DragEvent, MouseEvent, RefObject } from 'react';
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons'; import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
import DocumentThumbnailImage from './DocumentThumbnailImage'; import DocumentThumbnailImage from './DocumentThumbnailImage';
@@ -7,6 +7,7 @@ import { getTagColorStyle } from '../utils/colors';
import { resolveCorrespondents } from './correspondents'; import { resolveCorrespondents } from './correspondents';
import { writeTagTransferData } from './tagTransfer'; import { writeTagTransferData } from './tagTransfer';
import useInlineRename from './useInlineRename'; import useInlineRename from './useInlineRename';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
export type Identifier = string | number; export type Identifier = string | number;
@@ -55,8 +56,6 @@ type DocumentEventHandler = (document: DocumentLike, event: MouseEvent<HTMLDivEl
interface DocumentsGridProps { interface DocumentsGridProps {
entries: DocumentsGridEntry[]; entries: DocumentsGridEntry[];
selectedDocumentIdsSet?: Set<Identifier> | null;
selectedFolderIdsSet?: Set<Identifier | 'root'> | null;
draggingDocumentIdsSet?: Set<Identifier> | null; draggingDocumentIdsSet?: Set<Identifier> | null;
draggedFolderId?: Identifier | 'root' | null; draggedFolderId?: Identifier | 'root' | null;
onFolderClick?: FolderEventHandler; onFolderClick?: FolderEventHandler;
@@ -82,14 +81,11 @@ interface DocumentsGridProps {
scrollRef?: RefObject<HTMLElement | null>; scrollRef?: RefObject<HTMLElement | null>;
onCorrespondentClick?: (correspondentId?: Identifier | null) => void; onCorrespondentClick?: (correspondentId?: Identifier | null) => void;
activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null; activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null;
onClearSelection?: () => void;
onDocumentRename?: (docId: Identifier, title: string) => Promise<boolean> | boolean; onDocumentRename?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
} }
const DocumentsGrid: React.FC<DocumentsGridProps> = ({ const DocumentsGrid: React.FC<DocumentsGridProps> = ({
entries, entries,
selectedDocumentIdsSet,
selectedFolderIdsSet,
draggingDocumentIdsSet, draggingDocumentIdsSet,
draggedFolderId, draggedFolderId,
onFolderClick, onFolderClick,
@@ -114,10 +110,16 @@ const DocumentsGrid: React.FC<DocumentsGridProps> = ({
scrollRef, scrollRef,
onCorrespondentClick, onCorrespondentClick,
activeCorrespondentIdSet, activeCorrespondentIdSet,
onClearSelection,
onDocumentRename, onDocumentRename,
onFolderRename, onFolderRename,
}) => { }) => {
const {
selectedDocumentIds,
selectedFolderIds,
clearSelection,
} = useWorkspaceSelectionContext();
const selectedDocumentIdsSet = useMemo(() => new Set(selectedDocumentIds), [selectedDocumentIds]);
const selectedFolderIdsSet = useMemo(() => new Set(selectedFolderIds || []), [selectedFolderIds]);
const { const {
editingId: editingDocumentId, editingId: editingDocumentId,
draftValue: documentDraft, draftValue: documentDraft,
@@ -157,7 +159,7 @@ const DocumentsGrid: React.FC<DocumentsGridProps> = ({
style={{ '--documents-grid-icon-size': `${gridIconSize}px` }} style={{ '--documents-grid-icon-size': `${gridIconSize}px` }}
onClick={(event) => { onClick={(event) => {
if (event.target === event.currentTarget) { if (event.target === event.currentTarget) {
onClearSelection?.(); clearSelection();
} }
}} }}
> >
+13 -8
View File
@@ -1,4 +1,4 @@
import React from 'react'; import React, { useMemo } from 'react';
import type { DragEvent, MouseEvent, RefObject } from 'react'; import type { DragEvent, MouseEvent, RefObject } from 'react';
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons'; import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
import { getTagColorStyle } from '../utils/colors'; import { getTagColorStyle } from '../utils/colors';
@@ -8,6 +8,7 @@ import CorrespondentLinks from './CorrespondentLinks';
import { resolveCorrespondents } from './correspondents'; import { resolveCorrespondents } from './correspondents';
import { writeTagTransferData } from './tagTransfer'; import { writeTagTransferData } from './tagTransfer';
import useInlineRename from './useInlineRename'; import useInlineRename from './useInlineRename';
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
export type Identifier = string | number; export type Identifier = string | number;
@@ -60,8 +61,6 @@ export type DocumentEventHandler = (document: DocumentLike, event: MouseEvent<HT
export interface DocumentsListProps { export interface DocumentsListProps {
entries: DocumentsListEntry[]; entries: DocumentsListEntry[];
focusedRowKey?: string | null; focusedRowKey?: string | null;
selectedDocumentIdsSet?: Set<Identifier> | null;
selectedFolderIdsSet?: Set<Identifier | 'root'> | null;
draggingDocumentIdsSet?: Set<Identifier> | null; draggingDocumentIdsSet?: Set<Identifier> | null;
draggedFolderId?: Identifier | 'root' | null; draggedFolderId?: Identifier | 'root' | null;
ensureAssetUrl?: (...args: any[]) => unknown; ensureAssetUrl?: (...args: any[]) => unknown;
@@ -87,15 +86,12 @@ export interface DocumentsListProps {
onCorrespondentClick?: (correspondentId?: Identifier | null) => void; onCorrespondentClick?: (correspondentId?: Identifier | null) => void;
activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null; activeCorrespondentIdSet?: Set<Identifier | null | undefined> | null;
scrollRef?: RefObject<HTMLElement | null>; scrollRef?: RefObject<HTMLElement | null>;
onClearSelection?: () => void;
} }
const DocumentsList: React.FC<DocumentsListProps> = ({ const DocumentsList: React.FC<DocumentsListProps> = ({
entries, entries,
focusedRowKey, focusedRowKey,
selectedDocumentIdsSet,
selectedFolderIdsSet,
draggingDocumentIdsSet, draggingDocumentIdsSet,
draggedFolderId, draggedFolderId,
ensureAssetUrl, ensureAssetUrl,
@@ -121,8 +117,17 @@ const DocumentsList: React.FC<DocumentsListProps> = ({
onCorrespondentClick, onCorrespondentClick,
activeCorrespondentIdSet, activeCorrespondentIdSet,
scrollRef, scrollRef,
onClearSelection,
}) => { }) => {
const {
selectedDocumentIds,
selectedFolderIds,
clearSelection,
} = useWorkspaceSelectionContext();
const selectedDocumentIdsSet = useMemo(() => new Set(selectedDocumentIds), [selectedDocumentIds]);
const selectedFolderIdsSet = useMemo(
() => new Set(selectedFolderIds || []),
[selectedFolderIds],
);
const { const {
editingId: editingDocumentId, editingId: editingDocumentId,
draftValue: documentDraft, draftValue: documentDraft,
@@ -160,7 +165,7 @@ const DocumentsList: React.FC<DocumentsListProps> = ({
<table aria-multiselectable="true"> <table aria-multiselectable="true">
<thead <thead
onClick={() => { onClick={() => {
onClearSelection?.(); clearSelection();
}} }}
> >
<tr> <tr>
@@ -31,8 +31,6 @@ export interface UseDocumentsPanelPropsArgs {
handleFolderRename?: (...args: unknown[]) => void; handleFolderRename?: (...args: unknown[]) => void;
openDocumentPreview?: (...args: unknown[]) => void; openDocumentPreview?: (...args: unknown[]) => void;
handleDocumentTitleUpdate?: (...args: unknown[]) => void; handleDocumentTitleUpdate?: (...args: unknown[]) => void;
selectedDocumentIds?: Identifier[];
selectedFolderIds?: Identifier[];
focusedRowKey?: Identifier | string | null; focusedRowKey?: Identifier | string | null;
draggedDocumentIds?: Identifier[]; draggedDocumentIds?: Identifier[];
handleDocumentDragStart?: (...args: unknown[]) => void; handleDocumentDragStart?: (...args: unknown[]) => void;
@@ -40,8 +38,6 @@ export interface UseDocumentsPanelPropsArgs {
searchLoading?: boolean; searchLoading?: boolean;
tagLookupById?: unknown; tagLookupById?: unknown;
activeCorrespondentFilters?: Identifier[]; activeCorrespondentFilters?: Identifier[];
selectedEntries?: unknown[];
setFocusedRowKey?: (key: Identifier | string | null) => void;
ensureAssetUrl?: (...args: unknown[]) => void; ensureAssetUrl?: (...args: unknown[]) => void;
getDocumentAsset?: (...args: unknown[]) => unknown; getDocumentAsset?: (...args: unknown[]) => unknown;
toggleTagFilter?: (...args: unknown[]) => void; toggleTagFilter?: (...args: unknown[]) => void;
@@ -59,7 +55,6 @@ export interface UseDocumentsPanelPropsArgs {
handleDeleteSelection?: () => void; handleDeleteSelection?: () => void;
handleEntryPointerCore?: (...args: unknown[]) => void; handleEntryPointerCore?: (...args: unknown[]) => void;
inspectDocument?: (docId: Identifier | null, metadata?: unknown) => void; inspectDocument?: (docId: Identifier | null, metadata?: unknown) => void;
handleEntrySelection?: (...args: unknown[]) => void;
tags?: unknown[]; tags?: unknown[];
correspondents?: unknown[]; correspondents?: unknown[];
documentLookup?: unknown; documentLookup?: unknown;
@@ -91,8 +86,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
handleFolderRename, handleFolderRename,
openDocumentPreview, openDocumentPreview,
handleDocumentTitleUpdate, handleDocumentTitleUpdate,
selectedDocumentIds,
selectedFolderIds,
focusedRowKey, focusedRowKey,
draggedDocumentIds, draggedDocumentIds,
handleDocumentDragStart, handleDocumentDragStart,
@@ -100,8 +93,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
searchLoading, searchLoading,
tagLookupById, tagLookupById,
activeCorrespondentFilters, activeCorrespondentFilters,
selectedEntries,
setFocusedRowKey,
ensureAssetUrl, ensureAssetUrl,
getDocumentAsset, getDocumentAsset,
toggleTagFilter, toggleTagFilter,
@@ -115,11 +106,9 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
searchIncludeDescendants, searchIncludeDescendants,
toggleSearchIncludeDescendants, toggleSearchIncludeDescendants,
handleDocumentsViewModeChange, handleDocumentsViewModeChange,
clearDocumentSelection,
handleDeleteSelection, handleDeleteSelection,
handleEntryPointerCore, handleEntryPointerCore,
inspectDocument, inspectDocument,
handleEntrySelection,
tags, tags,
correspondents, correspondents,
documentLookup, documentLookup,
@@ -151,8 +140,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
onFolderRename: handleFolderRename, onFolderRename: handleFolderRename,
onDocumentOpen: openDocumentPreview, onDocumentOpen: openDocumentPreview,
onDocumentRename: handleDocumentTitleUpdate, onDocumentRename: handleDocumentTitleUpdate,
selectedDocumentIds,
selectedFolderIds,
focusedRowKey, focusedRowKey,
draggingDocumentIds: draggedDocumentIds, draggingDocumentIds: draggedDocumentIds,
onDocumentDragStart: handleDocumentDragStart, onDocumentDragStart: handleDocumentDragStart,
@@ -160,8 +147,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
isSearchLoading: searchLoading, isSearchLoading: searchLoading,
tagLookupById, tagLookupById,
activeCorrespondentIds: activeCorrespondentFilters, activeCorrespondentIds: activeCorrespondentFilters,
selectedEntries,
onFocusedRowChange: setFocusedRowKey,
ensureAssetUrl, ensureAssetUrl,
getDocumentAsset, getDocumentAsset,
onTagClick: toggleTagFilter, onTagClick: toggleTagFilter,
@@ -175,11 +160,9 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
searchIncludeDescendants, searchIncludeDescendants,
onToggleSearchIncludeDescendants: toggleSearchIncludeDescendants, onToggleSearchIncludeDescendants: toggleSearchIncludeDescendants,
onViewModeChange: handleDocumentsViewModeChange, onViewModeChange: handleDocumentsViewModeChange,
onClearSelection: clearDocumentSelection,
onDeleteSelection: handleDeleteSelection, onDeleteSelection: handleDeleteSelection,
onEntryPointer: handleEntryPointerCore, onEntryPointer: handleEntryPointerCore,
onInspectDocument: inspectDocument, onInspectDocument: inspectDocument,
onEntrySelection: handleEntrySelection,
tags, tags,
correspondents, correspondents,
documentLookup, documentLookup,
@@ -194,7 +177,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
[ [
activeCorrespondentFilters, activeCorrespondentFilters,
breadcrumbs, breadcrumbs,
clearDocumentSelection,
correspondents, correspondents,
currentFolderName, currentFolderName,
currentSubfolders, currentSubfolders,
@@ -221,7 +203,6 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
handleDocumentsSortFieldChange, handleDocumentsSortFieldChange,
handleDocumentsViewModeChange, handleDocumentsViewModeChange,
handleEntryPointerCore, handleEntryPointerCore,
handleEntrySelection,
handleFolderDragEnd, handleFolderDragEnd,
handleFolderDragStart, handleFolderDragStart,
handleFolderRename, handleFolderRename,
@@ -233,11 +214,7 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
searchIncludeDescendants, searchIncludeDescendants,
searchLoading, searchLoading,
searchResults, searchResults,
selectedDocumentIds,
selectedEntries,
selectedFolderIds,
selectFolder, selectFolder,
setFocusedRowKey,
tagLookupById, tagLookupById,
tags, tags,
toggleCorrespondentFilter, toggleCorrespondentFilter,
+20 -33
View File
@@ -7,6 +7,7 @@ import { isTagTransferEvent } from '../tagTransfer';
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay'; import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
import { useAssetNavigator } from '../../hooks/useAssetNavigator'; import { useAssetNavigator } from '../../hooks/useAssetNavigator';
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer'; import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
const DEFAULT_GRID_ICON_SIZE = 144; const DEFAULT_GRID_ICON_SIZE = 144;
@@ -37,19 +38,14 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
onFolderDragEnd, onFolderDragEnd,
draggedFolderId, draggedFolderId,
onFolderRename, onFolderRename,
selectedFolderIds = [],
selectedDocumentIds = [],
focusedRowKey,
draggingDocumentIds = [], draggingDocumentIds = [],
onDocumentDragStart, onDocumentDragStart,
onDocumentDragEnd, onDocumentDragEnd,
onDocumentRename, onDocumentRename,
onEntryPointer = null, onEntryPointer = null,
onEntrySelection = null,
onInspectDocument = null, onInspectDocument = null,
tagLookupById, tagLookupById,
activeCorrespondentIds = [], activeCorrespondentIds = [],
onFocusedRowChange,
ensureAssetUrl = null, ensureAssetUrl = null,
getDocumentAsset = defaultGetDocumentAsset, getDocumentAsset = defaultGetDocumentAsset,
onTagClick, onTagClick,
@@ -58,10 +54,15 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
onDocumentTagDrop, onDocumentTagDrop,
viewMode = 'list', viewMode = 'list',
onViewModeChange, onViewModeChange,
onClearSelection,
selectedEntries = [],
showHeader = true, showHeader = true,
}) => { }) => {
const {
selectedEntries,
focusedRowKey,
setFocusedRowKey,
handleEntrySelection,
clearSelection,
} = useWorkspaceSelectionContext();
const showingSearchResults = searchResults !== null; const showingSearchResults = searchResults !== null;
const rows = showingSearchResults ? searchResults : documents; const rows = showingSearchResults ? searchResults : documents;
@@ -89,9 +90,9 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
const changed = previous.type !== nextContext.type const changed = previous.type !== nextContext.type
|| previous.marker !== nextContext.marker; || previous.marker !== nextContext.marker;
if (changed) { if (changed) {
onClearSelection?.(); clearSelection();
} }
}, [showingSearchResults, currentFolderId, searchResults, onClearSelection]); }, [showingSearchResults, currentFolderId, searchResults, clearSelection]);
const entries = useMemo(() => { const entries = useMemo(() => {
const list = []; const list = [];
@@ -112,14 +113,6 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
return list; return list;
}, [showingSearchResults, subfolders, rows]); }, [showingSearchResults, subfolders, rows]);
const selectedSet = useMemo(
() => new Set(selectedDocumentIds),
[selectedDocumentIds],
);
const selectedFolderSet = useMemo(
() => new Set(selectedFolderIds || []),
[selectedFolderIds],
);
const draggingSet = useMemo( const draggingSet = useMemo(
() => new Set(draggingDocumentIds || []), () => new Set(draggingDocumentIds || []),
[draggingDocumentIds], [draggingDocumentIds],
@@ -271,12 +264,12 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
return; return;
} }
onFocusedRowChange?.(resolvedKey); setFocusedRowKey(resolvedKey);
}, [ }, [
focusedRowKey, focusedRowKey,
navigableRowKeys, navigableRowKeys,
navigableRows, navigableRows,
onFocusedRowChange, setFocusedRowKey,
selectedEntries, selectedEntries,
]); ]);
@@ -320,7 +313,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') { if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
if (activeRow) { if (activeRow) {
onEntrySelection?.(activeRow.key, event); handleEntrySelection(activeRow.key, event);
if (activeRow.type === EntryType.folder) { if (activeRow.type === EntryType.folder) {
onFolderSelect?.(activeRow.id); onFolderSelect?.(activeRow.id);
} else { } else {
@@ -353,8 +346,8 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
return; return;
} }
onFocusedRowChange?.(targetRow.key); setFocusedRowKey(targetRow.key);
onEntrySelection?.(targetRow.key, { handleEntrySelection(targetRow.key, {
shiftKey, shiftKey,
preventDefault: () => {}, preventDefault: () => {},
}); });
@@ -364,11 +357,11 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
getEntryByKey, getEntryByKey,
navigableRowKeys, navigableRowKeys,
navigableRows, navigableRows,
onEntrySelection,
onFocusedRowChange,
onFolderSelect, onFolderSelect,
selectedEntries, selectedEntries,
handleDocumentPreviewZoom, handleDocumentPreviewZoom,
handleEntrySelection,
setFocusedRowKey,
], ],
); );
@@ -529,10 +522,10 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
&& scrollRef.current && scrollRef.current
) { ) {
scrollRef.current.focus({ preventScroll: true }); scrollRef.current.focus({ preventScroll: true });
onFocusedRowChange?.(`folder:${folder.id}`); setFocusedRowKey(`folder:${folder.id}`);
} }
}, },
[onEntryPointer, onFocusedRowChange], [onEntryPointer, setFocusedRowKey],
); );
const handleDocumentDragStartLocal = useCallback( const handleDocumentDragStartLocal = useCallback(
@@ -666,7 +659,7 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
}} }}
onClick={(event) => { onClick={(event) => {
if (event.target === event.currentTarget) { if (event.target === event.currentTarget) {
onClearSelection?.(); clearSelection();
} }
}} }}
aria-activedescendant={isGridView ? undefined : activeDescendantId} aria-activedescendant={isGridView ? undefined : activeDescendantId}
@@ -674,8 +667,6 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
{isGridView ? ( {isGridView ? (
<DocumentsGrid <DocumentsGrid
entries={entries} entries={entries}
selectedDocumentIdsSet={selectedSet}
selectedFolderIdsSet={selectedFolderSet}
draggingDocumentIdsSet={draggingSet} draggingDocumentIdsSet={draggingSet}
draggedFolderId={draggedFolderId} draggedFolderId={draggedFolderId}
onFolderClick={handleFolderClick} onFolderClick={handleFolderClick}
@@ -700,7 +691,6 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
scrollRef={scrollRef} scrollRef={scrollRef}
onCorrespondentClick={onCorrespondentClick} onCorrespondentClick={onCorrespondentClick}
activeCorrespondentIdSet={activeCorrespondentIdSet} activeCorrespondentIdSet={activeCorrespondentIdSet}
onClearSelection={onClearSelection}
onDocumentRename={onDocumentRename} onDocumentRename={onDocumentRename}
onFolderRename={onFolderRename} onFolderRename={onFolderRename}
/> />
@@ -708,8 +698,6 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
<DocumentsList <DocumentsList
entries={entries} entries={entries}
focusedRowKey={focusedRowKey} focusedRowKey={focusedRowKey}
selectedDocumentIdsSet={selectedSet}
selectedFolderIdsSet={selectedFolderSet}
draggingDocumentIdsSet={draggingSet} draggingDocumentIdsSet={draggingSet}
draggedFolderId={draggedFolderId} draggedFolderId={draggedFolderId}
ensureAssetUrl={ensureAssetUrl} ensureAssetUrl={ensureAssetUrl}
@@ -735,7 +723,6 @@ const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
onCorrespondentClick={onCorrespondentClick} onCorrespondentClick={onCorrespondentClick}
activeCorrespondentIdSet={activeCorrespondentIdSet} activeCorrespondentIdSet={activeCorrespondentIdSet}
scrollRef={scrollRef} scrollRef={scrollRef}
onClearSelection={onClearSelection}
/> />
)} )}
</div> </div>
@@ -4,6 +4,27 @@ import SelectionFloatingActions from '../SelectionFloatingActions';
import createWorkspaceSurfaceConfig from '../workspaceHeader'; import createWorkspaceSurfaceConfig from '../workspaceHeader';
import DocumentsPanel from './DocumentsPanel'; import DocumentsPanel from './DocumentsPanel';
import { createDocumentsTableHeaderActions } from './DocumentsToolbar'; import { createDocumentsTableHeaderActions } from './DocumentsToolbar';
import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext';
type SelectionFloatingActionsBaseProps = Omit<React.ComponentProps<typeof SelectionFloatingActions>, 'selectionCount' | 'selectedDocumentIds' | 'selectedFolderIds'>;
const SelectionFloatingActionsWithSelection: React.FC<SelectionFloatingActionsBaseProps> = (props) => {
const { selectedDocumentIds, selectedFolderIds } = useWorkspaceSelectionContext();
const documentIds = Array.isArray(selectedDocumentIds) ? selectedDocumentIds : [];
const folderIds = Array.isArray(selectedFolderIds) ? selectedFolderIds : [];
const selectionCount = documentIds.length + folderIds.length;
if (selectionCount === 0) {
return null;
}
return (
<SelectionFloatingActions
selectionCount={selectionCount}
selectedDocumentIds={documentIds}
selectedFolderIds={folderIds}
{...props}
/>
);
};
interface CreateDocumentsSurfaceArgs { interface CreateDocumentsSurfaceArgs {
tableProps: Record<string, any>; tableProps: Record<string, any>;
@@ -30,8 +51,6 @@ const createDocumentsSurface = ({
sortDirection, sortDirection,
onSortFieldChange, onSortFieldChange,
onSortDirectionToggle, onSortDirectionToggle,
selectedDocumentIds,
selectedFolderIds,
onDeleteSelection, onDeleteSelection,
onClearSelection, onClearSelection,
tags, tags,
@@ -55,10 +74,6 @@ const createDocumentsSurface = ({
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}` ? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
: null; : null;
const documentSelectionCount = Array.isArray(selectedDocumentIds) ? selectedDocumentIds.length : 0;
const folderSelectionCount = Array.isArray(selectedFolderIds) ? selectedFolderIds.length : 0;
const selectionCount = documentSelectionCount + folderSelectionCount;
const actions = createDocumentsTableHeaderActions({ const actions = createDocumentsTableHeaderActions({
viewMode, viewMode,
onViewModeChange, onViewModeChange,
@@ -72,28 +87,23 @@ const createDocumentsSurface = ({
onToggleIncludeDescendants: onToggleSearchIncludeDescendants, onToggleIncludeDescendants: onToggleSearchIncludeDescendants,
}); });
const floatingActions = selectionCount > 0 const floatingActions = (
? ( <SelectionFloatingActionsWithSelection
<SelectionFloatingActions documentLookup={documentLookup}
selectionCount={selectionCount} tags={tags}
selectedDocumentIds={selectedDocumentIds} tagLookupById={tagLookupById}
selectedFolderIds={selectedFolderIds} correspondents={correspondents}
documentLookup={documentLookup} onBulkTagAdd={onBulkTagAdd}
tags={tags} onBulkTagRemove={onBulkTagRemove}
tagLookupById={tagLookupById} onBulkCorrespondentAdd={onBulkCorrespondentAdd}
correspondents={correspondents} onBulkCorrespondentRemove={onBulkCorrespondentRemove}
onBulkTagAdd={onBulkTagAdd} onBulkReanalyze={onBulkReanalyze}
onBulkTagRemove={onBulkTagRemove} onDeleteSelection={onDeleteSelection}
onBulkCorrespondentAdd={onBulkCorrespondentAdd} onClearSelection={onClearSelection}
onBulkCorrespondentRemove={onBulkCorrespondentRemove} folderOptions={folderOptions}
onBulkReanalyze={onBulkReanalyze} onMoveDocumentsToFolder={onMoveDocumentsToFolder}
onDeleteSelection={onDeleteSelection} />
onClearSelection={onClearSelection} );
folderOptions={folderOptions}
onMoveDocumentsToFolder={onMoveDocumentsToFolder}
/>
)
: null;
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
const detail = detailOpen && detailProps const detail = detailOpen && detailProps
@@ -1655,6 +1655,7 @@ const useDocumentsWorkspace = ({
dropOverlayState, dropOverlayState,
managementModals, managementModals,
contextValue, contextValue,
workspaceSelection: selection,
settingsOpen, settingsOpen,
closeSettings, closeSettings,
}; };
+1 -1
View File
@@ -67,7 +67,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
pointer-events: none; pointer-events: none;
z-index: 6; z-index: 20000;
height: 0; height: 0;
width: calc(100% - 2rem); width: calc(100% - 2rem);
margin: 0 1rem; margin: 0 1rem;