diff --git a/packages/scatterplot/index.d.ts b/packages/scatterplot/index.d.ts index 246d8613e..cbbde2180 100644 --- a/packages/scatterplot/index.d.ts +++ b/packages/scatterplot/index.d.ts @@ -14,6 +14,7 @@ import { MotionProps, CartesianMarkerProps, CssMixBlendMode, + PropertyAccessor, } from '@nivo/core' import { OrdinalColorScaleConfig } from '@nivo/colors' import { LegendProps } from '@nivo/legends' @@ -31,6 +32,11 @@ declare module '@nivo/scatterplot' { y: Value } + export interface NodeIdDatum extends Record { + serieId: Serie['id'] + index: number + } + export type DerivedDatumProp = (node: Datum) => T export interface Serie { @@ -138,6 +144,7 @@ declare module '@nivo/scatterplot' { axisBottom?: AxisProps | null axisLeft?: AxisProps | null + nodeId?: PropertyAccessor nodeSize?: number | DerivedDatumProp | DynamicSizeSpec | DerivedNodeProp isInteractive?: boolean diff --git a/packages/scatterplot/src/ScatterPlot.js b/packages/scatterplot/src/ScatterPlot.js index aa8ed65cd..95f4722c1 100644 --- a/packages/scatterplot/src/ScatterPlot.js +++ b/packages/scatterplot/src/ScatterPlot.js @@ -41,6 +41,7 @@ const ScatterPlot = props => { colors, blendMode, + nodeId, nodeSize, renderNode, @@ -87,6 +88,7 @@ const ScatterPlot = props => { yFormat, width: innerWidth, height: innerHeight, + nodeId, nodeSize, colors, }) diff --git a/packages/scatterplot/src/compute.js b/packages/scatterplot/src/compute.js index 6c924d4a7..48327ac91 100644 --- a/packages/scatterplot/src/compute.js +++ b/packages/scatterplot/src/compute.js @@ -42,23 +42,27 @@ export const getNodeSizeGenerator = size => { throw new Error('symbolSize is invalid, it should be either a function, a number or an object') } -export const computePoints = ({ series, formatX, formatY }) => { +export const computePoints = ({ series, formatX, formatY, getNodeId }) => { return series.reduce( (agg, serie) => [ ...agg, - ...serie.data.map((d, i) => ({ - index: agg.length + i, - id: `${serie.id}.${i}`, - x: d.position.x, - y: d.position.y, - data: { - ...d.data, - id: `${serie.id}.${i}`, - serieId: serie.id, - formattedX: formatX(d.data.x), - formattedY: formatY(d.data.y), - }, - })), + ...serie.data.map((d, index) => { + const id = getNodeId({ serieId: serie.id, index, ...d.data }) + + return { + index: agg.length + index, + id, + x: d.position.x, + y: d.position.y, + data: { + ...d.data, + id, + serieId: serie.id, + formattedX: formatX(d.data.x), + formattedY: formatY(d.data.y), + }, + } + }), ], [] ) diff --git a/packages/scatterplot/src/hooks.js b/packages/scatterplot/src/hooks.js index 085ea980f..d4082c59c 100644 --- a/packages/scatterplot/src/hooks.js +++ b/packages/scatterplot/src/hooks.js @@ -7,7 +7,7 @@ * file that was distributed with this source code. */ import { useMemo } from 'react' -import { useValueFormatter } from '@nivo/core' +import { useValueFormatter, usePropertyAccessor } from '@nivo/core' import { useOrdinalColorScale } from '@nivo/colors' import { computeXYScalesForSeries } from '@nivo/scales' import { useAnnotations } from '@nivo/annotations' @@ -23,6 +23,7 @@ export const useScatterPlot = ({ yFormat, width, height, + nodeId, nodeSize, colors, }) => { @@ -33,10 +34,12 @@ export const useScatterPlot = ({ const formatX = useValueFormatter(xFormat) const formatY = useValueFormatter(yFormat) - const rawNodes = useMemo(() => computePoints({ series, formatX, formatY }), [ + const getNodeId = usePropertyAccessor(nodeId) + const rawNodes = useMemo(() => computePoints({ series, formatX, formatY, getNodeId }), [ series, formatX, formatY, + getNodeId, ]) const getNodeSize = useNodeSize(nodeSize) diff --git a/packages/scatterplot/src/props.js b/packages/scatterplot/src/props.js index fa2f46227..7f4938209 100644 --- a/packages/scatterplot/src/props.js +++ b/packages/scatterplot/src/props.js @@ -120,6 +120,7 @@ const commonDefaultProps = { axisBottom: {}, axisLeft: {}, + nodeId: ({ serieId, index }) => `${serieId}.${index}`, nodeSize: 9, renderNode: Node, diff --git a/website/src/data/components/scatterplot/props.js b/website/src/data/components/scatterplot/props.js index 18f8575be..041ab3954 100644 --- a/website/src/data/components/scatterplot/props.js +++ b/website/src/data/components/scatterplot/props.js @@ -99,7 +99,7 @@ const props = [ If you use a time scale, you must provide a time format as values are converted to Date objects. - + Under the hood, nivo uses [d3-format](https://github.com/d3/d3-format), please have a look at it for available formats, you can also pass a function which will receive the raw value and should return the formatted one. @@ -165,12 +165,24 @@ const props = [ If you use a time scale, you must provide a time format as values are converted to Date objects. - + Under the hood, nivo uses [d3-format](https://github.com/d3/d3-format), please have a look at it for available formats, you can also pass a function which will receive the raw value and should return the formatted one. `, }, + { + key: 'nodeId', + group: 'Base', + defaultValue: defaults.nodeId, + type: 'string | (datum: NodeIdDatum): string', + help: `ID accessor for the node.`, + description: ` + Define how to determine the id of each node on the plot. + + By default nivo will join together the serie.id and datum index. + `, + }, { key: 'nodeSize', group: 'Base', @@ -306,7 +318,7 @@ const props = [ You can also use this to insert extra layers to the chart. - + For \`ScatterPlot\`, the extra layer should be a component and will receive current chart context as props. @@ -395,7 +407,7 @@ const props = [ A function allowing complete tooltip customisation, it must return a valid HTML element and will receive the node as a property. - + You can also customize the tooltip style using the \`theme.tooltip\` object. `, @@ -428,7 +440,6 @@ const props = [ type: '(node, event) => void', required: false, }, - , { key: 'legends', group: 'Legends',