Add AreaWithTopStroke helper

This commit is contained in:
Marshall Polaris 2022-09-26 16:32:47 -07:00
parent 575740963c
commit dc14edad26
2 changed files with 44 additions and 34 deletions

View File

@ -15,7 +15,7 @@ import {
import { range } from 'lodash' import { range } from 'lodash'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { SVGChart, LinePath, AreaPath } from './helpers' import { SVGChart, AreaPath, AreaWithTopStroke } from './helpers'
import { formatLargeNumber } from 'common/util/format' import { formatLargeNumber } from 'common/util/format'
import { useEvent } from 'web/hooks/use-event' import { useEvent } from 'web/hooks/use-event'
@ -90,21 +90,13 @@ export const SingleValueDistributionChart = (props: {
className="pointer-events-none absolute z-10 whitespace-pre rounded border-2 border-black bg-slate-600/75 p-2 text-white" className="pointer-events-none absolute z-10 whitespace-pre rounded border-2 border-black bg-slate-600/75 p-2 text-white"
/> />
<SVGChart w={w} h={h} xAxis={xAxis} yAxis={yAxis}> <SVGChart w={w} h={h} xAxis={xAxis} yAxis={yAxis}>
<LinePath <AreaWithTopStroke
color={color}
data={data} data={data}
curve={curveLinear}
px={px}
py={py1}
stroke={color}
/>
<AreaPath
data={data}
curve={curveLinear}
px={px} px={px}
py0={py0} py0={py0}
py1={py1} py1={py1}
fill={color} curve={curveLinear}
opacity={0.3}
/> />
</SVGChart> </SVGChart>
</div> </div>
@ -158,13 +150,6 @@ export const MultiValueHistoryChart = (props: {
<SVGChart w={w} h={h} xAxis={xAxis} yAxis={yAxis}> <SVGChart w={w} h={h} xAxis={xAxis} yAxis={yAxis}>
{d3Stack(data).map((s, i) => ( {d3Stack(data).map((s, i) => (
<g key={s.key}> <g key={s.key}>
<LinePath
data={s}
px={px}
py={py1}
curve={curveStepAfter}
stroke={colors[i]}
/>
<AreaPath <AreaPath
data={s} data={s}
px={px} px={px}
@ -246,21 +231,13 @@ export const SingleValueHistoryChart = (props: {
onMouseOver={onMouseOver} onMouseOver={onMouseOver}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
> >
<LinePath <AreaWithTopStroke
data={data} color={color}
px={px}
py={py1}
curve={curveStepAfter}
stroke={color}
/>
<AreaPath
data={data} data={data}
px={px} px={px}
py0={py0} py0={py0}
py1={py1} py1={py1}
curve={curveStepAfter} curve={curveStepAfter}
fill={color}
opacity={0.3}
/> />
</SVGChart> </SVGChart>
</div> </div>

View File

@ -1,5 +1,13 @@
import { ReactNode, SVGProps, memo, useRef, useEffect } from 'react' import { ReactNode, SVGProps, memo, useRef, useEffect } from 'react'
import { Axis, AxisDomain, CurveFactory, area, line, select } from 'd3' import {
Axis,
AxisDomain,
CurveFactory,
area,
curveStepAfter,
line,
select,
} from 'd3'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
@ -50,11 +58,11 @@ const LinePathInternal = <P,>(
data: P[] data: P[]
px: number | ((p: P) => number) px: number | ((p: P) => number)
py: number | ((p: P) => number) py: number | ((p: P) => number)
curve: CurveFactory curve?: CurveFactory
} & SVGProps<SVGPathElement> } & SVGProps<SVGPathElement>
) => { ) => {
const { data, px, py, curve, ...rest } = props const { data, px, py, curve, ...rest } = props
const d3Line = line<P>(px, py).curve(curve) const d3Line = line<P>(px, py).curve(curve ?? curveStepAfter)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return <path {...rest} fill="none" d={d3Line(data)!} /> return <path {...rest} fill="none" d={d3Line(data)!} />
} }
@ -66,16 +74,41 @@ const AreaPathInternal = <P,>(
px: number | ((p: P) => number) px: number | ((p: P) => number)
py0: number | ((p: P) => number) py0: number | ((p: P) => number)
py1: number | ((p: P) => number) py1: number | ((p: P) => number)
curve: CurveFactory curve?: CurveFactory
} & SVGProps<SVGPathElement> } & SVGProps<SVGPathElement>
) => { ) => {
const { data, px, py0, py1, curve, ...rest } = props const { data, px, py0, py1, curve, ...rest } = props
const d3Area = area<P>(px, py0, py1).curve(curve) const d3Area = area<P>(px, py0, py1).curve(curve ?? curveStepAfter)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return <path {...rest} d={d3Area(data)!} /> return <path {...rest} d={d3Area(data)!} />
} }
export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal
export const AreaWithTopStroke = <P,>(props: {
color: string
data: P[]
px: number | ((p: P) => number)
py0: number | ((p: P) => number)
py1: number | ((p: P) => number)
curve?: CurveFactory
}) => {
const { color, data, px, py0, py1, curve } = props
return (
<g>
<AreaPath
data={data}
px={px}
py0={py0}
py1={py1}
curve={curve}
fill={color}
opacity={0.3}
/>
<LinePath data={data} px={px} py={py1} curve={curve} stroke={color} />
</g>
)
}
export const SVGChart = <X extends AxisDomain, Y extends AxisDomain>(props: { export const SVGChart = <X extends AxisDomain, Y extends AxisDomain>(props: {
children: ReactNode children: ReactNode
w: number w: number