144 lines
4.2 KiB
JavaScript
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");
|
|
});
|
|
}
|