Skip to content

Commit

Permalink
fix: better elementType impls, grouped bar voronoi, general voronoi
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Aug 2, 2021
1 parent 6f103c8 commit a2471ea
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 549 deletions.
1 change: 1 addition & 0 deletions examples/simple/src/components/Bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default function Bar() {
data,
primaryAxis,
secondaryAxes,
showVoronoi: true,
}}
/>
</ResizableBox>
Expand Down
2 changes: 1 addition & 1 deletion examples/simple/src/components/Bubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default function Bubble() {
() => [
{
getValue: (datum) => datum.secondary,
elementType: "bubble",
},
],
[]
Expand All @@ -42,7 +43,6 @@ export default function Bubble() {
primaryAxis,
secondaryAxes,
interactionMode: "closest",
getSeriesStyle: () => ({ line: { opacity: 0 } }),
getDatumStyle: (datum) =>
({
circle: { r: datum.originalDatum.radius },
Expand Down
1 change: 0 additions & 1 deletion examples/simple/src/components/DynamicContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import ResizableBox from "../ResizableBox";
import useDemoConfig from "../useDemoConfig";
import React from "react";
import { AxisOptions, Chart } from "react-charts";
Expand Down
3 changes: 2 additions & 1 deletion examples/simple/src/components/MultipleAxes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default function MultipleAxes() {
});

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

const primaryAxis = React.useMemo<
AxisOptions<typeof data[number]["data"][number]>
Expand All @@ -28,6 +28,7 @@ export default function MultipleAxes() {
{
getValue: (datum) => datum.secondary,
elementType: "bar",
// stacked: true,
},
{
id: "2",
Expand Down
33 changes: 16 additions & 17 deletions examples/simple/src/components/StressTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function StressTest() {
liveData,
liveDataInterval,
showPoints,
dynamicStyles,
memoizeSeries,
},
setState,
] = React.useState({
Expand All @@ -24,7 +24,7 @@ export default function StressTest() {
liveData: false,
liveDataInterval: 1000,
showPoints: true,
dynamicStyles: true,
memoizeSeries: false,
});

const { data, randomizeData } = useDemoConfig({
Expand Down Expand Up @@ -141,19 +141,19 @@ export default function StressTest() {
checked={showPoints}
onChange={(e) => {
e.persist();
setState((old) => ({ ...old, showPoints: e.target.checked }));
setState((old) => ({ ...old, showPoints: !!e.target.checked }));
}}
/>
</label>
<br />
<label>
Dynamic Styles:{" "}
Memoize Series:{" "}
<input
type="checkbox"
checked={dynamicStyles}
checked={memoizeSeries}
onChange={(e) => {
e.persist();
setState((old) => ({ ...old, dynamicStyles: e.target.checked }));
setState((old) => ({ ...old, memoizeSeries: !!e.target.checked }));
}}
/>
</label>
Expand All @@ -165,7 +165,7 @@ export default function StressTest() {
checked={liveData}
onChange={(e) => {
e.persist();
setState((old) => ({ ...old, liveData: e.target.checked }));
setState((old) => ({ ...old, liveData: !!e.target.checked }));
}}
/>
</label>
Expand Down Expand Up @@ -202,16 +202,15 @@ export default function StressTest() {
data,
primaryAxis,
secondaryAxes,
getSeriesStyle: dynamicStyles
? (series) => ({
opacity:
activeSeriesIndex > -1
? series.index === activeSeriesIndex
? 1
: 0.1
: 1,
})
: undefined,
memoizeSeries,
getSeriesStyle: (series) => ({
opacity:
activeSeriesIndex > -1
? series.index === activeSeriesIndex
? 1
: 0.1
: 1,
}),
primaryCursor: {
value: primaryCursorValue,
onChange: (value) => {
Expand Down
8 changes: 5 additions & 3 deletions examples/simple/src/components/SyncedCursors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function SyncedCursors() {
2
)}
</pre>
<ResizableBox height={200}>
<ResizableBox height={100} width={200}>
<Chart
options={{
data,
Expand All @@ -68,7 +68,8 @@ export default function SyncedCursors() {
}}
/>
</ResizableBox>
<ResizableBox height={200}>
<div style={{ height: "1rem" }} />
<ResizableBox height={160} width={300}>
<Chart
options={{
data,
Expand All @@ -89,7 +90,8 @@ export default function SyncedCursors() {
}}
/>
</ResizableBox>
<ResizableBox height={200}>
<div style={{ height: "1rem" }} />
<ResizableBox height={300} width={500}>
<Chart
options={{
data,
Expand Down
14 changes: 13 additions & 1 deletion src/components/AxisLinear.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'

import { Axis, AxisLinear } from '../types'
import { getTickPx, translate } from '../utils/Utils'
import { translate } from '../utils/Utils'
import useChartContext from '../utils/chartContext'
//
import useMeasure from './AxisLinear.useMeasure'
Expand Down Expand Up @@ -229,3 +229,15 @@ export default function AxisLinearComp<TDatum>(axis: Axis<TDatum>) {
</g>
) : null
}

function getTickPx<TDatum>(scale: Axis<TDatum>['scale'], value: any) {
let px = scale(value) ?? NaN

// @ts-ignore
if (scale.bandwidth) {
// @ts-ignore
return px + scale.bandwidth() / 2
}

return px
}
89 changes: 55 additions & 34 deletions src/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import React, { ComponentPropsWithoutRef } from 'react'

import useGetLatest from '../hooks/useGetLatest'
import useIsomorphicLayoutEffect from '../hooks/useIsomorphicLayoutEffect'
import Area from '../seriesTypes/Area'
import Bar from '../seriesTypes/Bar'
import Bar, { getPrimary } from '../seriesTypes/Bar'
import Line from '../seriesTypes/Line'
//
import {
Axis,
AxisDimensions,
AxisOptions,
AxisOptionsWithScaleType,
Expand All @@ -24,7 +24,6 @@ import {
materializeStyles,
getSeriesStatus,
getDatumStatus,
sortDatumsBySecondaryPx,
} from '../utils/Utils'
import buildAxisLinear from '../utils/buildAxis.linear'
import { ChartContextProvider } from '../utils/chartContext'
Expand Down Expand Up @@ -234,55 +233,45 @@ function ChartInner<TDatum>({

const primaryAxisOptions = React.useMemo((): BuildAxisOptions<TDatum> => {
const firstValue = getFirstDefinedValue(options.primaryAxis, options.data)
const optionsWithScaleType = axisOptionsWithScaleType(
const axisOptions = axisOptionsWithScaleType(
options.primaryAxis,
firstValue
)

return { position: 'bottom', ...optionsWithScaleType }
return { position: 'bottom', ...axisOptions }
}, [options.data, options.primaryAxis])

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

const optionsWithScaleType = axisOptionsWithScaleType(
secondaryAxis,
firstValue
)
const axisOptions = axisOptionsWithScaleType(secondaryAxis, firstValue)

if (!optionsWithScaleType.elementType) {
if (!axisOptions.elementType) {
if (primaryAxisOptions.scaleType === 'band') {
optionsWithScaleType.elementType = 'bar'
} else if (optionsWithScaleType.stacked) {
optionsWithScaleType.elementType = 'area'
axisOptions.elementType = 'bar'
} else if (axisOptions.stacked) {
axisOptions.elementType = 'area'
}
}

if (
typeof optionsWithScaleType.stacked === 'undefined' &&
optionsWithScaleType.elementType &&
['area'].includes(optionsWithScaleType.elementType)
typeof axisOptions.stacked === 'undefined' &&
axisOptions.elementType &&
['area'].includes(axisOptions.elementType)
) {
optionsWithScaleType.stacked = true
axisOptions.stacked = true
}

return {
position: !i ? 'left' : 'right',
...optionsWithScaleType,
...axisOptions,
}
}
)
}, [options.data, options.secondaryAxes, primaryAxisOptions])

if (
primaryAxisOptions.scaleType === 'band' &&
!secondaryAxesOptions.some(axisOptions => axisOptions.stacked)
) {
primaryAxisOptions.stacked = primaryAxisOptions.stacked ?? true
}

// Resolve Tooltip Option
const tooltipOptions = React.useMemo(() => {
const tooltipOptions = defaultTooltip(options?.tooltip)
Expand Down Expand Up @@ -467,12 +456,27 @@ function ChartInner<TDatum>({
const datumsByInteractionGroup = new Map<any, Datum<TDatum>[]>()
const datumsByTooltipGroup = new Map<any, Datum<TDatum>[]>()

let getInteractionKey = (datum: Datum<TDatum>) => `${datum.primaryValue}`
let getInteractionPrimary = (datum: Datum<TDatum>) => {
if (secondaryAxes.every(d => d.elementType === 'bar' && !d.stacked)) {
const secondaryAxis = secondaryAxes.find(
d => d.id === datum.secondaryAxisId
)!

if (secondaryAxis.elementType === 'bar' && !secondaryAxis.stacked) {
return getPrimary(datum, primaryAxis, secondaryAxis)
}
}

return datum.primaryValue
}

let getInteractionKey = (datum: Datum<TDatum>) =>
`${getInteractionPrimary(datum)}`
let getTooltipKey = (datum: Datum<TDatum>) => `${datum.primaryValue}`

if (options.interactionMode === 'closest') {
getInteractionKey = datum =>
`${datum.primaryValue}_${datum.secondaryValue}`
`${getInteractionPrimary(datum)}_${datum.secondaryValue}`
}

if (tooltipOptions.groupingMode === 'single') {
Expand Down Expand Up @@ -610,8 +614,6 @@ function ChartInner<TDatum>({
[orderedSeries, secondaryAxes]
)

let memoizeSeries = !options.getDatumStyle && !options.getSeriesStyle

// eslint-disable-next-line react-hooks/exhaustive-deps
let getSeriesInfo = () => ({
primaryAxis,
Expand All @@ -628,7 +630,7 @@ function ChartInner<TDatum>({
[primaryAxis, secondaryAxes, seriesByAxisId]
)

if (memoizeSeries) {
if (options.memoizeSeries) {
getSeriesInfo = getMemoizedSeriesInfo
}

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

const { elementType } = secondaryAxis
const Component = (() => {
if (elementType === 'line') {
if (
elementType === 'line' ||
elementType === 'bubble' ||
elementType === 'area'
) {
return Line
}
if (elementType === 'bar') {
return Bar
}
if (elementType === 'area') {
return Area
}
throw new Error('Invalid elementType')
})()

Expand Down Expand Up @@ -747,3 +750,21 @@ function axisOptionsWithScaleType<TDatum>(

return { ...options, scaleType } as AxisOptionsWithScaleType<TDatum>
}

function sortDatumsBySecondaryPx<TDatum>(
datums: Datum<TDatum>[],
secondaryAxes: Axis<TDatum>[]
) {
return [...datums].sort((a, b) => {
const aAxis = secondaryAxes.find(d => d.id === a.secondaryAxisId)
const bAxis = secondaryAxes.find(d => d.id === b.secondaryAxisId)

const aPx =
aAxis?.scale(aAxis.stacked ? a.stackData?.[1] : a.secondaryValue) ?? NaN

const bPx =
bAxis?.scale(bAxis.stacked ? b.stackData?.[1] : b.secondaryValue) ?? NaN

return aPx - bPx
})
}
Loading

1 comment on commit a2471ea

@vercel
Copy link

@vercel vercel bot commented on a2471ea Aug 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.