Files
OpenStats/web/app.js
T

69 lines
2.7 KiB
JavaScript

/* OpenStats 官网 · 页面外壳:主题切换、菜单栏下拉菜单与时钟 */
(() => {
"use strict";
const root = document.documentElement;
const THEME_KEY = "os-theme";
const media = window.matchMedia("(prefers-color-scheme: dark)");
function saved() {
try { return localStorage.getItem(THEME_KEY); } catch { return null; }
}
function applyTheme(theme, persist) {
root.dataset.theme = theme;
if (!persist) return;
try { localStorage.setItem(THEME_KEY, theme); } catch { /* 隐私模式下不可用,忽略 */ }
}
applyTheme(saved() || (media.matches ? "dark" : "light"), false);
// 没有手动选过时跟随系统
media.addEventListener("change", (e) => { if (!saved()) applyTheme(e.matches ? "dark" : "light", false); });
window.OpenStatsSite = {
toggleTheme() { applyTheme(root.dataset.theme === "dark" ? "light" : "dark", true); },
};
document.getElementById("themeToggle")?.addEventListener("click", (e) => {
e.stopPropagation();
window.OpenStatsSite.toggleTheme();
});
// ── 菜单栏下拉菜单:点击展开,展开后悬停切换,点外部或 Esc 收起 ──
const menus = [...document.querySelectorAll(".mb-menu")];
let openMenu = null;
function setMenu(menu) {
for (const m of menus) {
const open = m === menu;
m.classList.toggle("is-open", open);
m.querySelector(".mb-title")?.setAttribute("aria-expanded", String(open));
}
openMenu = menu;
}
for (const menu of menus) {
const title = menu.querySelector(".mb-title");
title?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
setMenu(openMenu === menu ? null : menu);
});
menu.addEventListener("mouseenter", () => { if (openMenu && openMenu !== menu) setMenu(menu); });
menu.querySelector(".mb-drop")?.addEventListener("click", () => setMenu(null));
}
document.addEventListener("click", (e) => { if (openMenu && !openMenu.contains(e.target)) setMenu(null); });
document.addEventListener("keydown", (e) => { if (e.key === "Escape") setMenu(null); });
// ── 时钟:按访客本地时间显示 ──
const clock = document.getElementById("menubarClock");
const format = new Intl.DateTimeFormat("zh-CN", { month: "numeric", day: "numeric", weekday: "short", hour: "2-digit", minute: "2-digit", hour12: false });
function renderClock() {
if (!clock) return;
const now = new Date();
const parts = Object.fromEntries(format.formatToParts(now).map((p) => [p.type, p.value]));
clock.textContent = `${parts.month}月${parts.day}日 ${parts.weekday} ${parts.hour}:${parts.minute}`;
clock.dateTime = now.toISOString();
}
renderClock();
setInterval(renderClock, 15000);
})();