feat: Refactor tag interaction handling to use unified handlers
This commit is contained in:
@@ -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),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user