Skip to content

Commit

Permalink
feat(sunburst): remove recompose
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze authored and plouc committed Dec 2, 2020
1 parent 624d585 commit 3c0586b
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 312 deletions.
9 changes: 8 additions & 1 deletion packages/core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ declare module '@nivo/core' {
>

export function useTheme(): CompleteTheme
export function usePartialTheme(theme?: Theme): CompleteTheme

export type MotionProps = Partial<{
animate: boolean
Expand Down Expand Up @@ -159,7 +160,7 @@ declare module '@nivo/core' {
id: string
[key: string]: any
}[]
fill?: { id: string; match: object | SvgFillMatcher<T> | '*' }[]
fill?: { id: string; match: Record<string, unknown> | SvgFillMatcher<T> | '*' }[]
}

export interface CartesianMarkerProps {
Expand Down Expand Up @@ -276,6 +277,12 @@ declare module '@nivo/core' {
export function degreesToRadians(degrees: number): number
export function radiansToDegrees(radians: number): number

type Accessor<T, U> = T extends string ? U[T] : never

export function getAccessorFor(
directive: string | number
): <Datum, Value>(datum: Datum) => Value

export function useDimensions(
width: number,
height: number,
Expand Down
3 changes: 1 addition & 2 deletions packages/sunburst/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
"@nivo/tooltip": "0.66.0",
"d3-hierarchy": "^1.1.8",
"d3-shape": "^1.3.5",
"lodash": "^4.17.11",
"recompose": "^0.30.0"
"lodash": "^4.17.11"
},
"devDependencies": {
"@nivo/core": "0.66.0",
Expand Down
8 changes: 5 additions & 3 deletions packages/sunburst/src/ResponsiveSunburst.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react'
// @ts-ignore
import { ResponsiveWrapper } from '@nivo/core'
import Sunburst from './Sunburst'
import { Sunburst } from './Sunburst'
import { SunburstSvgProps } from './types'

export const ResponsiveSunburst = (props: Omit<SunburstSvgProps, 'width' | 'height'>) => (
export const ResponsiveSunburst = <Datum extends Record<string, unknown>>(
props: Omit<SunburstSvgProps<Datum>, 'width' | 'height'>
) => (
<ResponsiveWrapper>
{({ width, height }: Required<Pick<SunburstSvgProps, 'width' | 'height'>>) => (
{({ width, height }: Required<Pick<SunburstSvgProps<Datum>, 'width' | 'height'>>) => (
<Sunburst width={width} height={height} {...props} />
)}
</ResponsiveWrapper>
Expand Down
291 changes: 126 additions & 165 deletions packages/sunburst/src/Sunburst.tsx
Original file line number Diff line number Diff line change
@@ -1,206 +1,167 @@
import React from 'react'
import React, { useMemo } from 'react'
import sortBy from 'lodash/sortBy'
import cloneDeep from 'lodash/cloneDeep'
// @ts-ignore
import compose from 'recompose/compose'
// @ts-ignore
import defaultProps from 'recompose/defaultProps'
// @ts-ignore
import withPropsOnChange from 'recompose/withPropsOnChange'
// @ts-ignore
import withProps from 'recompose/withProps'
// @ts-ignore
import pure from 'recompose/pure'
import { partition as Partition, hierarchy } from 'd3-hierarchy'
import { partition as d3Partition, hierarchy } from 'd3-hierarchy'
import { arc } from 'd3-shape'
import {
// @ts-ignore
withTheme,
// @ts-ignore
withDimensions,
// @ts-ignore
getAccessorFor,
// @ts-ignore
getLabelGenerator,
// @ts-ignore
Container,
// @ts-ignore
SvgWrapper,
useDimensions,
usePartialTheme,
} from '@nivo/core'
// @ts-ignore
import { getOrdinalColorScale, getInheritedColorGenerator } from '@nivo/colors'
import SunburstLabels from './SunburstLabels'
import SunburstArc from './SunburstArc'
import { defaultProps as defaultSunburstProps } from './props'
import { SunburstSvgProps, SunburstNode, TooltipHandlers } from './types'
import { useOrdinalColorScale, useInheritedColor } from '@nivo/colors'
import { SunburstLabels } from './SunburstLabels'
import { SunburstArc } from './SunburstArc'
import { defaultProps } from './props'
import { SunburstSvgProps, SunburstNode, Data } from './types'

const getAncestor = (node: any): any => {
if (node.depth === 1) return node
if (node.parent) return getAncestor(node.parent)
return node
}

const Sunburst = ({
nodes,
export const Sunburst = <Datum extends Record<string, unknown>>(props: SunburstSvgProps<Datum>) => {
const {
data: _data,
identity,
value,

margin,
centerX,
centerY,
outerWidth,
outerHeight,
colors,
childColor,

arcGenerator,
margin: partialMargin,
width,
height,

borderWidth,
borderColor,
cornerRadius,
// arcGenerator,

// slices labels
enableSlicesLabels,
getSliceLabel,
slicesLabelsSkipAngle,
slicesLabelsTextColor,
borderWidth,
borderColor,

// theming
theme,
// slices labels
enableSlicesLabels,
sliceLabel,
slicesLabelsSkipAngle,
slicesLabelsTextColor,

role,
// theming
theme: _theme,

// interactivity
isInteractive,
tooltipFormat,
tooltip,
role,

// event handlers
onClick,
onMouseEnter,
onMouseLeave,
}: SunburstSvgProps & Required<typeof defaultSunburstProps>) => {
return (
<Container isInteractive={isInteractive} theme={theme} animate={false}>
{({ showTooltip, hideTooltip }: TooltipHandlers) => (
<SvgWrapper
width={outerWidth}
height={outerHeight}
margin={margin}
theme={theme}
role={role}
>
<g transform={`translate(${centerX}, ${centerY})`}>
{nodes
.filter(node => node.depth > 0)
.map((node, i) => (
<SunburstArc
key={i}
node={node}
arcGenerator={arcGenerator}
borderWidth={borderWidth}
borderColor={borderColor}
showTooltip={showTooltip}
hideTooltip={hideTooltip}
tooltipFormat={tooltipFormat}
tooltip={tooltip}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
))}
{enableSlicesLabels && (
<SunburstLabels
nodes={nodes}
theme={theme}
label={getSliceLabel}
skipAngle={slicesLabelsSkipAngle}
textColor={getInheritedColorGenerator(
slicesLabelsTextColor,
'labels.text.fill'
)}
/>
)}
</g>
</SvgWrapper>
)}
</Container>
)
}
// interactivity
isInteractive,
tooltipFormat,
tooltip,

// event handlers
onClick,
onMouseEnter,
onMouseLeave,
} = { ...defaultProps, ...props }

const { margin } = useDimensions(width, height, partialMargin)
const theme = usePartialTheme(_theme)

const enhance = compose(
defaultProps(defaultSunburstProps),
withTheme(),
withDimensions(),
withPropsOnChange(['colors'], ({ colors }: Required<SunburstSvgProps>) => ({
getColor: getOrdinalColorScale(colors, 'id'),
})),
withProps(({ width, height }: Record<string, number>) => {
const { centerX, centerY, radius } = useMemo(() => {
const radius = Math.min(width, height) / 2

const partition = Partition().size([2 * Math.PI, radius * radius])

return { radius, partition, centerX: width / 2, centerY: height / 2 }
}),
withPropsOnChange(['cornerRadius'], ({ cornerRadius }: { cornerRadius: number }) => ({
arcGenerator: arc<SunburstNode>()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1))
.cornerRadius(cornerRadius),
})),
withPropsOnChange(['identity'], ({ identity }: SunburstSvgProps) => ({
getIdentity: getAccessorFor(identity),
})),
withPropsOnChange(['value'], ({ value }: SunburstSvgProps) => ({
getValue: getAccessorFor(value),
})),
withPropsOnChange(['data', 'getValue'], ({ data, getValue }: Required<SunburstSvgProps>) => ({
data: hierarchy(data).sum(getValue as any),
})),
withPropsOnChange(['childColor', 'theme'], ({ childColor, theme }: SunburstSvgProps) => ({
getChildColor: getInheritedColorGenerator(childColor, theme),
})),
withPropsOnChange(
['data', 'partition', 'getIdentity', 'getChildColor'],
({
data,
partition,
getIdentity,
getColor,
childColor,
getChildColor,
}: Required<SunburstSvgProps>) => {
const total = (data as any).value

const nodes = sortBy(partition(cloneDeep(data)).descendants(), 'depth')
nodes.forEach(node => {
const ancestor = getAncestor(node).data
return { radius, centerX: width / 2, centerY: height / 2 }
}, [height, width])

const arcGenerator = useMemo(
() =>
arc<SunburstNode>()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1))
.cornerRadius(cornerRadius),
[cornerRadius]
)

const getColor = useOrdinalColorScale(colors, 'id')
const getChildColor = useInheritedColor(childColor, theme)

const getSliceLabel = useMemo(() => getLabelGenerator(sliceLabel), [sliceLabel])
const getIdentity = useMemo(() => getAccessorFor(identity), [identity])
const getValue = useMemo(() => getAccessorFor(value), [value])

delete node.children
delete node.data.children
const nodes = useMemo(() => {
const partition = d3Partition<Data<Datum>>().size([2 * Math.PI, radius * radius])
const data = hierarchy(_data).sum(getValue)
const total = data.value ?? 0

Object.assign(node.data, {
id: getIdentity(node.data),
value: node.value,
percentage: (100 * node.value) / total,
return sortBy(partition(cloneDeep(data)).descendants(), 'depth').reduce(
(acc, { children: _children, ...node }) => {
const ancestor = getAncestor(node).data
const value = node.value ?? 0
const id = getIdentity<Data<Datum>, string>(node.data)
const data = {
id,
value,
percentage: (100 * value) / total,
depth: node.depth,
ancestor,
})

if (node.depth === 1 || childColor === 'noinherit') {
node.data.color = getColor(node.data)
} else if (node.depth > 1) {
node.data.color = getChildColor(node.parent.data)
}
})

return { nodes }
}
),
withPropsOnChange(['sliceLabel'], ({ sliceLabel }: SunburstSvgProps) => ({
getSliceLabel: getLabelGenerator(sliceLabel),
})),
pure
)
const parent = acc.find(
n => node.parent && n.data.id === getIdentity(node.parent.data)
)
const color =
node.depth === 1 || childColor === 'noinherit'
? getColor(data)
: parent
? getChildColor(parent.data)
: node.data.color

return [...acc, { ...node, data: { ...data, color } }]
},
[] as SunburstNode<Datum>[]
)
}, [_data, childColor, getChildColor, getColor, getIdentity, getValue, radius])

const enhancedSunburst = (enhance(Sunburst as any) as unknown) as React.FC<SunburstSvgProps>
enhancedSunburst.displayName = 'Sunburst'

export default enhancedSunburst
return (
<Container isInteractive={isInteractive} theme={theme} animate={false}>
<SvgWrapper width={width} height={height} margin={margin} role={role}>
<g transform={`translate(${centerX}, ${centerY})`}>
{nodes
.filter(node => node.depth > 0)
.map((node, i) => (
<SunburstArc
key={i}
node={node}
arcGenerator={arcGenerator}
borderWidth={borderWidth}
borderColor={borderColor}
tooltipFormat={tooltipFormat}
tooltip={tooltip}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
))}
{enableSlicesLabels && (
<SunburstLabels
nodes={nodes}
label={getSliceLabel}
skipAngle={slicesLabelsSkipAngle}
textColor={slicesLabelsTextColor}
/>
)}
</g>
</SvgWrapper>
</Container>
)
}
Loading

0 comments on commit 3c0586b

Please sign in to comment.