100 lines
2.5 KiB
TypeScript
100 lines
2.5 KiB
TypeScript
import {
|
|
createSignal,
|
|
createEffect,
|
|
onCleanup,
|
|
onMount,
|
|
type VoidComponent,
|
|
} from "solid-js";
|
|
|
|
import Sun from "@icons/sun.svg?raw";
|
|
import Moon from "@icons/moon.svg?raw";
|
|
|
|
interface ThemeProps {
|
|
theme: "light" | "dark";
|
|
system: "light" | "dark";
|
|
}
|
|
|
|
const getCurrentTheme = (): ThemeProps => {
|
|
if (
|
|
typeof localStorage !== "undefined" &&
|
|
(localStorage.theme === "dark" ||
|
|
(!("theme" in localStorage) &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches))
|
|
) {
|
|
return {
|
|
theme: "dark",
|
|
system: window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
? "dark"
|
|
: "light",
|
|
};
|
|
}
|
|
return {
|
|
theme: "light",
|
|
system: window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
? "dark"
|
|
: "light",
|
|
};
|
|
};
|
|
|
|
const updateTheme = () => {
|
|
document.documentElement.classList.add("changing-theme");
|
|
if (
|
|
localStorage.theme === "dark" ||
|
|
(!("theme" in localStorage) &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches)
|
|
) {
|
|
document.documentElement.classList.add("dark");
|
|
} else {
|
|
document.documentElement.classList.remove("dark");
|
|
}
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
document.documentElement.classList.remove("changing-theme");
|
|
});
|
|
});
|
|
};
|
|
|
|
export const ThemeSelect: VoidComponent = () => {
|
|
const [currentTheme, setCurrentTheme] = createSignal<ThemeProps>({
|
|
theme: "dark",
|
|
system: "dark",
|
|
});
|
|
const [mounted, setMounted] = createSignal(false);
|
|
|
|
const toggleTheme = () => {
|
|
if (!mounted()) return;
|
|
setCurrentTheme(getCurrentTheme());
|
|
if (currentTheme()!.theme !== currentTheme()!.system) {
|
|
localStorage.removeItem("theme");
|
|
} else {
|
|
localStorage.theme = currentTheme()!.theme === "dark" ? "light" : "dark";
|
|
}
|
|
updateTheme();
|
|
setCurrentTheme(getCurrentTheme());
|
|
};
|
|
|
|
onMount(() => {
|
|
setMounted(true);
|
|
setCurrentTheme(getCurrentTheme());
|
|
});
|
|
|
|
createEffect(() => {
|
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
mediaQuery.addEventListener("change", updateTheme);
|
|
window.addEventListener("storage", updateTheme);
|
|
|
|
onCleanup(() => {
|
|
mediaQuery.removeEventListener("change", updateTheme);
|
|
window.removeEventListener("storage", updateTheme);
|
|
setMounted(false);
|
|
});
|
|
});
|
|
|
|
return (
|
|
<div
|
|
onclick={toggleTheme}
|
|
class="theme-toggle"
|
|
innerHTML={(mounted() && currentTheme().theme === "light") || currentTheme().system === "light" ? Sun : Moon}
|
|
/>
|
|
);
|
|
};
|