refactor: Consolidate document view props into a shared interface and refine tag drop payload handling.
This commit is contained in:
@@ -1,86 +1,21 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import type { DragEvent, MouseEvent, RefObject } from 'react';
|
||||
import { FolderIcon, CheckIcon, CloseIcon } from '../ui/icons';
|
||||
import DocumentThumbnailImage from './DocumentThumbnailImage';
|
||||
import CorrespondentLinks from './CorrespondentLinks';
|
||||
import { getTagColorStyle } from '../utils/colors';
|
||||
import { resolveCorrespondents } from './correspondents';
|
||||
import { writeTagTransferData } from './tagTransfer';
|
||||
import { writeTagTransferData, parseTagTransferPayload } from './tagTransfer';
|
||||
import useInlineRename from './useInlineRename';
|
||||
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
|
||||
import type { Identifier } from '../types/identifiers';
|
||||
|
||||
export interface FolderLike {
|
||||
id?: Identifier | 'root';
|
||||
name?: string;
|
||||
}
|
||||
import type {
|
||||
FolderLike,
|
||||
DocumentLike,
|
||||
} from './DocumentsList';
|
||||
import type { DocumentsViewProps } from './panel/DocumentsPanel';
|
||||
|
||||
export interface DocumentTag {
|
||||
id?: Identifier;
|
||||
label?: string;
|
||||
color?: string | null;
|
||||
}
|
||||
|
||||
export interface DocumentCorrespondent {
|
||||
id?: Identifier;
|
||||
name?: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
export interface DocumentLike {
|
||||
id?: Identifier;
|
||||
title?: string;
|
||||
tags?: DocumentTag[] | null;
|
||||
correspondents?: DocumentCorrespondent[] | null;
|
||||
}
|
||||
|
||||
export type FolderEntry = {
|
||||
type: 'folder';
|
||||
id: Identifier | 'root';
|
||||
key: string;
|
||||
folder: FolderLike;
|
||||
};
|
||||
|
||||
export type DocumentEntry = {
|
||||
type: 'document';
|
||||
id: Identifier;
|
||||
key: string;
|
||||
document: DocumentLike;
|
||||
};
|
||||
|
||||
export type DocumentsGridEntry = FolderEntry | DocumentEntry;
|
||||
|
||||
type FolderEventHandler = (folder: FolderLike, event: MouseEvent<HTMLDivElement>) => void;
|
||||
type DocumentEventHandler = (document: DocumentLike, event: MouseEvent<HTMLDivElement>) => void;
|
||||
|
||||
interface DocumentsGridProps {
|
||||
entries: DocumentsGridEntry[];
|
||||
draggingDocumentIdsSet?: Set<Identifier> | null;
|
||||
draggedFolderId?: Identifier | 'root' | null;
|
||||
onFolderClick?: FolderEventHandler;
|
||||
onFolderSelect?: (folderId: Identifier | 'root') => void;
|
||||
onFolderDragOver?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragLeave?: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onFolderDrop?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragStart?: (event: DragEvent<HTMLDivElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragEnd?: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onFolderRename?: (folderId: Identifier | 'root', nextName: string) => Promise<boolean> | boolean;
|
||||
onDocumentClick?: DocumentEventHandler;
|
||||
onDocumentActivate?: DocumentEventHandler;
|
||||
onDocumentDragStart?: (event: DragEvent<HTMLDivElement>, document: DocumentLike) => void;
|
||||
onDocumentDragEnd?: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onDocumentTagDragOver?: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onDocumentTagDragLeave?: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onDocumentTagDrop?: (event: DragEvent<HTMLDivElement>, documentId: Identifier) => void;
|
||||
ensureAssetUrl?: (...args: any[]) => unknown;
|
||||
getDocumentAsset?: (...args: any[]) => unknown;
|
||||
interface DocumentsGridProps extends DocumentsViewProps {
|
||||
gridIconSize?: number;
|
||||
tagLookupById?: Map<Identifier, DocumentTag> | null;
|
||||
onTagClick?: (tagId: Identifier) => void;
|
||||
scrollRef?: RefObject<HTMLElement | null>;
|
||||
onCorrespondentClick?: (correspondentId: Identifier) => void;
|
||||
activeCorrespondentIdSet?: Set<Identifier> | null;
|
||||
onDocumentRename?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
|
||||
}
|
||||
|
||||
const DocumentsGrid: React.FC<DocumentsGridProps> = ({
|
||||
@@ -332,8 +267,15 @@ const DocumentsGrid: React.FC<DocumentsGridProps> = ({
|
||||
onDragOverCapture={(event) => onDocumentTagDragOver?.(event)}
|
||||
onDragLeave={onDocumentTagDragLeave}
|
||||
onDragLeaveCapture={onDocumentTagDragLeave}
|
||||
onDrop={(event) => onDocumentTagDrop?.(event, doc.id)}
|
||||
onDropCapture={(event) => onDocumentTagDrop?.(event, doc.id)}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const payload = parseTagTransferPayload(event);
|
||||
if (payload && onDocumentTagDrop) {
|
||||
onDocumentTagDrop(doc.id, payload);
|
||||
}
|
||||
}}
|
||||
|
||||
>
|
||||
<DocumentThumbnailImage
|
||||
document={doc}
|
||||
|
||||
@@ -6,11 +6,11 @@ import { formatDate } from '../utils/date';
|
||||
import DocumentThumbnailImage from './DocumentThumbnailImage';
|
||||
import CorrespondentLinks from './CorrespondentLinks';
|
||||
import { resolveCorrespondents } from './correspondents';
|
||||
import { writeTagTransferData } from './tagTransfer';
|
||||
import { writeTagTransferData, parseTagTransferPayload } from './tagTransfer';
|
||||
import useInlineRename from './useInlineRename';
|
||||
import { useWorkspaceSelectionContext } from '../app/WorkspaceSelectionContext';
|
||||
import type { Identifier } from '../types/identifiers';
|
||||
import type { DocumentsListProps } from './panel/DocumentsPanel';
|
||||
import type { DocumentsViewProps } from './panel/DocumentsPanel';
|
||||
|
||||
export interface FolderLike {
|
||||
id?: Identifier | 'root';
|
||||
@@ -58,7 +58,7 @@ export type DocumentsListEntry = FolderEntry | DocumentEntry;
|
||||
export type FolderEventHandler = (folder: FolderLike, event: MouseEvent<HTMLTableRowElement>) => void;
|
||||
export type DocumentEventHandler = (document: DocumentLike, event: MouseEvent<HTMLTableRowElement>) => void;
|
||||
|
||||
const DocumentsList: React.FC<DocumentsListProps> = ({
|
||||
const DocumentsList: React.FC<DocumentsViewProps> = ({
|
||||
entries,
|
||||
draggingDocumentIdsSet,
|
||||
draggedFolderId,
|
||||
@@ -315,7 +315,14 @@ const DocumentsList: React.FC<DocumentsListProps> = ({
|
||||
onDragEnd={(event) => onDocumentDragEnd?.(event)}
|
||||
onDragOver={onDocumentTagDragOver}
|
||||
onDragLeave={onDocumentTagDragLeave}
|
||||
onDrop={(event) => onDocumentTagDrop?.(event, doc.id)}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const payload = parseTagTransferPayload(event);
|
||||
if (payload && onDocumentTagDrop) {
|
||||
onDocumentTagDrop(doc.id, payload);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<td className="thumb-cell">
|
||||
<DocumentThumbnailImage
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
DocumentTag,
|
||||
} from '../DocumentsList';
|
||||
import DesktopWorkspace from '../../desktop/DesktopWorkspace';
|
||||
import { isTagTransferEvent, parseTagTransferPayload } from '../tagTransfer';
|
||||
import { isTagTransferEvent } from '../tagTransfer';
|
||||
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
|
||||
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
|
||||
import {
|
||||
@@ -48,7 +48,7 @@ const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;
|
||||
|
||||
export type DocumentLinkLike = { url?: string | null; mimeType?: string | null };
|
||||
|
||||
export interface DocumentsListProps {
|
||||
export interface DocumentsViewProps {
|
||||
entries: DocumentsListEntry[];
|
||||
draggingDocumentIdsSet?: Set<Identifier> | null;
|
||||
draggedFolderId?: Identifier | 'root' | null;
|
||||
@@ -56,25 +56,33 @@ export interface DocumentsListProps {
|
||||
getDocumentAsset?: (...args: any[]) => unknown;
|
||||
onFolderClick?: FolderEventHandler;
|
||||
onFolderSelect?: (folderId: Identifier | 'root') => void;
|
||||
onFolderDragOver?: (event: DragEvent<HTMLTableRowElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragLeave?: (event: DragEvent<HTMLTableRowElement>) => void;
|
||||
onFolderDrop?: (event: DragEvent<HTMLTableRowElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragStart?: (event: DragEvent<HTMLTableRowElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragEnd?: (event: DragEvent<HTMLTableRowElement>) => void;
|
||||
onFolderDragOver?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragLeave?: (event: DragEvent<HTMLElement>) => void;
|
||||
onFolderDrop?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragStart?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
|
||||
onFolderDragEnd?: (event: DragEvent<HTMLElement>) => void;
|
||||
onFolderRename?: (folderId: Identifier | 'root', nextName: string) => Promise<boolean> | boolean;
|
||||
onDocumentClick?: DocumentEventHandler;
|
||||
onDocumentActivate?: DocumentEventHandler;
|
||||
onDocumentDragStart?: (event: DragEvent<HTMLTableRowElement>, document: DocumentLike) => void;
|
||||
onDocumentDragEnd?: (event: DragEvent<HTMLTableRowElement>) => void;
|
||||
onDocumentTagDragOver?: (event: DragEvent<HTMLTableRowElement>) => void;
|
||||
onDocumentTagDragLeave?: (event: DragEvent<HTMLTableRowElement>) => void;
|
||||
onDocumentTagDrop?: (event: DragEvent<HTMLTableRowElement>, documentId: Identifier) => void;
|
||||
onDocumentDragStart?: (event: DragEvent<HTMLElement>, document: DocumentLike) => void;
|
||||
onDocumentDragEnd?: (event: DragEvent<HTMLElement>) => void;
|
||||
onDocumentTagDragOver?: (event: DragEvent<HTMLElement>) => void;
|
||||
onDocumentTagDragLeave?: (event: DragEvent<HTMLElement>) => void;
|
||||
onDocumentTagDrop?: (documentId: Identifier, tag: any) => void;
|
||||
onDocumentRename?: (documentId: Identifier, nextTitle: string) => Promise<boolean> | boolean;
|
||||
tagLookupById?: Map<Identifier, DocumentTag> | null;
|
||||
onTagClick?: (tagId: Identifier) => void;
|
||||
onCorrespondentClick?: (correspondentId: Identifier) => void;
|
||||
activeCorrespondentIdSet?: Set<Identifier> | null;
|
||||
scrollRef?: RefObject<HTMLElement | null>;
|
||||
// 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>;
|
||||
}
|
||||
|
||||
const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
@@ -777,53 +785,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
[entries],
|
||||
);
|
||||
|
||||
const handleListDocumentTagDrop = useCallback(
|
||||
(event: React.DragEvent<HTMLElement>, docId: Identifier) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const payload = parseTagTransferPayload(event);
|
||||
if (payload && onDocumentTagDrop) {
|
||||
onDocumentTagDrop(docId, payload);
|
||||
}
|
||||
},
|
||||
[onDocumentTagDrop],
|
||||
);
|
||||
|
||||
const deskWorkspaceProps = useMemo(
|
||||
() => ({
|
||||
entries: rows,
|
||||
onDocumentActivate,
|
||||
onDocumentClick: onEntryPointer,
|
||||
onDocumentStackSelect: handleDeskDocumentStackSelect,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
onDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters,
|
||||
tenantId: currentTenantId,
|
||||
viewId: deskViewId,
|
||||
documentLinks: documentLinkMap,
|
||||
ensureDownloadUrl,
|
||||
}),
|
||||
[
|
||||
rows,
|
||||
onDocumentActivate,
|
||||
onEntryPointer,
|
||||
handleDeskDocumentStackSelect,
|
||||
promoteSelectionOrder,
|
||||
onDocumentTagDrop,
|
||||
ensureAssetUrl,
|
||||
getDocumentAsset,
|
||||
activeTagFilters,
|
||||
currentTenantId,
|
||||
deskViewId,
|
||||
documentLinkMap,
|
||||
ensureDownloadUrl,
|
||||
],
|
||||
);
|
||||
|
||||
const listProps: DocumentsListProps = {
|
||||
const viewProps: DocumentsViewProps = {
|
||||
entries,
|
||||
draggingDocumentIdsSet: draggingSet,
|
||||
draggedFolderId,
|
||||
@@ -840,7 +802,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
onDocumentDragEnd: handleDocumentDragEndLocal,
|
||||
onDocumentTagDragOver: handleDocumentTagDragOver,
|
||||
onDocumentTagDragLeave: handleDocumentTagDragLeave,
|
||||
onDocumentTagDrop: handleListDocumentTagDrop,
|
||||
onDocumentTagDrop,
|
||||
onDocumentRename,
|
||||
onFolderRename,
|
||||
ensureAssetUrl,
|
||||
@@ -850,6 +812,13 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
onCorrespondentClick: toggleCorrespondentFilter,
|
||||
activeCorrespondentIdSet: activeCorrespondentIdSet,
|
||||
scrollRef,
|
||||
// Desk specific
|
||||
onDocumentStackSelect: handleDeskDocumentStackSelect,
|
||||
onPromoteSelection: promoteSelectionOrder,
|
||||
tenantId: currentTenantId,
|
||||
viewId: deskViewId,
|
||||
documentLinks: documentLinkMap,
|
||||
ensureDownloadUrl,
|
||||
};
|
||||
|
||||
const renderBody = () => {
|
||||
@@ -874,12 +843,12 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
|
||||
switch (viewMode) {
|
||||
case 'desk':
|
||||
return <DesktopWorkspace {...deskWorkspaceProps} />;
|
||||
return <DesktopWorkspace {...viewProps} />;
|
||||
case 'grid':
|
||||
return <DocumentsGrid {...listProps} gridIconSize={gridIconSize} />;
|
||||
return <DocumentsGrid {...viewProps} gridIconSize={gridIconSize} />;
|
||||
case 'list':
|
||||
default:
|
||||
return <DocumentsList {...listProps} />;
|
||||
return <DocumentsList {...viewProps} />;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user