Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mesh to bump chart #2496

Merged
merged 10 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/bump/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@nivo/legends": "workspace:*",
"@nivo/scales": "workspace:*",
"@nivo/tooltip": "workspace:*",
"@nivo/voronoi": "workspace:*",
"@react-spring/web": "9.4.5 || ^9.7.2",
"@types/d3-scale": "^4.0.8",
"@types/d3-shape": "^2.0.0",
Expand Down
121 changes: 81 additions & 40 deletions packages/bump/src/bump/Bump.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import {
BumpCustomLayerProps,
BumpDatum,
BumpLayerId,
BumpPointMouseHandler,
BumpSerieExtraProps,
BumpSerieMouseHandler,
BumpSvgProps,
DefaultBumpDatum,
} from './types'
import { useBump } from './hooks'
import { bumpSvgDefaultProps } from './defaults'
import { Line } from './Line'
import { LinesLabels } from './LinesLabels'
import { Points } from './Points'
import { Mesh } from './Mesh'

type InnerBumpProps<Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps> = Omit<
BumpSvgProps<Datum, ExtraProps>,
Expand Down Expand Up @@ -86,8 +88,13 @@ const InnerBump = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraPro
onMouseMove,
onMouseLeave,
onClick,
tooltip = bumpSvgDefaultProps.tooltip as NonNullable<
BumpSvgProps<Datum, ExtraProps>['tooltip']
useMesh = bumpSvgDefaultProps.useMesh,
debugMesh = bumpSvgDefaultProps.debugMesh,
lineTooltip = bumpSvgDefaultProps.lineTooltip as NonNullable<
BumpSvgProps<Datum, ExtraProps>['lineTooltip']
>,
pointTooltip = bumpSvgDefaultProps.pointTooltip as NonNullable<
BumpSvgProps<Datum, ExtraProps>['pointTooltip']
>,
role = bumpSvgDefaultProps.role,
}: InnerBumpProps<Datum, ExtraProps>) => {
Expand All @@ -97,40 +104,50 @@ const InnerBump = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraPro
partialMargin
)

const { series, points, xScale, yScale, lineGenerator, activeSerieIds, setActiveSerieIds } =
useBump<Datum, ExtraProps>({
width: innerWidth,
height: innerHeight,
data,
interpolation,
xPadding,
xOuterPadding,
yOuterPadding,
lineWidth,
activeLineWidth,
inactiveLineWidth,
colors,
opacity,
activeOpacity,
inactiveOpacity,
pointSize,
activePointSize,
inactivePointSize,
pointColor,
pointBorderWidth,
activePointBorderWidth,
inactivePointBorderWidth,
pointBorderColor,
isInteractive,
defaultActiveSerieIds,
})
const {
series,
points,
xScale,
yScale,
lineGenerator,
activePointIds,
activeSerieIds,
setActiveSerieIds,
setActivePointIds,
santiperone marked this conversation as resolved.
Show resolved Hide resolved
} = useBump<Datum, ExtraProps>({
width: innerWidth,
height: innerHeight,
data,
interpolation,
xPadding,
xOuterPadding,
yOuterPadding,
lineWidth,
activeLineWidth,
inactiveLineWidth,
colors,
opacity,
activeOpacity,
inactiveOpacity,
pointSize,
activePointSize,
inactivePointSize,
pointColor,
pointBorderWidth,
activePointBorderWidth,
inactivePointBorderWidth,
pointBorderColor,
isInteractive,
defaultActiveSerieIds,
})

const layerById: Record<BumpLayerId, ReactNode> = {
grid: null,
axes: null,
labels: null,
lines: null,
points: null,
mesh: null,
}

if (layers.includes('grid')) {
Expand Down Expand Up @@ -172,27 +189,47 @@ const InnerBump = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraPro
lineGenerator={lineGenerator}
yStep={yScale.step()}
isInteractive={isInteractive}
onMouseEnter={onMouseEnter}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onClick={onClick}
tooltip={tooltip}
onMouseEnter={onMouseEnter as BumpSerieMouseHandler<Datum, ExtraProps>}
onMouseMove={onMouseMove as BumpSerieMouseHandler<Datum, ExtraProps>}
onMouseLeave={onMouseLeave as BumpSerieMouseHandler<Datum, ExtraProps>}
onClick={onClick as BumpSerieMouseHandler<Datum, ExtraProps>}
lineTooltip={lineTooltip}
useMesh={useMesh}
/>
))}
</Fragment>
)
}

