import { ReactNode, SVGProps, memo, useRef, useEffect } from 'react' import { Axis, CurveFactory, area, curveStepAfter, line, select } from 'd3' import dayjs from 'dayjs' import { Contract } from 'common/contract' export const MARGIN = { top: 20, right: 10, bottom: 20, left: 40 } export const MARGIN_X = MARGIN.right + MARGIN.left export const MARGIN_Y = MARGIN.top + MARGIN.bottom export const XAxis = (props: { w: number; h: number; axis: Axis }) => { const { h, axis } = props const axisRef = useRef(null) useEffect(() => { if (axisRef.current != null) { select(axisRef.current) .call(axis) .call((g) => g.select('.domain').remove()) } }, [h, axis]) return } export const YAxis = (props: { w: number; h: number; axis: Axis }) => { const { w, h, axis } = props const axisRef = useRef(null) useEffect(() => { if (axisRef.current != null) { select(axisRef.current) .call(axis) .call((g) => g.select('.domain').remove()) .call((g) => g.selectAll('.tick line').attr('x2', w).attr('stroke-opacity', 0.1) ) } }, [w, h, axis]) return } const LinePathInternal = ( props: { data: P[] px: number | ((p: P) => number) py: number | ((p: P) => number) curve?: CurveFactory } & SVGProps ) => { const { data, px, py, curve, ...rest } = props const d3Line = line

(px, py).curve(curve ?? curveStepAfter) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return } export const LinePath = memo(LinePathInternal) as typeof LinePathInternal const AreaPathInternal = ( props: { data: P[] px: number | ((p: P) => number) py0: number | ((p: P) => number) py1: number | ((p: P) => number) curve?: CurveFactory } & SVGProps ) => { const { data, px, py0, py1, curve, ...rest } = props const d3Area = area

(px, py0, py1).curve(curve ?? curveStepAfter) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return } export const AreaPath = memo(AreaPathInternal) as typeof AreaPathInternal export const AreaWithTopStroke = (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 ( ) } export const SVGChart = (props: { children: ReactNode w: number h: number xAxis: Axis yAxis: Axis onMouseOver?: (ev: React.PointerEvent) => void onMouseLeave?: (ev: React.PointerEvent) => void pct?: boolean }) => { const { children, w, h, xAxis, yAxis, onMouseOver, onMouseLeave } = props const innerW = w - MARGIN_X const innerH = h - MARGIN_Y return ( {children} ) } export type TooltipPosition = { top: number; left: number } export const ChartTooltip = ( props: TooltipPosition & { children: React.ReactNode } ) => { const { top, left, children } = props return (

{children}
) } export const getDateRange = (contract: Contract) => { const { createdTime, closeTime, resolutionTime } = contract const now = Date.now() const isClosed = !!closeTime && now > closeTime const endDate = resolutionTime ?? (isClosed ? closeTime : now) // the graph should be minimum an hour wide const adjustedEndDate = dayjs(createdTime).add(1, 'hour').isAfter(endDate) ? dayjs(endDate).add(1, 'hours') : dayjs(endDate) return [new Date(createdTime), adjustedEndDate.toDate()] as const }