import { optionalEl, warnOnce } from '../core/app-dom.js'; const setTheme = (theme) => { document.documentElement.dataset.theme = theme; }; const isDarkTheme = (theme) => theme && theme.startsWith('dark'); const setIcon = (iconEl, theme) => { if (!iconEl) {return;} iconEl.classList.remove('bi-sun-fill', 'bi-moon-stars-fill'); iconEl.classList.add(isDarkTheme(theme) ? 'bi-moon-stars-fill' : 'bi-sun-fill'); }; const updateTheme = async (menu, theme) => { const url = menu.dataset.themeUrl; const csrfKey = menu.dataset.csrfKey; const csrfToken = menu.dataset.csrfToken; if (!url || !csrfKey || !csrfToken) { return true; } const body = new URLSearchParams({ theme, [csrfKey]: csrfToken }); const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', 'X-Requested-With': 'fetch' }, body }); return response.ok; }; const initThemeMenu = () => { const menu = optionalEl('[data-theme-menu]'); if (!menu) {return;} const icon = menu.querySelector('[data-theme-icon]'); const options = Array.from(menu.querySelectorAll('[data-theme-option]')); if (!options.length) { warnOnce('UI_EL_MISSING', 'Missing theme options', { module: 'theme-toggle' }); return; } let pending = false; const getCurrent = () => document.documentElement.dataset.theme || ''; const setActive = (theme) => { options.forEach((option) => { const isActive = option.dataset.themeValue === theme; option.classList.toggle('active', isActive); if (isActive) { option.setAttribute('aria-current', 'true'); } else { option.removeAttribute('aria-current'); } }); }; setIcon(icon, getCurrent()); setActive(getCurrent()); options.forEach((option) => { option.addEventListener('click', async (event) => { event.preventDefault(); if (pending) {return;} const next = option.dataset.themeValue || ''; if (!next) {return;} const current = getCurrent(); if (next === current) {return;} pending = true; setTheme(next); setIcon(icon, next); setActive(next); const ok = await updateTheme(menu, next); if (!ok) { setTheme(current); setIcon(icon, current); setActive(current); } pending = false; }); }); }; const initThemeToggle = () => { const button = optionalEl('[data-theme-toggle]'); if (!button) {return;} const icon = button.querySelector('i'); let pending = false; const getCurrent = () => document.documentElement.dataset.theme || ''; setIcon(icon, getCurrent()); button.addEventListener('click', async () => { if (pending) {return;} const current = getCurrent(); const next = isDarkTheme(current) ? 'light' : 'dark'; pending = true; setTheme(next); setIcon(icon, next); const ok = await updateTheme(button, next); if (!ok) { setTheme(current); setIcon(icon, current); } pending = false; }); }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { initThemeMenu(); initThemeToggle(); }); } else { initThemeMenu(); initThemeToggle(); }