Skip to content

Commit

Permalink
feat(arcs): finalize arc link labels
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Dec 18, 2020
1 parent f0d9d19 commit 89c52e6
Show file tree
Hide file tree
Showing 14 changed files with 381 additions and 231 deletions.
38 changes: 29 additions & 9 deletions packages/arcs/src/arc_link_labels/ArcLinkLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
import React from 'react'
import { useTheme } from '@nivo/core'
import { SpringValue, Interpolation, animated } from 'react-spring'
import { DatumWithArcAndColor } from '../types'

export interface ArcLinkLabelProps<Datum extends DatumWithArcAndColor> {
datum: Datum
label: string
style: {
linkColor: SpringValue<string>
path: Interpolation<string>
thickness: number
textPosition: Interpolation<string>
textAnchor: Interpolation<'start' | 'end'>
linkColor: SpringValue<string>
opacity: SpringValue<number>
path: Interpolation<string>
textColor: SpringValue<string>
}
}

export const ArcLinkLabel = <Datum extends DatumWithArcAndColor>({
label,
style,
}: ArcLinkLabelProps<Datum>) => {
const theme = useTheme()

return (
<animated.path
fill="none"
stroke={style.linkColor}
strokeWidth={style.thickness}
opacity={style.opacity}
d={style.path}
/>
<animated.g opacity={style.opacity}>
<animated.path
fill="none"
stroke={style.linkColor}
strokeWidth={style.thickness}
d={style.path}
/>
<animated.text
transform={style.textPosition}
textAnchor={style.textAnchor}
dominantBaseline="central"
style={{
...theme.labels.text,
fill: style.textColor,
}}
>
{label}
</animated.text>
</animated.g>
)
}
30 changes: 27 additions & 3 deletions packages/arcs/src/arc_link_labels/ArcLinkLabelsLayer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import { usePropertyAccessor } from '@nivo/core'
import { DatumWithArcAndColor } from '../types'
import { useArcLinkLabelsTransition } from './useArcLinkLabelsTransition'
import { ArcLinkLabelsProps } from './props'
Expand Down Expand Up @@ -26,7 +27,7 @@ interface ArcLinkLabelsLayerProps<Datum extends DatumWithArcAndColor> {
export const ArcLinkLabelsLayer = <Datum extends DatumWithArcAndColor>({
center,
data,
label,
label: labelAccessor,
skipAngle,
offset,
diagonalLength,
Expand All @@ -37,9 +38,15 @@ export const ArcLinkLabelsLayer = <Datum extends DatumWithArcAndColor>({
linkColor,
component = ArcLinkLabel,
}: ArcLinkLabelsLayerProps<Datum>) => {
const { transition, interpolateLink } = useArcLinkLabelsTransition<Datum>({
const getLabel = usePropertyAccessor<Datum, string>(labelAccessor)

const {
transition,
interpolateLink,
interpolateTextAnchor,
interpolateTextPosition,
} = useArcLinkLabelsTransition<Datum>({
data,
label,
skipAngle,
offset,
diagonalLength,
Expand All @@ -57,6 +64,7 @@ export const ArcLinkLabelsLayer = <Datum extends DatumWithArcAndColor>({
return React.createElement(Label, {
key: datum.id,
datum,
label: getLabel(datum),
style: {
...transitionProps,
thickness: strokeWidth,
Expand All @@ -69,6 +77,22 @@ export const ArcLinkLabelsLayer = <Datum extends DatumWithArcAndColor>({
transitionProps.diagonalLength,
transitionProps.straightLength
),
textAnchor: interpolateTextAnchor(
transitionProps.startAngle,
transitionProps.endAngle,
transitionProps.innerRadius,
transitionProps.outerRadius
),
textPosition: interpolateTextPosition(
transitionProps.startAngle,
transitionProps.endAngle,
transitionProps.innerRadius,
transitionProps.outerRadius,
transitionProps.offset,
transitionProps.diagonalLength,
transitionProps.straightLength,
transitionProps.textOffset
),
},
})
})}
Expand Down
2 changes: 1 addition & 1 deletion packages/arcs/src/arc_link_labels/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
CompleteTheme,
} from '@nivo/core'
import { DatumWithArcAndColor } from '../types'
import { ArcLinkLabel } from '../links'
import { ArcLinkLabel } from './types'

export const drawCanvasArcLinkLabels = <Datum extends DatumWithArcAndColor>(
ctx: CanvasRenderingContext2D,
Expand Down
63 changes: 63 additions & 0 deletions packages/arcs/src/arc_link_labels/compute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { positionFromAngle } from '@nivo/core'
import { Arc, Point } from '../types'
import { getNormalizedAngle } from '../utils'
import { ArcLink } from './types'

/**
* Compute text anchor for a given arc.
*
* `computeArcLink` already computes a `side`, but when using
* `react-spring`, you cannot have a single interpolation
* returning several output values, so we need to compute
* them in separate interpolations.
*/
export const computeArcLinkTextAnchor = (arc: Arc): 'start' | 'end' => {
const centerAngle = getNormalizedAngle(
arc.startAngle + (arc.endAngle - arc.startAngle) / 2 - Math.PI / 2
)

if (centerAngle < Math.PI / 2 || centerAngle > Math.PI * 1.5) {
return 'start'
}

return 'end'
}

/**
* Compute the link of a single arc, returning its points,
* please not that points coordinates are relative to
* the center of the arc.
*/
export const computeArcLink = (
arc: Arc,
offset: number,
diagonalLength: number,
straightLength: number
): ArcLink => {
const centerAngle = getNormalizedAngle(
arc.startAngle + (arc.endAngle - arc.startAngle) / 2 - Math.PI / 2
)
const point0: Point = positionFromAngle(centerAngle, arc.outerRadius + offset)
const point1: Point = positionFromAngle(centerAngle, arc.outerRadius + offset + diagonalLength)

let side: ArcLink['side']
let point2: Point
if (centerAngle < Math.PI / 2 || centerAngle > Math.PI * 1.5) {
side = 'after'
point2 = {
x: point1.x + straightLength,
y: point1.y,
}
} else {
side = 'before'
point2 = {
x: point1.x - straightLength,
y: point1.y,
}
}

return {
side,
points: [point0, point1, point2],
}
}
4 changes: 4 additions & 0 deletions packages/arcs/src/arc_link_labels/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export * from './ArcLinkLabelsLayer'
export * from './canvas'
export * from './compute'
export * from './props'
export * from './types'
export * from './useArcLinkLabels'
export * from './useArcLinkLabelsTransition'
export * from './useArcLinks'
19 changes: 19 additions & 0 deletions packages/arcs/src/arc_link_labels/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DatumWithArc, DatumWithArcAndColor, Point } from '../types'

export interface ArcLink {
side: 'before' | 'after'
points: [Point, Point, Point]
}

export interface ArcLinkWithDatum<Datum extends DatumWithArc> extends ArcLink {
data: Datum
}

export interface ArcLinkLabel<Datum extends DatumWithArcAndColor> extends ArcLinkWithDatum<Datum> {
x: number
y: number
label: string
linkColor: string
textAnchor: 'start' | 'end'
textColor: string
}
76 changes: 76 additions & 0 deletions packages/arcs/src/arc_link_labels/useArcLinkLabels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useCallback } from 'react'
import { PropertyAccessor, usePropertyAccessor, useTheme } from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
import { DatumWithArcAndColor } from '../types'
import { ArcLinkWithDatum, ArcLinkLabel } from './types'
import { useArcLinks } from './useArcLinks'

/**
* Compute arc link labels, please note that the datum should
* contain a color in order to be able to compute the link/label text color.
*
* Please see `useArcLinks` for a more detailed explanation
* about the parameters.
*/
export const useArcLinkLabels = <Datum extends DatumWithArcAndColor>({
data,
skipAngle,
offset,
diagonalLength,
straightLength,
textOffset = 0,
label,
linkColor,
textColor,
}: {
data: Datum[]
skipAngle?: number
offset?: number
diagonalLength: number
straightLength: number
textOffset: number
label: PropertyAccessor<Datum, string>
linkColor: InheritedColorConfig<Datum>
textColor: InheritedColorConfig<Datum>
}) => {
const getLabel = usePropertyAccessor<Datum, string>(label)

const theme = useTheme()
const getLinkColor = useInheritedColor<Datum>(linkColor, theme)
const getTextColor = useInheritedColor<Datum>(textColor, theme)

const computeExtraProps = useCallback(
(link: ArcLinkWithDatum<Datum>) => {
const position = {
x: link.points[2].x,
y: link.points[2].y,
}
let textAnchor: ArcLinkLabel<Datum>['textAnchor']
if (link.side === 'before') {
position.x -= textOffset
textAnchor = 'end'
} else {
position.x += textOffset
textAnchor = 'start'
}

return {
...position,
label: getLabel(link.data),
linkColor: getLinkColor(link.data),
textAnchor,
textColor: getTextColor(link.data),
}
},
[getLabel, getLinkColor, getTextColor, textOffset]
)

return useArcLinks<Datum, Omit<ArcLinkLabel<Datum>, keyof ArcLinkWithDatum<Datum>>>({
data,
skipAngle,
offset,
diagonalLength,
straightLength,
computeExtraProps,
})
}
Loading

0 comments on commit 89c52e6

Please sign in to comment.