Skip to content

Commit 8c1f2f5

Browse files
committedSep 11, 2021
feat(radial-bar): add support for circular grid
1 parent 9a8f03f commit 8c1f2f5

File tree

18 files changed

+560
-85
lines changed

18 files changed

+560
-85
lines changed
 

‎packages/arcs/src/ArcLine.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { animated, to, AnimatedProps } from '@react-spring/web'
2+
import { ExtractProps } from '@nivo/core'
3+
import { generateSvgArc } from './utils'
4+
5+
type ArcLineProps = {
6+
animated: AnimatedProps<{
7+
radius: number
8+
startAngle: number
9+
endAngle: number
10+
opacity: number
11+
}>
12+
} & ExtractProps<typeof animated.path>
13+
14+
export const ArcLine = ({ animated: animatedProps, ...rest }: ArcLineProps) => (
15+
<animated.path
16+
d={to(
17+
[animatedProps.radius, animatedProps.startAngle, animatedProps.endAngle],
18+
(radius, start, end) => generateSvgArc(radius, start, end)
19+
)}
20+
{...rest}
21+
/>
22+
)

‎packages/arcs/src/boundingBox.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const computeArcBoundingBox = (
4444
}
4545

4646
points = points.map(([x, y]) => [centerX + x, centerY + y])
47-
if (includeCenter === true) {
47+
if (includeCenter) {
4848
points.push([centerX, centerY])
4949
}
5050

‎packages/arcs/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './arc_labels'
22
export * from './arc_link_labels'
3+
export * from './ArcLine'
34
export * from './ArcShape'
45
export * from './ArcsLayer'
56
export * from './arcTransitionMode'

‎packages/arcs/src/utils.ts

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useMemo } from 'react'
2-
import { radiansToDegrees } from '@nivo/core'
2+
import { radiansToDegrees, positionFromAngle, degreesToRadians } from '@nivo/core'
33
import { DatumWithArc } from './types'
44

55
/**
@@ -33,3 +33,53 @@ export const useFilteredDataBySkipAngle = <Datum extends DatumWithArc>(
3333
data: Datum[],
3434
skipAngle: number
3535
) => useMemo(() => filterDataBySkipAngle(data, skipAngle), [data, skipAngle])
36+
37+
export const svgEllipticalArcCommand = (
38+
radius: number,
39+
largeArcFlag: 0 | 1,
40+
sweepFlag: 0 | 1,
41+
x: number,
42+
y: number
43+
) =>
44+
[
45+
'A',
46+
radius,
47+
radius,
48+
0, // x-axis-rotation
49+
largeArcFlag,
50+
sweepFlag,
51+
x,
52+
y,
53+
].join(' ')
54+
55+
export const generateSvgArc = (
56+
radius: number,
57+
originalStartAngle: number,
58+
originalEndAngle: number
59+
): string => {
60+
const startAngle = Math.min(originalStartAngle, originalEndAngle)
61+
const endAngle = Math.max(originalStartAngle, originalEndAngle)
62+
63+
const start = positionFromAngle(degreesToRadians(endAngle), radius)
64+
const end = positionFromAngle(degreesToRadians(startAngle), radius)
65+
66+
// we have a full circle, we cannot use a single elliptical arc
67+
// to draw it, so we use 2 in that case.
68+
if (endAngle - startAngle >= 360) {
69+
const mid = positionFromAngle(degreesToRadians(startAngle + 180), radius)
70+
71+
return [
72+
`M ${start.x} ${start.y}`,
73+
svgEllipticalArcCommand(radius, 1, 1, mid.x, mid.y),
74+
`M ${start.x} ${start.y}`,
75+
svgEllipticalArcCommand(radius, 1, 0, mid.x, mid.y),
76+
].join(' ')
77+
}
78+
79+
const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1
80+
81+
return [
82+
`M ${start.x} ${start.y}`,
83+
svgEllipticalArcCommand(radius, largeArcFlag, 0, end.x, end.y),
84+
].join(' ')
85+
}

‎packages/core/index.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react'
22
import { Interpolation, SpringConfig } from '@react-spring/web'
33
import { CurveFactory } from 'd3-shape'
4+
import { ComponentType } from 'react'
45

56
declare module '@nivo/core' {
67
export type DatumValue = string | number | Date
@@ -315,6 +316,12 @@ declare module '@nivo/core' {
315316
export function degreesToRadians(degrees: number): number
316317
export function radiansToDegrees(radians: number): number
317318
export function absoluteAngleDegrees(degrees: number): number
319+
export function normalizeAngle(degrees: number): number
320+
export function clampArc(
321+
startAngle: number,
322+
endAngle: number,
323+
length?: number
324+
): [number, number]
318325

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

@@ -523,4 +530,8 @@ declare module '@nivo/core' {
523530
symbol?: DotsItemSymbolComponent
524531
}
525532
export const DotsItem: React.FunctionComponent<DotsItemProps>
533+
534+
export type ExtractProps<TComponent> = TComponent extends ComponentType<infer TProps>
535+
? TProps
536+
: never
526537
}

‎packages/core/src/lib/polar/utils.js

+34
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,37 @@ export const absoluteAngleDegrees = angle => {
2828
}
2929

3030
export const absoluteAngleRadians = angle => angle - TWO_PI * Math.floor((angle + Math.PI) / TWO_PI)
31+
32+
/**
33+
* Ensure angle is always between 0~360.
34+
*
35+
* @param {number} rawAngle - in degrees
36+
*
37+
* @returns {number}
38+
*/
39+
export const normalizeAngle = rawAngle => {
40+
if (rawAngle < 0) {
41+
return 360 - (-rawAngle % 360)
42+
}
43+
44+
return rawAngle % 360
45+
}
46+
47+
/**
48+
* Ensure the absolute difference between start and end angles
49+
* is at most given length.
50+
*
51+
* @param startAngle - in degrees
52+
* @param endAngle - in degrees
53+
* @param length - in degrees
54+
*
55+
* @returns {[number, number]}
56+
*/
57+
export const clampArc = (startAngle, endAngle, length = 360) => {
58+
let clampedEndAngle = endAngle
59+
if (Math.abs(endAngle - startAngle) > length) {
60+
clampedEndAngle = startAngle + (endAngle > startAngle ? length : -length)
61+
}
62+
63+
return [startAngle, clampedEndAngle]
64+
}

