Show full Gaussian probability tooltip

This commit is contained in:
2569718930@qq.com
2026-06-07 13:02:16 +08:00
parent f66127da44
commit 27e4f1efe0
2 changed files with 52 additions and 18 deletions
@@ -77,8 +77,8 @@ export function TemperatureTooltipContent({
</div>
)}
{probabilityRows.length > 0 && (
<div className={rows.length > 0 ? "mt-1.5 grid gap-1 border-t border-slate-100 pt-1.5" : "grid gap-1"}>
{probabilityRows.slice(0, 8).map((item) => (
<div className={rows.length > 0 ? "mt-1.5 grid max-h-[260px] gap-1 overflow-y-auto border-t border-slate-100 pt-1.5 pr-1" : "grid max-h-[260px] gap-1 overflow-y-auto pr-1"}>
{probabilityRows.map((item) => (
<div key={item.key} className="flex min-w-[160px] items-center justify-between gap-4">
<span className="inline-flex items-center gap-1.5 text-violet-700">
<span className="h-2 w-2 rounded-full" style={{ backgroundColor: item.color }} />
@@ -118,6 +118,18 @@ function buildTooltipRows(
.filter((item): item is TooltipRow => item !== null);
}
function formatProbabilityRangeValue(value: number) {
return Number(value.toFixed(1)).toString();
}
function formatProbabilityRangeLabel(
lower: number,
upper: number,
tempSymbol: string,
) {
return `${formatProbabilityRangeValue(lower)}-${formatProbabilityRangeValue(upper)}${tempSymbol}`;
}
function buildTooltipProbabilityRows(
probabilityOverlay: ProbabilityOverlay | null | undefined,
tempSymbol: string,
@@ -135,18 +147,20 @@ function buildTooltipProbabilityRows(
});
}
const topBand = [...probabilityOverlay.bands]
.filter((band) => validNumber(band.probability) !== null)
.sort((a, b) => b.probability - a.probability)[0];
if (topBand) {
const probabilityPct = Math.round(topBand.probability * 100);
rows.push({
key: topBand.key,
label: topBand.label,
value: `${probabilityPct}%`,
color: "#a78bfa",
});
}
rows.push(
...[...probabilityOverlay.bands]
.filter((band) => validNumber(band.probability) !== null && band.probability > 0)
.sort((a, b) => a.lower - b.lower)
.map((band) => {
const probabilityPct = Math.round(band.probability * 100);
return {
key: band.key,
label: formatProbabilityRangeLabel(band.lower, band.upper, tempSymbol),
value: `${probabilityPct}%`,
color: "#a78bfa",
};
}),
);
return rows;
}
@@ -61,6 +61,15 @@ export function runTests() {
engine: "legacy",
muLine: { value: 27.4, label: "Gaussian μ 27.4°C" },
bands: [
{
key: "legacy_probability_26_0",
value: 26,
lower: 25.5,
upper: 26.5,
probability: 0.18,
label: "26°C 18%",
opacity: 0.08,
},
{
key: "legacy_probability_27_0",
value: 27,
@@ -70,6 +79,15 @@ export function runTests() {
label: "27°C 42%",
opacity: 0.13,
},
{
key: "legacy_probability_28_0",
value: 28,
lower: 27.5,
upper: 28.5,
probability: 0.31,
label: "28°C 31%",
opacity: 0.11,
},
],
},
"°C",
@@ -77,13 +95,15 @@ export function runTests() {
);
assert(
probabilityRows.length === 2,
"temperature tooltip should show Gaussian μ and the leading probability bucket as compact context",
probabilityRows.length === 4,
"temperature tooltip should show Gaussian μ and every available probability bucket as compact context",
);
assert(
probabilityRows.some((row) => row.key === "legacy_probability_mu" && row.value === "27.4°C") &&
probabilityRows.some((row) => row.key === "legacy_probability_27_0" && row.value === "42%"),
"temperature tooltip should format Gaussian μ and probability values without drawing a full probability band on the main chart",
probabilityRows.some((row) => row.key === "legacy_probability_26_0" && row.label === "25.5-26.5°C" && row.value === "18%") &&
probabilityRows.some((row) => row.key === "legacy_probability_27_0" && row.label === "26.5-27.5°C" && row.value === "42%") &&
probabilityRows.some((row) => row.key === "legacy_probability_28_0" && row.label === "27.5-28.5°C" && row.value === "31%"),
"temperature tooltip should format the full Gaussian probability distribution by temperature range without drawing it on the main chart",
);
const projectRoot = process.cwd();