feat(graph): focusOnHover (#954)

by default, globalGraph will enable focusOnHover, similar to Obsidian.

---------

Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com>
This commit is contained in:
Aaron Pham 2024-03-04 15:09:20 -05:00 committed by GitHub
parent bcb5b2df09
commit cec3662c74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 6 deletions

View file

@ -17,6 +17,7 @@ export interface D3Config {
opacityScale: number opacityScale: number
removeTags: string[] removeTags: string[]
showTags: boolean showTags: boolean
focusOnHover?: boolean
} }
interface GraphOptions { interface GraphOptions {
@ -37,6 +38,7 @@ const defaultOptions: GraphOptions = {
opacityScale: 1, opacityScale: 1,
showTags: true, showTags: true,
removeTags: [], removeTags: [],
focusOnHover: false,
}, },
globalGraph: { globalGraph: {
drag: true, drag: true,
@ -50,6 +52,7 @@ const defaultOptions: GraphOptions = {
opacityScale: 1, opacityScale: 1,
showTags: true, showTags: true,
removeTags: [], removeTags: [],
focusOnHover: true,
}, },
} }

View file

@ -44,6 +44,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
opacityScale, opacityScale,
removeTags, removeTags,
showTags, showTags,
focusOnHover,
} = JSON.parse(graph.dataset["cfg"]!) } = JSON.parse(graph.dataset["cfg"]!)
const data: Map<SimpleSlug, ContentDetails> = new Map( const data: Map<SimpleSlug, ContentDetails> = new Map(
@ -189,6 +190,8 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
return 2 + Math.sqrt(numLinks) return 2 + Math.sqrt(numLinks)
} }
let connectedNodes: SimpleSlug[] = []
// draw individual nodes // draw individual nodes
const node = graphNode const node = graphNode
.append("circle") .append("circle")
@ -202,17 +205,25 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
window.spaNavigate(new URL(targ, window.location.toString())) window.spaNavigate(new URL(targ, window.location.toString()))
}) })
.on("mouseover", function (_, d) { .on("mouseover", function (_, d) {
const neighbours: SimpleSlug[] = data.get(slug)?.links ?? []
const neighbourNodes = d3
.selectAll<HTMLElement, NodeData>(".node")
.filter((d) => neighbours.includes(d.id))
const currentId = d.id const currentId = d.id
const linkNodes = d3 const linkNodes = d3
.selectAll(".link") .selectAll(".link")
.filter((d: any) => d.source.id === currentId || d.target.id === currentId) .filter((d: any) => d.source.id === currentId || d.target.id === currentId)
// highlight neighbour nodes if (focusOnHover) {
neighbourNodes.transition().duration(200).attr("fill", color) // fade out non-neighbour nodes
connectedNodes = linkNodes.data().flatMap((d: any) => [d.source.id, d.target.id])
d3.selectAll<HTMLElement, NodeData>(".link")
.transition()
.duration(200)
.style("opacity", 0.2)
d3.selectAll<HTMLElement, NodeData>(".node")
.filter((d) => !connectedNodes.includes(d.id))
.transition()
.duration(200)
.style("opacity", 0.2)
}
// highlight links // highlight links
linkNodes.transition().duration(200).attr("stroke", "var(--gray)").attr("stroke-width", 1) linkNodes.transition().duration(200).attr("stroke", "var(--gray)").attr("stroke-width", 1)
@ -231,6 +242,10 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
.style("font-size", bigFont + "em") .style("font-size", bigFont + "em")
}) })
.on("mouseleave", function (_, d) { .on("mouseleave", function (_, d) {
if (focusOnHover) {
d3.selectAll<HTMLElement, NodeData>(".link").transition().duration(200).style("opacity", 1)
d3.selectAll<HTMLElement, NodeData>(".node").transition().duration(200).style("opacity", 1)
}
const currentId = d.id const currentId = d.id
const linkNodes = d3 const linkNodes = d3
.selectAll(".link") .selectAll(".link")