Skip to content

Commit

Permalink
feat(radar): add support for custom layers
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Sep 7, 2021
1 parent e259c04 commit 3271a6b
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 35 deletions.
17 changes: 10 additions & 7 deletions packages/radar/src/Radar.tsx
@@ -1,4 +1,4 @@
import { ReactNode, Fragment } from 'react'
import { ReactNode, Fragment, createElement } from 'react'
import { Container, useDimensions, SvgWrapper } from '@nivo/core'
import { BoxLegendSvg } from '@nivo/legends'
import { RadarLayer } from './RadarLayer'
Expand Down Expand Up @@ -69,6 +69,7 @@ const InnerRadar = <D extends Record<string, unknown>>({
angleStep,
curveFactory,
legendData,
customLayerProps,
} = useRadar<D>({
data,
keys,
Expand All @@ -92,7 +93,7 @@ const InnerRadar = <D extends Record<string, unknown>>({
if (layers.includes('grid')) {
layerById.grid = (
<g key="grid" transform={`translate(${centerX}, ${centerY})`}>
<RadarGrid
<RadarGrid<D>
levels={gridLevels}
shape={gridShape}
radius={radius}
Expand Down Expand Up @@ -194,11 +195,13 @@ const InnerRadar = <D extends Record<string, unknown>>({
ariaLabelledBy={ariaLabelledBy}
ariaDescribedBy={ariaDescribedBy}
>
{layerById.grid}
{layerById.layers}
{layerById.slices}
{layerById.dots}
{layerById.legends}
{layers.map((layer, i) => {
if (typeof layer === 'function') {
return <Fragment key={i}>{createElement(layer, customLayerProps)}</Fragment>
}

return layerById?.[layer] ?? null
})}
</SvgWrapper>
)
}
Expand Down
8 changes: 4 additions & 4 deletions packages/radar/src/RadarDots.tsx
Expand Up @@ -11,13 +11,13 @@ interface RadarDotsProps<D extends Record<string, unknown>> {
getIndex: (d: D) => string
colorByKey: RadarColorMapping
angleStep: number
symbol?: RadarCommonProps['dotSymbol']
symbol?: RadarCommonProps<D>['dotSymbol']
size: number
color: RadarCommonProps['dotColor']
color: RadarCommonProps<D>['dotColor']
borderWidth: number
borderColor: RadarCommonProps['dotBorderColor']
borderColor: RadarCommonProps<D>['dotBorderColor']
enableLabel: boolean
label: RadarCommonProps['dotLabel']
label: RadarCommonProps<D>['dotLabel']
formatValue: (value: number, context: string) => string
labelYOffset: number
}
Expand Down
14 changes: 7 additions & 7 deletions packages/radar/src/RadarGrid.tsx
@@ -1,28 +1,28 @@
import { useMemo } from 'react'
import { SVGProps, useMemo } from 'react'
import { positionFromAngle, useTheme } from '@nivo/core'
import { RadarGridLabels } from './RadarGridLabels'
import { RadarGridLevels } from './RadarGridLevels'
import { GridLabelComponent, RadarCommonProps } from './types'

interface RadarGridProps {
interface RadarGridProps<D extends Record<string, unknown>> {
indices: string[]
shape: RadarCommonProps['gridShape']
shape: RadarCommonProps<D>['gridShape']
radius: number
levels: number
angleStep: number
label: GridLabelComponent
labelOffset: number
}

export const RadarGrid = ({
export const RadarGrid = <D extends Record<string, unknown>>({
indices,
levels,
shape,
radius,
angleStep,
label,
labelOffset,
}: RadarGridProps) => {
}: RadarGridProps<D>) => {
const theme = useTheme()
const { radii, angles } = useMemo(() => {
return {
Expand All @@ -44,12 +44,12 @@ export const RadarGrid = ({
y1={0}
x2={position.x}
y2={position.y}
{...(theme.grid.line as any)}
{...(theme.grid.line as SVGProps<SVGLineElement>)}
/>
)
})}
{radii.map((radius, i) => (
<RadarGridLevels
<RadarGridLevels<D>
key={`level.${i}`}
shape={shape}
radius={radius}
Expand Down
9 changes: 6 additions & 3 deletions packages/radar/src/RadarGridLevels.tsx
Expand Up @@ -57,14 +57,17 @@ const RadarGridLevelLinear = ({ radius, angleStep, dataLength }: RadarGridLevelL
)
}

interface RadarGridLevelsProps {
shape: RadarCommonProps['gridShape']
interface RadarGridLevelsProps<D extends Record<string, unknown>> {
shape: RadarCommonProps<D>['gridShape']
radius: number
angleStep: number
dataLength: number
}

export const RadarGridLevels = ({ shape, ...props }: RadarGridLevelsProps) => {
export const RadarGridLevels = <D extends Record<string, unknown>>({
shape,
...props
}: RadarGridLevelsProps<D>) => {
return shape === 'circular' ? (
<RadarGridLevelCircular radius={props.radius} />
) : (
Expand Down
8 changes: 4 additions & 4 deletions packages/radar/src/RadarLayer.tsx
Expand Up @@ -13,10 +13,10 @@ interface RadarLayerProps<D extends Record<string, unknown>> {
radiusScale: ScaleLinear<number, number>
angleStep: number
curveFactory: CurveFactory
borderWidth: RadarCommonProps['borderWidth']
borderColor: RadarCommonProps['borderColor']
fillOpacity: RadarCommonProps['fillOpacity']
blendMode: RadarCommonProps['blendMode']
borderWidth: RadarCommonProps<D>['borderWidth']
borderColor: RadarCommonProps<D>['borderColor']
fillOpacity: RadarCommonProps<D>['fillOpacity']
blendMode: RadarCommonProps<D>['blendMode']
}

export const RadarLayer = <D extends Record<string, unknown>>({
Expand Down
2 changes: 1 addition & 1 deletion packages/radar/src/RadarSlice.tsx
Expand Up @@ -14,7 +14,7 @@ interface RadarSliceProps<D extends Record<string, unknown>> {
endAngle: number
radius: number
arcGenerator: Arc<void, { startAngle: number; endAngle: number }>
tooltip: RadarCommonProps['sliceTooltip']
tooltip: RadarCommonProps<D>['sliceTooltip']
}

export const RadarSlice = <D extends Record<string, unknown>>({
Expand Down
2 changes: 1 addition & 1 deletion packages/radar/src/RadarSlices.tsx
Expand Up @@ -10,7 +10,7 @@ interface RadarSlicesProps<D extends Record<string, unknown>> {
colorByKey: RadarColorMapping
radius: number
angleStep: number
tooltip: RadarCommonProps['sliceTooltip']
tooltip: RadarCommonProps<D>['sliceTooltip']
}

export const RadarSlices = <D extends Record<string, unknown>>({
Expand Down
25 changes: 20 additions & 5 deletions packages/radar/src/hooks.ts
Expand Up @@ -3,7 +3,7 @@ import { scaleLinear } from 'd3-scale'
import { useCurveInterpolation, usePropertyAccessor, useValueFormatter } from '@nivo/core'
import { useOrdinalColorScale } from '@nivo/colors'
import { svgDefaultProps } from './props'
import { RadarColorMapping, RadarCommonProps, RadarDataProps } from './types'
import { RadarColorMapping, RadarCommonProps, RadarDataProps, RadarCustomLayerProps } from './types'

export const useRadar = <D extends Record<string, unknown>>({
data,
Expand All @@ -19,12 +19,12 @@ export const useRadar = <D extends Record<string, unknown>>({
data: RadarDataProps<D>['data']
keys: RadarDataProps<D>['keys']
indexBy: RadarDataProps<D>['indexBy']
maxValue: RadarCommonProps['maxValue']
valueFormat?: RadarCommonProps['valueFormat']
curve: RadarCommonProps['curve']
maxValue: RadarCommonProps<D>['maxValue']
valueFormat?: RadarCommonProps<D>['valueFormat']
curve: RadarCommonProps<D>['curve']
width: number
height: number
colors: RadarCommonProps['colors']
colors: RadarCommonProps<D>['colors']
}) => {
const getIndex = usePropertyAccessor<D, string>(indexBy)
const indices = useMemo(() => data.map(getIndex), [data, getIndex])
Expand Down Expand Up @@ -63,6 +63,20 @@ export const useRadar = <D extends Record<string, unknown>>({

const curveFactory = useCurveInterpolation(curve)

const customLayerProps: RadarCustomLayerProps<D> = useMemo(
() => ({
data,
keys,
indices,
colorByKey,
centerX,
centerY,
radiusScale,
angleStep,
}),
[data, keys, indices, colorByKey, centerX, centerY, radiusScale, angleStep]
)

const legendData = keys.map(key => ({
id: key,
label: key,
Expand All @@ -81,5 +95,6 @@ export const useRadar = <D extends Record<string, unknown>>({
angleStep,
curveFactory,
legendData,
customLayerProps,
}
}
21 changes: 18 additions & 3 deletions packages/radar/src/types.ts
Expand Up @@ -13,6 +13,7 @@ import {
} from '@nivo/core'
import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors'
import { LegendProps } from '@nivo/legends'
import { ScaleLinear } from 'd3-scale'

export interface RadarDataProps<D extends Record<string, unknown>> {
data: D[]
Expand Down Expand Up @@ -65,16 +66,30 @@ export interface RadarSliceTooltipProps {
}
export type RadarSliceTooltipComponent = FunctionComponent<RadarSliceTooltipProps>

export interface RadarCustomLayerProps<D extends Record<string, unknown>> {
data: D[]
keys: string[]
indices: string[] | number[]
colorByKey: RadarColorMapping
centerX: number
centerY: number
radiusScale: ScaleLinear<number, number>
angleStep: number
}
export type RadarCustomLayer<D extends Record<string, unknown>> = FunctionComponent<
RadarCustomLayerProps<D>
>

export type RadarLayerId = 'grid' | 'layers' | 'slices' | 'dots' | 'legends'

export type RadarColorMapping = Record<string, string>

export interface RadarCommonProps {
export interface RadarCommonProps<D extends Record<string, unknown>> {
maxValue: number | 'auto'
// second argument passed to the formatter is the key
valueFormat: ValueFormat<number, string>

layers: RadarLayerId[]
layers: (RadarLayerId | RadarCustomLayer<D>)[]

margin: Box

Expand Down Expand Up @@ -116,7 +131,7 @@ export interface RadarCommonProps {
ariaDescribedBy: AriaAttributes['aria-describedby']
}

export type RadarSvgProps<D extends Record<string, unknown>> = Partial<RadarCommonProps> &
export type RadarSvgProps<D extends Record<string, unknown>> = Partial<RadarCommonProps<D>> &
RadarDataProps<D> &
Dimensions &
ModernMotionProps
16 changes: 16 additions & 0 deletions website/src/data/components/radar/props.ts
Expand Up @@ -405,6 +405,22 @@ const props: ChartProperty[] = [
max: 24,
},
},
{
key: 'layers',
group: 'Customization',
help: 'Defines the order of layers and add custom layers.',
description: `
You can also use this to insert extra layers
to the chart, the extra layer should be a component.
The layer function which will receive the chart's
context & computed data and must return a valid SVG element.
`,
required: false,
type: '(RadarLayerId | FunctionComponent<RadarCustomLayerProps>)[]',
flavors: ['svg'],
defaultValue: svgDefaultProps.layers,
},
{
key: 'isInteractive',
group: 'Interactivity',
Expand Down

0 comments on commit 3271a6b

Please sign in to comment.