Skip to content

Commit a2471ea

Browse files
committed
fix: better elementType impls, grouped bar voronoi, general voronoi
1 parent 6f103c8 commit a2471ea

File tree

15 files changed

+587
-549
lines changed

15 files changed

+587
-549
lines changed

examples/simple/src/components/Bar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function Bar() {
4040
data,
4141
primaryAxis,
4242
secondaryAxes,
43+
showVoronoi: true,
4344
}}
4445
/>
4546
</ResizableBox>

examples/simple/src/components/Bubble.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function Bubble() {
2525
() => [
2626
{
2727
getValue: (datum) => datum.secondary,
28+
elementType: "bubble",
2829
},
2930
],
3031
[]
@@ -42,7 +43,6 @@ export default function Bubble() {
4243
primaryAxis,
4344
secondaryAxes,
4445
interactionMode: "closest",
45-
getSeriesStyle: () => ({ line: { opacity: 0 } }),
4646
getDatumStyle: (datum) =>
4747
({
4848
circle: { r: datum.originalDatum.radius },

examples/simple/src/components/DynamicContainer.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import ResizableBox from "../ResizableBox";
21
import useDemoConfig from "../useDemoConfig";
32
import React from "react";
43
import { AxisOptions, Chart } from "react-charts";

examples/simple/src/components/MultipleAxes.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function MultipleAxes() {
1010
});
1111

1212
// @ts-ignore
13-
data.forEach((d, i) => (d.secondaryAxisId = i % 2 === 0 ? "2" : undefined));
13+
data.forEach((d, i) => (d.secondaryAxisId = i % 3 === 0 ? "2" : undefined));
1414

1515
const primaryAxis = React.useMemo<
1616
AxisOptions<typeof data[number]["data"][number]>
@@ -28,6 +28,7 @@ export default function MultipleAxes() {
2828
{
2929
getValue: (datum) => datum.secondary,
3030
elementType: "bar",
31+
// stacked: true,
3132
},
3233
{
3334
id: "2",

examples/simple/src/components/StressTest.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function StressTest() {
1313
liveData,
1414
liveDataInterval,
1515
showPoints,
16-
dynamicStyles,
16+
memoizeSeries,
1717
},
1818
setState,
1919
] = React.useState({
@@ -24,7 +24,7 @@ export default function StressTest() {
2424
liveData: false,
2525
liveDataInterval: 1000,
2626
showPoints: true,
27-
dynamicStyles: true,
27+
memoizeSeries: false,
2828
});
2929

3030
const { data, randomizeData } = useDemoConfig({
@@ -141,19 +141,19 @@ export default function StressTest() {
141141
checked={showPoints}
142142
onChange={(e) => {
143143
e.persist();
144-
setState((old) => ({ ...old, showPoints: e.target.checked }));
144+
setState((old) => ({ ...old, showPoints: !!e.target.checked }));
145145
}}
146146
/>
147147
</label>
148148
<br />
149149
<label>
150-
Dynamic Styles:{" "}
150+
Memoize Series:{" "}
151151
<input
152152
type="checkbox"
153-
checked={dynamicStyles}
153+
checked={memoizeSeries}
154154
onChange={(e) => {
155155
e.persist();
156-
setState((old) => ({ ...old, dynamicStyles: e.target.checked }));
156+
setState((old) => ({ ...old, memoizeSeries: !!e.target.checked }));
157157
}}
158158
/>
159159
</label>
@@ -165,7 +165,7 @@ export default function StressTest() {
165165
checked={liveData}
166166
onChange={(e) => {
167167
e.persist();
168-
setState((old) => ({ ...old, liveData: e.target.checked }));
168+
setState((old) => ({ ...old, liveData: !!e.target.checked }));
169169
}}
170170
/>
171171
</label>
@@ -202,16 +202,15 @@ export default function StressTest() {
202202
data,
203203
primaryAxis,
204204
secondaryAxes,
205-
getSeriesStyle: dynamicStyles
206-
? (series) => ({
207-
opacity:
208-
activeSeriesIndex > -1
209-
? series.index === activeSeriesIndex
210-
? 1
211-
: 0.1
212-
: 1,
213-
})
214-
: undefined,
205+
memoizeSeries,
206+
getSeriesStyle: (series) => ({
207+
opacity:
208+
activeSeriesIndex > -1
209+
? series.index === activeSeriesIndex
210+
? 1
211+
: 0.1
212+
: 1,
213+
}),
215214
primaryCursor: {
216215
value: primaryCursorValue,
217216
onChange: (value) => {

examples/simple/src/components/SyncedCursors.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default function SyncedCursors() {
4747
2
4848
)}
4949
</pre>
50-
<ResizableBox height={200}>
50+
<ResizableBox height={100} width={200}>
5151
<Chart
5252
options={{
5353
data,
@@ -68,7 +68,8 @@ export default function SyncedCursors() {
6868
}}
6969
/>
7070
</ResizableBox>
71-
<ResizableBox height={200}>
71+
<div style={{ height: "1rem" }} />
72+
<ResizableBox height={160} width={300}>
7273
<Chart
7374
options={{
7475
data,
@@ -89,7 +90,8 @@ export default function SyncedCursors() {
8990
}}
9091
/>
9192
</ResizableBox>
92-
<ResizableBox height={200}>
93+
<div style={{ height: "1rem" }} />
94+
<ResizableBox height={300} width={500}>
9395
<Chart
9496
options={{
9597
data,

src/components/AxisLinear.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22

33
import { Axis, AxisLinear } from '../types'
4-
import { getTickPx, translate } from '../utils/Utils'
4+
import { translate } from '../utils/Utils'
55
import useChartContext from '../utils/chartContext'
66
//
77
import useMeasure from './AxisLinear.useMeasure'
@@ -229,3 +229,15 @@ export default function AxisLinearComp<TDatum>(axis: Axis<TDatum>) {
229229
</g>
230230
) : null
231231
}
232+
233+
function getTickPx<TDatum>(scale: Axis<TDatum>['scale'], value: any) {
234+
let px = scale(value) ?? NaN
235+
236+
// @ts-ignore
237+
if (scale.bandwidth) {
238+
// @ts-ignore
239+
return px + scale.bandwidth() / 2
240+
}
241+
242+
return px
243+
}

src/components/Chart.tsx

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import React, { ComponentPropsWithoutRef } from 'react'
33

44
import useGetLatest from '../hooks/useGetLatest'
55
import useIsomorphicLayoutEffect from '../hooks/useIsomorphicLayoutEffect'
6-
import Area from '../seriesTypes/Area'
7-
import Bar from '../seriesTypes/Bar'
6+
import Bar, { getPrimary } from '../seriesTypes/Bar'
87
import Line from '../seriesTypes/Line'
98
//
109
import {
10+
Axis,
1111
AxisDimensions,
1212
AxisOptions,
1313
AxisOptionsWithScaleType,
@@ -24,7 +24,6 @@ import {
2424
materializeStyles,
2525
getSeriesStatus,
2626
getDatumStatus,
27-
sortDatumsBySecondaryPx,
2827
} from '../utils/Utils'
2928
import buildAxisLinear from '../utils/buildAxis.linear'
3029
import { ChartContextProvider } from '../utils/chartContext'
@@ -234,55 +233,45 @@ function ChartInner<TDatum>({
234233

235234
const primaryAxisOptions = React.useMemo((): BuildAxisOptions<TDatum> => {
236235
const firstValue = getFirstDefinedValue(options.primaryAxis, options.data)
237-
const optionsWithScaleType = axisOptionsWithScaleType(
236+
const axisOptions = axisOptionsWithScaleType(
238237
options.primaryAxis,
239238
firstValue
240239
)
241240

242-
return { position: 'bottom', ...optionsWithScaleType }
241+
return { position: 'bottom', ...axisOptions }
243242
}, [options.data, options.primaryAxis])
244243

245244
const secondaryAxesOptions = React.useMemo(() => {
246245
return options.secondaryAxes.map(
247246
(secondaryAxis, i): BuildAxisOptions<TDatum> => {
248247
const firstValue = getFirstDefinedValue(secondaryAxis, options.data)
249248

250-
const optionsWithScaleType = axisOptionsWithScaleType(
251-
secondaryAxis,
252-
firstValue
253-
)
249+
const axisOptions = axisOptionsWithScaleType(secondaryAxis, firstValue)
254250

255-
if (!optionsWithScaleType.elementType) {
251+
if (!axisOptions.elementType) {
256252
if (primaryAxisOptions.scaleType === 'band') {
257-
optionsWithScaleType.elementType = 'bar'
258-
} else if (optionsWithScaleType.stacked) {
259-
optionsWithScaleType.elementType = 'area'
253+
axisOptions.elementType = 'bar'
254+
} else if (axisOptions.stacked) {
255+
axisOptions.elementType = 'area'
260256
}
261257
}
262258

263259
if (
264-
typeof optionsWithScaleType.stacked === 'undefined' &&
265-
optionsWithScaleType.elementType &&
266-
['area'].includes(optionsWithScaleType.elementType)
260+
typeof axisOptions.stacked === 'undefined' &&
261+
axisOptions.elementType &&
262+
['area'].includes(axisOptions.elementType)
267263
) {
268-
optionsWithScaleType.stacked = true
264+
axisOptions.stacked = true
269265
}
270266

271267
return {
272268
position: !i ? 'left' : 'right',
273-
...optionsWithScaleType,
269+
...axisOptions,
274270
}
275271
}
276272
)
277273
}, [options.data, options.secondaryAxes, primaryAxisOptions])
278274

279-
if (
280-
primaryAxisOptions.scaleType === 'band' &&
281-
!secondaryAxesOptions.some(axisOptions => axisOptions.stacked)
282-
) {
283-
primaryAxisOptions.stacked = primaryAxisOptions.stacked ?? true
284-
}
285-
286275
// Resolve Tooltip Option
287276
const tooltipOptions = React.useMemo(() => {
288277
const tooltipOptions = defaultTooltip(options?.tooltip)
@@ -467,12 +456,27 @@ function ChartInner<TDatum>({
467456
const datumsByInteractionGroup = new Map<any, Datum<TDatum>[]>()
468457
const datumsByTooltipGroup = new Map<any, Datum<TDatum>[]>()
469458

470-
let getInteractionKey = (datum: Datum<TDatum>) => `${datum.primaryValue}`
459+
let getInteractionPrimary = (datum: Datum<TDatum>) => {
460+
if (secondaryAxes.every(d => d.elementType === 'bar' && !d.stacked)) {
461+
const secondaryAxis = secondaryAxes.find(
462+
d => d.id === datum.secondaryAxisId
463+
)!
464+
465+
if (secondaryAxis.elementType === 'bar' && !secondaryAxis.stacked) {
466+
return getPrimary(datum, primaryAxis, secondaryAxis)
467+
}
468+
}
469+
470+
return datum.primaryValue
471+
}
472+
473+
let getInteractionKey = (datum: Datum<TDatum>) =>
474+
`${getInteractionPrimary(datum)}`
471475
let getTooltipKey = (datum: Datum<TDatum>) => `${datum.primaryValue}`
472476

473477
if (options.interactionMode === 'closest') {
474478
getInteractionKey = datum =>
475-
`${datum.primaryValue}_${datum.secondaryValue}`
479+
`${getInteractionPrimary(datum)}_${datum.secondaryValue}`
476480
}
477481

478482
if (tooltipOptions.groupingMode === 'single') {
@@ -610,8 +614,6 @@ function ChartInner<TDatum>({
610614
[orderedSeries, secondaryAxes]
611615
)
612616

613-
let memoizeSeries = !options.getDatumStyle && !options.getSeriesStyle
614-
615617
// eslint-disable-next-line react-hooks/exhaustive-deps
616618
let getSeriesInfo = () => ({
617619
primaryAxis,
@@ -628,7 +630,7 @@ function ChartInner<TDatum>({
628630
[primaryAxis, secondaryAxes, seriesByAxisId]
629631
)
630632

631-
if (memoizeSeries) {
633+
if (options.memoizeSeries) {
632634
getSeriesInfo = getMemoizedSeriesInfo
633635
}
634636

@@ -643,15 +645,16 @@ function ChartInner<TDatum>({
643645

644646
const { elementType } = secondaryAxis
645647
const Component = (() => {
646-
if (elementType === 'line') {
648+
if (
649+
elementType === 'line' ||
650+
elementType === 'bubble' ||
651+
elementType === 'area'
652+
) {
647653
return Line
648654
}
649655
if (elementType === 'bar') {
650656
return Bar
651657
}
652-
if (elementType === 'area') {
653-
return Area
654-
}
655658
throw new Error('Invalid elementType')
656659
})()
657660

@@ -747,3 +750,21 @@ function axisOptionsWithScaleType<TDatum>(
747750

748751
return { ...options, scaleType } as AxisOptionsWithScaleType<TDatum>
749752
}
753+
754+
function sortDatumsBySecondaryPx<TDatum>(
755+
datums: Datum<TDatum>[],
756+
secondaryAxes: Axis<TDatum>[]
757+
) {
758+
return [...datums].sort((a, b) => {
759+
const aAxis = secondaryAxes.find(d => d.id === a.secondaryAxisId)
760+
const bAxis = secondaryAxes.find(d => d.id === b.secondaryAxisId)
761+
762+
const aPx =
763+
aAxis?.scale(aAxis.stacked ? a.stackData?.[1] : a.secondaryValue) ?? NaN
764+
765+
const bPx =
766+
bAxis?.scale(bAxis.stacked ? b.stackData?.[1] : b.secondaryValue) ?? NaN
767+
768+
return aPx - bPx
769+
})
770+
}

0 commit comments

Comments
 (0)