Implement color as function of point on SingleValueHistoryChart

This commit is contained in:
Marshall Polaris 2022-10-02 23:57:31 -07:00
parent 9bddae17bb
commit 63cc319b3a
2 changed files with 50 additions and 5 deletions

View File

@ -20,10 +20,12 @@ import {
AreaWithTopStroke,
Point,
TooltipComponent,
computeColorStops,
formatPct,
} from './helpers'
import { useEvent } from 'web/hooks/use-event'
import { formatMoney } from 'common/util/format'
import { nanoid } from 'nanoid'
export type MultiPoint<T = unknown> = Point<Date, number[], T>
export type HistoryPoint<T = unknown> = Point<Date, number, T>
@ -161,7 +163,7 @@ export const MultiValueHistoryChart = <P extends MultiPoint>(props: {
const series = useMemo(() => {
const d3Stack = stack<P, number>()
.keys(range(0, Math.max(...data.map(({ y }) => y.length))))
.value(({ y }, o) => y[o])
.value(({ y }, k) => y[k])
.order(stackOrderReverse)
return d3Stack(data)
}, [data])
@ -214,7 +216,7 @@ export const SingleValueHistoryChart = <P extends HistoryPoint>(props: {
data: P[]
w: number
h: number
color: string
color: string | ((p: P) => string)
margin: Margin
xScale: ScaleTime<number, number>
yScale: ScaleContinuousNumeric<number, number>
@ -269,6 +271,13 @@ export const SingleValueHistoryChart = <P extends HistoryPoint>(props: {
}
})
const gradientId = useMemo(() => nanoid(), [])
const stops = useMemo(
() =>
typeof color !== 'string' ? computeColorStops(data, color, px) : null,
[color, data, px]
)
return (
<SVGChart
w={w}
@ -280,8 +289,17 @@ export const SingleValueHistoryChart = <P extends HistoryPoint>(props: {
onMouseOver={onMouseOver}
Tooltip={Tooltip}
>
{stops && (
<defs>
<linearGradient gradientUnits="userSpaceOnUse" id={gradientId}>
{stops.map((s, i) => (
<stop key={i} offset={`${s.x / w}`} stopColor={s.color} />
))}
</linearGradient>
</defs>
)}
<AreaWithTopStroke
color={color}
color={typeof color === 'string' ? color : `url(#${gradientId})`}
data={data}
px={px}
py0={py0}

View File

@ -99,14 +99,14 @@ const AreaPathInternal = <P,>(
export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal
export const AreaWithTopStroke = <P,>(props: {
color: string
data: P[]
color: string
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
const { data, color, px, py0, py1, curve } = props
return (
<g>
<AreaPath
@ -308,6 +308,33 @@ export const TooltipContainer = (props: {
)
}
export const computeColorStops = <P,>(
data: P[],
pc: (p: P) => string,
px: (p: P) => number
) => {
const segments: { x: number; color: string }[] = []
let currOffset = 0
let currColor = pc(data[0])
for (const p of data) {
const c = pc(p)
if (c !== currColor) {
segments.push({ x: currOffset, color: currColor })
currOffset = px(p)
currColor = c
}
}
segments.push({ x: currOffset, color: currColor })
const stops: { x: number; color: string }[] = []
stops.push({ x: segments[0].x, color: segments[0].color })
for (const s of segments.slice(1)) {
stops.push({ x: s.x, color: stops[stops.length - 1].color })
stops.push({ x: s.x, color: s.color })
}
return stops
}
export const getDateRange = (contract: Contract) => {
const { createdTime, closeTime, resolutionTime } = contract
const isClosed = !!closeTime && Date.now() > closeTime