Skip to content

Commit

Permalink
feat(dendogram): add support for links tooltips
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed May 3, 2024
1 parent ac5c4a9 commit 10bbe2f
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 68 deletions.
2 changes: 2 additions & 0 deletions packages/dendogram/src/Dendogram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const InnerDendogram = <Datum extends object>({
onLinkMouseMove,
onLinkMouseLeave,
onLinkClick,
linkTooltip,
role = svgDefaultProps.role,
ariaLabel,
ariaLabelledBy,
Expand Down Expand Up @@ -69,6 +70,7 @@ const InnerDendogram = <Datum extends object>({
onMouseMove={onLinkMouseMove}
onMouseLeave={onLinkMouseLeave}
onClick={onLinkClick}
tooltip={linkTooltip}
/>
)
}
Expand Down
3 changes: 2 additions & 1 deletion packages/dendogram/src/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ export const Link = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
tooltip,
}: LinkComponentProps<Datum>) => {
const eventHandlers = useLinkMouseEventHandlers<Datum>(link, {
isInteractive,
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
// tooltip,
tooltip,
})

return (
Expand Down
6 changes: 4 additions & 2 deletions packages/dendogram/src/Links.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createElement } from 'react'
import { useTransition } from '@react-spring/web'
import { useMotionConfig } from '@nivo/core'
import { ComputedLink, LinkComponent, LinkMouseEventHandler } from './types'
import { ComputedLink, LinkComponent, LinkMouseEventHandler, LinkTooltip } from './types'

interface LinksProps<Datum extends object> {
links: ComputedLink<Datum>[]
Expand All @@ -11,7 +11,7 @@ interface LinksProps<Datum extends object> {
onMouseMove?: LinkMouseEventHandler<Datum>
onMouseLeave?: LinkMouseEventHandler<Datum>
onClick?: LinkMouseEventHandler<Datum>
// tooltip?: NodeTooltip<Datum>
tooltip?: LinkTooltip<Datum>
}

const regularTransition = <Datum extends object>(link: ComputedLink<Datum>) => ({
Expand All @@ -35,6 +35,7 @@ export const Links = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
tooltip,
}: LinksProps<Datum>) => {
const { animate, config: springConfig } = useMotionConfig()

Expand Down Expand Up @@ -67,6 +68,7 @@ export const Links = <Datum extends object>({
onMouseMove,
onMouseLeave,
onClick,
tooltip,
})
)}
</>
Expand Down
67 changes: 28 additions & 39 deletions packages/dendogram/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,23 @@ import {
IntermediateComputedLink,
LinkThicknessFunction,
LinkMouseEventHandler,
LinkTooltip,
} from './types'
import { commonDefaultProps } from './defaults'

export const useHierarchy = <Datum extends object>({ root }: { root: Datum }) =>
useMemo(() => d3Hierarchy<Datum>(root) as HierarchyDendogramNode<Datum>, [root])

export const useCluster = <Datum extends object>({
width,
height,
layout,
}: {
export const useCluster = <Datum extends object>(_props: {
width: number
height: number
layout: Layout
}) =>
useMemo(() => {
const cluster = d3Cluster<Datum>().size([1, 1])

if (layout === 'bottom-to-top') {
// cluster.size([width, -height])
} else {
// cluster.size([width, height])
}

return cluster
}, [width, height, layout])
}, [])

const computeNodePath = <Datum extends object>(
node: HierarchyDendogramNode<Datum>,
Expand Down Expand Up @@ -142,7 +133,7 @@ const useNodes = <Datum extends object>({
nodes,
nodeByUid,
}
}, [root, xScale, yScale])
}, [root, getIdentity, layout, xScale, yScale])

