Files
site/WESP_REL/static/js/wesp-theme.js
T
2026-07-17 12:57:18 +03:00

144 lines
4.2 KiB
JavaScript

/** Общая тема зоотех-страниц: localStorage + переключатель #wespThemePicker */
export const THEME_KEY = "theme";
export const HIGH_CONTRAST_KEY = "wespHighContrast";
export const THEMES = ["light", "organic", "dark"];
/** @deprecated Админка всегда ultra-dark; ключ оставлен для совместимости zootech-тем. */
export const ADMIN_ULTRA_DARK_KEY = "wespAdminUltraDark";
const THEME_LABELS = {
light: "Светлая",
organic: "Зелёная",
dark: "Тёмная",
};
export function getTheme() {
const saved = localStorage.getItem(THEME_KEY);
if (THEMES.includes(saved)) return saved;
return "dark";
}
/** Ручной выбор в настройках; иначе — системный prefers-contrast. */
export function resolveHighContrast() {
try {
const saved = localStorage.getItem(HIGH_CONTRAST_KEY);
if (saved === "1") return true;
if (saved === "0") return false;
} catch {
/* ignore */
}
return globalThis.matchMedia?.("(prefers-contrast: more)")?.matches ?? false;
}
export function getHighContrast() {
return resolveHighContrast();
}
export function applyHighContrast(on) {
document.documentElement.dataset.highContrast = on ? "true" : "false";
}
export function setHighContrast(on) {
try {
localStorage.setItem(HIGH_CONTRAST_KEY, on ? "1" : "0");
} catch {
/* ignore */
}
applyHighContrast(!!on);
syncHighContrastUI();
}
export function applyTheme(theme) {
document.documentElement.dataset.theme = theme;
}
export function setTheme(theme) {
if (!THEMES.includes(theme)) return;
if (theme === "light" || theme === "organic") {
try {
localStorage.removeItem(ADMIN_ULTRA_DARK_KEY);
} catch (e) {
/* ignore */
}
}
localStorage.setItem(THEME_KEY, theme);
applyTheme(theme);
syncThemePickerUI();
}
function syncThemePickerUI() {
const mount = document.getElementById("wespThemePicker");
if (!mount) return;
const current = getTheme();
mount.querySelectorAll("[data-theme-value]").forEach((btn) => {
const active = btn.dataset.themeValue === current;
btn.classList.toggle("is-active", active);
btn.setAttribute("aria-checked", active ? "true" : "false");
});
}
function syncHighContrastUI() {
const el = document.getElementById("wespHighContrastToggle");
if (!el) return;
const on = getHighContrast();
el.checked = on;
el.setAttribute("aria-checked", on ? "true" : "false");
}
/** Сегментированный переключатель тем (3 варианта). */
export function initThemePicker() {
const mount = document.getElementById("wespThemePicker");
if (!mount) return;
if (mount.dataset.bound !== "1") {
mount.dataset.bound = "1";
mount.classList.add("theme-picker");
mount.innerHTML = THEMES.map(
(id) =>
`<button type="button" class="theme-picker__btn" data-theme-value="${id}" role="radio" aria-checked="false">${THEME_LABELS[id]}</button>`
).join("");
mount.addEventListener("click", (e) => {
const btn = e.target.closest("[data-theme-value]");
if (!btn) return;
setTheme(btn.dataset.themeValue);
});
}
syncThemePickerUI();
}
/** Переключатель усиленного контраста (под выбором темы). */
export function initHighContrastToggle() {
const el = document.getElementById("wespHighContrastToggle");
if (!el) return;
if (el.dataset.bound !== "1") {
el.dataset.bound = "1";
el.addEventListener("change", () => setHighContrast(el.checked));
}
syncHighContrastUI();
}
/** @deprecated Используйте initThemePicker. Оставлен для обратной совместимости. */
export function initThemeToggle() {
if (document.getElementById("wespThemePicker")) {
initThemePicker();
return;
}
const el = document.getElementById("darkThemeToggle");
if (!el) return;
if (el.dataset.bound === "1") {
el.checked = document.documentElement.dataset.theme === "dark";
return;
}
el.dataset.bound = "1";
el.checked = document.documentElement.dataset.theme === "dark";
el.addEventListener("change", () => {
setTheme(el.checked ? "dark" : "light");
});
}