Skip to content

Commit

Permalink
feat(radar): add support for patterns and gradients (#1807)
Browse files Browse the repository at this point in the history
* add defs and fill props

* add story

* add defs to website

* run make fmt

* fix lint issue

* update interface

* add ts-ignore for import like rest of packages

* export type

* rename story

* add story to website
  • Loading branch information
adamberro committed Nov 7, 2021
1 parent 2757063 commit 0b3bdb0
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 5 deletions.
8 changes: 8 additions & 0 deletions packages/radar/src/Radar.tsx
Expand Up @@ -50,6 +50,8 @@ const InnerRadar = <D extends Record<string, unknown>>({
ariaLabel,
ariaLabelledBy,
ariaDescribedBy,
defs = svgDefaultProps.defs,
fill = svgDefaultProps.fill,
}: InnerRadarProps<D>) => {
const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
width,
Expand All @@ -62,6 +64,8 @@ const InnerRadar = <D extends Record<string, unknown>>({
indices,
formatValue,
colorByKey,
fillByKey,
boundDefs,
radius,
radiusScale,
centerX,
Expand All @@ -81,6 +85,8 @@ const InnerRadar = <D extends Record<string, unknown>>({
height: innerHeight,
colors,
legends,
defs,
fill,
})

const layerById: Record<RadarLayerId, ReactNode> = {
Expand Down Expand Up @@ -116,6 +122,7 @@ const InnerRadar = <D extends Record<string, unknown>>({
data={data}
item={key}
colorByKey={colorByKey}
fillByKey={fillByKey}
radiusScale={radiusScale}
angleStep={angleStep}
curveFactory={curveFactory}
Expand Down Expand Up @@ -187,6 +194,7 @@ const InnerRadar = <D extends Record<string, unknown>>({

return (
<SvgWrapper
defs={boundDefs}
width={outerWidth}
height={outerHeight}
margin={margin}
Expand Down
5 changes: 4 additions & 1 deletion packages/radar/src/RadarLayer.tsx
Expand Up @@ -10,6 +10,7 @@ interface RadarLayerProps<D extends Record<string, unknown>> {
data: D[]
item: string
colorByKey: Record<string | number, string>
fillByKey: Record<string, string | null>
radiusScale: ScaleLinear<number, number>
angleStep: number
curveFactory: CurveFactory
Expand All @@ -23,6 +24,7 @@ export const RadarLayer = <D extends Record<string, unknown>>({
data,
item: key,
colorByKey,
fillByKey,
radiusScale,
angleStep,
curveFactory,
Expand All @@ -49,12 +51,13 @@ export const RadarLayer = <D extends Record<string, unknown>>({
config: springConfig,
immediate: !animate,
})
const fill = fillByKey[key] ?? animatedProps.fill

return (
<animated.path
key={key}
d={animatedPath}
fill={animatedProps.fill}
fill={fill}
fillOpacity={fillOpacity}
stroke={animatedProps.stroke}
strokeWidth={borderWidth}
Expand Down
28 changes: 27 additions & 1 deletion packages/radar/src/hooks.ts
@@ -1,13 +1,20 @@
import { useMemo } from 'react'
import { scaleLinear } from 'd3-scale'
import { useCurveInterpolation, usePropertyAccessor, useValueFormatter } from '@nivo/core'
import {
// @ts-ignore
bindDefs,
useCurveInterpolation,
usePropertyAccessor,
useValueFormatter,
} from '@nivo/core'
import { useOrdinalColorScale } from '@nivo/colors'
import { svgDefaultProps } from './props'
import {
RadarColorMapping,
RadarCommonProps,
RadarDataProps,
RadarCustomLayerProps,
RadarSvgProps,
BoundLegendProps,
} from './types'

Expand All @@ -22,6 +29,8 @@ export const useRadar = <D extends Record<string, unknown>>({
height,
colors = svgDefaultProps.colors,
legends,
defs,
fill,
}: {
data: RadarDataProps<D>['data']
keys: RadarDataProps<D>['keys']
Expand All @@ -33,6 +42,8 @@ export const useRadar = <D extends Record<string, unknown>>({
height: number
colors: RadarCommonProps<D>['colors']
legends: RadarCommonProps<D>['legends']
defs: RadarSvgProps<D>['defs']
fill: RadarSvgProps<D>['fill']
}) => {
const getIndex = usePropertyAccessor<D, string>(indexBy)
const indices = useMemo(() => data.map(getIndex), [data, getIndex])
Expand All @@ -48,6 +59,19 @@ export const useRadar = <D extends Record<string, unknown>>({
[keys, getColor]
)

const { boundDefs, fillByKey } = useMemo(() => {
// expand keys into structure expected by bindDefs
const keyData = keys.map(k => ({ key: k, color: colorByKey[k], data, fill: null }))
const boundDefs = bindDefs(defs, keyData, fill)
const fillByKey = keyData.reduce<Record<string, string | null>>((mapping, keyDatum) => {
const { key: keyName, fill } = keyDatum
mapping[keyName] = fill
return mapping
}, {})

return { boundDefs, fillByKey }
}, [keys, data, defs, fill, colorByKey])

const { radius, radiusScale, centerX, centerY, angleStep } = useMemo(() => {
const allValues: number[] = data.reduce(
(acc: number[], d) => [...acc, ...keys.map(key => d[key] as number)],
Expand Down Expand Up @@ -107,6 +131,8 @@ export const useRadar = <D extends Record<string, unknown>>({
indices,
formatValue,
colorByKey,
fillByKey,
boundDefs,
radius,
radiusScale,
centerX,
Expand Down
3 changes: 3 additions & 0 deletions packages/radar/src/props.ts
Expand Up @@ -38,4 +38,7 @@ export const svgDefaultProps = {

animate: true,
motionConfig: 'gentle' as const,

defs: [],
fill: [],
}
10 changes: 9 additions & 1 deletion packages/radar/src/types.ts
Expand Up @@ -10,6 +10,7 @@ import {
ValueFormat,
ClosedCurveFactoryId,
DotsItemSymbolComponent,
SvgDefsAndFill,
} from '@nivo/core'
import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors'
import { LegendProps } from '@nivo/legends'
Expand Down Expand Up @@ -131,9 +132,16 @@ export interface RadarCommonProps<D extends Record<string, unknown>> {
ariaDescribedBy: AriaAttributes['aria-describedby']
}

export interface RadarSvgFillMatcherDatum<D extends Record<string, unknown>> {
color: string
data: RadarDataProps<D>['data']
key: string
}

export type RadarSvgProps<D extends Record<string, unknown>> = Partial<RadarCommonProps<D>> &
RadarDataProps<D> &
Dimensions &
ModernMotionProps
ModernMotionProps &
SvgDefsAndFill<RadarSvgFillMatcherDatum<D>>

export type BoundLegendProps = Required<Pick<LegendProps, 'data'>> & Omit<LegendProps, 'data'>
34 changes: 33 additions & 1 deletion packages/radar/stories/radar.stories.tsx
@@ -1,7 +1,7 @@
import { Meta } from '@storybook/react'
import { withKnobs, select } from '@storybook/addon-knobs'
import { generateWinesTastes } from '@nivo/generators'
import { ClosedCurveFactoryId } from '@nivo/core'
import { ClosedCurveFactoryId, patternDotsDef, patternSquaresDef } from '@nivo/core'
// @ts-ignore
import { Radar, GridLabelProps } from '../src'

Expand Down Expand Up @@ -163,3 +163,35 @@ export const CustomLegendLabel = () => (
]}
/>
)

export const WithPatterns = () => (
<Radar
{...commonProperties}
defs={[
patternDotsDef('dots', {
background: '#fc0341',
color: 'inherit',
size: 4,
padding: 2,
stagger: true,
}),
patternSquaresDef('squares', {
background: '#4287f5',
color: 'inherit',
size: 6,
padding: 4,
stagger: false,
}),
]}
fill={[
{
match: node => node.key === commonProperties.keys[0],
id: 'dots',
},
{
match: node => node.key === commonProperties.keys[1],
id: 'squares',
},
]}
/>
)
2 changes: 2 additions & 0 deletions website/src/data/components/radar/meta.yml
Expand Up @@ -15,6 +15,8 @@ Radar:
link: radar--with-formatted-values
- label: Custom label component
link: radar--custom-label-component
- label: Adding patterns to radar layers
link: radar--with-patterns
description: |
Generates a radar chart from an array of data.
Note that margin object does not take grid labels into account,
Expand Down
8 changes: 7 additions & 1 deletion website/src/data/components/radar/props.ts
@@ -1,6 +1,11 @@
import { closedCurvePropKeys } from '@nivo/core'
import { svgDefaultProps } from '@nivo/radar'
import { themeProperty, motionProperties, groupProperties } from '../../../lib/componentProperties'
import {
themeProperty,
motionProperties,
groupProperties,
defsProperties,
} from '../../../lib/componentProperties'
import { ChartProperty } from '../../../types'

const props: ChartProperty[] = [
Expand Down Expand Up @@ -210,6 +215,7 @@ const props: ChartProperty[] = [
defaultValue: svgDefaultProps.borderColor,
controlType: 'inheritedColor',
},
...defsProperties('Style', ['svg']),
{
key: 'gridLevels',
group: 'Grid',
Expand Down

0 comments on commit 0b3bdb0

Please sign in to comment.