diff --git a/frontend/src/app/useWorkspaceSelection.ts b/frontend/src/app/useWorkspaceSelection.ts index c2b13a1..d80c354 100644 --- a/frontend/src/app/useWorkspaceSelection.ts +++ b/frontend/src/app/useWorkspaceSelection.ts @@ -14,7 +14,7 @@ interface WorkspaceSelectionOptions { isDocumentRowKey?: (key: RowKey | SelectionEntry) => boolean; isFolderRowKey?: (key: RowKey | SelectionEntry) => boolean; getRowId?: (key: RowKey | SelectionEntry) => string | number | null; - onInspectDocument?: (id: string | number) => void; + onDocumentActivate?: (id: string | number) => void; onInspectFolder?: (id: string | number) => void; } @@ -26,7 +26,7 @@ export const useWorkspaceSelection = ({ isDocumentRowKey = () => false, isFolderRowKey = () => false, getRowId = () => null, - onInspectDocument = identity, + onDocumentActivate = identity, onInspectFolder = identity, }: WorkspaceSelectionOptions = {}) => { const selection = useDocumentSelection({ @@ -88,9 +88,9 @@ export const useWorkspaceSelection = ({ const inspectDocument = useCallback( (documentId?: string | number | null) => { if (!documentId) return; - onInspectDocument(documentId); + onDocumentActivate(documentId); }, - [onInspectDocument], + [onDocumentActivate], ); const inspectFolder = useCallback( diff --git a/frontend/src/desktop/DesktopDocumentCard.tsx b/frontend/src/desktop/DesktopDocumentCard.tsx index 944e88f..9a1504b 100644 --- a/frontend/src/desktop/DesktopDocumentCard.tsx +++ b/frontend/src/desktop/DesktopDocumentCard.tsx @@ -30,7 +30,7 @@ interface DesktopDocumentCardProps { getDocumentAsset?: (...args: any[]) => unknown; handleNavigatorSnapshot?: (...args: any[]) => void; cardPointerHandlers?: React.HTMLAttributes; - onInspectDocument?: (id: string | number) => void; + onDocumentActivate?: (id: string | number) => void; onTagDragEnter?: (event: React.DragEvent, docId: string | number) => void; onTagDragOver?: (event: React.DragEvent, docId: string | number) => void; onTagDragLeave?: (event: React.DragEvent, docId: string | number) => void; @@ -57,7 +57,7 @@ const DesktopDocumentCard: React.FC = ({ getDocumentAsset, handleNavigatorSnapshot, cardPointerHandlers, - onInspectDocument, + onDocumentActivate, onTagDragEnter, onTagDragOver, onTagDragLeave, @@ -117,7 +117,7 @@ const DesktopDocumentCard: React.FC = ({ onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { preventAll(event); - onInspectDocument?.(doc.id); + onDocumentActivate?.(doc.id); } }} > diff --git a/frontend/src/desktop/DesktopWorkspace.tsx b/frontend/src/desktop/DesktopWorkspace.tsx index 1cd0fed..73faf7d 100644 --- a/frontend/src/desktop/DesktopWorkspace.tsx +++ b/frontend/src/desktop/DesktopWorkspace.tsx @@ -28,6 +28,7 @@ import usePreviewMetadata from './hooks/usePreviewMetadata'; import '../styles/workspace/workspace-layout.css'; import '../styles/workspace/workspace-items.css'; import '../styles/workspace/workspace-cards.css'; +import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext'; type Identifier = string | number; @@ -118,17 +119,13 @@ type WorkspaceSnapshotState = { }; interface DesktopWorkspaceProps { - documents?: DeskDocument[]; - onInspectDocument?: (...args: unknown[]) => void; - onEntryPointer?: (...args: unknown[]) => void; - onDocumentStackSelect?: (docIds: Identifier[]) => void; - onPromoteSelection?: (...args: unknown[]) => void; - onAssignTagToDocument?: (...args: unknown[]) => void; + entries?: DeskDocument[]; + onDocumentActivate?: (...args: unknown[]) => void; + onDocumentClick?: (...args: unknown[]) => void; + onDocumentTagDrop?: (...args: unknown[]) => void; ensureAssetUrl?: EnsureAssetUrl; getDocumentAsset?: GetAsset; - activeTagIds?: Array; - selectedDocumentIds?: Identifier[]; - onClearSelection?: () => void; + activeTagFilters?: Array; tenantId?: Identifier | null; viewId?: string | null; } @@ -167,12 +164,12 @@ interface DesktopWorkspaceViewProps { overlayOriginRect: DOMRect | null; overlayOriginTransform: OverlayOriginTransform | null; overlayDocument: DeskDocument | null; - onEntryPointer?: DesktopWorkspaceProps['onEntryPointer']; - onDocumentStackSelect?: DesktopWorkspaceProps['onDocumentStackSelect']; - onPromoteSelection?: DesktopWorkspaceProps['onPromoteSelection']; - selectedDocumentIds: Identifier[]; - onClearSelection?: DesktopWorkspaceProps['onClearSelection']; + onDocumentClick?: DesktopWorkspaceProps['onDocumentClick']; + onDocumentStackSelect?: (docIds: Identifier[], event?: PointerEvent | MouseEvent | null) => void; + onPromoteSelection?: (docId: Identifier | null) => void; documentLookup: Map; + selectedDocumentIds: Identifier[]; + onClearSelection: () => void; resolveBaseMetrics: (doc: DeskDocument | null, cardWidth: number, cardHeight: number) => { baseWidth: number; baseHeight: number; @@ -184,7 +181,7 @@ interface DesktopWorkspaceViewProps { openOverlayForDoc: (docId: Identifier | null, originInfo?: OverlayOriginHint | null) => void; recalcVisibleDocIds: () => void; dragSettings: DragSettings; - onInspectDocument?: DesktopWorkspaceProps['onInspectDocument']; + onDocumentActivate?: DesktopWorkspaceProps['onDocumentActivate']; markLayoutDirty: () => void; } @@ -193,23 +190,60 @@ const DEBUG_FOCUS = false; const defaultGetDocumentAsset: GetAsset = () => null; const DesktopWorkspace: React.FC = ({ - documents = [], - onInspectDocument = null, - onEntryPointer = null, - onDocumentStackSelect = null, - onPromoteSelection = null, - onAssignTagToDocument = null, + entries = [], + onDocumentActivate = null, + onDocumentClick = null, + onDocumentTagDrop = null, ensureAssetUrl = null, getDocumentAsset = defaultGetDocumentAsset, - activeTagIds = [], - selectedDocumentIds = [], - onClearSelection = null, + activeTagFilters = [], tenantId = null, viewId = 'default', documentLinks, ensureDownloadUrl, }) => { - const items = useMemo(() => documents, [documents]); + const { + selectedDocumentIds, + clearSelection, + handleEntrySelection, + promoteSelectionOrder, + } = useWorkspaceSelectionContext(); + const items = useMemo( + () => (Array.isArray(entries) ? entries.filter((doc): doc is DeskDocument => Boolean(doc)) : []), + [entries], + ); + + const getDocRowKey = useCallback((id: Identifier | null) => (id != null ? `document:${id}` : null), []); + + const handleStackSelect = useCallback( + (docIds: Identifier[], event?: PointerEvent | MouseEvent | null) => { + if (!Array.isArray(docIds) || docIds.length === 0) { + return; + } + const syntheticEvent = event || ({ + metaKey: true, + ctrlKey: true, + preventDefault: () => {}, + } as unknown as PointerEvent); + docIds.forEach((id) => { + const key = getDocRowKey(id); + if (key) { + handleEntrySelection(key, syntheticEvent); + } + }); + }, + [getDocRowKey, handleEntrySelection], + ); + + const handlePromoteSelection = useCallback( + (docId: Identifier | null) => { + const key = getDocRowKey(docId); + if (key && promoteSelectionOrder) { + promoteSelectionOrder(key); + } + }, + [getDocRowKey, promoteSelectionOrder], + ); const allowLayoutPersistence = Boolean(tenantId && viewId && viewId.startsWith('folder:')); const documentLinkMap = documentLinks instanceof Map ? documentLinks : null; @@ -380,17 +414,17 @@ const DesktopWorkspace: React.FC = ({ [applySnapshotDimensions], ); const activeTagSet = useMemo>(() => { - if (!Array.isArray(activeTagIds) || activeTagIds.length === 0) { + if (!Array.isArray(activeTagFilters) || activeTagFilters.length === 0) { return new Set(); } const set = new Set(); - activeTagIds.forEach((id) => { + activeTagFilters.forEach((id) => { if (id != null) { set.add(String(id)); } }); return set; - }, [activeTagIds]); + }, [activeTagFilters]); useLayoutEffect(() => { const container = containerRef.current; @@ -480,7 +514,7 @@ const DesktopWorkspace: React.FC = ({ const tagInteractions = useDeskTagInteractions({ engine, - onAssignTagToDocument, + onAssignTagToDocument: onDocumentTagDrop, requestCanvasFocus, }); @@ -782,11 +816,11 @@ const DesktopWorkspace: React.FC = ({ overlayOriginRect, overlayOriginTransform, overlayDocument, - onEntryPointer, - onDocumentStackSelect, - onPromoteSelection, + onDocumentClick, + onDocumentStackSelect: handleStackSelect, + onPromoteSelection: handlePromoteSelection, selectedDocumentIds, - onClearSelection, + onClearSelection: clearSelection, documentLookup, resolveBaseMetrics, bringToFront, @@ -795,7 +829,7 @@ const DesktopWorkspace: React.FC = ({ openOverlayForDoc, recalcVisibleDocIds, dragSettings, - onInspectDocument, + onDocumentActivate, markLayoutDirty, }), [ @@ -827,10 +861,7 @@ const DesktopWorkspace: React.FC = ({ items, layoutRef, layoutSnapshot, - onClearSelection, - onDocumentStackSelect, - onEntryPointer, - onPromoteSelection, + onDocumentClick, openOverlayForDoc, overlayDisplay, overlayOriginRect, @@ -843,7 +874,7 @@ const DesktopWorkspace: React.FC = ({ resolveBaseMetrics, setDraggingId, selectedDocumentIds, - onInspectDocument, + onDocumentActivate, markLayoutDirty, tagDropTargetId, visibleDocIds, @@ -885,7 +916,7 @@ function DesktopWorkspaceView({ overlayOriginRect, overlayOriginTransform, overlayDocument, - onEntryPointer, + onDocumentClick, onDocumentStackSelect, onPromoteSelection, selectedDocumentIds, @@ -898,7 +929,7 @@ function DesktopWorkspaceView({ openOverlayForDoc, recalcVisibleDocIds, dragSettings, - onInspectDocument, + onDocumentActivate, markLayoutDirty, dragTransformsRef, }: DesktopWorkspaceViewProps) { @@ -918,8 +949,7 @@ function DesktopWorkspaceView({ recalcVisibleDocIds, settings: dragSettings, containerRef, - onInspectDocument, - onDocumentStackSelect, + onDocumentActivate, selectedDocumentIds, markLayoutDirty, }) as { @@ -939,10 +969,10 @@ function DesktopWorkspaceView({ handlePointerMove, handlePointerUp, handlePointerCancel, - onEntryPointer, + onDocumentClick, onDocumentStackSelect, onPromoteSelection, - onInspectDocument, + onDocumentActivate, selectedDocumentIds, openOverlayForDoc, }) as { @@ -967,7 +997,7 @@ function DesktopWorkspaceView({ <>
{ if (event.target === event.currentTarget) { - onClearSelection?.(); + onClearSelection(); } focusShell(); }} @@ -982,7 +1012,7 @@ function DesktopWorkspaceView({ onDrop={handleCanvasDrop} onPointerDown={(event) => { if (event.target === event.currentTarget) { - onClearSelection?.(); + onClearSelection(); } focusShell(); }} @@ -1073,7 +1103,7 @@ function DesktopWorkspaceView({ getDocumentAsset={getDocumentAsset} handleNavigatorSnapshot={handleNavigatorSnapshot} cardPointerHandlers={cardPointerHandlers} - onInspectDocument={onInspectDocument} + onDocumentActivate={onDocumentActivate} onTagDragEnter={handleTagDragEnterDoc} onTagDragOver={handleTagDragOverDoc} onTagDragLeave={handleTagDragLeaveDoc} diff --git a/frontend/src/desktop/pointer/useDeskPointer.js b/frontend/src/desktop/pointer/useDeskPointer.js index 5c87d61..383acaa 100644 --- a/frontend/src/desktop/pointer/useDeskPointer.js +++ b/frontend/src/desktop/pointer/useDeskPointer.js @@ -32,10 +32,10 @@ export const useDeskPointer = ({ handlePointerMove, handlePointerUp, handlePointerCancel, - onEntryPointer, + onDocumentClick, onDocumentStackSelect, onPromoteSelection, - onInspectDocument, + onDocumentActivate, selectedDocumentIds, openOverlayForDoc = null, }) => { @@ -242,16 +242,16 @@ export const useDeskPointer = ({ stackHits, }); - if (intent.selectedAtDown) { - safeInvoke(onPromoteSelection, doc.id, event); - } + if (intent.selectedAtDown) { + safeInvoke(onPromoteSelection, doc.id, event); + } - applyClickPlanImmediately({ - intent, - event, - onEntryPointer, - onDocumentStackSelect, - }); + applyClickPlanImmediately({ + intent, + event, + onEntryPointer: onDocumentClick, + onDocumentStackSelect, + }); pointerIntentRef.current = intent; @@ -272,7 +272,7 @@ export const useDeskPointer = ({ [ handlePointerDown, onPromoteSelection, - onEntryPointer, + onDocumentClick, onDocumentStackSelect, resolveStackDocIds, resetLongPressState, @@ -308,7 +308,7 @@ export const useDeskPointer = ({ finalizeClickSelection({ intent: pointerState, event, - onEntryPointer, + onEntryPointer: onDocumentClick, onDocumentStackSelect, }); @@ -325,7 +325,7 @@ export const useDeskPointer = ({ const stillSelected = Array.isArray(selectedDocumentIds) && selectedDocumentIds.includes(doc.id); if (isPrimaryRelease && stillSelected) { - safeInvoke(onInspectDocument, doc.id); + safeInvoke(onDocumentActivate, doc.id); } } } @@ -335,9 +335,9 @@ export const useDeskPointer = ({ }, [ handlePointerUp, - onInspectDocument, + onDocumentActivate, onDocumentStackSelect, - onEntryPointer, + onDocumentClick, resetLongPressState, selectedDocumentIds, ], diff --git a/frontend/src/desktop/useDocumentDrag.ts b/frontend/src/desktop/useDocumentDrag.ts index b031b31..7c6c9ca 100644 --- a/frontend/src/desktop/useDocumentDrag.ts +++ b/frontend/src/desktop/useDocumentDrag.ts @@ -108,7 +108,7 @@ interface UseDocumentDragOptions { recalcVisibleDocIds: () => void; settings?: DragSettings; containerRef?: RefObject; - onInspectDocument?: (docId: Identifier | null, event?: PointerEvent | ReactPointerEvent) => void; + onDocumentActivate?: (docId: Identifier | null, event?: PointerEvent | ReactPointerEvent) => void; onDocumentStackSelect?: ( docIds: Identifier[], event: PointerEvent | ReactPointerEvent, @@ -208,15 +208,15 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => { bringToFront, setDraggingId, canvasSize, - openOverlayForDoc, - recalcVisibleDocIds, - settings, - containerRef: providedContainerRef, - onInspectDocument, - onDocumentStackSelect, - selectedDocumentIds = [], - markLayoutDirty, - } = options; + openOverlayForDoc, + recalcVisibleDocIds, + settings, + containerRef: providedContainerRef, + onDocumentActivate, + onDocumentStackSelect, + selectedDocumentIds = [], + markLayoutDirty, +} = options; const fallbackContainerRef = useRef(null); const containerRef = providedContainerRef ?? fallbackContainerRef; @@ -246,7 +246,7 @@ const useDocumentDrag = (options: UseDocumentDragOptions) => { openOverlayForDoc?.(data.docId, data.originInfo); return; } - onInspectDocument?.(data.docId, event); + onDocumentActivate?.(data.docId, event); }, }); const dragStateRef = useRef(null); diff --git a/frontend/src/documents/DocumentsList.tsx b/frontend/src/documents/DocumentsList.tsx index 62cc7ec..570e71d 100644 --- a/frontend/src/documents/DocumentsList.tsx +++ b/frontend/src/documents/DocumentsList.tsx @@ -60,7 +60,6 @@ export type DocumentEventHandler = (document: DocumentLike, event: MouseEvent | null; draggedFolderId?: Identifier | 'root' | null; ensureAssetUrl?: (...args: any[]) => unknown; @@ -91,7 +90,6 @@ export interface DocumentsListProps { const DocumentsList: React.FC = ({ entries, - focusedRowKey, draggingDocumentIdsSet, draggedFolderId, ensureAssetUrl, @@ -185,7 +183,6 @@ const DocumentsList: React.FC = ({ const canDragFolder = folder.id !== 'root'; const isDraggingFolder = draggedFolderId === folder.id; const isSelectedFolder = selectedFolderIdsSet?.has(folder.id); - const rowKey = `folder:${folder.id}`; const canRenameFolder = Boolean(onFolderRename) && folder.id !== 'root'; const isFolderEditing = editingFolderId === folder.id; const folderDraftValue = isFolderEditing ? folderDraft : folder.name; @@ -198,9 +195,7 @@ const DocumentsList: React.FC = ({ return ( onFolderClick?.(folder, event)} onDoubleClick={(event) => { diff --git a/frontend/src/documents/hooks/useDocumentsPanelProps.ts b/frontend/src/documents/hooks/useDocumentsPanelProps.ts index 224ee3c..2112984 100644 --- a/frontend/src/documents/hooks/useDocumentsPanelProps.ts +++ b/frontend/src/documents/hooks/useDocumentsPanelProps.ts @@ -55,7 +55,7 @@ export interface UseDocumentsPanelPropsArgs { clearDocumentSelection?: () => void; handleDeleteSelection?: () => void; handleEntryPointerCore?: (...args: unknown[]) => void; - inspectDocument?: (docId: Identifier | null, metadata?: unknown) => void; + onDocumentActivate?: (docId: Identifier | null, metadata?: unknown) => void; tags?: unknown[]; correspondents?: unknown[]; documentLookup?: unknown; @@ -107,7 +107,7 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { handleDocumentsViewModeChange, handleDeleteSelection, handleEntryPointerCore, - inspectDocument, + onDocumentActivate, tags, correspondents, documentLookup, @@ -159,7 +159,7 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { onViewModeChange: handleDocumentsViewModeChange, onDeleteSelection: handleDeleteSelection, onEntryPointer: handleEntryPointerCore, - onInspectDocument: inspectDocument, + onDocumentActivate, tags, correspondents, documentLookup, @@ -206,7 +206,7 @@ const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { handleFolderDragEnd, handleFolderDragStart, handleFolderRename, - inspectDocument, + onDocumentActivate, moveDocumentsToFolder, openDocumentPreview, refreshCurrentFolder, diff --git a/frontend/src/documents/panel/DocumentsPanel.tsx b/frontend/src/documents/panel/DocumentsPanel.tsx index 643dcac..175add7 100644 --- a/frontend/src/documents/panel/DocumentsPanel.tsx +++ b/frontend/src/documents/panel/DocumentsPanel.tsx @@ -61,7 +61,7 @@ const DocumentsPanelInner: React.FC = ({ onDocumentDragEnd, onDocumentRename, onEntryPointer = null, - onInspectDocument = null, + onDocumentActivate = null, tagLookupById, activeCorrespondentIds = [], ensureAssetUrl = null, @@ -368,9 +368,9 @@ type ZoomSource = { url: string; alt?: string | null; mimeType?: string | null } handleDocumentPreviewZoom(doc); return; } - onInspectDocument?.(doc.id); + onDocumentActivate?.(doc.id); }, - [handleDocumentPreviewZoom, onInspectDocument], + [handleDocumentPreviewZoom, onDocumentActivate], ); const navigableRows = useMemo( diff --git a/frontend/src/documents/useEntryPointer.ts b/frontend/src/documents/useEntryPointer.ts index 7a89665..b79bccc 100644 --- a/frontend/src/documents/useEntryPointer.ts +++ b/frontend/src/documents/useEntryPointer.ts @@ -29,7 +29,7 @@ interface UseEntryPointerOptions { resolveDocumentRowKey?: (id: string | number) => string | null; resolveFolderRowKey?: (id: string | number) => string | null; onSelectEntry?: (entry: WorkspaceEntry, event?: PointerEventLike | null, metadata?: EntryPointerMetadata) => void; - onInspectDocument?: (id: string | number, metadata?: EntryPointerMetadata) => void; + onDocumentActivate?: (id: string | number, metadata?: EntryPointerMetadata) => void; } export interface EntryPointerMetadata { @@ -44,7 +44,7 @@ export const useEntryPointer = ({ resolveDocumentRowKey, resolveFolderRowKey, onSelectEntry, - onInspectDocument, + onDocumentActivate, }: UseEntryPointerOptions) => useCallback( (entry?: WorkspaceEntry | null, event?: PointerEventLike | null) => { @@ -70,10 +70,10 @@ export const useEntryPointer = ({ onSelectEntry?.(entry, event, metadata); if (type === 'document' && !modifierClick && primaryClick) { - onInspectDocument?.(id, metadata); + onDocumentActivate?.(id, metadata); } }, - [resolveDocumentRowKey, resolveFolderRowKey, onSelectEntry, onInspectDocument], + [resolveDocumentRowKey, resolveFolderRowKey, onSelectEntry, onDocumentActivate], ); export default useEntryPointer; diff --git a/frontend/src/hooks/documents/useDocumentsWorkspace.ts b/frontend/src/hooks/documents/useDocumentsWorkspace.ts index d6d5dca..0f2b1b4 100644 --- a/frontend/src/hooks/documents/useDocumentsWorkspace.ts +++ b/frontend/src/hooks/documents/useDocumentsWorkspace.ts @@ -1420,17 +1420,15 @@ const useDocumentsWorkspace = ({ const deskWorkspaceProps = useMemo( () => ({ - documents: viewDocuments, - onInspectDocument: inspectDocumentForDesk, - onEntryPointer: handleEntryPointerCore, + entries: viewDocuments, + onDocumentActivate: inspectDocumentForDesk, + onDocumentClick: handleEntryPointerCore, onDocumentStackSelect: handleDeskDocumentStackSelect, onPromoteSelection: promoteSelectionOrder, - onAssignTagToDocument: handleDocumentTagDrop, + onDocumentTagDrop: handleDocumentTagDrop, ensureAssetUrl, getDocumentAsset, - activeTagIds: activeTagFilters, - selectedDocumentIds, - onClearSelection: clearDocumentSelection, + activeTagFilters, tenantId: currentTenantId, viewId: deskViewId, documentLinks, @@ -1446,8 +1444,6 @@ const useDocumentsWorkspace = ({ ensureAssetUrl, getDocumentAsset, activeTagFilters, - selectedDocumentIds, - clearDocumentSelection, currentTenantId, deskViewId, documentLinks, @@ -1487,7 +1483,7 @@ const useDocumentsWorkspace = ({ clearDocumentSelection, handleDeleteSelection, handleEntryPointerCore, - inspectDocument, + onDocumentActivate: inspectDocument, tags, correspondents, documentLookup,