From a68d98d1640746bd6252dfadedacf25b2ac3610d Mon Sep 17 00:00:00 2001 From: plouc Date: Wed, 29 Dec 2021 20:18:35 +0900 Subject: [PATCH] feat(bump): improve AreaBump typings and add support for series extra properties --- packages/bump/src/area-bump/Area.tsx | 41 ++--- packages/bump/src/area-bump/AreaBump.tsx | 77 +++++--- packages/bump/src/area-bump/AreaTooltip.tsx | 20 ++- packages/bump/src/area-bump/AreasLabels.tsx | 24 ++- .../bump/src/area-bump/ResponsiveAreaBump.tsx | 18 +- packages/bump/src/area-bump/compute.ts | 37 ++-- packages/bump/src/area-bump/defaults.ts | 6 +- packages/bump/src/area-bump/hooks.ts | 165 ++++++++++-------- packages/bump/src/area-bump/types.ts | 137 ++++++++++----- packages/bump/src/bump/Bump.tsx | 39 +++-- packages/bump/src/bump/defaults.ts | 6 +- packages/bump/src/bump/types.ts | 4 +- packages/bump/tests/Bump.test.tsx | 4 +- 13 files changed, 356 insertions(+), 222 deletions(-) diff --git a/packages/bump/src/area-bump/Area.tsx b/packages/bump/src/area-bump/Area.tsx index b9bbc3c10b..9d5833400c 100644 --- a/packages/bump/src/area-bump/Area.tsx +++ b/packages/bump/src/area-bump/Area.tsx @@ -6,22 +6,23 @@ import { AreaBumpCommonProps, AreaBumpComputedSerie, AreaBumpDatum, + AreaBumpSerieExtraProps, } from './types' -interface AreaProps { - serie: AreaBumpComputedSerie +interface AreaProps { + serie: AreaBumpComputedSerie areaGenerator: AreaBumpAreaGenerator - blendMode: AreaBumpCommonProps['blendMode'] - isInteractive: AreaBumpCommonProps['isInteractive'] - onMouseEnter?: AreaBumpCommonProps['onMouseEnter'] - onMouseMove?: AreaBumpCommonProps['onMouseMove'] - onMouseLeave?: AreaBumpCommonProps['onMouseLeave'] - onClick?: AreaBumpCommonProps['onClick'] - setCurrentSerie: any - tooltip: AreaBumpCommonProps['tooltip'] + blendMode: AreaBumpCommonProps['blendMode'] + isInteractive: AreaBumpCommonProps['isInteractive'] + onMouseEnter?: AreaBumpCommonProps['onMouseEnter'] + onMouseMove?: AreaBumpCommonProps['onMouseMove'] + onMouseLeave?: AreaBumpCommonProps['onMouseLeave'] + onClick?: AreaBumpCommonProps['onClick'] + setActiveSerieIds: (serieIds: string[]) => void + tooltip: AreaBumpCommonProps['tooltip'] } -export const Area = ({ +export const Area = ({ serie, areaGenerator, blendMode, @@ -30,23 +31,23 @@ export const Area = ({ onMouseMove, onMouseLeave, onClick, - setCurrentSerie, + setActiveSerieIds, tooltip, -}: AreaProps) => { - const handlers = useAreaBumpSerieHandlers({ +}: AreaProps) => { + const handlers = useAreaBumpSerieHandlers({ serie, isInteractive, onMouseEnter, onMouseMove, onMouseLeave, onClick, - setCurrent: setCurrentSerie, + setActiveSerieIds, tooltip, }) const { animate, config: springConfig } = useMotionConfig() - const animatedPath = useAnimatedPath(areaGenerator(serie.areaPoints)!) + const animatedPath = useAnimatedPath(areaGenerator(serie.areaPoints) || '') const animatedProps = useSpring<{ color: string fillOpacity: number @@ -54,9 +55,9 @@ export const Area = ({ strokeOpacity: number }>({ color: serie.color, - fillOpacity: serie.style.fillOpacity, - stroke: serie.style.borderColor, - strokeOpacity: serie.style.borderOpacity, + fillOpacity: serie.fillOpacity, + stroke: serie.borderColor, + strokeOpacity: serie.borderOpacity, config: springConfig, immediate: !animate, }) @@ -67,7 +68,7 @@ export const Area = ({ fill={serie.fill ? serie.fill : animatedProps.color} fillOpacity={animatedProps.fillOpacity} stroke={animatedProps.stroke} - strokeWidth={serie.style.borderWidth} + strokeWidth={serie.borderWidth} strokeOpacity={animatedProps.strokeOpacity} style={{ mixBlendMode: blendMode }} onMouseEnter={handlers.onMouseEnter} diff --git a/packages/bump/src/area-bump/AreaBump.tsx b/packages/bump/src/area-bump/AreaBump.tsx index db30a80f7f..5975587fce 100644 --- a/packages/bump/src/area-bump/AreaBump.tsx +++ b/packages/bump/src/area-bump/AreaBump.tsx @@ -1,4 +1,4 @@ -import { useState, Fragment, useMemo, ReactNode, createElement } from 'react' +import { Fragment, useMemo, ReactNode, createElement } from 'react' import { // @ts-ignore bindDefs, @@ -16,15 +16,19 @@ import { DefaultAreaBumpDatum, AreaBumpLayerId, AreaBumpCustomLayerProps, + AreaBumpSerieExtraProps, } from './types' import { areaBumpSvgDefaultProps } from './defaults' -type InnerAreaBumpProps = Omit< - AreaBumpSvgProps, +type InnerAreaBumpProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = Omit< + AreaBumpSvgProps, 'animate' | 'motionConfig' | 'renderWrapper' | 'theme' > -const InnerAreaBump = ({ +const InnerAreaBump = ({ data, align = areaBumpSvgDefaultProps.align, @@ -32,55 +36,73 @@ const InnerAreaBump = ({ height, margin: partialMargin, - layers = areaBumpSvgDefaultProps.layers, + layers = areaBumpSvgDefaultProps.layers as NonNullable< + AreaBumpSvgProps['layers'] + >, interpolation = areaBumpSvgDefaultProps.interpolation, spacing = areaBumpSvgDefaultProps.spacing, xPadding = areaBumpSvgDefaultProps.xPadding, - colors = areaBumpSvgDefaultProps.colors, + colors = areaBumpSvgDefaultProps.colors as NonNullable< + AreaBumpSvgProps['colors'] + >, blendMode = areaBumpSvgDefaultProps.blendMode, fillOpacity = areaBumpSvgDefaultProps.fillOpacity, activeFillOpacity = areaBumpSvgDefaultProps.activeFillOpacity, inactiveFillOpacity = areaBumpSvgDefaultProps.inactiveFillOpacity, defs = areaBumpSvgDefaultProps.defs, - fill = areaBumpSvgDefaultProps.fill, + fill = areaBumpSvgDefaultProps.fill as NonNullable['fill']>, borderWidth = areaBumpSvgDefaultProps.borderWidth, activeBorderWidth = areaBumpSvgDefaultProps.activeBorderWidth, inactiveBorderWidth = areaBumpSvgDefaultProps.inactiveBorderWidth, - borderColor = areaBumpSvgDefaultProps.borderColor, + borderColor = areaBumpSvgDefaultProps.borderColor as NonNullable< + AreaBumpSvgProps['borderColor'] + >, borderOpacity = areaBumpSvgDefaultProps.borderOpacity, activeBorderOpacity = areaBumpSvgDefaultProps.activeBorderOpacity, inactiveBorderOpacity = areaBumpSvgDefaultProps.inactiveBorderOpacity, - startLabel = areaBumpSvgDefaultProps.startLabel, + startLabel = areaBumpSvgDefaultProps.startLabel as NonNullable< + AreaBumpSvgProps['startLabel'] + >, startLabelPadding = areaBumpSvgDefaultProps.startLabelPadding, - startLabelTextColor = areaBumpSvgDefaultProps.startLabelTextColor, - endLabel = areaBumpSvgDefaultProps.endLabel, + startLabelTextColor = areaBumpSvgDefaultProps.startLabelTextColor as NonNullable< + AreaBumpSvgProps['startLabelTextColor'] + >, + endLabel = areaBumpSvgDefaultProps.endLabel as NonNullable< + AreaBumpSvgProps['endLabel'] + >, endLabelPadding = areaBumpSvgDefaultProps.endLabelPadding, - endLabelTextColor = areaBumpSvgDefaultProps.endLabelTextColor, + endLabelTextColor = areaBumpSvgDefaultProps.endLabelTextColor as NonNullable< + AreaBumpSvgProps['endLabelTextColor'] + >, enableGridX = areaBumpSvgDefaultProps.enableGridX, axisTop = areaBumpSvgDefaultProps.axisTop, axisBottom = areaBumpSvgDefaultProps.axisBottom, isInteractive = areaBumpSvgDefaultProps.isInteractive, + defaultActiveSerieIds = areaBumpSvgDefaultProps.defaultActiveSerieIds, onMouseEnter, onMouseMove, onMouseLeave, onClick, - tooltip = areaBumpSvgDefaultProps.tooltip, + tooltip = areaBumpSvgDefaultProps.tooltip as NonNullable< + AreaBumpSvgProps['tooltip'] + >, role = areaBumpSvgDefaultProps.role, -}: InnerAreaBumpProps) => { - const [currentSerie, setCurrentSerie] = useState(null) - +}: InnerAreaBumpProps) => { const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( width, height, partialMargin ) - const { series, xScale, heightScale, areaGenerator } = useAreaBump({ + const { series, xScale, heightScale, areaGenerator, setActiveSerieIds } = useAreaBump< + Datum, + ExtraProps + >({ data, width: innerWidth, height: innerHeight, @@ -100,7 +122,7 @@ const InnerAreaBump = ({ activeBorderOpacity, inactiveBorderOpacity, isInteractive, - current: currentSerie, + defaultActiveSerieIds, }) const boundDefs = useMemo( @@ -137,13 +159,13 @@ const InnerAreaBump = ({ layerById.areas = ( {series.map(serie => ( - + key={serie.id} areaGenerator={areaGenerator} serie={serie} blendMode={blendMode} isInteractive={isInteractive} - setCurrentSerie={setCurrentSerie} + setActiveSerieIds={setActiveSerieIds} onMouseEnter={onMouseEnter} onMouseMove={onMouseMove} onMouseLeave={onMouseLeave} @@ -159,7 +181,7 @@ const InnerAreaBump = ({ layerById.labels = ( {startLabel !== false && ( - + label={startLabel} series={series} position="start" @@ -168,7 +190,7 @@ const InnerAreaBump = ({ /> )} {endLabel !== false && ( - + label={endLabel} series={series} position="end" @@ -180,7 +202,7 @@ const InnerAreaBump = ({ ) } - const customLayerProps: AreaBumpCustomLayerProps = useMemo( + const customLayerProps: AreaBumpCustomLayerProps = useMemo( () => ({ innerWidth, innerHeight, @@ -212,14 +234,17 @@ const InnerAreaBump = ({ ) } -export const AreaBump = ({ +export const AreaBump = < + Datum extends AreaBumpDatum = DefaultAreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps = Record +>({ isInteractive = areaBumpSvgDefaultProps.isInteractive, animate = areaBumpSvgDefaultProps.animate, motionConfig = areaBumpSvgDefaultProps.motionConfig, theme, renderWrapper, ...otherProps -}: AreaBumpSvgProps) => ( +}: AreaBumpSvgProps) => ( ({ theme, }} > - isInteractive={isInteractive} {...otherProps} /> + isInteractive={isInteractive} {...otherProps} /> ) diff --git a/packages/bump/src/area-bump/AreaTooltip.tsx b/packages/bump/src/area-bump/AreaTooltip.tsx index f17c8b1489..744d012f71 100644 --- a/packages/bump/src/area-bump/AreaTooltip.tsx +++ b/packages/bump/src/area-bump/AreaTooltip.tsx @@ -1,10 +1,18 @@ import { BasicTooltip } from '@nivo/tooltip' -import { AreaBumpDatum, AreaBumpComputedSerie } from './types' +import { AreaBumpDatum, AreaBumpComputedSerie, AreaBumpSerieExtraProps } from './types' -interface AreaTooltipProps { - serie: AreaBumpComputedSerie +interface AreaTooltipProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> { + serie: AreaBumpComputedSerie } -export const AreaTooltip = ({ serie }: AreaTooltipProps) => { - return -} +export const AreaTooltip = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ + serie, +}: AreaTooltipProps) => ( + +) diff --git a/packages/bump/src/area-bump/AreasLabels.tsx b/packages/bump/src/area-bump/AreasLabels.tsx index be6e7a7490..d10d58a8da 100644 --- a/packages/bump/src/area-bump/AreasLabels.tsx +++ b/packages/bump/src/area-bump/AreasLabels.tsx @@ -1,28 +1,36 @@ import { useSprings, animated } from '@react-spring/web' import { useTheme, useMotionConfig } from '@nivo/core' import { InheritedColorConfig } from '@nivo/colors' -import { AreaBumpComputedSerie, AreaBumpDatum, AreaBumpLabel } from './types' +import { + AreaBumpComputedSerie, + AreaBumpDatum, + AreaBumpLabel, + AreaBumpSerieExtraProps, +} from './types' import { useAreaBumpSeriesLabels } from './hooks' -interface AreaLabelsProps { - label: Exclude, false> - series: AreaBumpComputedSerie[] +interface AreaLabelsProps { + label: Exclude, false> + series: AreaBumpComputedSerie[] position: 'start' | 'end' padding: number - color: InheritedColorConfig> + color: InheritedColorConfig> } -export const AreasLabels = ({ +export const AreasLabels = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ label, series, position, padding, color, -}: AreaLabelsProps) => { +}: AreaLabelsProps) => { const theme = useTheme() const { animate, config: springConfig } = useMotionConfig() - const labels = useAreaBumpSeriesLabels({ + const labels = useAreaBumpSeriesLabels({ label, series, position, diff --git a/packages/bump/src/area-bump/ResponsiveAreaBump.tsx b/packages/bump/src/area-bump/ResponsiveAreaBump.tsx index 7d84355631..e2268b4999 100644 --- a/packages/bump/src/area-bump/ResponsiveAreaBump.tsx +++ b/packages/bump/src/area-bump/ResponsiveAreaBump.tsx @@ -1,11 +1,21 @@ import { ResponsiveWrapper } from '@nivo/core' -import { AreaBumpDatum, AreaBumpSvgProps, DefaultAreaBumpDatum } from './types' +import { + AreaBumpDatum, + AreaBumpSerieExtraProps, + AreaBumpSvgProps, + DefaultAreaBumpDatum, +} from './types' import { AreaBump } from './AreaBump' -export const ResponsiveAreaBump = ( - props: Omit, 'width' | 'height'> +export const ResponsiveAreaBump = < + Datum extends AreaBumpDatum = DefaultAreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps = Record +>( + props: Omit, 'width' | 'height'> ) => ( - {({ width, height }) => width={width} height={height} {...props} />} + {({ width, height }) => ( + width={width} height={height} {...props} /> + )} ) diff --git a/packages/bump/src/area-bump/compute.ts b/packages/bump/src/area-bump/compute.ts index 4b7855a4e0..bf2c24fffc 100644 --- a/packages/bump/src/area-bump/compute.ts +++ b/packages/bump/src/area-bump/compute.ts @@ -5,9 +5,13 @@ import { AreaBumpComputedSerie, AreaBumpDataProps, AreaBumpDatum, + AreaBumpSerieExtraProps, } from './types' -export const computeSeries = ({ +export const computeSeries = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ data, width, height, @@ -15,21 +19,24 @@ export const computeSeries = ({ spacing, xPadding, }: { - data: AreaBumpDataProps['data'] + data: AreaBumpDataProps['data'] width: number height: number - align: AreaBumpCommonProps['align'] - spacing: AreaBumpCommonProps['spacing'] - xPadding: AreaBumpCommonProps['xPadding'] + align: AreaBumpCommonProps['align'] + spacing: AreaBumpCommonProps['spacing'] + xPadding: AreaBumpCommonProps['xPadding'] }): { - series: Omit, 'color' | 'style'>[] - xScale: ScalePoint + series: Omit< + AreaBumpComputedSerie, + 'color' | 'fill' | 'fillOpacity' | 'borderWidth' | 'borderColor' | 'borderOpacity' + >[] + xScale: ScalePoint heightScale: ScaleLinear } => { const slices = new Map< - D['x'], + Datum['x'], { - id: D['x'] + id: Datum['x'] total: number x: number values: Map< @@ -81,8 +88,8 @@ export const computeSeries = ({ }) }) - const xScale = castPointScale( - scalePoint().domain(Array.from(slices.keys())).range([0, width]) + const xScale = castPointScale( + scalePoint().domain(Array.from(slices.keys())).range([0, width]) ) const heightScale = castLinearScale( @@ -119,8 +126,12 @@ export const computeSeries = ({ const areaPointPadding = xScale.step() * Math.min(xPadding * 0.5, 0.5) const series = data.map(serie => { - const computedSerie: Omit, 'color' | 'style'> = { - ...serie, + const computedSerie: Omit< + AreaBumpComputedSerie, + 'color' | 'fill' | 'fillOpacity' | 'borderWidth' | 'borderColor' | 'borderOpacity' + > = { + id: serie.id, + data: serie, points: [], areaPoints: [], } diff --git a/packages/bump/src/area-bump/defaults.ts b/packages/bump/src/area-bump/defaults.ts index 939004d8bb..2b4b0f346e 100644 --- a/packages/bump/src/area-bump/defaults.ts +++ b/packages/bump/src/area-bump/defaults.ts @@ -1,9 +1,9 @@ import { ModernMotionProps, SvgDefsAndFill } from '@nivo/core' -import { AreaBumpCommonProps, AreaBumpComputedSerie } from './types' +import { AreaBumpCommonProps, AreaBumpComputedSerie, DefaultAreaBumpDatum } from './types' import { AreaTooltip } from './AreaTooltip' const commonDefaultProps: Omit< - AreaBumpCommonProps, + AreaBumpCommonProps>, | 'onMouseEnter' | 'onMouseMove' | 'onMouseLeave' @@ -52,7 +52,7 @@ const commonDefaultProps: Omit< } export const areaBumpSvgDefaultProps: typeof commonDefaultProps & - SvgDefsAndFill> & { + SvgDefsAndFill>> & { animate: boolean motionConfig: ModernMotionProps['motionConfig'] } = { diff --git a/packages/bump/src/area-bump/hooks.ts b/packages/bump/src/area-bump/hooks.ts index ce93cd9ad3..644ccd639e 100644 --- a/packages/bump/src/area-bump/hooks.ts +++ b/packages/bump/src/area-bump/hooks.ts @@ -1,4 +1,4 @@ -import { createElement, useMemo, useCallback } from 'react' +import { createElement, useMemo, useCallback, useState } from 'react' import { area as d3Area, curveBasis, curveLinear } from 'd3-shape' import { useTheme, usePropertyAccessor } from '@nivo/core' import { useOrdinalColorScale, useInheritedColor, InheritedColorConfig } from '@nivo/colors' @@ -13,9 +13,14 @@ import { AreaBumpInterpolation, AreaBumpLabel, AreaBumpLabelData, + AreaBumpSerieExtraProps, + DefaultAreaBumpDatum, } from './types' -const useAreaBumpSeries = ({ +const useAreaBumpSeries = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ data, width, height, @@ -23,15 +28,15 @@ const useAreaBumpSeries = ({ spacing, xPadding, }: { - data: AreaBumpDataProps['data'] + data: AreaBumpDataProps['data'] width: number height: number - align: AreaBumpCommonProps['align'] - spacing: AreaBumpCommonProps['spacing'] - xPadding: AreaBumpCommonProps['xPadding'] + align: AreaBumpCommonProps['align'] + spacing: AreaBumpCommonProps['spacing'] + xPadding: AreaBumpCommonProps['xPadding'] }) => useMemo( - () => computeSeries({ data, width, height, align, spacing, xPadding }), + () => computeSeries({ data, width, height, align, spacing, xPadding }), [data, width, height, align, spacing, xPadding] ) @@ -54,7 +59,7 @@ const useSerieDerivedProp = ( return () => instruction }, [instruction]) -const useSerieStyle = ({ +const useSerieStyle = ({ fillOpacity, activeFillOpacity, inactiveFillOpacity, @@ -66,22 +71,25 @@ const useSerieStyle = ({ activeBorderOpacity, inactiveBorderOpacity, isInteractive, - current, + activeSerieIds, }: { - fillOpacity: AreaBumpCommonProps['fillOpacity'] - activeFillOpacity: AreaBumpCommonProps['activeFillOpacity'] - inactiveFillOpacity: AreaBumpCommonProps['inactiveFillOpacity'] - borderWidth: AreaBumpCommonProps['borderWidth'] - activeBorderWidth: AreaBumpCommonProps['activeBorderWidth'] - inactiveBorderWidth: AreaBumpCommonProps['inactiveBorderWidth'] - borderColor: AreaBumpCommonProps['borderColor'] - borderOpacity: AreaBumpCommonProps['borderOpacity'] - activeBorderOpacity: AreaBumpCommonProps['activeBorderOpacity'] - inactiveBorderOpacity: AreaBumpCommonProps['inactiveBorderOpacity'] - isInteractive: AreaBumpCommonProps['isInteractive'] - current: any + fillOpacity: AreaBumpCommonProps['fillOpacity'] + activeFillOpacity: AreaBumpCommonProps['activeFillOpacity'] + inactiveFillOpacity: AreaBumpCommonProps['inactiveFillOpacity'] + borderWidth: AreaBumpCommonProps['borderWidth'] + activeBorderWidth: AreaBumpCommonProps['activeBorderWidth'] + inactiveBorderWidth: AreaBumpCommonProps['inactiveBorderWidth'] + borderColor: AreaBumpCommonProps['borderColor'] + borderOpacity: AreaBumpCommonProps['borderOpacity'] + activeBorderOpacity: AreaBumpCommonProps['activeBorderOpacity'] + inactiveBorderOpacity: AreaBumpCommonProps['inactiveBorderOpacity'] + isInteractive: AreaBumpCommonProps['isInteractive'] + activeSerieIds: string[] }) => { - type Serie = Omit, 'style'> + type Serie = Omit< + AreaBumpComputedSerie, + 'fillOpacity' | 'borderWidth' | 'borderColor' | 'borderOpacity' + > const getFillOpacity = useSerieDerivedProp(fillOpacity) const getActiveFillOpacity = useSerieDerivedProp(activeFillOpacity) @@ -128,15 +136,18 @@ const useSerieStyle = ({ return useCallback( (serie: Serie) => { - if (!isInteractive || current === null) return getNormalStyle(serie) - if (serie.id === current) return getActiveStyle(serie) + if (!isInteractive || activeSerieIds.length === 0) return getNormalStyle(serie) + if (activeSerieIds.includes(serie.id)) return getActiveStyle(serie) return getInactiveStyle(serie) }, - [getNormalStyle, getActiveStyle, getInactiveStyle, isInteractive, current] + [getNormalStyle, getActiveStyle, getInactiveStyle, isInteractive, activeSerieIds] ) } -export const useAreaBump = ({ +export const useAreaBump = < + Datum extends AreaBumpDatum = DefaultAreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps = Record +>({ data, width, height, @@ -156,34 +167,36 @@ export const useAreaBump = ({ activeBorderOpacity, inactiveBorderOpacity, isInteractive, - current, + defaultActiveSerieIds, }: { - data: AreaBumpDataProps['data'] + data: AreaBumpDataProps['data'] width: number height: number - align: AreaBumpCommonProps['align'] - spacing: AreaBumpCommonProps['spacing'] - xPadding: AreaBumpCommonProps['xPadding'] - interpolation: AreaBumpCommonProps['interpolation'] - colors: AreaBumpCommonProps['colors'] - fillOpacity: AreaBumpCommonProps['fillOpacity'] - activeFillOpacity: AreaBumpCommonProps['activeFillOpacity'] - inactiveFillOpacity: AreaBumpCommonProps['inactiveFillOpacity'] - borderWidth: AreaBumpCommonProps['borderWidth'] - activeBorderWidth: AreaBumpCommonProps['activeBorderWidth'] - inactiveBorderWidth: AreaBumpCommonProps['inactiveBorderWidth'] - borderColor: AreaBumpCommonProps['borderColor'] - borderOpacity: AreaBumpCommonProps['borderOpacity'] - activeBorderOpacity: AreaBumpCommonProps['activeBorderOpacity'] - inactiveBorderOpacity: AreaBumpCommonProps['inactiveBorderOpacity'] - isInteractive: AreaBumpCommonProps['isInteractive'] - current: any + align: AreaBumpCommonProps['align'] + spacing: AreaBumpCommonProps['spacing'] + xPadding: AreaBumpCommonProps['xPadding'] + interpolation: AreaBumpCommonProps['interpolation'] + colors: AreaBumpCommonProps['colors'] + fillOpacity: AreaBumpCommonProps['fillOpacity'] + activeFillOpacity: AreaBumpCommonProps['activeFillOpacity'] + inactiveFillOpacity: AreaBumpCommonProps['inactiveFillOpacity'] + borderWidth: AreaBumpCommonProps['borderWidth'] + activeBorderWidth: AreaBumpCommonProps['activeBorderWidth'] + inactiveBorderWidth: AreaBumpCommonProps['inactiveBorderWidth'] + borderColor: AreaBumpCommonProps['borderColor'] + borderOpacity: AreaBumpCommonProps['borderOpacity'] + activeBorderOpacity: AreaBumpCommonProps['activeBorderOpacity'] + inactiveBorderOpacity: AreaBumpCommonProps['inactiveBorderOpacity'] + isInteractive: AreaBumpCommonProps['isInteractive'] + defaultActiveSerieIds: string[] }) => { + const [activeSerieIds, setActiveSerieIds] = useState(defaultActiveSerieIds) + const { series: rawSeries, xScale, heightScale, - } = useAreaBumpSeries({ + } = useAreaBumpSeries({ data, width, height, @@ -195,7 +208,7 @@ export const useAreaBump = ({ const areaGenerator = useAreaGenerator(interpolation) const getColor = useOrdinalColorScale(colors, 'id') - const getSerieStyle = useSerieStyle({ + const getSerieStyle = useSerieStyle({ fillOpacity, activeFillOpacity, inactiveFillOpacity, @@ -207,20 +220,20 @@ export const useAreaBump = ({ activeBorderOpacity, inactiveBorderOpacity, isInteractive, - current, + activeSerieIds, }) - const series: AreaBumpComputedSerie[] = useMemo( + const series: AreaBumpComputedSerie[] = useMemo( () => rawSeries.map(serie => { const serieWithColor = { ...serie, - color: getColor(serie), + color: getColor(serie.data), } return { ...serieWithColor, - style: getSerieStyle(serieWithColor), + ...getSerieStyle(serieWithColor), } }), [rawSeries, getColor, getSerieStyle] @@ -231,37 +244,42 @@ export const useAreaBump = ({ xScale, heightScale, areaGenerator, + activeSerieIds, + setActiveSerieIds, } } -export const useAreaBumpSerieHandlers = ({ +export const useAreaBumpSerieHandlers = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ serie, isInteractive, onMouseEnter, onMouseMove, onMouseLeave, onClick, - setCurrent, + setActiveSerieIds, tooltip, }: { - serie: AreaBumpComputedSerie - isInteractive: AreaBumpCommonProps['isInteractive'] - onMouseEnter?: AreaBumpCommonProps['onMouseEnter'] - onMouseMove?: AreaBumpCommonProps['onMouseMove'] - onMouseLeave?: AreaBumpCommonProps['onMouseLeave'] - onClick?: AreaBumpCommonProps['onClick'] - setCurrent: any - tooltip: AreaBumpCommonProps['tooltip'] + serie: AreaBumpComputedSerie + isInteractive: AreaBumpCommonProps['isInteractive'] + onMouseEnter?: AreaBumpCommonProps['onMouseEnter'] + onMouseMove?: AreaBumpCommonProps['onMouseMove'] + onMouseLeave?: AreaBumpCommonProps['onMouseLeave'] + onClick?: AreaBumpCommonProps['onClick'] + setActiveSerieIds: (serieIds: string[]) => void + tooltip: AreaBumpCommonProps['tooltip'] }) => { const { showTooltipFromEvent, hideTooltip } = useTooltip() const handleMouseEnter = useCallback( event => { showTooltipFromEvent(createElement(tooltip, { serie }), event) - setCurrent(serie.id) + setActiveSerieIds([serie.id]) onMouseEnter && onMouseEnter(serie, event) }, - [serie, onMouseEnter, showTooltipFromEvent, setCurrent] + [serie, onMouseEnter, showTooltipFromEvent, setActiveSerieIds, tooltip] ) const handleMouseMove = useCallback( @@ -269,16 +287,16 @@ export const useAreaBumpSerieHandlers = ({ showTooltipFromEvent(createElement(tooltip, { serie }), event) onMouseMove && onMouseMove(serie, event) }, - [serie, onMouseMove, showTooltipFromEvent] + [serie, onMouseMove, showTooltipFromEvent, tooltip] ) const handleMouseLeave = useCallback( event => { hideTooltip() - setCurrent(null) + setActiveSerieIds([]) onMouseLeave && onMouseLeave(serie, event) }, - [serie, onMouseLeave, hideTooltip, setCurrent] + [serie, onMouseLeave, hideTooltip, setActiveSerieIds] ) const handleClick = useCallback( @@ -299,19 +317,22 @@ export const useAreaBumpSerieHandlers = ({ ) } -export const useAreaBumpSeriesLabels = ({ +export const useAreaBumpSeriesLabels = < + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +>({ label, series, position, padding, color, }: { - label: Exclude, false> - series: AreaBumpComputedSerie[] + label: Exclude, false> + series: AreaBumpComputedSerie[] position: 'start' | 'end' padding: number - color: InheritedColorConfig> -}): AreaBumpLabelData[] => { + color: InheritedColorConfig> +}): AreaBumpLabelData[] => { const theme = useTheme() const getColor = useInheritedColor(color, theme) @@ -338,7 +359,7 @@ export const useAreaBumpSeriesLabels = ({ x: point.x + signedPadding, y: point.y, color: getColor(serie), - opacity: serie.style.fillOpacity, + opacity: serie.fillOpacity, serie, textAnchor, } diff --git a/packages/bump/src/area-bump/types.ts b/packages/bump/src/area-bump/types.ts index 7a6d8b5683..d2d052f1c3 100644 --- a/packages/bump/src/area-bump/types.ts +++ b/packages/bump/src/area-bump/types.ts @@ -1,4 +1,5 @@ import { FunctionComponent, MouseEvent } from 'react' +import { Area } from 'd3-shape' import { PropertyAccessor, Box, @@ -10,7 +11,6 @@ import { } from '@nivo/core' import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors' import { AxisProps } from '@nivo/axes' -import { Area } from 'd3-shape' import { ScalePoint } from '@nivo/scales' export interface AreaBumpDatum { @@ -23,16 +23,21 @@ export interface DefaultAreaBumpDatum { y: number } -export interface AreaBumpSerie { +export type AreaBumpSerieExtraProps = Record + +export type AreaBumpSerie< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = ExtraProps & { id: string - data: D[] + data: Datum[] } -export interface AreaBumpPoint { +export interface AreaBumpPoint { x: number y: number height: number - data: D + data: Datum } export interface AreaBumpAreaPoint { @@ -43,33 +48,43 @@ export interface AreaBumpAreaPoint { export type AreaBumpAreaGenerator = Area -export interface AreaBumpComputedSerie extends AreaBumpSerie { - points: AreaBumpPoint[] +export interface AreaBumpComputedSerie< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> { + id: string + data: AreaBumpSerie + points: AreaBumpPoint[] areaPoints: AreaBumpAreaPoint[] color: string fill?: string - style: { - fillOpacity: number - borderWidth: number - borderColor: string - borderOpacity: number - } + fillOpacity: number + borderWidth: number + borderColor: string + borderOpacity: number } export type AreaBumpAlign = 'start' | 'middle' | 'end' -export type AreaBumpDataProps = { - data: AreaBumpSerie[] +export type AreaBumpDataProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = { + data: AreaBumpSerie[] } export type AreaBumpInterpolation = 'smooth' | 'linear' -export type AreaBumpLabel = - | PropertyAccessor, string> - | false -export interface AreaBumpLabelData { - serie: AreaBumpComputedSerie - id: AreaBumpSerie['id'] +export type AreaBumpLabel< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = PropertyAccessor, string> | false +export interface AreaBumpLabelData< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> { + id: string + serie: AreaBumpComputedSerie label: string x: number y: number @@ -78,26 +93,44 @@ export interface AreaBumpLabelData { textAnchor: 'start' | 'end' } -export type AreaBumpMouseHandler = ( - serie: AreaBumpComputedSerie, - event: MouseEvent -) => void +export type AreaBumpMouseHandler< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = (serie: AreaBumpComputedSerie, event: MouseEvent) => void export type AreaBumpLayerId = 'grid' | 'axes' | 'labels' | 'areas' -export interface AreaBumpCustomLayerProps { +export interface AreaBumpCustomLayerProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> { innerWidth: number innerHeight: number outerWidth: number outerHeight: number - series: AreaBumpComputedSerie[] - xScale: ScalePoint + series: AreaBumpComputedSerie[] + xScale: ScalePoint areaGenerator: AreaBumpAreaGenerator } -export type AreaBumpCustomLayer = FunctionComponent< - AreaBumpCustomLayerProps -> - -export type AreaBumpCommonProps = { +export type AreaBumpCustomLayer< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = FunctionComponent> +export type AreaBumpLayer< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = AreaBumpLayerId | AreaBumpCustomLayer + +export type AreaBumpAreaTooltip< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = FunctionComponent<{ + serie: AreaBumpComputedSerie +}> + +export type AreaBumpCommonProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = { margin: Box align: AreaBumpAlign @@ -106,7 +139,7 @@ export type AreaBumpCommonProps = { xPadding: number theme: Theme - colors: OrdinalColorScaleConfig, 'color' | 'style'>> + colors: OrdinalColorScaleConfig> blendMode: CssMixBlendMode fillOpacity: number activeFillOpacity: number @@ -114,17 +147,22 @@ export type AreaBumpCommonProps = { borderWidth: number activeBorderWidth: number inactiveBorderWidth: number - borderColor: InheritedColorConfig, 'style'>> + borderColor: InheritedColorConfig< + Omit< + AreaBumpComputedSerie, + 'fillOpacity' | 'borderWidth' | 'borderColor' | 'borderOpacity' + > + > borderOpacity: number activeBorderOpacity: number inactiveBorderOpacity: number - startLabel: AreaBumpLabel + startLabel: AreaBumpLabel startLabelPadding: number - startLabelTextColor: InheritedColorConfig> - endLabel: AreaBumpLabel + startLabelTextColor: InheritedColorConfig> + endLabel: AreaBumpLabel endLabelPadding: number - endLabelTextColor: InheritedColorConfig> + endLabelTextColor: InheritedColorConfig> enableGridX: boolean axisBottom: AxisProps | null @@ -132,20 +170,23 @@ export type AreaBumpCommonProps = { isInteractive: boolean defaultActiveSerieIds: string[] - onMouseEnter: AreaBumpMouseHandler - onMouseMove: AreaBumpMouseHandler - onMouseLeave: AreaBumpMouseHandler - onClick: AreaBumpMouseHandler - tooltip: FunctionComponent<{ serie: AreaBumpComputedSerie }> + onMouseEnter: AreaBumpMouseHandler + onMouseMove: AreaBumpMouseHandler + onMouseLeave: AreaBumpMouseHandler + onClick: AreaBumpMouseHandler + tooltip: AreaBumpAreaTooltip role: string - layers: (AreaBumpLayerId | AreaBumpCustomLayer)[] + layers: AreaBumpLayer[] renderWrapper: boolean } -export type AreaBumpSvgProps = Partial> & - AreaBumpDataProps & - SvgDefsAndFill> & +export type AreaBumpSvgProps< + Datum extends AreaBumpDatum, + ExtraProps extends AreaBumpSerieExtraProps +> = Partial> & + AreaBumpDataProps & + SvgDefsAndFill> & Dimensions & ModernMotionProps diff --git a/packages/bump/src/bump/Bump.tsx b/packages/bump/src/bump/Bump.tsx index 5128010e13..d880364b32 100644 --- a/packages/bump/src/bump/Bump.tsx +++ b/packages/bump/src/bump/Bump.tsx @@ -4,9 +4,7 @@ import { Grid, Axes } from '@nivo/axes' import { BumpCustomLayerProps, BumpDatum, - BumpLayer, BumpLayerId, - BumpLineTooltip, BumpSerieExtraProps, BumpSvgProps, DefaultBumpDatum, @@ -29,14 +27,14 @@ const InnerBump = [], + layers = bumpSvgDefaultProps.layers as NonNullable['layers']>, interpolation = bumpSvgDefaultProps.interpolation, xPadding = bumpSvgDefaultProps.xPadding, xOuterPadding = bumpSvgDefaultProps.xOuterPadding, yOuterPadding = bumpSvgDefaultProps.yOuterPadding, - colors = bumpSvgDefaultProps.colors, + colors = bumpSvgDefaultProps.colors as NonNullable['colors']>, lineWidth = bumpSvgDefaultProps.lineWidth, activeLineWidth = bumpSvgDefaultProps.activeLineWidth, inactiveLineWidth = bumpSvgDefaultProps.inactiveLineWidth, @@ -44,25 +42,36 @@ const InnerBump = ['startLabel'] + >, startLabelPadding = bumpSvgDefaultProps.startLabelPadding, - startLabelTextColor = bumpSvgDefaultProps.startLabelTextColor, - endLabel = bumpSvgDefaultProps.endLabel, + startLabelTextColor = bumpSvgDefaultProps.startLabelTextColor as NonNullable< + BumpSvgProps['startLabelTextColor'] + >, + endLabel = bumpSvgDefaultProps.endLabel as NonNullable< + BumpSvgProps['endLabel'] + >, endLabelPadding = bumpSvgDefaultProps.endLabelPadding, - endLabelTextColor = bumpSvgDefaultProps.endLabelTextColor, + endLabelTextColor = bumpSvgDefaultProps.endLabelTextColor as NonNullable< + BumpSvgProps['endLabelTextColor'] + >, - pointComponent = bumpSvgDefaultProps.pointComponent as Exclude< - BumpSvgProps['pointComponent'], - undefined + pointComponent = bumpSvgDefaultProps.pointComponent as NonNullable< + BumpSvgProps['pointComponent'] >, pointSize = bumpSvgDefaultProps.pointSize, activePointSize = bumpSvgDefaultProps.activePointSize, inactivePointSize = bumpSvgDefaultProps.inactivePointSize, - pointColor = bumpSvgDefaultProps.pointColor, + pointColor = bumpSvgDefaultProps.pointColor as NonNullable< + BumpSvgProps['pointColor'] + >, pointBorderWidth = bumpSvgDefaultProps.pointBorderWidth, activePointBorderWidth = bumpSvgDefaultProps.activePointBorderWidth, inactivePointBorderWidth = bumpSvgDefaultProps.inactivePointBorderWidth, - pointBorderColor = bumpSvgDefaultProps.pointBorderColor, + pointBorderColor = bumpSvgDefaultProps.pointBorderColor as NonNullable< + BumpSvgProps['pointBorderColor'] + >, enableGridX = bumpSvgDefaultProps.enableGridX, enableGridY = bumpSvgDefaultProps.enableGridY, @@ -77,7 +86,9 @@ const InnerBump = , + tooltip = bumpSvgDefaultProps.tooltip as NonNullable< + BumpSvgProps['tooltip'] + >, role = bumpSvgDefaultProps.role, }: InnerBumpProps) => { const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( diff --git a/packages/bump/src/bump/defaults.ts b/packages/bump/src/bump/defaults.ts index fe72ae3c84..6bd6e071c9 100644 --- a/packages/bump/src/bump/defaults.ts +++ b/packages/bump/src/bump/defaults.ts @@ -1,10 +1,10 @@ import { ModernMotionProps } from '@nivo/core' import { LineTooltip } from './LineTooltip' import { Point } from './Point' -import { BumpCommonProps, BumpPointComponent, BumpDatum } from './types' +import { BumpCommonProps, BumpPointComponent, DefaultBumpDatum } from './types' const commonDefaultProps: Omit< - BumpCommonProps>, + BumpCommonProps>, | 'onMouseEnter' | 'onMouseMove' | 'onMouseLeave' @@ -59,7 +59,7 @@ const commonDefaultProps: Omit< } export const bumpSvgDefaultProps: typeof commonDefaultProps & { - pointComponent: BumpPointComponent> + pointComponent: BumpPointComponent> animate: boolean motionConfig: ModernMotionProps['motionConfig'] } = { diff --git a/packages/bump/src/bump/types.ts b/packages/bump/src/bump/types.ts index be62259cf9..29f1ccd1a6 100644 --- a/packages/bump/src/bump/types.ts +++ b/packages/bump/src/bump/types.ts @@ -15,9 +15,7 @@ export interface DefaultBumpDatum { y: number } -export type BumpSerieExtraProps = { - [key: string]: unknown -} +export type BumpSerieExtraProps = Record export type BumpSerie< Datum extends BumpDatum, diff --git a/packages/bump/tests/Bump.test.tsx b/packages/bump/tests/Bump.test.tsx index e22e30ccc7..c46fa04844 100644 --- a/packages/bump/tests/Bump.test.tsx +++ b/packages/bump/tests/Bump.test.tsx @@ -7,7 +7,7 @@ interface Datum { y: number } -const sampleData: BumpSvgProps['data'] = [ +const sampleData: BumpSvgProps>['data'] = [ { id: 'A', data: [ @@ -73,7 +73,7 @@ const sampleData: BumpSvgProps['data'] = [ }, ] -const baseProps: BumpSvgProps = { +const baseProps: BumpSvgProps> = { width: 800, height: 600, data: sampleData,