const useLinks = <Datum extends object>({
root,
Expand Down Expand Up @@ -257,8 +248,7 @@ export const useNodeMouseEventHandlers = <Datum extends object>(
createElement(tooltip, {
node,
}),
event,
'left'
event
)
},
[node, tooltip, showTooltipFromEvent]
Expand Down Expand Up @@ -316,54 +306,53 @@ export const useLinkMouseEventHandlers = <Datum extends object>(
onMouseMove,
onMouseLeave,
onClick,
}: // tooltip,
{
tooltip,
}: {
isInteractive: boolean
onMouseEnter?: LinkMouseEventHandler<Datum>
onMouseMove?: LinkMouseEventHandler<Datum>
onMouseLeave?: LinkMouseEventHandler<Datum>
onClick?: LinkMouseEventHandler<Datum>
// tooltip?: NodeTooltip<Datum>
tooltip?: LinkTooltip<Datum>
}
) => {
// const { showTooltipFromEvent, hideTooltip } = useTooltip()

// const showTooltip = useCallback(
// (event: MouseEvent) => {
// tooltip !== undefined &&
// showTooltipFromEvent(
// createElement(tooltip, {
// node,
// }),
// event,
// 'left'
// )
// },
// [node, tooltip, showTooltipFromEvent]
// )
const { showTooltipFromEvent, hideTooltip } = useTooltip()

const showTooltip = useCallback(
(event: MouseEvent) => {
tooltip !== undefined &&
showTooltipFromEvent(
createElement(tooltip, {
link,
}),
event
)
},
[link, tooltip, showTooltipFromEvent]
)

const handleMouseEnter = useCallback(
(event: MouseEvent) => {
// showTooltip(event)
showTooltip(event)
onMouseEnter?.(link, event)
},
[link, onMouseEnter]
[link, showTooltip, onMouseEnter]
)

const handleMouseMove = useCallback(
(event: MouseEvent) => {
// showTooltip(event)
showTooltip(event)
onMouseMove?.(link, event)
},
[link, onMouseMove]
[link, showTooltip, onMouseMove]
)

const handleMouseLeave = useCallback(
(event: MouseEvent) => {
// hideTooltip()
hideTooltip()
onMouseLeave?.(link, event)
},
[link, onMouseLeave]
[link, hideTooltip, onMouseLeave]
)

const handleClick = useCallback(
Expand Down
8 changes: 7 additions & 1 deletion packages/dendogram/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export interface LinkComponentProps<Datum extends object> {
onMouseMove?: LinkMouseEventHandler<Datum>
onMouseLeave?: LinkMouseEventHandler<Datum>
onClick?: LinkMouseEventHandler<Datum>
// tooltip?: NodeTooltip<Datum>
tooltip?: LinkTooltip<Datum>
animatedProps: SpringValues<{
sourceX: number
sourceY: number
Expand All @@ -94,6 +94,11 @@ export type LinkMouseEventHandler<Datum extends object> = (
event: MouseEvent
) => void

export interface LinkTooltipProps<Datum extends object> {
link: ComputedLink<Datum>
}
export type LinkTooltip<Datum extends object> = FunctionComponent<LinkTooltipProps<Datum>>

export interface CustomLayerProps<Datum extends object> {
nodes: ComputedNode<Datum>[]
links: ComputedLink<Datum>[]
Expand Down Expand Up @@ -125,6 +130,7 @@ export interface CommonProps<Datum extends object> extends MotionProps {
onLinkMouseMove: LinkMouseEventHandler<Datum>
onLinkMouseLeave: LinkMouseEventHandler<Datum>
onLinkClick: LinkMouseEventHandler<Datum>
linkTooltip: LinkTooltip<Datum>

role: string
renderWrapper: boolean
Expand Down
81 changes: 69 additions & 12 deletions storybook/stories/dendogram/Dendogram.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Meta, StoryObj } from '@storybook/react'
import { generateLibTree } from '@nivo/generators'
import { useTheme } from '@nivo/core'
import {
Dendogram,
useNodeMouseEventHandlers,
NodeComponentProps,
NodeTooltipProps,
LinkTooltipProps,
} from '@nivo/dendogram'

const meta: Meta<typeof Dendogram> = {
Expand All @@ -14,7 +16,7 @@ const meta: Meta<typeof Dendogram> = {
argTypes: {
layout: {
control: 'select',
options: ['top-to-bottom', 'right-to-left', 'bottom-to-top', 'left-to-right', 'radial'],
options: ['top-to-bottom', 'right-to-left', 'bottom-to-top', 'left-to-right'],
},
onNodeMouseEnter: { action: 'node mouse enter' },
onNodeMouseMove: { action: 'node mouse move' },
Expand Down Expand Up @@ -45,7 +47,31 @@ const commonProperties = {
}

const NodeTooltip = ({ node }: NodeTooltipProps<any>) => {
return node.path
const theme = useTheme()

return (
<div style={theme.tooltip.container}>
id: <strong>{node.id}</strong>
<br />
path: <strong>{node.pathComponents.join(' > ')}</strong>
<br />
uid: <strong>{node.uid}</strong>
</div>
)
}

const LinkTooltip = ({ link }: LinkTooltipProps<any>) => {
const theme = useTheme()

return (
<div style={theme.tooltip.container}>
id: <strong>{link.id}</strong>
<br />
source: <strong>{link.source.id}</strong>
<br />
target: <strong>{link.target.id}</strong>
</div>
)
}

export const Basic: Story = {
Expand Down Expand Up @@ -75,6 +101,21 @@ export const WithNodeTooltip: Story = {
),
}

export const WithLinkTooltip: Story = {
render: args => (
<Dendogram
{...commonProperties}
linkThickness={12}
layout={args.layout}
linkTooltip={LinkTooltip}
onNodeMouseEnter={args.onNodeMouseEnter}
onNodeMouseMove={args.onNodeMouseMove}
onNodeMouseLeave={args.onNodeMouseLeave}
onNodeClick={args.onNodeClick}
/>
),
}

const CUSTOM_NODE_SIZE = 32
const CustomNode = ({
node,
Expand All @@ -95,16 +136,31 @@ const CustomNode = ({
})

return (
<rect
x={node.x - CUSTOM_NODE_SIZE / 2}
y={node.y - CUSTOM_NODE_SIZE / 2}
width={CUSTOM_NODE_SIZE}
height={CUSTOM_NODE_SIZE}
rx={3}
ry={3}
fill="red"
{...eventHandlers}
/>
<g
transform={`translate(${node.x - CUSTOM_NODE_SIZE / 2}, ${
node.y - CUSTOM_NODE_SIZE / 2
})`}
>
<rect
y={5}
width={CUSTOM_NODE_SIZE}
height={CUSTOM_NODE_SIZE}
rx={3}
ry={3}
fill="black"
opacity={0.15}
/>
<rect
width={CUSTOM_NODE_SIZE}
height={CUSTOM_NODE_SIZE}
rx={3}
ry={3}
fill="white"
strokeWidth={1}
stroke="red"
{...eventHandlers}
/>
</g>
)
}

Expand All @@ -113,6 +169,7 @@ export const CustomNodeComponent: Story = {
<Dendogram
{...commonProperties}
layout={args.layout}
linkThickness={4}
nodeTooltip={NodeTooltip}
nodeComponent={CustomNode}
onNodeMouseEnter={args.onNodeMouseEnter}
Expand Down
10 changes: 6 additions & 4 deletions website/src/data/components/dendogram/meta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ Dendogram:
tags:
- hierarchy
stories:
- label: Using custom tooltip
link: treemap--custom-tooltip
- label: Patterns & Gradients
link: treemap--patterns-and-gradients
- label: Custom node component
link: dendogram--custom-node-component
- label: With node tooltip
link: dendogram--with-node-tooltip
- label: With link tooltip
link: dendogram--with-link-tooltip
description: |
Dendogram
Loading

0 comments on commit 10bbe2f

Please sign in to comment.