Skip to content

Commit fc2f80f

Browse files
authored
fix: multi-axis tooltip/cursor formatting, proper extent for multi-axis (#281)
1 parent 9feb503 commit fc2f80f

File tree

4 files changed

+79
-64
lines changed

4 files changed

+79
-64
lines changed

src/components/Cursors.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ function Cursor<TDatum>(props: {
9393
const [focusedDatum] = focusedDatumState
9494
const latestFocusedDatum = useLatestWhen(focusedDatum, !!focusedDatum)
9595

96-
const secondaryAxis = secondaryAxes.find(
97-
d => d.id === props.options.axisId || latestFocusedDatum?.secondaryAxisId
98-
)!
96+
const secondaryAxis =
97+
secondaryAxes.find(d => d.id === latestFocusedDatum?.secondaryAxisId) ??
98+
secondaryAxes[0]
9999

100100
const axis = props.primary ? primaryAxis : secondaryAxis
101101

src/components/TooltipRenderer.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ function TooltipRenderer<TDatum>(props: TooltipRendererProps<TDatum>) {
4444
return null
4545
}
4646

47-
const { primaryAxis, secondaryAxis, getDatumStyle, focusedDatum } = props
47+
const {
48+
primaryAxis,
49+
secondaryAxis,
50+
getDatumStyle,
51+
focusedDatum,
52+
secondaryAxes,
53+
} = props
4854

4955
const { tooltip, dark } = props.getOptions()
5056

@@ -239,6 +245,10 @@ function TooltipRenderer<TDatum>(props: TooltipRendererProps<TDatum>) {
239245
{visibleSortedGroupDatums.map((sortedDatum, i) => {
240246
const active = sortedDatum === focusedDatum
241247

248+
const datumSecondaryAxis = secondaryAxes.find(
249+
d => d.id === sortedDatum.secondaryAxisId
250+
)
251+
242252
return (
243253
<tr
244254
key={i}
@@ -280,9 +290,9 @@ function TooltipRenderer<TDatum>(props: TooltipRendererProps<TDatum>) {
280290
textAlign: 'right',
281291
}}
282292
>
283-
{(secondaryAxis as AxisTime<any>).formatters.tooltip(
284-
sortedDatum.secondaryValue
285-
)}
293+
{(
294+
datumSecondaryAxis as AxisTime<any>
295+
).formatters.tooltip(sortedDatum.secondaryValue)}
286296
</td>
287297
</React.Fragment>
288298
) : tooltip.groupingMode === 'secondary' ? (
@@ -306,9 +316,9 @@ function TooltipRenderer<TDatum>(props: TooltipRendererProps<TDatum>) {
306316
textAlign: 'right',
307317
}}
308318
>
309-
{(secondaryAxis as AxisTime<any>).formatters.tooltip(
310-
sortedDatum.secondaryValue
311-
)}
319+
{(
320+
datumSecondaryAxis as AxisTime<any>
321+
).formatters.tooltip(sortedDatum.secondaryValue)}
312322
</td>
313323
</React.Fragment>
314324
)}

src/types.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ export type ChartOptions<TDatum> = {
1010
primaryAxis: AxisOptions<TDatum>
1111
secondaryAxes: AxisOptions<TDatum>[]
1212
padding?:
13-
| number
14-
| {
15-
left?: number
16-
right?: number
17-
top?: number
18-
bottom?: number
19-
}
13+
| number
14+
| {
15+
left?: number
16+
right?: number
17+
top?: number
18+
bottom?: number
19+
}
2020
getSeriesStyle?: (
2121
series: Series<TDatum>,
2222
status: SeriesFocusStatus
@@ -53,9 +53,9 @@ export type ChartOptions<TDatum> = {
5353
tooltip?: boolean | TooltipOptions<TDatum>
5454
useIntersectionObserver?: boolean
5555
intersectionObserverRootMargin?:
56-
| `${number}px`
57-
| `${number}px ${number}px`
58-
| `${number}px ${number}px ${number}px ${number}px`
56+
| `${number}px`
57+
| `${number}px ${number}px`
58+
| `${number}px ${number}px ${number}px ${number}px`
5959
}
6060

6161
export type RequiredChartOptions<TDatum> = TSTB.Object.Required<
@@ -493,7 +493,6 @@ export type CursorOptions = {
493493
show?: boolean
494494
showLine?: boolean
495495
showLabel?: boolean
496-
axisId?: string
497496
onChange?: (value: any) => void
498497
}
499498

src/utils/buildAxis.linear.ts

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,16 @@ export default function buildAxisLinear<TDatum>(
7373
// Give the scale a home
7474
return options.scaleType === 'time' || options.scaleType === 'localTime'
7575
? buildTimeAxis(
76-
isPrimary,
77-
options,
78-
series,
79-
allDatums,
80-
isVertical,
81-
range,
82-
outerRange
83-
)
76+
isPrimary,
77+
options,
78+
series,
79+
allDatums,
80+
isVertical,
81+
range,
82+
outerRange
83+
)
8484
: options.scaleType === 'linear' || options.scaleType === 'log'
85-
? buildLinearAxis(
85+
? buildLinearAxis(
8686
isPrimary,
8787
options,
8888
series,
@@ -91,11 +91,11 @@ export default function buildAxisLinear<TDatum>(
9191
range,
9292
outerRange
9393
)
94-
: options.scaleType === 'band'
95-
? buildBandAxis(isPrimary, options, series, isVertical, range, outerRange)
96-
: (() => {
97-
throw new Error('Invalid scale type')
98-
})()
94+
: options.scaleType === 'band'
95+
? buildBandAxis(isPrimary, options, series, isVertical, range, outerRange)
96+
: (() => {
97+
throw new Error('Invalid scale type')
98+
})()
9999
}
100100

101101
function buildTimeAxis<TDatum>(
@@ -111,6 +111,11 @@ function buildTimeAxis<TDatum>(
111111

112112
let isInvalid = false
113113

114+
series = isPrimary ? series : series
115+
.filter(s => s.secondaryAxisId === options.id)
116+
117+
allDatums = isPrimary ? allDatums : allDatums.filter(d => d.secondaryAxisId === options.id)
118+
114119
// Now set the range
115120
const scale = scaleFn(range)
116121

@@ -144,7 +149,7 @@ function buildTimeAxis<TDatum>(
144149
}
145150

146151
if (minValue === undefined || maxValue === undefined) {
147-
console.info('Invalid scale min/max was detect for a chart:', {
152+
console.info('Invalid scale min/max', {
148153
options,
149154
series,
150155
range,
@@ -242,28 +247,32 @@ function buildLinearAxis<TDatum>(
242247

243248
let isInvalid = false
244249

250+
series = isPrimary ? series : series
251+
.filter(s => s.secondaryAxisId === options.id)
252+
253+
allDatums = isPrimary ? allDatums : allDatums.filter(d => d.secondaryAxisId === options.id)
254+
245255
if (options.stacked) {
246256
stackSeries(series, options)
247257
}
248258

249259
let [minValue, maxValue] = options.stacked
250260
? extent(
251-
series
252-
.map(s =>
253-
s.datums.map(datum => {
254-
const value = options.getValue(datum.originalDatum)
255-
datum[isPrimary ? 'primaryValue' : 'secondaryValue'] = value
256-
257-
return datum.stackData ?? []
258-
})
259-
)
260-
.flat(2) as unknown as number[]
261-
)
261+
series
262+
.map(s =>
263+
s.datums.map(datum => {
264+
const value = options.getValue(datum.originalDatum)
265+
datum[isPrimary ? 'primaryValue' : 'secondaryValue'] = value
266+
return datum.stackData ?? []
267+
})
268+
)
269+
.flat(2) as unknown as number[]
270+
)
262271
: extent(allDatums, datum => {
263-
const value = options.getValue(datum.originalDatum)
264-
datum[isPrimary ? 'primaryValue' : 'secondaryValue'] = value
265-
return value
266-
})
272+
const value = options.getValue(datum.originalDatum)
273+
datum[isPrimary ? 'primaryValue' : 'secondaryValue'] = value
274+
return value
275+
})
267276

268277
let shouldNice = options.shouldNice
269278

@@ -310,7 +319,7 @@ function buildLinearAxis<TDatum>(
310319
})
311320
minValue = minValue ?? 0
312321
maxValue = maxValue ?? 0
313-
// throw new Error('Invalid scale min/max')
322+
// throw new Error('Invalid scale min/max'
314323
}
315324

316325
// Set the domain
@@ -465,13 +474,12 @@ function stackSeries<TDatum>(
465474
series: Series<TDatum>[],
466475
axisOptions: AxisOptions<TDatum>
467476
) {
468-
const axisSeries = series.filter(s => s.secondaryAxisId === axisOptions.id)
469-
const seriesIndices = Object.keys(axisSeries)
477+
const seriesIndices = Object.keys(series)
470478
const stacker = stack()
471479
.keys(seriesIndices)
472480
.value((_, seriesIndex, index) => {
473481
const originalDatum =
474-
axisSeries[Number(seriesIndex)]?.datums[index]?.originalDatum
482+
series[Number(seriesIndex)]?.datums[index]?.originalDatum
475483

476484
const val =
477485
typeof originalDatum !== 'undefined'
@@ -488,7 +496,7 @@ function stackSeries<TDatum>(
488496

489497
const stacked = stacker(
490498
Array.from({
491-
length: axisSeries.sort((a, b) => b.datums.length - a.datums.length)[0]
499+
length: series.sort((a, b) => b.datums.length - a.datums.length)[0]
492500
.datums.length,
493501
})
494502
)
@@ -499,11 +507,11 @@ function stackSeries<TDatum>(
499507
for (let i = 0; i < s.length; i++) {
500508
const datum = s[i]
501509

502-
if (axisSeries[sIndex].datums[i]) {
510+
if (series[sIndex].datums[i]) {
503511
// @ts-ignore
504-
datum.data = axisSeries[sIndex].datums[i]
512+
datum.data = series[sIndex].datums[i]
505513

506-
axisSeries[sIndex].datums[i].stackData =
514+
series[sIndex].datums[i].stackData =
507515
datum as unknown as StackDatum<TDatum>
508516
}
509517
}
@@ -561,9 +569,7 @@ function buildSeriesBandScale<TDatum>(
561569
primaryBandScale: ScaleBand<number>,
562570
series: Series<TDatum>[]
563571
) {
564-
const bandDomain = d3Range(
565-
series.filter(d => d.secondaryAxisId === options.id).length
566-
)
572+
const bandDomain = d3Range(series.length)
567573

568574
const seriesBandScale = scaleBand(bandDomain, [
569575
0,
@@ -572,11 +578,11 @@ function buildSeriesBandScale<TDatum>(
572578
.round(false)
573579
.paddingOuter(
574580
options.outerSeriesBandPadding ??
575-
(options.outerBandPadding ? options.outerBandPadding / 2 : 0)
581+
(options.outerBandPadding ? options.outerBandPadding / 2 : 0)
576582
)
577583
.paddingInner(
578584
options.innerSeriesBandPadding ??
579-
(options.innerBandPadding ? options.innerBandPadding / 2 : 0)
585+
(options.innerBandPadding ? options.innerBandPadding / 2 : 0)
580586
)
581587

582588
const scale = (seriesIndex: number) =>

0 commit comments

Comments
 (0)