- Add estimate of likeliest option to top - Or of event happening for yes/no questions - Restore selection highlight from legend - But avoid weird color errors - Add a bunch of parameters to QuestionOptions - I could just have added a flag for isInQuestionPage, - Not sure if this is the best way to go about this I'm not particularly planning to continue with changes in the upcoming days
211 lines
5.6 KiB
TypeScript
211 lines
5.6 KiB
TypeScript
import { differenceInDays, format } from "date-fns";
|
||
import {
|
||
VictoryAxis,
|
||
VictoryChart,
|
||
VictoryGroup,
|
||
VictoryStack,
|
||
VictoryLabel,
|
||
VictoryLine,
|
||
VictoryScatter,
|
||
VictoryArea,
|
||
VictoryTheme,
|
||
VictoryTooltip,
|
||
VictoryVoronoiContainer,
|
||
} from "victory";
|
||
|
||
import { chartColors, ChartData, ChartSeries, height, width } from "./utils";
|
||
|
||
let dateFormat = "MMM do y"; // "yyyy-MM-dd"
|
||
|
||
// can't be replaced with React component, VictoryChart requires VictoryGroup elements to be immediate children
|
||
const getVictoryGroup = ({
|
||
data,
|
||
i,
|
||
highlight,
|
||
isBinary,
|
||
}: {
|
||
data: ChartSeries;
|
||
i: number;
|
||
highlight?: boolean;
|
||
isBinary?: boolean;
|
||
}) => {
|
||
return (
|
||
<VictoryGroup color={chartColors[i] || "darkgray"} data={data} key={i}>
|
||
<VictoryLine
|
||
name={`line-${i}`}
|
||
style={{
|
||
data: {
|
||
// strokeOpacity: highlight ? 1 : 0.5,
|
||
strokeOpacity: highlight && !isBinary ? 0.8 : 0.6,
|
||
strokeWidth: highlight && !isBinary ? 4 : 3,
|
||
},
|
||
}}
|
||
/>
|
||
{isBinary ? (
|
||
<VictoryArea
|
||
standalone={false}
|
||
style={{
|
||
data: { fill: chartColors[i], fillOpacity: 0.1, strokeOpacity: 0 },
|
||
}}
|
||
data={data}
|
||
/>
|
||
) : null}
|
||
|
||
<VictoryScatter
|
||
name={`scatter-${i}`}
|
||
size={({ active }) => (active || highlight ? 0 : 0)} //(active || highlight ? 3.75 : 3)}
|
||
/>
|
||
</VictoryGroup>
|
||
);
|
||
};
|
||
|
||
export type Props = {
|
||
data: ChartData;
|
||
highlight: number | undefined;
|
||
};
|
||
|
||
export const InnerChart: React.FC<Props> = ({
|
||
data: { maxProbability, seriesList, minDate, maxDate },
|
||
highlight,
|
||
}) => {
|
||
const domainMax =
|
||
maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1;
|
||
const padding = {
|
||
top: 20,
|
||
bottom: 75,
|
||
left: 70,
|
||
right: 17,
|
||
};
|
||
|
||
const isBinary = seriesList.length == 1;
|
||
console.log(isBinary);
|
||
|
||
return (
|
||
<VictoryChart
|
||
domainPadding={{ x: 0 }}
|
||
padding={padding}
|
||
theme={VictoryTheme.material}
|
||
height={height}
|
||
width={width}
|
||
containerComponent={
|
||
<VictoryVoronoiContainer
|
||
labels={() => "Not shown"}
|
||
labelComponent={
|
||
<VictoryTooltip
|
||
constrainToVisibleArea
|
||
pointerLength={0}
|
||
dy={-12}
|
||
labelComponent={
|
||
<VictoryLabel
|
||
style={[
|
||
{
|
||
fontSize: 16,
|
||
fill: "black",
|
||
strokeWidth: 0.05,
|
||
},
|
||
{
|
||
fontSize: 16,
|
||
fill: "#777",
|
||
strokeWidth: 0.05,
|
||
},
|
||
]}
|
||
/>
|
||
}
|
||
text={({ datum }) =>
|
||
`${datum.name}: ${Math.round(datum.y * 100)}%\n${format(
|
||
datum.x,
|
||
dateFormat
|
||
)}`
|
||
}
|
||
style={{
|
||
fontSize: 17, // needs to be set here and not just in labelComponent for text size calculations
|
||
fontFamily:
|
||
'"Gill Sans", "Gill Sans MT", "Seravek", "Trebuchet MS", sans-serif',
|
||
// default font family from Victory, need to be specified explicitly for some reason, otherwise text size gets miscalculated
|
||
}}
|
||
flyoutStyle={{
|
||
stroke: "#999",
|
||
fill: "white",
|
||
}}
|
||
cornerRadius={4}
|
||
flyoutPadding={{ top: 4, bottom: 4, left: 16, right: 16 }}
|
||
/>
|
||
}
|
||
radius={50}
|
||
voronoiBlacklist={
|
||
[...Array(seriesList.length).keys()].map((i) => `line-${i}`)
|
||
// see: https://github.com/FormidableLabs/victory/issues/545
|
||
}
|
||
/>
|
||
}
|
||
scale={{
|
||
x: "time",
|
||
y: "linear",
|
||
}}
|
||
domain={{
|
||
x: [minDate, maxDate],
|
||
y: [0, domainMax],
|
||
}}
|
||
>
|
||
{
|
||
// Note: axis is not in fact unaligned. Fetchers are at ~12:00
|
||
// whereas the date is at the beginning of the day
|
||
// however, it still doesn't look very pretty.
|
||
}
|
||
<VictoryAxis
|
||
tickCount={Math.min(7, differenceInDays(maxDate, minDate) + 1)}
|
||
style={{
|
||
grid: { strokeWidth: 0.5 },
|
||
}}
|
||
tickLabelComponent={
|
||
<VictoryLabel
|
||
dx={-40}
|
||
dy={0}
|
||
angle={-30}
|
||
style={{ fontSize: 15, fill: "#777" }}
|
||
/>
|
||
}
|
||
scale={{ x: "time" }}
|
||
tickFormat={(t) => format(t, dateFormat)}
|
||
/>
|
||
<VictoryAxis
|
||
dependentAxis
|
||
style={{
|
||
grid: { stroke: "#D3D3D3", strokeWidth: 0.5 },
|
||
}}
|
||
tickLabelComponent={
|
||
<VictoryLabel dy={0} style={{ fontSize: 18, fill: "#777" }} />
|
||
}
|
||
// tickFormat specifies how ticks should be displayed
|
||
tickFormat={(x) => `${x * 100}%`}
|
||
/>
|
||
|
||
{[...Array(seriesList.length).keys()]
|
||
.reverse() // affects svg render order, we want to render largest datasets on top of others
|
||
//.filter((i) => i !== highlight)
|
||
.map((i) =>
|
||
getVictoryGroup({
|
||
data: seriesList[i],
|
||
i,
|
||
highlight: i == highlight, // false
|
||
isBinary: isBinary,
|
||
})
|
||
)}
|
||
|
||
{
|
||
// note: this produces an annoying change of color effect
|
||
/*
|
||
highlight === undefined
|
||
? null
|
||
: // render highlighted series on top of everything else
|
||
getVictoryGroup({
|
||
data: seriesList[highlight],
|
||
i: highlight,
|
||
highlight: true,
|
||
})
|
||
*/
|
||
}
|
||
</VictoryChart>
|
||
);
|
||
};
|