79 lines
2.1 KiB
JavaScript
79 lines
2.1 KiB
JavaScript
/** 浅色/深色/跟随系统三态主题,使用共享 Nuxt 状态与 html.app-dark 驱动组件和原生控件。 */
|
|
import { computed } from "vue";
|
|
import { useState } from "#app";
|
|
// 存储不可用或值未知时安全回落 auto,不让浏览器隐私限制阻断页面初始化。
|
|
function n(e) {
|
|
try {
|
|
let t = (e ?? globalThis.localStorage)?.getItem(`theme`);
|
|
if (t === `light` || t === `dark`) return t;
|
|
} catch {}
|
|
return `auto`;
|
|
}
|
|
var r = [`light`, `dark`, `auto`],
|
|
i = {
|
|
light: `浅色`,
|
|
dark: `深色`,
|
|
auto: `跟随系统`,
|
|
},
|
|
a = {
|
|
light: `pi pi-sun`,
|
|
dark: `pi pi-moon`,
|
|
auto: `pi pi-desktop`,
|
|
},
|
|
o = `theme`;
|
|
function s() {
|
|
let s = useState(`theme-mode`, () => n()),
|
|
c = useState(
|
|
`theme-dark`,
|
|
() =>
|
|
typeof document < `u` &&
|
|
document.documentElement.classList.contains(`app-dark`),
|
|
),
|
|
l = () => window.matchMedia(`(prefers-color-scheme: dark)`);
|
|
// 仅 auto 读取系统偏好;显式 light/dark 不受系统变化影响。
|
|
function u() {
|
|
c.value = s.value === `dark` || (s.value === `auto` && l().matches);
|
|
}
|
|
function d() {
|
|
let e = document.documentElement;
|
|
(e.classList.toggle(`app-dark`, c.value),
|
|
(e.style.colorScheme = c.value ? `dark` : `light`));
|
|
}
|
|
function f() {
|
|
(u(), d());
|
|
}
|
|
let p = null,
|
|
m = () => {
|
|
s.value === `auto` && f();
|
|
};
|
|
// init 前先移除旧监听,dispose 供应用入口卸载时释放媒体查询监听。
|
|
function h() {
|
|
(p?.removeEventListener(`change`, m), (p = null));
|
|
}
|
|
function g() {
|
|
(h(), (s.value = n()), f(), (p = l()), p.addEventListener(`change`, m));
|
|
}
|
|
// 调用方传入已知模式;持久化失败不影响当前页面切换。
|
|
function _(e) {
|
|
s.value = e;
|
|
try {
|
|
localStorage.setItem(o, e);
|
|
} catch {}
|
|
f();
|
|
}
|
|
function v() {
|
|
_(r[(r.indexOf(s.value) + 1) % r.length]);
|
|
}
|
|
return {
|
|
mode: s,
|
|
isDark: c,
|
|
init: g,
|
|
dispose: h,
|
|
set: _,
|
|
cycle: v,
|
|
label: computed(() => i[s.value]),
|
|
icon: computed(() => a[s.value]),
|
|
};
|
|
}
|
|
export { s as t };
|