if (layers.includes('points')) {
layerById.points = (
<Points<Datum, ExtraProps>
key="points"
pointComponent={pointComponent}
if (isInteractive && useMesh && layers.includes('mesh')) {
layerById.mesh = (
<Mesh
key="mesh"
points={points}
width={innerWidth}
height={innerHeight}
margin={margin}
setActivePointIds={setActivePointIds}
setActiveSerieIds={setActiveSerieIds}
onMouseEnter={onMouseEnter as BumpPointMouseHandler<Datum, ExtraProps>}
onMouseMove={onMouseMove as BumpPointMouseHandler<Datum, ExtraProps>}
onMouseLeave={onMouseLeave as BumpPointMouseHandler<Datum, ExtraProps>}
onClick={onClick as BumpPointMouseHandler<Datum, ExtraProps>}
tooltip={pointTooltip}
debug={debugMesh}
/>
)
}

if (layers.includes('points')) {
layerById.points = points.map(point =>
createElement(pointComponent, {
key: point.id,
point,
})
)
}

if (layers.includes('labels')) {
layerById.labels = (
<Fragment key="legends">
Expand Down Expand Up @@ -228,10 +265,14 @@ const InnerBump = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraPro
xScale,
yScale,
activeSerieIds,
activePointIds,
setActiveSerieIds,
setActivePointIds,
}),
[
activePointIds,
activeSerieIds,
setActivePointIds,
setActiveSerieIds,
innerHeight,
innerWidth,
Expand Down
26 changes: 17 additions & 9 deletions packages/bump/src/bump/Line.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { useSpring, animated } from '@react-spring/web'
import { Line as D3Line } from 'd3-shape'
import { useAnimatedPath, useMotionConfig } from '@nivo/core'
import { BumpCommonProps, BumpComputedSerie, BumpDatum, BumpSerieExtraProps } from './types'
import {
BumpCommonProps,
BumpComputedSerie,
BumpDatum,
BumpSerieExtraProps,
BumpSerieMouseHandler,
} from './types'
import { useBumpSerieHandlers } from './hooks'

interface LineProps<Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps> {
serie: BumpComputedSerie<Datum, ExtraProps>
lineGenerator: D3Line<[number, number | null]>
yStep: number
isInteractive: BumpCommonProps<Datum, ExtraProps>['isInteractive']
onMouseEnter?: BumpCommonProps<Datum, ExtraProps>['onMouseEnter']
onMouseMove?: BumpCommonProps<Datum, ExtraProps>['onMouseMove']
onMouseLeave?: BumpCommonProps<Datum, ExtraProps>['onMouseLeave']
onClick?: BumpCommonProps<Datum, ExtraProps>['onClick']
onMouseEnter?: BumpSerieMouseHandler<Datum, ExtraProps>
onMouseMove?: BumpSerieMouseHandler<Datum, ExtraProps>
onMouseLeave?: BumpSerieMouseHandler<Datum, ExtraProps>
onClick?: BumpSerieMouseHandler<Datum, ExtraProps>
setActiveSerieIds: (serieIds: string[]) => void
tooltip: BumpCommonProps<Datum, ExtraProps>['tooltip']
lineTooltip: BumpCommonProps<Datum, ExtraProps>['lineTooltip']
useMesh: BumpCommonProps<Datum, ExtraProps>['useMesh']
}

export const Line = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps>({
Expand All @@ -27,7 +34,8 @@ export const Line = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraP
onMouseLeave,
onClick,
setActiveSerieIds,
tooltip,
lineTooltip,
useMesh,
}: LineProps<Datum, ExtraProps>) => {
const handlers = useBumpSerieHandlers<Datum, ExtraProps>({
serie,
Expand All @@ -37,7 +45,7 @@ export const Line = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraP
onMouseLeave,
onClick,
setActiveSerieIds,
tooltip,
lineTooltip,
})

const { animate, config: springConfig } = useMotionConfig()
Expand Down Expand Up @@ -69,7 +77,7 @@ export const Line = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraP
strokeOpacity={animatedProps.opacity}
style={{ pointerEvents: 'none' }}
/>
{isInteractive && (
{isInteractive && !useMesh && (
<path
data-testid={`line.${serie.id}.interactive`}
fill="none"
Expand Down
120 changes: 120 additions & 0 deletions packages/bump/src/bump/Mesh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { MouseEvent } from 'react'
import { createElement, memo, useCallback } from 'react'
import { Margin } from '@nivo/core'
import { useTooltip } from '@nivo/tooltip'
import { Mesh as BaseMesh } from '@nivo/voronoi'
import {
BumpCommonProps,
BumpDatum,
BumpPoint,
BumpPointMouseHandler,
BumpSerieExtraProps,
} from './types'

interface MeshProps<Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps> {
points: BumpPoint<Datum, ExtraProps>[]
width: number
height: number
margin: Margin
setActivePointIds: (ids: string[]) => void
setActiveSerieIds: (ids: string[]) => void
onMouseEnter?: BumpPointMouseHandler<Datum, ExtraProps>
onMouseMove?: BumpPointMouseHandler<Datum, ExtraProps>
onMouseLeave?: BumpPointMouseHandler<Datum, ExtraProps>
onClick?: BumpPointMouseHandler<Datum, ExtraProps>
tooltip: BumpCommonProps<Datum, ExtraProps>['pointTooltip']
debug: boolean
}

const InnerMesh = <Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps>({
points,
width,
height,
margin,
setActivePointIds,
setActiveSerieIds,
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
tooltip,
debug,
}: MeshProps<Datum, ExtraProps>) => {
const { showTooltipAt, hideTooltip } = useTooltip()

const handleMouseEnter = useCallback(
(point: BumpPoint<Datum, ExtraProps>, event: MouseEvent) => {
showTooltipAt(
createElement(tooltip, { point }),
[point.x + margin.left, point.y ?? 0 + margin.top],
'top'
)
setActivePointIds([point.id])
setActiveSerieIds([point.serie.id])
onMouseEnter && onMouseEnter(point, event)
},
[
showTooltipAt,
tooltip,
margin.left,
margin.top,
setActivePointIds,
setActiveSerieIds,
onMouseEnter,
]
)

const handleMouseMove = useCallback(
(point: BumpPoint<Datum, ExtraProps>, event: MouseEvent) => {
showTooltipAt(
createElement(tooltip, { point }),
[point.x + margin.left, point.y ?? 0 + margin.top],
'top'
)
setActivePointIds([point.id])
setActiveSerieIds([point.serie.id])
onMouseMove && onMouseMove(point, event)
},
[
showTooltipAt,
tooltip,
margin.left,
margin.top,
setActivePointIds,
setActiveSerieIds,
onMouseMove,
]
)

const handleMouseLeave = useCallback(
(point: BumpPoint<Datum, ExtraProps>, event: MouseEvent) => {
hideTooltip()
setActivePointIds([])
setActiveSerieIds([])
onMouseLeave && onMouseLeave(point, event)
},
[hideTooltip, onMouseLeave, setActivePointIds, setActiveSerieIds]
)

const handleClick = useCallback(
(point: BumpPoint<Datum, ExtraProps>, event: MouseEvent) => {
onClick && onClick(point, event)
},
[onClick]
)

return (
<BaseMesh
nodes={points}
width={width}
height={height}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
debug={debug}
/>
)
}

export const Mesh = memo(InnerMesh) as typeof InnerMesh
6 changes: 4 additions & 2 deletions packages/bump/src/bump/Point.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { useSpring, animated, to } from '@react-spring/web'
import { useMotionConfig } from '@nivo/core'
import { BumpDatum, BumpPoint, BumpSerieExtraProps } from './types'

const pointStyle: SVGAttributes<SVGCircleElement>['style'] = { pointerEvents: 'none' }
const pointStyle: SVGAttributes<SVGCircleElement>['style'] = {
pointerEvents: 'none',
}

interface PointProps<Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps> {
export interface PointProps<Datum extends BumpDatum, ExtraProps extends BumpSerieExtraProps> {
point: BumpPoint<Datum, ExtraProps>
}

Expand Down
Loading
Loading