Skip to content

Commit 370ece9

Browse files
committed
feat(axes): allow axes style overrides
1 parent 35b3f82 commit 370ece9

File tree

17 files changed

+308
-48
lines changed

17 files changed

+308
-48
lines changed

packages/axes/src/canvas.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { degreesToRadians } from '@nivo/core'
2-
import { Theme } from '@nivo/theming'
2+
import { Theme, PartialTheme, extendAxisTheme } from '@nivo/theming'
33
import { setCanvasFont, drawCanvasText } from '@nivo/text'
44
import { ScaleValue, AnyScale, TicksSpec } from '@nivo/scales'
55
import { computeCartesianTicks, getFormatter, computeGridLines } from './compute'
@@ -24,6 +24,7 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
2424
legendPosition = 'end',
2525
legendOffset = 0,
2626
theme,
27+
style,
2728
}: {
2829
axis: 'x' | 'y'
2930
scale: AnyScale
@@ -40,6 +41,7 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
4041
legendPosition?: AxisLegendPosition
4142
legendOffset?: number
4243
theme: Theme
44+
style?: PartialTheme['axis']
4345
}
4446
) => {
4547
const { ticks, textAlign, textBaseline } = computeCartesianTicks({
@@ -56,18 +58,20 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
5658
ctx.save()
5759
ctx.translate(x, y)
5860

61+
const axisTheme = extendAxisTheme(theme.axis, style)
62+
5963
ctx.textAlign = textAlign
6064
ctx.textBaseline = textBaseline
6165

62-
setCanvasFont(ctx, theme.axis.ticks.text)
66+
setCanvasFont(ctx, axisTheme.ticks.text)
6367

64-
const domainLineWidth = theme.axis.domain.line.strokeWidth ?? 0
68+
const domainLineWidth = axisTheme.domain.line.strokeWidth ?? 0
6569
if (typeof domainLineWidth !== 'string' && domainLineWidth > 0) {
6670
ctx.lineWidth = domainLineWidth
6771
ctx.lineCap = 'square'
6872

69-
if (theme.axis.domain.line.stroke) {
70-
ctx.strokeStyle = theme.axis.domain.line.stroke
73+
if (axisTheme.domain.line.stroke) {
74+
ctx.strokeStyle = axisTheme.domain.line.stroke
7175
}
7276

7377
ctx.beginPath()
@@ -78,15 +82,15 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
7882

7983
const format = typeof _format === 'function' ? _format : (value: unknown) => `${value}`
8084

81-
const tickLineWidth = theme.axis.ticks.line.strokeWidth ?? 0
85+
const tickLineWidth = axisTheme.ticks.line.strokeWidth ?? 0
8286
const shouldRenderTickLine = typeof tickLineWidth !== 'string' && tickLineWidth > 0
8387
ticks.forEach(tick => {
8488
if (shouldRenderTickLine) {
8589
ctx.lineWidth = tickLineWidth
8690
ctx.lineCap = 'square'
8791

88-
if (theme.axis.ticks.line.stroke) {
89-
ctx.strokeStyle = theme.axis.ticks.line.stroke
92+
if (axisTheme.ticks.line.stroke) {
93+
ctx.strokeStyle = axisTheme.ticks.line.stroke
9094
}
9195

9296
ctx.beginPath()
@@ -101,7 +105,7 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
101105
ctx.translate(tick.x + tick.textX, tick.y + tick.textY)
102106
ctx.rotate(degreesToRadians(tickRotation))
103107

104-
drawCanvasText(ctx, theme.axis.ticks.text, `${value}`)
108+
drawCanvasText(ctx, axisTheme.ticks.text, `${value}`)
105109

106110
ctx.fillText(`${value}`, 0, 0)
107111
ctx.restore()
@@ -140,17 +144,15 @@ export const renderAxisToCanvas = <Value extends ScaleValue>(
140144

141145
ctx.translate(legendX, legendY)
142146
ctx.rotate(degreesToRadians(legendRotation))
143-
ctx.font = `${
144-
theme.axis.legend.text.fontWeight ? `${theme.axis.legend.text.fontWeight} ` : ''
145-
}${theme.axis.legend.text.fontSize}px ${theme.axis.legend.text.fontFamily}`
147+
setCanvasFont(ctx, axisTheme.legend.text)
146148

147-
if (theme.axis.legend.text.fill) {
148-
ctx.fillStyle = theme.axis.legend.text.fill
149+
if (axisTheme.legend.text.fill) {
150+
ctx.fillStyle = axisTheme.legend.text.fill
149151
}
150152

151153
ctx.textAlign = textAlign
152154
ctx.textBaseline = 'middle'
153-
ctx.fillText(legend, 0, 0)
155+
drawCanvasText(ctx, axisTheme.legend.text, legend)
154156
}
155157

156158
ctx.restore()
@@ -163,12 +165,10 @@ export const renderAxesToCanvas = <X extends ScaleValue, Y extends ScaleValue>(
163165
yScale,
164166
width,
165167
height,
166-
167168
top,
168169
right,
169170
bottom,
170171
left,
171-
172172
theme,
173173
}: {
174174
xScale: AnyScale

packages/axes/src/components/Axis.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useMotionConfig } from '@nivo/core'
2-
import { useTheme } from '@nivo/theming'
2+
import { useTheme, useExtendedAxisTheme } from '@nivo/theming'
33
import { Text } from '@nivo/text'
44
import { AnyScale, ScaleValue } from '@nivo/scales'
55
import { animated, useSpring, useTransition } from '@react-spring/web'
@@ -26,6 +26,7 @@ export const NonMemoizedAxis = <Value extends ScaleValue>({
2626
legend,
2727
legendPosition = 'end',
2828
legendOffset = 0,
29+
style,
2930
onClick,
3031
ariaHidden,
3132
}: AxisProps<Value> & {
@@ -37,6 +38,7 @@ export const NonMemoizedAxis = <Value extends ScaleValue>({
3738
onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, value: Value | string) => void
3839
}) => {
3940
const theme = useTheme()
41+
const axisTheme = useExtendedAxisTheme(theme.axis, style)
4042

4143
const formatValue = useMemo(() => getFormatter(format, scale), [format, scale])
4244

@@ -89,7 +91,7 @@ export const NonMemoizedAxis = <Value extends ScaleValue>({
8991
transform={`translate(${legendX}, ${legendY}) rotate(${legendRotation})`}
9092
textAnchor={textAnchor}
9193
style={{
92-
...theme.axis.legend.text,
94+
...axisTheme.legend.text,
9395
dominantBaseline: 'central',
9496
}}
9597
>
@@ -155,12 +157,13 @@ export const NonMemoizedAxis = <Value extends ScaleValue>({
155157
textAnchor: textAlign,
156158
truncateTickAt: truncateTickAt,
157159
animatedProps: transitionProps,
160+
theme: axisTheme.ticks,
158161
...tick,
159162
...(onClick ? { onClick } : {}),
160163
})
161164
})}
162165
<animated.line
163-
style={theme.axis.domain.line}
166+
style={axisTheme.domain.line}
164167
x1={0}
165168
x2={animatedProps.lineX2}
166169
y1={0}

packages/axes/src/components/AxisTick.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useMemo, memo } from 'react'
22
import * as React from 'react'
33
import { animated } from '@react-spring/web'
4-
import { useTheme } from '@nivo/theming'
54
import { Text } from '@nivo/text'
65
import { ScaleValue } from '@nivo/scales'
76
import { AxisTickProps } from '../types'
@@ -14,11 +13,9 @@ const AxisTick = <Value extends ScaleValue>({
1413
onClick,
1514
textBaseline,
1615
textAnchor,
16+
theme,
1717
animatedProps,
1818
}: AxisTickProps<Value>) => {
19-
const theme = useTheme()
20-
const lineStyle = theme.axis.ticks.line
21-
2219
const value = format?.(_value) ?? _value
2320

2421
const props = useMemo(() => {
@@ -36,12 +33,12 @@ const AxisTick = <Value extends ScaleValue>({
3633

3734
return (
3835
<animated.g transform={animatedProps.transform} {...props}>
39-
<line x1={0} x2={lineX} y1={0} y2={lineY} style={lineStyle} />
36+
<line x1={0} x2={lineX} y1={0} y2={lineY} style={theme.line} />
4037
<Text
4138
dominantBaseline={textBaseline}
4239
textAnchor={textAnchor}
4340
transform={animatedProps.textTransform}
44-
style={theme.axis.ticks.text}
41+
style={theme.text}
4542
>
4643
{`${value}`}
4744
</Text>

packages/axes/src/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import * as React from 'react'
12
import { ScaleValue, TicksSpec } from '@nivo/scales'
23
import { SpringValues } from '@react-spring/web'
3-
import * as React from 'react'
4+
import { PartialTheme, Theme } from '@nivo/theming'
45

56
export type GridValuesBuilder<T> = T extends number
67
? number[]
@@ -33,6 +34,7 @@ export interface AxisProps<Value extends ScaleValue = any> {
3334
legend?: React.ReactNode
3435
legendPosition?: AxisLegendPosition
3536
legendOffset?: number
37+
style?: PartialTheme['axis']
3638
ariaHidden?: boolean
3739
}
3840

@@ -55,6 +57,7 @@ export interface AxisTickProps<Value extends ScaleValue> {
5557
textAnchor: string
5658
opacity?: number
5759
rotate?: number
60+
theme: Theme['axis']['ticks']
5861
animatedProps: SpringValues<{
5962
opacity: number
6063
textTransform: string

packages/polar-axes/src/CircularAxis.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createElement, SVGProps, useMemo } from 'react'
22
import { useSpring, useTransition } from '@react-spring/web'
33
import { useMotionConfig, positionFromAngle, degreesToRadians } from '@nivo/core'
4-
import { useTheme } from '@nivo/theming'
4+
import { useExtendedAxisTheme, useTheme } from '@nivo/theming'
55
import { AnyScale, getScaleTicks, centerScale } from '@nivo/scales'
66
import { ArcLine } from '@nivo/arcs'
77
import { CircularAxisConfig, CircularAxisTickAnimatedProps } from './types'
@@ -47,12 +47,11 @@ export const CircularAxis = ({
4747
tickSize = 5,
4848
tickPadding = 12,
4949
tickComponent = CircularAxisTick,
50+
style,
5051
}: CircularAxisProps) => {
5152
const startAngle = originalStartAngle - 90
5253
const endAngle = originalEndAngle - 90
5354

54-
const theme = useTheme()
55-
5655
const { animate, config: springConfig } = useMotionConfig()
5756
const spring = useSpring<{
5857
radius: number
@@ -113,17 +112,21 @@ export const CircularAxis = ({
113112
config: springConfig,
114113
})
115114

115+
const theme = useTheme()
116+
const axisTheme = useExtendedAxisTheme(theme.axis, style)
117+
116118
return (
117119
<g transform={`translate(${center[0]}, ${center[1]})`} style={{ pointerEvents: 'none' }}>
118120
<ArcLine
119121
animated={spring}
120-
{...(theme.axis.domain.line as Omit<SVGProps<SVGPathElement>, 'ref'>)}
122+
{...(axisTheme.domain.line as Omit<SVGProps<SVGPathElement>, 'ref'>)}
121123
fill="none"
122124
/>
123125
{transition((animatedProps, tick) =>
124126
createElement(tickComponent, {
125127
key: tick.key,
126128
label: tick.label,
129+
theme: axisTheme,
127130
animated: animatedProps,
128131
})
129132
)}

packages/polar-axes/src/CircularAxisTick.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import { animated } from '@react-spring/web'
2-
import { useTheme } from '@nivo/theming'
32
import { Text } from '@nivo/text'
43
import { CircularAxisTickProps } from './types'
54

6-
export const CircularAxisTick = ({ label, animated: animatedProps }: CircularAxisTickProps) => {
7-
const theme = useTheme()
8-
5+
export const CircularAxisTick = ({
6+
label,
7+
theme,
8+
animated: animatedProps,
9+
}: CircularAxisTickProps) => {
910
return (
1011
<animated.g opacity={animatedProps.opacity}>
1112
<animated.line
1213
x1={animatedProps.x1}
1314
y1={animatedProps.y1}
1415
x2={animatedProps.x2}
1516
y2={animatedProps.y2}
16-
style={theme.axis.ticks.line}
17+
style={theme.ticks.line}
1718
/>
1819
<Text
1920
dx={animatedProps.textX}
2021
dy={animatedProps.textY}
2122
dominantBaseline="central"
22-
style={theme.axis.ticks.text}
23+
style={theme.ticks.text}
2324
textAnchor="middle"
2425
>
2526
{label}

packages/polar-axes/src/RadialAxis.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createElement, useMemo } from 'react'
22
import { useSpring, useTransition, animated } from '@react-spring/web'
33
import { useMotionConfig, normalizeAngle } from '@nivo/core'
44
import { AnyScale, getScaleTicks } from '@nivo/scales'
5+
import { useExtendedAxisTheme, useTheme } from '@nivo/theming'
56
import { RadialAxisConfig, RadialAxisTickAnimatedProps } from './types'
67
import { RadialAxisTick } from './RadialAxisTick'
78

@@ -22,6 +23,7 @@ export const RadialAxis = ({
2223
tickPadding = 5,
2324
tickRotation: extraRotation = 0,
2425
tickComponent = RadialAxisTick,
26+
style,
2527
}: RadialAxisProps) => {
2628
const angle = normalizeAngle(rawAngle)
2729

@@ -130,6 +132,9 @@ export const RadialAxis = ({
130132
config: springConfig,
131133
})
132134

135+
const theme = useTheme()
136+
const axisTheme = useExtendedAxisTheme(theme.axis, style)
137+
133138
return (
134139
<g transform={`translate(${center[0]}, ${center[1]})`} style={{ pointerEvents: 'none' }}>
135140
<animated.g transform={spring.rotation.to(value => `rotate(${value})`)}>
@@ -142,6 +147,7 @@ export const RadialAxis = ({
142147
rotation: tickRotation,
143148
length: lineX,
144149
textAnchor,
150+
theme: axisTheme,
145151
animated: animatedProps,
146152
})
147153
)}

packages/polar-axes/src/RadialAxisTick.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { animated, to } from '@react-spring/web'
2-
import { useTheme } from '@nivo/theming'
32
import { Text } from '@nivo/text'
43
import { RadialAxisTickProps } from './types'
54

65
export const RadialAxisTick = ({
76
label,
87
textAnchor,
8+
theme,
99
animated: animatedProps,
1010
}: RadialAxisTickProps) => {
11-
const theme = useTheme()
12-
1311
return (
1412
<animated.g
1513
opacity={animatedProps.opacity}
@@ -18,12 +16,12 @@ export const RadialAxisTick = ({
1816
(y, rotation) => `translate(${y}, 0) rotate(${rotation})`
1917
)}
2018
>
21-
<animated.line x2={animatedProps.length} style={theme.axis.ticks.line} />
19+
<animated.line x2={animatedProps.length} style={theme.ticks.line} />
2220
<Text
2321
dx={animatedProps.textX}
2422
textAnchor={textAnchor}
2523
dominantBaseline="central"
26-
style={theme.axis.ticks.text}
24+
style={theme.ticks.text}
2725
>
2826
{label}
2927
</Text>

0 commit comments

Comments
 (0)