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} doc={doc}
tags={tags} tags={tags}
tagHandlers={tagHandlers} tagHandlers={tagHandlers}
onTagClick={tagHandlers?.onTagClick}
/> />
</div> </div>
)} )}
@@ -7,7 +7,6 @@ import type { TagInteractionHandlers } from '../interactions/useTagInteractions'
interface DocumentTagsProps { interface DocumentTagsProps {
tags: DocumentTag[]; tags: DocumentTag[];
tagLookupById?: Map<Identifier, DocumentTag> | null; tagLookupById?: Map<Identifier, DocumentTag> | null;
onTagClick?: (tagId: Identifier) => void;
doc: Document; doc: Document;
tagHandlers?: TagInteractionHandlers; tagHandlers?: TagInteractionHandlers;
} }
@@ -15,7 +14,6 @@ interface DocumentTagsProps {
const DocumentTags: React.FC<DocumentTagsProps> = ({ const DocumentTags: React.FC<DocumentTagsProps> = ({
tags, tags,
tagLookupById, tagLookupById,
onTagClick,
doc, doc,
tagHandlers, tagHandlers,
}) => { }) => {
@@ -37,7 +35,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
const colorSource = tag?.color || tagLookupById?.get(tag.id)?.color; const colorSource = tag?.color || tagLookupById?.get(tag.id)?.color;
const style = getTagColorStyle(colorSource); const style = getTagColorStyle(colorSource);
const tagId = tag?.id ?? null; const tagId = tag?.id ?? null;
const clickable = tagId != null && typeof onTagClick === 'function'; const clickable = tagId != null && typeof tagHandlers?.onTagClick === 'function';
const draggable = !!tagId; const draggable = !!tagId;
const key = tagId ?? `${doc.id}-tag-${index}`; const key = tagId ?? `${doc.id}-tag-${index}`;
@@ -51,7 +49,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
onClick={clickable ? (event) => { onClick={clickable ? (event) => {
event.stopPropagation(); event.stopPropagation();
if (tagId == null) return; if (tagId == null) return;
onTagClick?.(tagId); tagHandlers?.onTagClick?.(tagId);
} : undefined} } : undefined}
draggable={!!tagId} draggable={!!tagId}
onDragStart={(event) => tagId && tagHandlers?.onTagDragStart(event, doc, tag)} onDragStart={(event) => tagId && tagHandlers?.onTagDragStart(event, doc, tag)}
@@ -61,7 +59,7 @@ const DocumentTags: React.FC<DocumentTagsProps> = ({
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (tagId == null) return; if (tagId == null) return;
onTagClick?.(tagId); tagHandlers?.onTagClick?.(tagId);
} }
} : undefined} } : undefined}
> >
@@ -27,7 +27,6 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext(); const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext();
const { const {
correspondents: { onClick: onCorrespondentClick }, correspondents: { onClick: onCorrespondentClick },
tags: { onClick: onTagClick }
} = useDocumentsCommandContext(); } = useDocumentsCommandContext();
if (entry.type === 'folder') { if (entry.type === 'folder') {
@@ -125,7 +124,6 @@ const DocumentsGridCard: React.FC<DocumentsGridCardProps> = (props) => {
<DocumentTags <DocumentTags
tags={doc.tags || []} tags={doc.tags || []}
tagLookupById={tagLookupById} tagLookupById={tagLookupById}
onTagClick={onTagClick}
doc={doc} doc={doc}
tagHandlers={logic.handlers.tagHandlers} tagHandlers={logic.handlers.tagHandlers}
/> />
@@ -29,7 +29,6 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext(); const { scrollRef, activeCorrespondentIdSet, tagLookupById } = useDocumentsViewStateContext();
const { const {
correspondents: { onClick: onCorrespondentClick }, correspondents: { onClick: onCorrespondentClick },
tags: { onClick: onTagClick }
} = useDocumentsCommandContext(); } = useDocumentsCommandContext();
if (entry.type === 'folder') { if (entry.type === 'folder') {
@@ -140,7 +139,6 @@ const DocumentsListRow: React.FC<DocumentsListRowProps> = (props) => {
<DocumentTags <DocumentTags
tags={doc.tags || []} tags={doc.tags || []}
tagLookupById={tagLookupById} tagLookupById={tagLookupById}
onTagClick={onTagClick}
doc={doc} doc={doc}
tagHandlers={logic.handlers.tagHandlers} tagHandlers={logic.handlers.tagHandlers}
/> />
@@ -22,11 +22,6 @@ interface DocumentsCommandContextValue {
end?: (event: DragEvent<HTMLElement>) => void; end?: (event: DragEvent<HTMLElement>) => void;
}; };
}; };
tags: {
onAttach?: (documentId: Identifier, tagId: Identifier) => void;
onDetach?: (documentId: Identifier, tagId: Identifier) => void;
onClick?: (tagId: Identifier) => void;
};
correspondents: { correspondents: {
onClick?: (correspondentId: Identifier) => void; onClick?: (correspondentId: Identifier) => void;
}; };
@@ -37,7 +32,6 @@ interface DocumentsCommandContextValue {
export const DocumentsCommandContext = createContext<DocumentsCommandContextValue>({ export const DocumentsCommandContext = createContext<DocumentsCommandContextValue>({
folder: { onDrag: {} }, folder: { onDrag: {} },
document: { onDrag: {} }, document: { onDrag: {} },
tags: {},
correspondents: {}, correspondents: {},
}); });
@@ -1,6 +1,9 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import type { MutableRefObject } from 'react'; import type { MutableRefObject } from 'react';
import { isTagTransferEvent } from '../features/tagging/tagTransfer'; import {
isTagTransferEvent,
getActiveDragState,
} from '../features/tagging/tagTransfer';
interface UseWorkspaceDragDropArgs { interface UseWorkspaceDragDropArgs {
shellRef: MutableRefObject<HTMLElement | null>; shellRef: MutableRefObject<HTMLElement | null>;
@@ -16,21 +19,58 @@ const useWorkspaceDragDrop = ({
return; return;
} }
// Allow drop on workspace // Allow drop on workspace (global app shell)
const handleTagDragOver = (event: DragEvent) => { const handleTagDragOver = (event: DragEvent) => {
if (!isTagTransferEvent(event)) { if (!isTagTransferEvent(event)) {
return; return;
} }
event.preventDefault(); const { sourceDocId } = getActiveDragState();
if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';
// 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 // Use bubbling (false) so children can stopPropagation
host.addEventListener('dragenter', handleTagDragEnter, false);
host.addEventListener('dragover', handleTagDragOver, false); host.addEventListener('dragover', handleTagDragOver, false);
host.addEventListener('drop', handleTagDrop, false);
return () => { return () => {
host.removeEventListener('dragenter', handleTagDragEnter, false);
host.removeEventListener('dragover', handleTagDragOver, false); host.removeEventListener('dragover', handleTagDragOver, false);
host.removeEventListener('drop', handleTagDrop, false);
}; };
}, [shellRef]); }, [shellRef]);
@@ -183,11 +183,6 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => {
end: (e: any) => latestHandlersRef.current.handleDocumentDragEndLocal(e), 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: { correspondents: {
onClick: (c: any) => latestHandlersRef.current.toggleCorrespondentFilter(c), onClick: (c: any) => latestHandlersRef.current.toggleCorrespondentFilter(c),
}, },