const { useState: useStateP, useMemo: useMemoP, useRef: useRefP } = React;
function PollingSection({ lang = "en" }) {
const data = window.POLLING_DATA;
const t = (en, fr) => (lang === "fr" ? fr : en);
const [mode, setMode] = useStateP("parties"); // 'parties' | 'approval'
const [hoverIdx, setHoverIdx] = useStateP(null);
return (
{t("Public Support · Rolling aggregate", "Appui public · Moyenne mobile")}
{mode === "parties"
? t(<>Federal vote intention since the 2025 election .>, <>Intentions de vote depuis les élections de 2025 .>)
: t(<>Carney approval, Angus Reid .>, <>Approbation de Carney, Angus Reid .>)}
setMode("parties")}>
{t("Vote share", "Intentions de vote")}
setMode("approval")}>
{t("Carney approval", "Approbation Carney")}
{mode === "parties"
?
: }
{data.meta.note}
);
}
/* ──────────────────────────────────────────────────────────
Shared chart geometry
────────────────────────────────────────────────────────── */
const W = 1180, H = 320, PL = 44, PR = 56, PT = 24, PB = 36;
const innerW = W - PL - PR;
const innerH = H - PT - PB;
function ticksY(min, max, step) {
const ticks = [];
for (let v = min; v <= max + 0.0001; v += step) ticks.push(v);
return ticks;
}
function fmtMonth(d) {
const [y, m] = d.split("-");
return new Date(Number(y), Number(m) - 1, 1)
.toLocaleDateString("en-CA", { month: "short" }) + (m === "01" ? ` ${y.slice(2)}` : "");
}
/* ──────────────────────────────────────────────────────────
Parties chart — multi-line with hover crosshair
────────────────────────────────────────────────────────── */
function PartiesChart({ data, hoverIdx, setHoverIdx }) {
const svgRef = useRefP(null);
const yMin = 0, yMax = 50;
const yTicks = ticksY(yMin, yMax, 10);
const N = data.series.length;
const x = (i) => PL + (i / (N - 1)) * innerW;
const y = (v) => PT + innerH - ((v - yMin) / (yMax - yMin)) * innerH;
const handleMove = (e) => {
const rect = svgRef.current.getBoundingClientRect();
const px = ((e.clientX - rect.left) / rect.width) * W;
const i = Math.round(((px - PL) / innerW) * (N - 1));
setHoverIdx(Math.max(0, Math.min(N - 1, i)));
};
const lastRow = data.series[N - 1];
return (
setHoverIdx(null)}
>
{/* Y gridlines + labels */}
{yTicks.map(v => (
{v}%
))}
{/* X labels */}
{data.series.map((row, i) => {
if (i % 2 !== 0 && i !== N - 1) return null;
return (
{fmtMonth(row.date)}
);
})}
{/* Hover crosshair */}
{hoverIdx != null && (
)}
{/* Party lines */}
{data.parties.map(p => {
const pts = data.series.map((row, i) => `${x(i)},${y(row[p.id])}`).join(" ");
return (
{/* End-of-line label */}
{p.name}
{lastRow[p.id].toFixed(1)}%
);
})}
{/* Hover dots */}
{hoverIdx != null && data.parties.map(p => (
))}
{/* Hover readout */}
{hoverIdx != null && (
{fmtMonth(data.series[hoverIdx].date)}
{data.parties.map(p => (
{p.name}
{data.series[hoverIdx][p.id].toFixed(1)}%
))}
)}
);
}
/* ──────────────────────────────────────────────────────────
Approval chart — single area + line
────────────────────────────────────────────────────────── */
function ApprovalChart({ data, hoverIdx, setHoverIdx, lang = "en" }) {
const t = (en, fr) => (lang === "fr" ? fr : en);
const loc = lang === "fr" ? "fr-CA" : "en-CA";
const svgRef = useRefP(null);
const yMin = 30, yMax = 70;
const yTicks = ticksY(yMin, yMax, 10);
const N = data.length;
const x = (i) => PL + (i / (N - 1)) * innerW;
const y = (v) => PT + innerH - ((v - yMin) / (yMax - yMin)) * innerH;
const handleMove = (e) => {
const rect = svgRef.current.getBoundingClientRect();
const px = ((e.clientX - rect.left) / rect.width) * W;
const i = Math.round(((px - PL) / innerW) * (N - 1));
setHoverIdx(Math.max(0, Math.min(N - 1, i)));
};
const pts = data.map((row, i) => `${x(i)},${y(row.value)}`).join(" ");
const areaPath = `M ${PL},${y(yMin)} L ${data.map((r, i) => `${x(i)},${y(r.value)}`).join(" L ")} L ${x(N-1)},${y(yMin)} Z`;
const last = data[N - 1];
const first = data[0];
const delta = last.value - first.value;
const peak = data.reduce((a, b) => (b.value > a.value ? b : a));
const monthYear = (d) => { const [y, m] = d.split("-"); return new Date(+y, +m - 1, 1).toLocaleDateString(loc, { month: "short", year: "numeric" }); };
const monthApos = (d) => { const [y, m] = d.split("-"); return new Date(+y, +m - 1, 1).toLocaleDateString(loc, { month: "short" }) + " '" + y.slice(2); };
return (
setHoverIdx(null)}
>
{yTicks.map(v => (
{v}%
))}
{data.map((row, i) => {
if (i % 2 !== 0 && i !== N - 1) return null;
return {fmtMonth(row.date)} ;
})}
{/* 50% reference line */}
50%
{hoverIdx != null && (
<>
>
)}
{/* End label */}
{t("Now", "Auj.")}
{last.value}%
{t("Approval", "Approbation")}, {monthYear(last.date)}
{last.value}%
{t("Since taking office", "Depuis l'entrée en fonction")}
{delta > 0 ? "+" : ""}{delta} pts
{t("Peak", "Sommet")}
{peak.value}% {monthApos(peak.date)}
{hoverIdx != null && (
{fmtMonth(data[hoverIdx].date)}
Approval
{data[hoverIdx].value}%
)}
);
}
window.PollingSection = PollingSection;