‎packages/radial-bar/src/RadialBar.tsx

+40-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { createElement, Fragment, ReactNode } from 'react'
2-
import { Container, useDimensions, SvgWrapper } from '@nivo/core'
2+
import { Container, useDimensions, SvgWrapper, clampArc } from '@nivo/core'
33
import { ArcLabelsLayer } from '@nivo/arcs'
4+
import { BoxLegendSvg } from '@nivo/legends'
45
import { RadialBarLayerId, RadialBarSvgProps, ComputedBar } from './types'
56
import { svgDefaultProps } from './props'
67
import { useRadialBar } from './hooks'
78
import { RadialBarArcs } from './RadialBarArcs'
89
import { PolarGrid } from './polar_grid'
9-
import { BoxLegendSvg } from '@nivo/legends'
1010
import { RadialBarTracks } from './RadialBarTracks'
11+
import { RadialAxis } from './radial_axis'
1112

1213
type InnerRadialBarProps = Omit<
1314
RadialBarSvgProps,
@@ -17,8 +18,8 @@ type InnerRadialBarProps = Omit<
1718
const InnerRadialBar = ({
1819
data,
1920
valueFormat,
20-
startAngle = svgDefaultProps.startAngle,
21-
endAngle = svgDefaultProps.endAngle,
21+
startAngle: originalStartAngle = svgDefaultProps.startAngle,
22+
endAngle: originalEndAngle = svgDefaultProps.endAngle,
2223
padding = svgDefaultProps.padding,
2324
padAngle = svgDefaultProps.padAngle,
2425
cornerRadius = svgDefaultProps.cornerRadius,
@@ -28,8 +29,10 @@ const InnerRadialBar = ({
2829
layers = svgDefaultProps.layers,
2930
enableTracks = svgDefaultProps.enableTracks,
3031
tracksColor = svgDefaultProps.tracksColor,
31-
enableGridAngles = svgDefaultProps.enableGridAngles,
32-
enableGridRadii = svgDefaultProps.enableGridRadii,
32+
enableRadialGrid = svgDefaultProps.enableRadialGrid,
33+
enableCircularGrid = svgDefaultProps.enableCircularGrid,
34+
radialAxisStart = svgDefaultProps.radialAxisStart,
35+
radialAxisEnd = svgDefaultProps.radialAxisEnd,
3336
colors = svgDefaultProps.colors,
3437
borderWidth = svgDefaultProps.borderWidth,
3538
borderColor = svgDefaultProps.borderColor,
@@ -57,6 +60,8 @@ const InnerRadialBar = ({
5760
partialMargin
5861
)
5962

63+
const [startAngle, endAngle] = clampArc(originalStartAngle, originalEndAngle)
64+
6065
const {
6166
center,
6267
bars,
@@ -90,14 +95,35 @@ const InnerRadialBar = ({
9095

9196
if (layers.includes('grid')) {
9297
layerById.grid = (
93-
<PolarGrid
94-
key="grid"
95-
center={center}
96-
enableAngles={enableGridAngles}
97-
enableRadii={enableGridRadii}
98-
angleScale={valueScale}
99-
radiusScale={radiusScale}
100-
/>
98+
<Fragment key="grid">
99+
<PolarGrid
100+
center={center}
101+
enableRadialGrid={enableRadialGrid}
102+
enableCircularGrid={enableCircularGrid}
103+
angleScale={valueScale}
104+
radiusScale={radiusScale}
105+
startAngle={startAngle}
106+
endAngle={endAngle}
107+
/>
108+
{radialAxisStart && (
109+
<RadialAxis
110+
type="start"
111+
center={center}
112+
angle={Math.min(startAngle, endAngle)}
113+
scale={radiusScale}
114+
{...radialAxisStart}
115+
/>
116+
)}
117+
{radialAxisEnd && (
118+
<RadialAxis
119+
type="end"
120+
center={center}
121+
angle={Math.max(startAngle, endAngle)}
122+
scale={radiusScale}
123+
{...radialAxisEnd}
124+
/>
125+
)}
126+
</Fragment>
101127
)
102128
}
103129

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { SVGProps, useMemo } from 'react'
2+
import { ScaleBand } from 'd3-scale'
3+
import { useTransition } from '@react-spring/web'
4+
import { useTheme, useMotionConfig } from '@nivo/core'
5+
import { ArcLine } from '@nivo/arcs'
6+
7+
interface CircularGridProps {
8+
scale: ScaleBand<string>
9+
startAngle: number
10+
endAngle: number
11+
}
12+
13+
export const CircularGrid = ({
14+
scale,
15+
startAngle: originalStartAngle,
16+
endAngle: originalEndAngle,
17+
}: CircularGridProps) => {
18+
const theme = useTheme()
19+
20+
const startAngle = originalStartAngle - 90
21+
const endAngle = originalEndAngle - 90
22+
23+
const radii = useMemo(
24+
() =>
25+
scale.domain().map((value, index) => ({
26+
id: index,
27+
radius: (scale(value) as number) + scale.bandwidth() / 2,
28+
})),
29+
[scale]
30+
)
31+
32+
const { animate, config: springConfig } = useMotionConfig()
33+
const transition = useTransition<
34+
{ id: number; radius: number },
35+
{ radius: number; startAngle: number; endAngle: number; opacity: number }
36+
>(radii, {
37+
keys: item => item.id,
38+
initial: item => ({
39+
radius: item.radius,
40+
startAngle,
41+
endAngle,
42+
opacity: 1,
43+
}),
44+
from: item => ({
45+
radius: item.radius,
46+
startAngle,
47+
endAngle,
48+
opacity: 0,
49+
}),
50+
enter: item => ({
51+
radius: item.radius,
52+
startAngle,
53+
endAngle,
54+
opacity: 1,
55+
}),
56+
update: item => ({
57+
radius: item.radius,
58+
startAngle,
59+
endAngle,
60+
opacity: 1,
61+
}),
62+
leave: item => ({
63+
radius: item.radius,
64+
startAngle,
65+
endAngle,
66+
opacity: 1,
67+
}),
68+
config: springConfig,
69+
immediate: !animate,
70+
})
71+
72+
return (
73+
<>
74+
{transition((style, item) => (
75+
<ArcLine
76+
key={item.id}
77+
animated={style}
78+
{...(theme.grid.line as Omit<SVGProps<SVGPathElement>, 'ref'>)}
79+
strokeOpacity={style.opacity}
80+
fill="none"
81+
/>
82+
))}
83+
</>
84+
)
85+
}

‎packages/radial-bar/src/polar_grid/PolarGrid.tsx

+24-50
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,41 @@
1-
import { SVGProps } from 'react'
21
import { ScaleBand, ScaleLinear } from 'd3-scale'
3-
import { useTransition, animated } from '@react-spring/web'
4-
import { useMotionConfig, useTheme } from '@nivo/core'
2+
import { RadialGrid } from './RadialGrid'
3+
import { CircularGrid } from './CircularGrid'
54

65
interface PolarGridProps {
76
center: [number, number]
8-
enableAngles: boolean
9-
enableRadii: boolean
7+
enableRadialGrid: boolean
8+
enableCircularGrid: boolean
109
angleScale: ScaleLinear<number, number>
1110
radiusScale: ScaleBand<string>
11+
startAngle: number
12+
endAngle: number
1213
}
1314

14-
export const PolarGrid = ({ center, angleScale, radiusScale, enableAngles }: PolarGridProps) => {
15+
export const PolarGrid = ({
16+
center,
17+
enableRadialGrid,
18+
enableCircularGrid,
19+
angleScale,
20+
radiusScale,
21+
startAngle,
22+
endAngle,
23+
}: PolarGridProps) => {
1524
// const radiuses = radiusScale.ticks()
1625
const innerRadius = Math.min(...radiusScale.range())
1726
const outerRadius = Math.max(...radiusScale.range())
1827

19-
const angleValues = angleScale.ticks()
20-
const angles = angleValues.map((angleValue, index) => ({
21-
id: index,
22-
angle: angleScale(angleValue) - 90,
23-
}))
24-
25-
const theme = useTheme()
26-
27-
const { animate, config: springConfig } = useMotionConfig()
28-
29-
const anglesTransition = useTransition<
30-
{ id: number; angle: number },
31-
{ angle: number; opacity: number }
32-
>(angles, {
33-
keys: item => item.id,
34-
from: item => ({
35-
angle: item.angle,
36-
opacity: 1,
37-
}),
38-
update: item => ({
39-
angle: item.angle,
40-
opacity: 1,
41-
}),
42-
config: springConfig,
43-
immediate: !animate,
44-
})
45-
4628
return (
4729
<g transform={`translate(${center[0]},${center[1]})`}>
48-
{enableAngles && (
49-
<>
50-
{anglesTransition((style, angle) => {
51-
return (
52-
<animated.g
53-
key={angle.id}
54-
transform={style.angle.to(v => `rotate(${v})`)}
55-
>
56-
<line
57-
x1={innerRadius}
58-
x2={outerRadius}
59-
{...(theme.grid.line as SVGProps<SVGLineElement>)}
60-
/>
61-
</animated.g>
62-
)
63-
})}
64-
</>
30+
{enableRadialGrid && (
31+
<RadialGrid
32+
scale={angleScale}
33+
innerRadius={innerRadius}
34+
outerRadius={outerRadius}
35+
/>
36+
)}
37+
{enableCircularGrid && (
38+
<CircularGrid scale={radiusScale} startAngle={startAngle} endAngle={endAngle} />
6539
)}
6640
</g>
6741
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { SVGProps, useMemo } from 'react'
2+
import { ScaleLinear } from 'd3-scale'
3+
import { useTransition, animated } from '@react-spring/web'
4+
import { useMotionConfig, useTheme } from '@nivo/core'
5+
6+
interface PolarGridProps {
7+
scale: ScaleLinear<number, number>
8+
innerRadius: number
9+
outerRadius: number
10+
}
11+
12+
export const RadialGrid = ({ scale, innerRadius, outerRadius }: PolarGridProps) => {
13+
const theme = useTheme()
14+
15+
const angles = useMemo(
16+
() =>
17+
scale.ticks().map((angleValue, index) => ({
18+
id: index,
19+
angle: scale(angleValue) - 90,
20+
})),
21+
[scale]
22+
)
23+
24+
const { animate, config: springConfig } = useMotionConfig()
25+
const transition = useTransition<
26+
{ id: number; angle: number },
27+
{ angle: number; opacity: number }
28+
>(angles, {
29+
keys: item => item.id,
30+
from: item => ({
31+
angle: item.angle,
32+
opacity: 1,
33+
}),
34+
update: item => ({
35+
angle: item.angle,
36+
opacity: 1,
37+
}),
38+
config: springConfig,
39+
immediate: !animate,
40+
})
41+
42+
return (
43+
<>
44+
{transition((style, angle) => (
45+
<animated.g key={angle.id} transform={style.angle.to(v => `rotate(${v})`)}>
46+
<line
47+
x1={innerRadius}
48+
x2={outerRadius}
49+
{...(theme.grid.line as SVGProps<SVGLineElement>)}
50+
/>
51+
</animated.g>
52+
))}
53+
</>
54+
)
55+
}

‎packages/radial-bar/src/props.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ export const commonDefaultProps = {
1414
enableTracks: true,
1515
tracksColor: 'rgba(0, 0, 0, .15)',
1616

17-
enableGridAngles: true,
18-
enableGridRadii: true,
17+
enableRadialGrid: true,
18+
enableCircularGrid: true,
19+
20+
radialAxisStart: {},
21+
radialAxisEnd: null,
1922

2023
colors: { scheme: 'nivo' as const },
2124
borderWidth: 0,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { createElement, useMemo } from 'react'
2+
import { ScaleBand } from 'd3-scale'
3+
import { useSpring, useTransition, animated } from '@react-spring/web'
4+
import { useMotionConfig, normalizeAngle } from '@nivo/core'
5+
import { RadialAxisConfig, RadialAxisTickAnimatedProps } from './types'
6+
import { RadialAxisTick } from './RadialAxisTick'
7+
8+
type RadialAxisProps = {
9+
type: 'start' | 'end'
10+
center: [number, number]
11+
angle: number
12+
scale: ScaleBand<string>
13+
} & RadialAxisConfig
14+
15+
export const RadialAxis = ({
16+
type,
17+
center,
18+
angle: rawAngle,
19+
scale,
20+
tickSize = 5,
21+
tickPadding = 5,
22+
tickRotation: extraRotation = 0,
23+
tickComponent = RadialAxisTick,
24+
}: RadialAxisProps) => {
25+
const angle = normalizeAngle(rawAngle)
26+
27+
let textAnchor: 'start' | 'end'
28+
let lineX: number
29+
let textX: number
30+
let tickRotation: number
31+
32+
if (type === 'start') {
33+
tickRotation = 90 + extraRotation
34+
if (angle <= 90) {
35+
lineX = -tickSize
36+
textX = lineX - tickPadding
37+
textAnchor = 'end'
38+
} else if (angle < 270) {
39+
lineX = tickSize
40+
textX = lineX + tickPadding
41+
textAnchor = 'start'
42+
tickRotation -= 180
43+
} else {
44+
lineX = -tickSize
45+
textX = lineX - tickPadding
46+
textAnchor = 'end'
47+
}
48+
} else {
49+
tickRotation = 90 + extraRotation
50+
if (angle < 90) {
51+
lineX = tickSize
52+
textX = lineX + tickPadding
53+
textAnchor = 'start'
54+
} else if (angle < 270) {
55+
lineX = -tickSize
56+
textX = lineX - tickPadding
57+
textAnchor = 'end'
58+
tickRotation -= 180
59+
} else {
60+
lineX = tickSize
61+
textX = lineX + tickPadding
62+
textAnchor = 'start'
63+
}
64+
}
65+
66+
const ticks = useMemo(
67+
() =>
68+
scale.domain().map((value, index) => ({
69+
key: index,
70+
label: value,
71+
position: (scale(value) as number) + scale.bandwidth() / 2,
72+
})),
73+
[scale]
74+
)
75+
76+
const { animate, config: springConfig } = useMotionConfig()
77+
78+
const spring = useSpring<{ rotation: string }>({
79+
rotation: rawAngle - 90,
80+
immediate: !animate,
81+
config: springConfig,
82+
})
83+
84+
const transition = useTransition<typeof ticks[0], RadialAxisTickAnimatedProps>(ticks, {
85+
keys: tick => tick.key,
86+
initial: tick => ({
87+
y: tick.position,
88+
textX,
89+
rotation: tickRotation,
90+
length: lineX,
91+
opacity: 1,
92+
}),
93+
from: tick => ({
94+
y: tick.position,
95+
textX,
96+
rotation: tickRotation,
97+
length: lineX,
98+
opacity: 0,
99+
}),
100+
enter: tick => ({
101+
y: tick.position,
102+
textX,
103+
rotation: tickRotation,
104+
length: lineX,
105+
opacity: 1,
106+
}),
107+
update: tick => ({
108+
y: tick.position,
109+
textX,
110+
rotation: tickRotation,
111+
length: lineX,
112+
opacity: 1,
113+
}),
114+
leave: tick => ({
115+
y: tick.position,
116+
textX,
117+
rotation: tickRotation,
118+
length: lineX,
119+
opacity: 0,
120+
}),
121+
immediate: !animate,
122+
config: springConfig,
123+
})
124+
125+
return (
126+
<g transform={`translate(${center[0]}, ${center[1]})`}>
127+
<animated.g transform={spring.rotation.to(value => `rotate(${value})`)}>
128+
{transition((animatedProps, tick) => {
129+
return createElement(tickComponent, {
130+
key: tick.key,
131+
label: tick.label,
132+
y: tick.position,
133+
textX,
134+
rotation: tickRotation,
135+
length: lineX,
136+
textAnchor,
137+
animated: animatedProps,
138+
})
139+
})}
140+
</animated.g>
141+
</g>
142+
)
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { animated, to } from '@react-spring/web'
2+
import { useTheme } from '@nivo/core'
3+
import { RadialAxisTickProps } from './types'
4+
5+
export const RadialAxisTick = ({
6+
label,
7+
textAnchor,
8+
animated: animatedProps,
9+
}: RadialAxisTickProps) => {
10+
const theme = useTheme()
11+
12+
return (
13+
<animated.g
14+
opacity={animatedProps.opacity}
15+
transform={to(
16+
[animatedProps.y, animatedProps.rotation],
17+
(y, rotation) => `translate(${y}, 0) rotate(${rotation})`
18+
)}
19+
>
20+
<animated.line x2={animatedProps.length} style={theme.axis.ticks.line} />
21+
<animated.text
22+
dx={animatedProps.textX}
23+
textAnchor={textAnchor}
24+
dominantBaseline="central"
25+
style={theme.axis.ticks.text}
26+
>
27+
{label}
28+
</animated.text>
29+
</animated.g>
30+
)
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './RadialAxis'
2+
export * from './types'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { AnimatedProps } from '@react-spring/web'
2+
import { ValueFormat } from '@nivo/core'
3+
import { FunctionComponent } from 'react'
4+
5+
export type RadialAxisTickAnimatedProps = {
6+
y: number
7+
textX: number
8+
rotation: number
9+
length: number
10+
opacity: number
11+
}
12+
13+
export interface RadialAxisTickProps {
14+
label: string
15+
y: number
16+
rotation: number
17+
textX: number
18+
length: number
19+
textAnchor: 'start' | 'end'
20+
animated: AnimatedProps<RadialAxisTickAnimatedProps>
21+
}
22+
export type RadialAxisTickComponent = FunctionComponent<RadialAxisTickProps>
23+
24+
export interface RadialAxisConfig {
25+
tickSize?: number
26+
tickPadding?: number
27+
tickRotation?: number
28+
format?: ValueFormat<string | number | Date>
29+
tickComponent?: RadialAxisTickComponent
30+
ariaHidden?: boolean
31+
}

‎packages/radial-bar/src/types.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { Arc, ArcGenerator, ArcLabelsProps, ArcTransitionMode } from '@nivo/arcs'
1212
import { InheritedColorConfig, OrdinalColorScaleConfig } from '@nivo/colors'
1313
import { LegendProps } from '@nivo/legends'
14+
import { RadialAxisConfig } from './radial_axis'
1415

1516
export interface RadialBarDatum {
1617
x: string
@@ -82,8 +83,11 @@ export type RadialBarCommonProps = {
8283
enableTracks: boolean
8384
tracksColor: string
8485

85-
enableGridAngles: boolean
86-
enableGridRadii: boolean
86+
enableRadialGrid: boolean
87+
enableCircularGrid: boolean
88+
89+
radialAxisStart: RadialAxisConfig | null
90+
radialAxisEnd: RadialAxisConfig | null
8791

8892
enableLabels: boolean
8993
label: PropertyAccessor<ComputedBar, string>

‎website/src/data/components/radial-bar/props.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ const props: ChartProperty[] = [
145145
defaultValue: svgDefaultProps.startAngle,
146146
controlType: 'angle',
147147
controlOptions: {
148-
min: -180,
148+
min: -360,
149149
max: 360,
150150
step: 5,
151151
},
@@ -268,23 +268,23 @@ const props: ChartProperty[] = [
268268
controlType: 'colorPicker',
269269
},
270270
{
271-
key: 'enableGridAngles',
271+
key: 'enableRadialGrid',
272272
group: 'Grid & Axes',
273273
type: 'boolean',
274274
required: false,
275-
help: 'Enable grid angles',
275+
help: 'Enable radial grid (rays)',
276276
flavors: ['svg'],
277-
defaultValue: svgDefaultProps.enableGridAngles,
277+
defaultValue: svgDefaultProps.enableRadialGrid,
278278
controlType: 'switch',
279279
},
280280
{
281-
key: 'enableGridRadii',
281+
key: 'enableCircularGrid',
282282
group: 'Grid & Axes',
283283
type: 'boolean',
284284
required: false,
285-
help: 'Enable grid radii',
285+
help: 'Enable circular grid (rings)',
286286
flavors: ['svg'],
287-
defaultValue: svgDefaultProps.enableGridRadii,
287+
defaultValue: svgDefaultProps.enableCircularGrid,
288288
controlType: 'switch',
289289
},
290290
{

‎website/src/pages/radial-bar/index.tsx

+11-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const initialProperties: UnmappedRadarProps = {
2424

2525
margin: {
2626
top: 20,
27-
right: 100,
27+
right: 120,
2828
bottom: 20,
2929
left: 20,
3030
},
@@ -36,8 +36,11 @@ const initialProperties: UnmappedRadarProps = {
3636
enableTracks: svgDefaultProps.enableTracks,
3737
tracksColor: svgDefaultProps.tracksColor,
3838

39-
enableGridAngles: svgDefaultProps.enableGridAngles,
40-
enableGridRadii: svgDefaultProps.enableGridRadii,
39+
enableRadialGrid: svgDefaultProps.enableRadialGrid,
40+
enableCircularGrid: svgDefaultProps.enableCircularGrid,
41+
42+
radialAxisStart: svgDefaultProps.radialAxisStart,
43+
radialAxisEnd: svgDefaultProps.radialAxisEnd,
4144

4245
enableLabels: svgDefaultProps.enableLabels,
4346
label: svgDefaultProps.label,
@@ -60,7 +63,7 @@ const initialProperties: UnmappedRadarProps = {
6063
translateY: 0,
6164
itemsSpacing: 6,
6265
itemDirection: 'left-to-right',
63-
itemWidth: 80,
66+
itemWidth: 100,
6467
itemHeight: 18,
6568
itemTextColor: '#999',
6669
symbolSize: 18,
@@ -81,14 +84,14 @@ const initialProperties: UnmappedRadarProps = {
8184
}
8285

8386
const generateData = () => {
84-
const ids = ['A', 'B', 'C']
87+
const ids = ['Supermarket', 'Combini', 'Online']
8588
if (Math.random() > 0.5) {
86-
ids.push('D')
89+
ids.push('Marché')
8790
}
8891

89-
const categories = ['first', 'second', 'third']
92+
const categories = ['Vegetables', 'Fruits', 'Meat']
9093
if (Math.random() < 0.5) {
91-
categories.push('fourth')
94+
categories.push('Fish')
9295
}
9396

9497
return ids.map(id => ({

0 commit comments

Comments
 (0)
Please sign in to comment.