mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-01 21:37:33 +08:00
90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
import { useCallback, useMemo, useState, type ComponentProps } from 'react';
|
|
import ForceGraph2D from 'react-force-graph-2d';
|
|
import { renderNodeLabel } from './node-label';
|
|
import {
|
|
DefaultLinkWidth,
|
|
DimmedAlpha,
|
|
HighlightLinkWidth,
|
|
} from './node-style';
|
|
import { type ArtifactGraphLink, type ArtifactGraphNode } from './types';
|
|
import { getBaseLinkColor, withAlpha } from './utils';
|
|
|
|
type PaintNodeFn = NonNullable<
|
|
ComponentProps<typeof ForceGraph2D>['nodeCanvasObject']
|
|
>;
|
|
|
|
export const useGraphHighlight = (
|
|
containerRef: React.RefObject<HTMLElement>,
|
|
pinnedNode?: ArtifactGraphNode | null,
|
|
) => {
|
|
const [hoverNode, setHoverNode] = useState<ArtifactGraphNode | null>(null);
|
|
|
|
// Real hover takes precedence; falls back to the externally controlled pinned node when not hovering
|
|
const activeNode = hoverNode ?? pinnedNode ?? null;
|
|
|
|
const highlightNodes = useMemo(() => {
|
|
const nodes = new Set<ArtifactGraphNode>();
|
|
if (activeNode) {
|
|
nodes.add(activeNode);
|
|
activeNode.__neighbors?.forEach((neighbor) => nodes.add(neighbor));
|
|
}
|
|
return nodes;
|
|
}, [activeNode]);
|
|
|
|
const highlightLinks = useMemo(
|
|
() => new Set<ArtifactGraphLink>(activeNode?.__links ?? []),
|
|
[activeNode],
|
|
);
|
|
|
|
const handleNodeHover = useCallback((node: ArtifactGraphNode | null) => {
|
|
setHoverNode(node ?? null);
|
|
}, []);
|
|
|
|
const getNodeColor = useCallback(
|
|
(node: ArtifactGraphNode) =>
|
|
activeNode && !highlightNodes.has(node)
|
|
? withAlpha(node.__color, DimmedAlpha)
|
|
: node.__color,
|
|
[activeNode, highlightNodes],
|
|
);
|
|
|
|
const getLinkColor = useCallback(
|
|
(link: ArtifactGraphLink) => {
|
|
const baseColor = getBaseLinkColor(containerRef.current);
|
|
return activeNode && !highlightLinks.has(link)
|
|
? withAlpha(baseColor, DimmedAlpha)
|
|
: baseColor;
|
|
},
|
|
[containerRef, activeNode, highlightLinks],
|
|
);
|
|
|
|
const getLinkWidth = useCallback(
|
|
(link: ArtifactGraphLink) =>
|
|
highlightLinks.has(link) ? HighlightLinkWidth : DefaultLinkWidth,
|
|
[highlightLinks],
|
|
);
|
|
|
|
const paintNode = useCallback<PaintNodeFn>(
|
|
(node, ctx, globalScale) => {
|
|
const dimmed =
|
|
activeNode !== null && !highlightNodes.has(node as ArtifactGraphNode);
|
|
if (dimmed) {
|
|
ctx.globalAlpha = DimmedAlpha;
|
|
}
|
|
renderNodeLabel(node, ctx, globalScale);
|
|
if (dimmed) {
|
|
ctx.globalAlpha = 1;
|
|
}
|
|
},
|
|
[highlightNodes, activeNode],
|
|
);
|
|
|
|
return {
|
|
handleNodeHover,
|
|
getNodeColor,
|
|
getLinkColor,
|
|
getLinkWidth,
|
|
paintNode,
|
|
};
|
|
};
|