New tooltip for binary market chart, with date and time (#529)
* New tooltip for binary market chart * Tooltip on one line and rewrote date formatter * Interpolate graph points and updated date format * Reduced point count to 300 desktop, 50 mobile
This commit is contained in:
parent
0df4ca8f1e
commit
7a09365f00
|
@ -76,6 +76,7 @@ export const AnswersGraph = memo(function AnswersGraph(props: {
|
||||||
? new Date(contract.createdTime)
|
? new Date(contract.createdTime)
|
||||||
: hoursAgo.toDate()
|
: hoursAgo.toDate()
|
||||||
|
|
||||||
|
const multiYear = !dayjs(startDate).isSame(latestTime, 'year')
|
||||||
const lessThanAWeek = dayjs(startDate).add(1, 'week').isAfter(latestTime)
|
const lessThanAWeek = dayjs(startDate).add(1, 'week').isAfter(latestTime)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -97,10 +98,12 @@ export const AnswersGraph = memo(function AnswersGraph(props: {
|
||||||
min: startDate,
|
min: startDate,
|
||||||
max: latestTime.toDate(),
|
max: latestTime.toDate(),
|
||||||
}}
|
}}
|
||||||
xFormat={(d) => formatTime(+d.valueOf(), lessThanAWeek)}
|
xFormat={(d) =>
|
||||||
|
formatTime(+d.valueOf(), multiYear, lessThanAWeek, lessThanAWeek)
|
||||||
|
}
|
||||||
axisBottom={{
|
axisBottom={{
|
||||||
tickValues: numXTickValues,
|
tickValues: numXTickValues,
|
||||||
format: (time) => formatTime(+time, lessThanAWeek),
|
format: (time) => formatTime(+time, multiYear, lessThanAWeek, false),
|
||||||
}}
|
}}
|
||||||
colors={{ scheme: 'pastel1' }}
|
colors={{ scheme: 'pastel1' }}
|
||||||
pointSize={0}
|
pointSize={0}
|
||||||
|
@ -145,14 +148,34 @@ function formatPercent(y: DatumValue) {
|
||||||
return `${Math.round(+y.toString())}%`
|
return `${Math.round(+y.toString())}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(time: number, includeTime: boolean) {
|
function formatTime(
|
||||||
|
time: number,
|
||||||
|
includeYear: boolean,
|
||||||
|
includeHour: boolean,
|
||||||
|
includeMinute: boolean
|
||||||
|
) {
|
||||||
const d = dayjs(time)
|
const d = dayjs(time)
|
||||||
|
|
||||||
if (d.isSame(Date.now(), 'day')) return d.format('ha')
|
if (d.add(1, 'minute').isAfter(Date.now())) return 'Now'
|
||||||
|
|
||||||
if (includeTime) return dayjs(time).format('MMM D, ha')
|
let format: string
|
||||||
|
if (d.isSame(Date.now(), 'day')) {
|
||||||
|
format = '[Today]'
|
||||||
|
} else if (d.add(1, 'day').isSame(Date.now(), 'day')) {
|
||||||
|
format = '[Yesterday]'
|
||||||
|
} else {
|
||||||
|
format = 'MMM D'
|
||||||
|
}
|
||||||
|
|
||||||
return dayjs(time).format('MMM D')
|
if (includeMinute) {
|
||||||
|
format += ', h:mma'
|
||||||
|
} else if (includeHour) {
|
||||||
|
format += ', ha'
|
||||||
|
} else if (includeYear) {
|
||||||
|
format += ', YYYY'
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.format(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
const computeProbsByOutcome = (bets: Bet[], contract: FreeResponseContract) => {
|
const computeProbsByOutcome = (bets: Bet[], contract: FreeResponseContract) => {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { DatumValue } from '@nivo/core'
|
import { DatumValue } from '@nivo/core'
|
||||||
import { ResponsiveLine } from '@nivo/line'
|
import { ResponsiveLine, SliceTooltipProps } from '@nivo/line'
|
||||||
|
import { BasicTooltip } from '@nivo/tooltip'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
|
@ -38,9 +39,6 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
times.push(latestTime.toDate())
|
times.push(latestTime.toDate())
|
||||||
probs.push(probs[probs.length - 1])
|
probs.push(probs[probs.length - 1])
|
||||||
|
|
||||||
const points = probs.map((prob, i) => ({ x: times[i], y: prob * 100 }))
|
|
||||||
const data = [{ id: 'Yes', data: points, color: '#11b981' }]
|
|
||||||
|
|
||||||
const yTickValues = [0, 25, 50, 75, 100]
|
const yTickValues = [0, 25, 50, 75, 100]
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
@ -51,7 +49,36 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
? times[0]
|
? times[0]
|
||||||
: hoursAgo.toDate()
|
: hoursAgo.toDate()
|
||||||
|
|
||||||
const lessThanAWeek = dayjs(startDate).add(1, 'week').isAfter(latestTime)
|
// Minimum number of points for the graph to have. For smooth tooltip movement
|
||||||
|
// On first load, width is undefined, skip adding extra points to let page load faster
|
||||||
|
// This fn runs again once DOM is finished loading
|
||||||
|
const totalPoints = width ? (width > 800 ? 300 : 50) : 1
|
||||||
|
|
||||||
|
const timeStep: number = latestTime.diff(startDate, 'ms') / totalPoints
|
||||||
|
const points: { x: Date; y: number }[] = []
|
||||||
|
for (let i = 0; i < times.length; i++) {
|
||||||
|
points[points.length] = { x: times[i], y: probs[i] * 100 }
|
||||||
|
const numPoints: number = Math.floor(
|
||||||
|
dayjs(times[i + 1]).diff(dayjs(times[i]), 'ms') / timeStep
|
||||||
|
)
|
||||||
|
if (numPoints > 1) {
|
||||||
|
const thisTimeStep: number =
|
||||||
|
dayjs(times[i + 1]).diff(dayjs(times[i]), 'ms') / numPoints
|
||||||
|
for (let n = 1; n < numPoints; n++) {
|
||||||
|
points[points.length] = {
|
||||||
|
x: dayjs(times[i])
|
||||||
|
.add(thisTimeStep * n, 'ms')
|
||||||
|
.toDate(),
|
||||||
|
y: probs[i] * 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = [{ id: 'Yes', data: points, color: '#11b981' }]
|
||||||
|
|
||||||
|
const multiYear = !dayjs(startDate).isSame(latestTime, 'year')
|
||||||
|
const lessThanAWeek = dayjs(startDate).add(8, 'day').isAfter(latestTime)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -72,14 +99,16 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
min: startDate,
|
min: startDate,
|
||||||
max: latestTime.toDate(),
|
max: latestTime.toDate(),
|
||||||
}}
|
}}
|
||||||
xFormat={(d) => formatTime(+d.valueOf(), lessThanAWeek)}
|
xFormat={(d) =>
|
||||||
|
formatTime(+d.valueOf(), multiYear, lessThanAWeek, lessThanAWeek)
|
||||||
|
}
|
||||||
axisBottom={{
|
axisBottom={{
|
||||||
tickValues: numXTickValues,
|
tickValues: numXTickValues,
|
||||||
format: (time) => formatTime(+time, lessThanAWeek),
|
format: (time) => formatTime(+time, multiYear, lessThanAWeek, false),
|
||||||
}}
|
}}
|
||||||
colors={{ datum: 'color' }}
|
colors={{ datum: 'color' }}
|
||||||
curve="stepAfter"
|
curve="stepAfter"
|
||||||
pointSize={0}
|
enablePoints={false}
|
||||||
pointBorderWidth={1}
|
pointBorderWidth={1}
|
||||||
pointBorderColor="#fff"
|
pointBorderColor="#fff"
|
||||||
enableSlices="x"
|
enableSlices="x"
|
||||||
|
@ -87,21 +116,54 @@ export const ContractProbGraph = memo(function ContractProbGraph(props: {
|
||||||
enableArea
|
enableArea
|
||||||
margin={{ top: 20, right: 20, bottom: 25, left: 40 }}
|
margin={{ top: 20, right: 20, bottom: 25, left: 40 }}
|
||||||
animate={false}
|
animate={false}
|
||||||
|
sliceTooltip={SliceTooltip}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SliceTooltip = ({ slice }: SliceTooltipProps) => {
|
||||||
|
return (
|
||||||
|
<BasicTooltip
|
||||||
|
id={slice.points.map((point) => [
|
||||||
|
<span key="date">
|
||||||
|
<strong>{point.data[`yFormatted`]}</strong> {point.data['xFormatted']}
|
||||||
|
</span>,
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function formatPercent(y: DatumValue) {
|
function formatPercent(y: DatumValue) {
|
||||||
return `${Math.round(+y.toString())}%`
|
return `${Math.round(+y.toString())}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(time: number, includeTime: boolean) {
|
function formatTime(
|
||||||
|
time: number,
|
||||||
|
includeYear: boolean,
|
||||||
|
includeHour: boolean,
|
||||||
|
includeMinute: boolean
|
||||||
|
) {
|
||||||
const d = dayjs(time)
|
const d = dayjs(time)
|
||||||
|
|
||||||
if (d.isSame(Date.now(), 'day')) return d.format('ha')
|
if (d.add(1, 'minute').isAfter(Date.now())) return 'Now'
|
||||||
|
|
||||||
if (includeTime) return dayjs(time).format('MMM D, ha')
|
let format: string
|
||||||
|
if (d.isSame(Date.now(), 'day')) {
|
||||||
|
format = '[Today]'
|
||||||
|
} else if (d.add(1, 'day').isSame(Date.now(), 'day')) {
|
||||||
|
format = '[Yesterday]'
|
||||||
|
} else {
|
||||||
|
format = 'MMM D'
|
||||||
|
}
|
||||||
|
|
||||||
return dayjs(time).format('MMM D')
|
if (includeMinute) {
|
||||||
|
format += ', h:mma'
|
||||||
|
} else if (includeHour) {
|
||||||
|
format += ', ha'
|
||||||
|
} else if (includeYear) {
|
||||||
|
format += ', YYYY'
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.format(format)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user