@@ -0,0 +1,143 @@
|
||||
/** Общая тема зоотех-страниц: 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");
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user