refactor: begin spatial workspace architecture refactor with design document and updated pointer event handling for document activation.
This commit is contained in:
@@ -31,7 +31,7 @@ interface DesktopDocumentCardProps {
|
||||
getDocumentAsset?: (...args: any[]) => unknown;
|
||||
handleNavigatorSnapshot?: (...args: any[]) => void;
|
||||
cardPointerHandlers?: React.HTMLAttributes<HTMLDivElement>;
|
||||
onDocumentActivate?: (id: string) => void;
|
||||
onDocumentActivate?: (id: string, event?: any) => void;
|
||||
onTagDragEnter?: (event: React.DragEvent<HTMLDivElement>, docId: DocumentId) => void;
|
||||
onTagDragOver?: (event: React.DragEvent<HTMLDivElement>, docId: DocumentId) => void;
|
||||
onTagDragLeave?: (event: React.DragEvent<HTMLDivElement>, docId: DocumentId) => void;
|
||||
@@ -116,7 +116,7 @@ const DesktopDocumentCard: React.FC<DesktopDocumentCardProps> = ({
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
preventAll(event);
|
||||
onDocumentActivate?.(doc.id);
|
||||
onDocumentActivate?.(doc.id, event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -31,7 +31,7 @@ interface DesktopPreviewCardProps {
|
||||
doc: Document | null;
|
||||
title?: string;
|
||||
ensureAssetUrl?: EnsureAssetUrl | null;
|
||||
getDocumentAsset: GetDocumentAsset;
|
||||
getDocumentAsset?: GetDocumentAsset;
|
||||
onNavigatorSnapshot?: (docId: Identifier, snapshot: NavigatorSnapshot | null) => void;
|
||||
shouldLoad?: boolean;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export interface DesktopWorkspaceProps {
|
||||
const DesktopDocumentContainer: React.FC<React.ComponentProps<typeof DesktopDocumentCard> & {
|
||||
onSelect: (ids: string[], extend?: boolean) => void;
|
||||
onDeselect: (ids: string[]) => void;
|
||||
onDocumentActivate?: (id: string) => void;
|
||||
onDocumentActivate?: (id: string, event?: any) => void;
|
||||
selection: string[];
|
||||
}> = React.memo((props) => {
|
||||
const { layoutCard, selected, onSelect, onDeselect, onDocumentActivate, selection } = props;
|
||||
@@ -280,7 +280,7 @@ const DesktopWorkspaceContent: React.FC<DesktopWorkspaceProps> = ({
|
||||
ensureAssetUrl={ensureAssetUrl}
|
||||
getDocumentAsset={getDocumentAsset}
|
||||
handleNavigatorSnapshot={() => { }}
|
||||
onDocumentActivate={(id) => { onDocumentActivate?.({ id } as DeskDocument) }}
|
||||
onDocumentActivate={(_id, event) => { onDocumentActivate?.(doc, event) }}
|
||||
layoutCard={layoutCard}
|
||||
onTagDragEnter={tagInteractions.handleTagDragEnterDoc}
|
||||
onTagDragOver={tagInteractions.handleTagDragOverDoc}
|
||||
|
||||
@@ -14,11 +14,12 @@ export const useCardPointer = (
|
||||
selection: string[],
|
||||
onSelect: (ids: string[], extend?: boolean) => void,
|
||||
onDeselect: (ids: string[]) => void,
|
||||
onDocumentActivate?: (id: string) => void
|
||||
onDocumentActivate?: (id: string, event?: React.PointerEvent) => void
|
||||
) => {
|
||||
const [state, setState] = React.useState<PointerState>('idle');
|
||||
const initialPosition = useRef<{ x: number, y: number } | null>(null);
|
||||
const lastPosition = useRef<{ x: number, y: number } | null>(null);
|
||||
const lastClickTime = useRef<number>(0);
|
||||
const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
|
||||
@@ -37,8 +38,8 @@ export const useCardPointer = (
|
||||
}, [state]);
|
||||
|
||||
const onPointerDown = useCallback((e: React.PointerEvent) => {
|
||||
// Only left click
|
||||
if (e.button !== 0) return;
|
||||
// Allow left (0) and middle (1) click
|
||||
if (e.button !== 0 && e.button !== 1) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
@@ -201,13 +202,15 @@ export const useCardPointer = (
|
||||
if (state === 'drag' || state === 'drag-start') {
|
||||
handleDragEnd(card.store, selection, e.pointerId);
|
||||
} else if (state === 'click') {
|
||||
if (isSelected) {
|
||||
const isUnobstructed = card.isUnobstructed();
|
||||
const now = Date.now();
|
||||
if (now - lastClickTime.current < 300) {
|
||||
onDocumentActivate?.(card.id, e);
|
||||
}
|
||||
lastClickTime.current = now;
|
||||
|
||||
if (isSelected) {
|
||||
if (hasModifier) {
|
||||
onDeselect([card.id]);
|
||||
} else if (isUnobstructed) {
|
||||
onDocumentActivate?.(card.id);
|
||||
}
|
||||
} else {
|
||||
onSelect([card.id], hasModifier);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { type DragEvent } from 'react';
|
||||
import { parseTagTransferPayload } from '../tagTransfer';
|
||||
import { createDocumentEntryKey } from '../../app/entryKey';
|
||||
import type { Document } from '../../types/documents';
|
||||
import type { DocumentsViewProps } from '../panel/DocumentsPanel';
|
||||
import type { DocumentViewLogic } from './useDocumentViewLogic';
|
||||
@@ -14,7 +15,6 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
|
||||
doc,
|
||||
viewLogic,
|
||||
draggingDocumentIdsSet,
|
||||
onDocumentClick,
|
||||
onDocumentActivate,
|
||||
onDocumentDragStart,
|
||||
onDocumentDragEnd,
|
||||
@@ -37,6 +37,7 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
|
||||
savingId: savingDocumentId,
|
||||
attachInputRef: attachDocumentInputRef,
|
||||
},
|
||||
handleEntrySelection,
|
||||
} = viewLogic;
|
||||
|
||||
const isSelected = selectedDocumentIdsSet?.has(doc.id);
|
||||
@@ -50,7 +51,10 @@ export const useDocumentItemLogic = (props: UseDocumentItemLogicProps) => {
|
||||
const allowInlineDocumentEdit = onDocumentRename && isSelected && totalSelectionCount === 1;
|
||||
|
||||
const handlers = {
|
||||
onClick: (event: React.MouseEvent) => onDocumentClick?.(doc, event),
|
||||
onClick: (event: React.MouseEvent) => {
|
||||
const key = createDocumentEntryKey(doc.id);
|
||||
handleEntrySelection(key, event);
|
||||
},
|
||||
onDoubleClick: (event: React.MouseEvent) => onDocumentActivate?.(doc, event),
|
||||
onDragStart: (event: DragEvent<HTMLElement>) => onDocumentDragStart?.(event, doc),
|
||||
onDragEnd: (event: DragEvent<HTMLElement>) => onDocumentDragEnd?.(event),
|
||||
|
||||
@@ -16,6 +16,7 @@ export const useDocumentViewLogic = ({
|
||||
selectedDocumentIds,
|
||||
selectedFolderIds,
|
||||
clearSelection,
|
||||
handleEntrySelection,
|
||||
} = useWorkspaceSelectionContext();
|
||||
|
||||
const selectedDocumentIdsSet = useMemo(() => new Set(selectedDocumentIds), [selectedDocumentIds]);
|
||||
@@ -42,6 +43,7 @@ export const useDocumentViewLogic = ({
|
||||
selectedDocumentIdsSet,
|
||||
selectedFolderIdsSet,
|
||||
clearSelection,
|
||||
handleEntrySelection,
|
||||
totalSelectionCount,
|
||||
documentRename,
|
||||
folderRename,
|
||||
|
||||
@@ -42,8 +42,6 @@ interface DocumentsPanelProps extends DocumentsPanelInnerProps {
|
||||
selectionValue: WorkspaceSelectionValue;
|
||||
}
|
||||
|
||||
const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;
|
||||
|
||||
export type DocumentLinkLike = { url?: string | null; mimeType?: string | null };
|
||||
|
||||
export interface DocumentsViewProps {
|
||||
@@ -60,8 +58,7 @@ export interface DocumentsViewProps {
|
||||
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;
|
||||
onDocumentOpen?: DocumentEventHandler;
|
||||
onDocumentDragStart?: (event: DragEvent<HTMLElement>, document: Document) => void;
|
||||
onDocumentDragEnd?: (event: DragEvent<HTMLElement>) => void;
|
||||
onDocumentTagDragOver?: (event: DragEvent<HTMLElement>) => void;
|
||||
@@ -105,7 +102,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
tagLookupById,
|
||||
activeCorrespondentIds = [],
|
||||
ensureAssetUrl = null,
|
||||
getDocumentAsset = defaultGetDocumentAsset,
|
||||
getDocumentAsset,
|
||||
isSearchLoading = false,
|
||||
viewMode = 'list',
|
||||
onViewModeChange,
|
||||
@@ -345,29 +342,19 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
[isTagDragEvent],
|
||||
);
|
||||
|
||||
const handleDocumentClick = useCallback(
|
||||
(doc, event) => {
|
||||
if (!doc || suppressDocumentClickRef.current || !onEntryPointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
onEntryPointer(
|
||||
{ type: EntryType.document, id: doc.id, key: `document:${doc.id}`, document: doc },
|
||||
event,
|
||||
);
|
||||
},
|
||||
[onEntryPointer],
|
||||
);
|
||||
|
||||
const handleDocumentActivate = useCallback(
|
||||
(doc, event?: React.MouseEvent | KeyboardEvent | null) => {
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
if (event && (event.altKey || (event.button === 1))) {
|
||||
|
||||
// Handle Preview (Alt+Click or Middle Click)
|
||||
if (event && (event.altKey || ((event as React.MouseEvent).button === 1))) {
|
||||
openPreview(doc);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Activation (Double Click, Enter, or explicit call)
|
||||
if (onDocumentActivate) {
|
||||
onDocumentActivate(doc);
|
||||
}
|
||||
@@ -435,7 +422,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
|
||||
onFolderDragStart,
|
||||
onFolderDragEnd,
|
||||
onFolderRename,
|
||||
onDocumentClick: handleDocumentClick,
|
||||
|
||||
onDocumentActivate: handleDocumentActivate,
|
||||
onDocumentDragStart: handleDocumentDragStartLocal,
|
||||
onDocumentDragEnd: handleDocumentDragEndLocal,
|
||||
|
||||
Reference in New Issue
Block a user