metaforecast/src/web/questions/components/QuestionOptions.tsx

149 lines
4.2 KiB
TypeScript

import { QuestionFragment } from "../../fragments.generated";
import { formatProbability } from "../utils";
type Option = QuestionFragment["options"][0];
const textColor = (probability: number) => {
if (probability < 0.03) {
return "text-red-600";
} else if (probability < 0.1) {
return "text-red-600 opacity-80";
} else if (probability < 0.2) {
return "text-red-600 opacity-80";
} else if (probability < 0.3) {
return "text-red-600 opacity-70";
} else if (probability < 0.4) {
return "text-red-600 opacity-70";
} else if (probability < 0.5) {
return "text-gray-500";
} else if (probability < 0.6) {
return "text-gray-500";
} else if (probability < 0.7) {
return "text-green-600 opacity-70";
} else if (probability < 0.8) {
return "text-green-600 opacity-70";
} else if (probability < 0.9) {
return "text-green-600 opacity-80";
} else if (probability < 0.97) {
return "text-green-600 opacity-80";
} else {
return "text-green-600";
}
};
const primaryForecastColor = (probability: number) => {
if (probability < 0.03) {
return "bg-red-600";
} else if (probability < 0.1) {
return "bg-red-600 opacity-80";
} else if (probability < 0.2) {
return "bg-red-600 opacity-70";
} else if (probability < 0.3) {
return "bg-red-600 opacity-60";
} else if (probability < 0.4) {
return "bg-red-600 opacity-50";
} else if (probability < 0.5) {
return "bg-gray-500";
} else if (probability < 0.6) {
return "bg-gray-500";
} else if (probability < 0.7) {
return "bg-green-600 opacity-50";
} else if (probability < 0.8) {
return "bg-green-600 opacity-60";
} else if (probability < 0.9) {
return "bg-green-600 opacity-70";
} else if (probability < 0.97) {
return "bg-green-600 opacity-80";
} else {
return "bg-green-600";
}
};
const primaryEstimateAsText = (probability: number) => {
if (probability < 0.03) {
return "Exceptionally unlikely";
} else if (probability < 0.1) {
return "Very unlikely";
} else if (probability < 0.4) {
return "Unlikely";
} else if (probability < 0.6) {
return "About Even";
} else if (probability < 0.9) {
return "Likely";
} else if (probability < 0.97) {
return "Very likely";
} else {
return "Virtually certain";
}
};
const chooseColor = (probability: number) => {
if (probability < 0.1) {
return "bg-blue-50 text-blue-500";
} else if (probability < 0.3) {
return "bg-blue-100 text-blue-600";
} else if (probability < 0.7) {
return "bg-blue-200 text-blue-700";
} else {
return "bg-blue-300 text-blue-800";
}
};
const OptionRow: React.FC<{ option: Option }> = ({ option }) => {
return (
<div className="flex items-center">
<div
className={`${chooseColor(
option.probability
)} w-14 flex-none rounded-md py-0.5 text-sm text-center`}
>
{formatProbability(option.probability)}
</div>
<div className="text-gray-700 pl-3 leading-snug text-sm">
{option.name}
</div>
</div>
);
};
export const QuestionOptions: React.FC<{ options: Option[] }> = ({
options,
}) => {
const isBinary =
options.length === 2 &&
(options[0].name === "Yes" || options[0].name === "No");
if (isBinary) {
const yesOption = options.find((o) => o.name === "Yes");
return (
<div className="space-x-2">
<span
className={`${primaryForecastColor(
yesOption.probability
)} text-white w-16 rounded-md px-1.5 py-0.5 font-bold`}
>
{formatProbability(yesOption.probability)}
</span>
<span
className={`${textColor(
yesOption.probability
)} text-gray-500 inline-block`}
>
{primaryEstimateAsText(yesOption.probability)}
</span>
</div>
);
} else {
const optionsSorted = options.sort((a, b) => b.probability - a.probability);
const optionsMax5 = !!optionsSorted.slice ? optionsSorted.slice(0, 5) : []; // display max 5 options.
return (
<div className="space-y-2">
{optionsMax5.map((option, i) => (
<OptionRow option={option} key={i} />
))}
</div>
);
}
};