Skip to content

Commit

Permalink
feat(sunburst): add parent to node data
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze authored and plouc committed Dec 2, 2020
1 parent ee59382 commit c586676
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 42 deletions.
6 changes: 3 additions & 3 deletions packages/sunburst/src/ResponsiveSunburst.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { ResponsiveWrapper } from '@nivo/core'
import { Sunburst } from './Sunburst'
import { SvgProps } from './types'

export const ResponsiveSunburst = <Datum extends Record<string, unknown>>(
props: Omit<SvgProps<Datum>, 'width' | 'height'>
export const ResponsiveSunburst = <RawDatum,>(
props: Omit<SvgProps<RawDatum>, 'width' | 'height'>
) => (
<ResponsiveWrapper>
{({ width, height }: Required<Pick<SvgProps<Datum>, 'width' | 'height'>>) => (
{({ width, height }: Required<Pick<SvgProps<RawDatum>, 'width' | 'height'>>) => (
<Sunburst width={width} height={height} {...props} />
)}
</ResponsiveWrapper>
Expand Down
6 changes: 3 additions & 3 deletions packages/sunburst/src/SunburstLabels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export const SunburstLabels = <RawDatum,>({
label,
nodes,
skipAngle = 0,
textColor: _textColor,
textColor,
}: SunburstLabelProps<RawDatum>) => {
const theme = useTheme()
const textColor = useInheritedColor(_textColor, theme)
const getTextColor = useInheritedColor(textColor, theme)

const getLabel = useMemo(() => getLabelGenerator(label), [label])

Expand Down Expand Up @@ -60,7 +60,7 @@ export const SunburstLabels = <RawDatum,>({
textAnchor="middle"
style={{
...theme.labels.text,
fill: textColor(node.data),
fill: getTextColor(node.data),
}}
>
{getLabel(node.data)}
Expand Down
32 changes: 18 additions & 14 deletions packages/sunburst/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import sortBy from 'lodash/sortBy'
import cloneDeep from 'lodash/cloneDeep'
import { getAccessorFor, useTheme } from '@nivo/core'
import { arc } from 'd3-shape'
import { partition as d3Partition, hierarchy as d3Hierarchy } from 'd3-hierarchy'
import { useOrdinalColorScale, useInheritedColor } from '@nivo/colors'
import { useMemo } from 'react'
import { partition as d3Partition, hierarchy as d3Hierarchy } from 'd3-hierarchy'
import { CommonProps, ComputedDatum, DataProps, NormalizedDatum } from './types'

type MaybeColor = { color?: string }
Expand All @@ -21,17 +21,19 @@ export const useSunburst = <RawDatum extends MaybeColor>({
value,
radius,
}: {
childColor: CommonProps['childColor']
colors: CommonProps['colors']
cornerRadius: CommonProps['cornerRadius']
childColor: CommonProps<RawDatum>['childColor']
colors: CommonProps<RawDatum>['colors']
cornerRadius: CommonProps<RawDatum>['cornerRadius']
data: DataProps<RawDatum>['data']
id: DataProps<RawDatum>['id']
radius: number
value: DataProps<RawDatum>['value']
}) => {
const theme = useTheme()
const getColor = useOrdinalColorScale(colors, 'id')
const getChildColor = useInheritedColor(childColor, theme) as (datum: NormalizedDatum) => string
const getChildColor = useInheritedColor(childColor, theme) as (
datum: NormalizedDatum<RawDatum>
) => string

const getId = useMemo(() => getAccessorFor(id), [id])
const getValue = useMemo(() => getAccessorFor(value), [value])
Expand All @@ -43,32 +45,34 @@ export const useSunburst = <RawDatum extends MaybeColor>({

return sortBy(partition(cloneDeep(hierarchy)).descendants(), 'depth').reduce<
Array<ComputedDatum<RawDatum>>
>((acc, xx) => {
>((acc, descendant) => {
// Maybe the types are wrong from d3, but value prop is always present, but types make it optional
const node = {
value: 0,
...pick(xx, 'x0', 'y0', 'x1', 'y1', 'depth', 'height', 'parent', 'value'),
...pick(descendant, 'x0', 'y0', 'x1', 'y1', 'depth', 'height', 'parent', 'value'),
}

const { value } = node
const id = getId(xx.data)

const id = getId(descendant.data)
const data = {
depth: node.depth,
id,
value,
percentage: (100 * value) / total,
depth: node.depth,
value,
}

const parent = acc.find(n => node.parent && n.data.id === getId(node.parent.data))
const parent = acc.find(
computed => node.parent && computed.data.id === getId(node.parent.data)
)

const color =
node.depth === 1 || childColor === 'noinherit'
? getColor(data)
: parent
? getChildColor(parent.data)
: xx.data.color
: descendant.data.color

return [...acc, { ...node, data: { ...data, color } }]
return [...acc, { ...node, data: { ...data, color, parent } }]
}, [])
}, [data, childColor, getChildColor, getColor, getId, getValue, radius])

Expand Down
43 changes: 22 additions & 21 deletions packages/sunburst/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,70 +22,71 @@ export interface DataProps<RawDatum> {
value: string | number | DatumPropertyAccessor<RawDatum, DatumValue>
}

export interface NormalizedDatum {
export interface NormalizedDatum<RawDatum> {
color?: string
fill?: string
id: DatumId
value: DatumValue
depth: number
id: DatumId
fill?: string
parent?: ComputedDatum<RawDatum>
percentage: number
value: DatumValue
}

export interface ComputedDatum<RawDatum> {
x0: number
y0: number
x1: number
y1: number
data: NormalizedDatum
data: NormalizedDatum<RawDatum>
depth: number
height: number
parent: HierarchyRectangularNode<RawDatum> | null
value: number
}

export type CommonProps = {
export type CommonProps<RawDatum> = {
margin: Box

cornerRadius: number

colors: OrdinalColorScaleConfig<Omit<NormalizedDatum, 'color' | 'fill'>>
colors: OrdinalColorScaleConfig<Omit<NormalizedDatum<RawDatum>, 'color' | 'fill' | 'parent'>>
borderWidth: number
borderColor: string

childColor: InheritedColorConfig<NormalizedDatum>
childColor: InheritedColorConfig<NormalizedDatum<RawDatum>>

// slice labels
enableSliceLabels: boolean
sliceLabel: string | LabelAccessorFunction<NormalizedDatum>
sliceLabel: string | LabelAccessorFunction<NormalizedDatum<RawDatum>>
sliceLabelsSkipAngle: number
sliceLabelsTextColor: InheritedColorConfig<NormalizedDatum>
sliceLabelsTextColor: InheritedColorConfig<NormalizedDatum<RawDatum>>

role: string

theme: Theme

isInteractive: boolean
tooltipFormat: DataFormatter
tooltip: (payload: NormalizedDatum) => JSX.Element
tooltip: (payload: NormalizedDatum<RawDatum>) => JSX.Element
}

export type MouseEventHandler<ElementType> = (
datum: NormalizedDatum,
export type MouseEventHandler<RawDatum, ElementType> = (
datum: NormalizedDatum<RawDatum>,
event: React.MouseEvent<ElementType>
) => void

export type MouseEventHandlers<ElementType> = Partial<{
onClick: MouseEventHandler<ElementType>
onMouseEnter: MouseEventHandler<ElementType>
onMouseLeave: MouseEventHandler<ElementType>
export type MouseEventHandlers<RawDatum, ElementType> = Partial<{
onClick: MouseEventHandler<RawDatum, ElementType>
onMouseEnter: MouseEventHandler<RawDatum, ElementType>
onMouseLeave: MouseEventHandler<RawDatum, ElementType>
}>

export type SvgProps<RawDatum> = DataProps<RawDatum> &
Dimensions &
SvgDefsAndFill<RawDatum> &
MouseEventHandlers<SVGPathElement> &
MouseEventHandlers<RawDatum, SVGPathElement> &
ModernMotionProps &
Partial<CommonProps>
Partial<CommonProps<RawDatum>>

export type SunburstArcProps<RawDatum> = Pick<
SvgProps<RawDatum>,
Expand All @@ -102,8 +103,8 @@ export type SunburstArcProps<RawDatum> = Pick<
}

export type SunburstLabelProps<RawDatum> = {
label: CommonProps['sliceLabel']
label: CommonProps<RawDatum>['sliceLabel']
nodes: Array<ComputedDatum<RawDatum>>
skipAngle?: number
textColor: CommonProps['sliceLabelsTextColor']
textColor: CommonProps<RawDatum>['sliceLabelsTextColor']
}
7 changes: 6 additions & 1 deletion website/src/pages/sunburst/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React from 'react'
import { ResponsiveSunburst } from '@nivo/sunburst'
import { generateLibTree } from '@nivo/generators'
import { omit } from 'lodash'
import ComponentTemplate from '../../components/components/ComponentTemplate'
import meta from '../../data/components/sunburst/meta.yml'
import mapper from '../../data/components/sunburst/mapper'
Expand Down Expand Up @@ -77,7 +78,11 @@ const Sunburst = () => {
Math.round(node.percentage * 100) / 100
}%`,
color: node.color,
data: node,
// prevent cyclic dependency
data: {
...omit(node, ['parent']),
parent: omit(node.parent, ['data', 'parent', 'children']),
},
})
}
/>
Expand Down

0 comments on commit c586676

Please sign in to comment.