feat: Refactor tag interaction handling to use unified handlers

This commit is contained in:
2025-12-09 01:00:54 +01:00
parent 4f28230243
commit ebc7ce67a1
7 changed files with 47 additions and 25 deletions
@@ -117,7 +117,6 @@ const DesktopDocumentCard: React.FC<DesktopDocumentCardProps> = ({
doc={doc}
tags={tags}
tagHandlers={tagHandlers}
onTagClick={tagHandlers?.onTagClick}
/>
</div>
)}
@@ -7,7 +7,6 @@ import type { TagInteractionHandlers } from '../interactions/useTagInteractions'
interface DocumentTagsProps {
tags: DocumentTag[];
tagLookupById?: Map<Identifier, DocumentTag> | null;
onTagClick?: (tagId: Identifier) => void;
doc: Document;
tagHandlers?: TagInteractionHandlers;
}
@@ -15,7 +14,6 @@ interface DocumentTagsProps {
const DocumentTags: React.FC<DocumentTagsProps> = ({
tags,
tagLookupById,
onTagClick,
doc,
tagHandlers,
}) => {
@@ -37,7 +35,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
const colorSource = tag?.color || tagLookupById?.get(tag.id)?.color;
const style = getTagColorStyle(colorSource);
const tagId = tag?.id ?? null;
const clickable = tagId != null && typeof onTagClick === 'function';
const clickable = tagId != null && typeof tagHandlers?.onTagClick === 'function';
const draggable = !!tagId;
const key = tagId ?? `${doc.id}-tag-${index}`;
@@ -51,7 +49,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
onClick={clickable ? (event) => {
event.stopPropagation();
if (tagId == null) return;
onTagClick?.(tagId);
tagHandlers?.onTagClick?.(tagId);
} : undefined}
draggable={!!tagId}
onDragStart={(event) => tagId && tagHandlers?.onTagDragStart(event, doc, tag)}
@@ -61,7 +59,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
event.preventDefault();
event.stopPropagation();
if (tagId == null) return;
onTagClick?.(tagId);
tagHandlers?.onTagClick?.(tagId);
}
} : undefined}
>
@@ -27,7 +27,6 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext();
const {
correspondents: { onClick: onCorrespondentClick },
tags: { onClick: onTagClick }
} = useDocumentsCommandContext();
if (entry.type === 'folder') {
@@ -125,7 +124,6 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
<DocumentTags
tags={doc.tags || []}
tagLookupById={tagLookupById}
onTagClick={onTagClick}
doc={doc}
tagHandlers={logic.handlers.tagHandlers}
/>
@@ -29,7 +29,6 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext();
const {
correspondents: { onClick: onCorrespondentClick },
tags: { onClick: onTagClick }
} = useDocumentsCommandContext();
if (entry.type === 'folder') {
@@ -140,7 +139,6 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
<DocumentTags
tags={doc.tags || []}
tagLookupById={tagLookupById}
onTagClick={onTagClick}
doc={doc}
tagHandlers={logic.handlers.tagHandlers}
/>
@@ -22,11 +22,6 @@ interface DocumentsCommandContextValue {
end?: (event: DragEvent<HTMLElement>) => void;
};
};
tags: {
onAttach?: (documentId: Identifier, tagId: Identifier) => void;
onDetach?: (documentId: Identifier, tagId: Identifier) => void;
onClick?: (tagId: Identifier) => void;
};
correspondents: {
onClick?: (correspondentId: Identifier) => void;
};
@@ -37,7 +32,6 @@ interface DocumentsCommandContextValue {
export const DocumentsCommandContext = createContext<DocumentsCommandContextValue>({
folder: { onDrag: {} },
document: { onDrag: {} },
tags: {},
correspondents: {},
});
@@ -1,6 +1,9 @@
import { useEffect } from 'react';
import type { MutableRefObject } from 'react';
import { isTagTransferEvent } from '../features/tagging/tagTransfer';
import {
isTagTransferEvent,
getActiveDragState,
} from '../features/tagging/tagTransfer';
interface UseWorkspaceDragDropArgs {
shellRef: MutableRefObject<HTMLElement | null>;
@@ -16,21 +19,58 @@ const useWorkspaceDragDrop = ({
return;
}
// Allow drop on workspace
// Allow drop on workspace (global app shell)
const handleTagDragOver = (event: DragEvent) => {
if (!isTagTransferEvent(event)) {
return;
}
event.preventDefault();
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';
const { sourceDocId } = getActiveDragState();
// Only allow "Move" (removal) if the tag came from a document
if (sourceDocId) {
event.preventDefault(); // Necessary to allow dropping
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
}
}
};
const handleTagDragEnter = (event: DragEvent) => {
if (!isTagTransferEvent(event)) {
return;
}
const { sourceDocId } = getActiveDragState();
if (sourceDocId) {
event.preventDefault();
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'move';
}
}
};
const handleTagDrop = (event: DragEvent) => {
if (!isTagTransferEvent(event)) {
return;
}
const { sourceDocId } = getActiveDragState();
if (sourceDocId) {
// Consume the event so the browser reports 'move' back to the source
event.preventDefault();
}
};
// Use bubbling (false) so children can stopPropagation
host.addEventListener('dragenter', handleTagDragEnter, false);
host.addEventListener('dragover', handleTagDragOver, false);
host.addEventListener('drop', handleTagDrop, false);
return () => {
host.removeEventListener('dragenter', handleTagDragEnter, false);
host.removeEventListener('dragover', handleTagDragOver, false);
host.removeEventListener('drop', handleTagDrop, false);
};
}, [shellRef]);
@@ -183,11 +183,6 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => {
end: (e: any) => latestHandlersRef.current.handleDocumentDragEndLocal(e),
},
},
tags: {
onAttach: (d: any, t: any) => latestPropsRef.current.onDocumentTagAttach?.(d, t),
onDetach: (d: any, t: any) => latestPropsRef.current.onDocumentTagDetach?.(d, t),
onClick: (t: any) => latestHandlersRef.current.toggleTagFilter(t),
},
correspondents: {
onClick: (c: any) => latestHandlersRef.current.toggleCorrespondentFilter(c),
},