Skip to content

Commit

Permalink
feat(dendogram): add the ability to highligh ancestor/descendant nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed May 3, 2024
1 parent f10b717 commit ed28d78
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 72 deletions.
14 changes: 12 additions & 2 deletions packages/dendogram/src/Dendogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const InnerDendogram = <Datum extends object>({
data,
identity,
nodeSize = svgDefaultProps.nodeSize,
activeNodeSize,
inactiveNodeSize,
nodeColor = svgDefaultProps.nodeColor,
nodeComponent = svgDefaultProps.nodeComponent,
linkThickness = svgDefaultProps.linkThickness,
Expand All @@ -30,6 +32,8 @@ const InnerDendogram = <Datum extends object>({
useMesh = svgDefaultProps.useMesh,
meshDetectionThreshold = svgDefaultProps.meshDetectionThreshold,
debugMesh = svgDefaultProps.debugMesh,
highlightAncestorNodes = svgDefaultProps.highlightAncestorNodes,
highlightDescendantNodes = svgDefaultProps.highlightDescendantNodes,
onNodeMouseEnter,
onNodeMouseMove,
onNodeMouseLeave,
Expand All @@ -51,14 +55,18 @@ const InnerDendogram = <Datum extends object>({
partialMargin
)

const { nodes, links } = useDendogram<Datum>({
const { nodes, links, setCurrentNode } = useDendogram<Datum>({
data,
identity,
layout,
width: innerWidth,
height: innerHeight,
nodeSize,
activeNodeSize,
inactiveNodeSize,
nodeColor,
highlightAncestorNodes,
highlightDescendantNodes,
linkThickness,
linkColor,
})
Expand Down Expand Up @@ -97,14 +105,15 @@ const InnerDendogram = <Datum extends object>({
onMouseMove={onNodeMouseMove}
onMouseLeave={onNodeMouseLeave}
onClick={onNodeClick}
setCurrentNode={setCurrentNode}
tooltip={nodeTooltip}
/>
)
}

if (layers.includes('mesh') && isInteractive && useMesh) {
layerById.mesh = (
<Mesh
<Mesh<Datum>
key="mesh"
nodes={nodes}
width={innerWidth}
Expand All @@ -116,6 +125,7 @@ const InnerDendogram = <Datum extends object>({
onMouseMove={onNodeMouseMove}
onMouseLeave={onNodeMouseLeave}
onClick={onNodeClick}
setCurrentNode={setCurrentNode}
/>
)
}
Expand Down
13 changes: 9 additions & 4 deletions packages/dendogram/src/Mesh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createElement, memo, useCallback } from 'react'
import { Margin } from '@nivo/core'
import { useTooltip } from '@nivo/tooltip'
import { Mesh as BaseMesh } from '@nivo/voronoi'
import { ComputedNode, NodeMouseEventHandler, NodeTooltip } from './types'
import { ComputedNode, CurrentNodeSetter, NodeMouseEventHandler, NodeTooltip } from './types'

interface MeshProps<Datum extends object> {
nodes: ComputedNode<Datum>[]
Expand All @@ -14,6 +14,7 @@ interface MeshProps<Datum extends object> {
onMouseMove?: NodeMouseEventHandler<Datum>
onMouseLeave?: NodeMouseEventHandler<Datum>
onClick?: NodeMouseEventHandler<Datum>
setCurrentNode: CurrentNodeSetter<Datum>
tooltip?: NodeTooltip<Datum>
detectionThreshold: number
debug: boolean
Expand All @@ -28,6 +29,7 @@ const NonMemoizedMesh = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
setCurrentNode,
tooltip,
detectionThreshold,
debug,
Expand All @@ -36,6 +38,7 @@ const NonMemoizedMesh = <Datum extends object>({

const handleMouseEnter = useCallback(
(node: ComputedNode<Datum>, event: MouseEvent) => {
setCurrentNode(node)
if (tooltip !== undefined) {
showTooltipAt(
createElement(tooltip, { node }),
Expand All @@ -45,11 +48,12 @@ const NonMemoizedMesh = <Datum extends object>({
}
onMouseEnter && onMouseEnter(node, event)
},
[showTooltipAt, tooltip, margin.left, margin.top, onMouseEnter]
[showTooltipAt, tooltip, margin.left, margin.top, setCurrentNode, onMouseEnter]
)

const handleMouseMove = useCallback(
(node: ComputedNode<Datum>, event: MouseEvent) => {
setCurrentNode(node)
if (tooltip !== undefined) {
showTooltipAt(
createElement(tooltip, { node }),
Expand All @@ -59,15 +63,16 @@ const NonMemoizedMesh = <Datum extends object>({
}
onMouseMove && onMouseMove(node, event)
},
[showTooltipAt, tooltip, margin.left, margin.top, onMouseMove]
[showTooltipAt, tooltip, margin.left, margin.top, setCurrentNode, onMouseMove]
)

const handleMouseLeave = useCallback(
(node: ComputedNode<Datum>, event: MouseEvent) => {
setCurrentNode(null)
hideTooltip()
onMouseLeave && onMouseLeave(node, event)
},
[hideTooltip, onMouseLeave]
[hideTooltip, setCurrentNode, onMouseLeave]
)

const handleClick = useCallback(
Expand Down
6 changes: 4 additions & 2 deletions packages/dendogram/src/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const Node = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
setCurrentNode,
tooltip,
animatedProps,
}: NodeComponentProps<Datum>) => {
Expand All @@ -18,13 +19,14 @@ export const Node = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
setCurrentNode,
tooltip,
})

return (
<animated.circle
r={node.size / 2}
fill={node.color}
r={animatedProps.size.to(size => size / 2)}
fill={animatedProps.color}
cx={animatedProps.x}
cy={animatedProps.y}
{...eventHandlers}
Expand Down
28 changes: 18 additions & 10 deletions packages/dendogram/src/Nodes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { createElement } from 'react'
import { useTransition } from '@react-spring/web'
import { useMotionConfig } from '@nivo/core'
import { ComputedNode, NodeComponent, NodeMouseEventHandler, NodeTooltip } from './types'
import {
ComputedNode,
CurrentNodeSetter,
NodeComponent,
NodeMouseEventHandler,
NodeTooltip,
NodeAnimatedProps,
} from './types'

interface NodesProps<Datum extends object> {
nodes: ComputedNode<Datum>[]
Expand All @@ -11,16 +18,21 @@ interface NodesProps<Datum extends object> {
onMouseMove?: NodeMouseEventHandler<Datum>
onMouseLeave?: NodeMouseEventHandler<Datum>
onClick?: NodeMouseEventHandler<Datum>
setCurrentNode: CurrentNodeSetter<Datum>
tooltip?: NodeTooltip<Datum>
}

const regularTransition = <Datum extends object>(node: ComputedNode<Datum>) => ({
const regularTransition = <Datum extends object>(node: ComputedNode<Datum>): NodeAnimatedProps => ({
x: node.x,
y: node.y,
size: node.size,
color: node.color,
})
const leaveTransition = <Datum extends object>(node: ComputedNode<Datum>) => ({
const leaveTransition = <Datum extends object>(node: ComputedNode<Datum>): NodeAnimatedProps => ({
x: node.x,
y: node.y,
size: 0,
color: node.color,
})

export const Nodes = <Datum extends object>({
Expand All @@ -31,17 +43,12 @@ export const Nodes = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
setCurrentNode,
tooltip,
}: NodesProps<Datum>) => {
const { animate, config: springConfig } = useMotionConfig()

const transition = useTransition<
ComputedNode<Datum>,
{
x: number
y: number
}
>(nodes, {
const transition = useTransition<ComputedNode<Datum>, NodeAnimatedProps>(nodes, {
keys: node => node.uid,
from: regularTransition,
enter: regularTransition,
Expand All @@ -61,6 +68,7 @@ export const Nodes = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
setCurrentNode,
tooltip,
animatedProps,
})
Expand Down
6 changes: 5 additions & 1 deletion packages/dendogram/src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const commonDefaultProps: Pick<
| 'useMesh'
| 'meshDetectionThreshold'
| 'debugMesh'
| 'highlightAncestorNodes'
| 'highlightDescendantNodes'
| 'role'
| 'animate'
| 'motionConfig'
Expand All @@ -25,9 +27,11 @@ export const commonDefaultProps: Pick<
linkThickness: 1,
linkColor: { from: 'source.color', modifiers: [['opacity', 0.3]] },
isInteractive: true,
useMesh: false,
useMesh: true,
meshDetectionThreshold: Infinity,
debugMesh: false,
highlightAncestorNodes: true,
highlightDescendantNodes: false,
role: 'img',
animate: true,
motionConfig: 'gentle',
Expand Down
Loading

0 comments on commit ed28d78

Please sign in to comment.