Implement color as function of point on SingleValueHistoryChart
This commit is contained in:
parent
9bddae17bb
commit
63cc319b3a
|
@ -20,10 +20,12 @@ import {
|
||||||
AreaWithTopStroke,
|
AreaWithTopStroke,
|
||||||
Point,
|
Point,
|
||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
|
computeColorStops,
|
||||||
formatPct,
|
formatPct,
|
||||||
} from './helpers'
|
} from './helpers'
|
||||||
import { useEvent } from 'web/hooks/use-event'
|
import { useEvent } from 'web/hooks/use-event'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
|
||||||
export type MultiPoint<T = unknown> = Point<Date, number[], T>
|
export type MultiPoint<T = unknown> = Point<Date, number[], T>
|
||||||
export type HistoryPoint<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 series = useMemo(() => {
|
||||||
const d3Stack = stack<P, number>()
|
const d3Stack = stack<P, number>()
|
||||||
.keys(range(0, Math.max(...data.map(({ y }) => y.length))))
|
.keys(range(0, Math.max(...data.map(({ y }) => y.length))))
|
||||||
.value(({ y }, o) => y[o])
|
.value(({ y }, k) => y[k])
|
||||||
.order(stackOrderReverse)
|
.order(stackOrderReverse)
|
||||||
return d3Stack(data)
|
return d3Stack(data)
|
||||||
}, [data])
|
}, [data])
|
||||||
|
@ -214,7 +216,7 @@ export const SingleValueHistoryChart = <P extends HistoryPoint>(props: {
|
||||||
data: P[]
|
data: P[]
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
color: string
|
color: string | ((p: P) => string)
|
||||||
margin: Margin
|
margin: Margin
|
||||||
xScale: ScaleTime<number, number>
|
xScale: ScaleTime<number, number>
|
||||||
yScale: ScaleContinuousNumeric<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 (
|
return (
|
||||||
<SVGChart
|
<SVGChart
|
||||||
w={w}
|
w={w}
|
||||||
|
@ -280,8 +289,17 @@ export const SingleValueHistoryChart = <P extends HistoryPoint>(props: {
|
||||||
onMouseOver={onMouseOver}
|
onMouseOver={onMouseOver}
|
||||||
Tooltip={Tooltip}
|
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
|
<AreaWithTopStroke
|
||||||
color={color}
|
color={typeof color === 'string' ? color : `url(#${gradientId})`}
|
||||||
data={data}
|
data={data}
|
||||||
px={px}
|
px={px}
|
||||||
py0={py0}
|
py0={py0}
|
||||||
|
|
|
@ -99,14 +99,14 @@ const AreaPathInternal = <P,>(
|
||||||
export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal
|
export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal
|
||||||
|
|
||||||
export const AreaWithTopStroke = <P,>(props: {
|
export const AreaWithTopStroke = <P,>(props: {
|
||||||
color: string
|
|
||||||
data: P[]
|
data: P[]
|
||||||
|
color: string
|
||||||
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
|
||||||
}) => {
|
}) => {
|
||||||
const { color, data, px, py0, py1, curve } = props
|
const { data, color, px, py0, py1, curve } = props
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<AreaPath
|
<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) => {
|
export const getDateRange = (contract: Contract) => {
|
||||||
const { createdTime, closeTime, resolutionTime } = contract
|
const { createdTime, closeTime, resolutionTime } = contract
|
||||||
const isClosed = !!closeTime && Date.now() > closeTime
|
const isClosed = !!closeTime && Date.now() > closeTime
|
||||||
|
|
Loading…
Reference in New Issue
Block a user