improve charts

This commit is contained in:
2026-01-28 08:51:41 +11:00
parent 940af3680f
commit 14b904cbad
4 changed files with 181 additions and 20 deletions

View File

@@ -4,6 +4,7 @@ const state = {
tz: "local",
rangeStart: null,
rangeEnd: null,
singleChartId: null,
charts: {},
timer: null,
};
@@ -141,6 +142,80 @@ function computeRange(range, tz) {
return { start, end, axisStart, axisEnd };
}
function readStateFromURL() {
const url = new URL(window.location.href);
const range = url.searchParams.get("range");
const tz = url.searchParams.get("tz");
if (range) {
state.range = range;
}
if (tz === "utc" || tz === "local") {
state.tz = tz;
}
state.bucket = state.range === "6h" ? "1m" : "5m";
const chartParam = url.searchParams.get("chart");
const chartMatch = url.pathname.match(/^\/chart\/([^/]+)/);
if (chartMatch && chartMatch[1]) {
state.singleChartId = chartMatch[1];
} else if (chartParam) {
state.singleChartId = chartParam;
}
}
function syncControls() {
const rangeButtons = document.querySelectorAll(".btn[data-range]");
rangeButtons.forEach((btn) => {
btn.classList.toggle("active", btn.dataset.range === state.range);
});
const tzButtons = document.querySelectorAll(".btn[data-tz]");
tzButtons.forEach((btn) => {
btn.classList.toggle("active", btn.dataset.tz === state.tz);
});
}
function updateSingleChartMode() {
const cards = document.querySelectorAll(".chart-card");
if (!state.singleChartId) {
document.body.classList.remove("single-chart");
cards.forEach((card) => card.classList.remove("active"));
return;
}
document.body.classList.add("single-chart");
cards.forEach((card) => {
const id = card.dataset.chart;
card.classList.toggle("active", id === state.singleChartId);
});
}
function buildChartURL(chartId) {
const url = new URL(window.location.origin + "/chart/" + chartId);
url.searchParams.set("range", state.range);
url.searchParams.set("tz", state.tz);
return url.toString();
}
function setupShareLinks() {
const buttons = document.querySelectorAll(".chart-link");
buttons.forEach((btn) => {
btn.addEventListener("click", async () => {
const chartId = btn.dataset.chart;
if (!chartId) return;
const url = buildChartURL(chartId);
try {
await navigator.clipboard.writeText(url);
btn.textContent = "Copied";
setTimeout(() => {
btn.textContent = "Share";
}, 1200);
} catch (err) {
window.prompt("Copy chart URL:", url);
}
});
});
}
function minMax(values) {
let min = null;
let max = null;
@@ -399,6 +474,8 @@ function renderDashboard(data) {
options: baseOptions(range),
};
upsertChart("chart-precip", precipChart);
updateSingleChartMode();
}
function buildCommentary(latest, forecast) {
@@ -486,6 +563,7 @@ function setupControls() {
btn.classList.add("active");
state.range = btn.dataset.range;
state.bucket = state.range === "6h" ? "1m" : "5m";
updateURLParams();
loadAndRender();
});
});
@@ -496,13 +574,25 @@ function setupControls() {
tzButtons.forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
state.tz = btn.dataset.tz;
updateURLParams();
loadAndRender();
});
});
}
function updateURLParams() {
const url = new URL(window.location.href);
url.searchParams.set("range", state.range);
url.searchParams.set("tz", state.tz);
history.replaceState({}, "", url);
}
document.addEventListener("DOMContentLoaded", () => {
readStateFromURL();
syncControls();
setupControls();
setupShareLinks();
updateSingleChartMode();
loadAndRender();
if (state.timer) clearInterval(state.timer);
state.timer = setInterval(loadAndRender, 60 * 1000);