54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { createSignal, createContext, useContext, createEffect, type ParentComponent } from "solid-js";
|
|
|
|
type Theme = "dark" | "light" | "system";
|
|
|
|
interface ThemeProviderState {
|
|
theme: () => Theme;
|
|
setTheme: (theme: Theme) => void;
|
|
resolvedTheme: () => "light" | "dark";
|
|
}
|
|
|
|
const ThemeProviderContext = createContext<ThemeProviderState>();
|
|
|
|
export const ThemeProvider: ParentComponent = (props) => {
|
|
const [theme, setTheme] = createSignal<Theme>("system");
|
|
const [resolvedTheme, setResolvedTheme] = createSignal<"light" | "dark">("light");
|
|
|
|
// Load from local storage on mount
|
|
const storedTheme = localStorage.getItem("tasgrid-theme") as Theme | null;
|
|
if (storedTheme) {
|
|
setTheme(storedTheme);
|
|
}
|
|
|
|
createEffect(() => {
|
|
const root = window.document.documentElement;
|
|
root.classList.remove("light", "dark");
|
|
|
|
const currentTheme = theme();
|
|
let resolved: "light" | "dark";
|
|
|
|
if (currentTheme === "system") {
|
|
resolved = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
root.classList.add(resolved);
|
|
} else {
|
|
resolved = currentTheme === "dark" ? "dark" : "light";
|
|
root.classList.add(resolved);
|
|
}
|
|
|
|
setResolvedTheme(resolved);
|
|
localStorage.setItem("tasgrid-theme", currentTheme);
|
|
});
|
|
|
|
return (
|
|
<ThemeProviderContext.Provider value={{ theme, setTheme, resolvedTheme }}>
|
|
{props.children}
|
|
</ThemeProviderContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useTheme = () => {
|
|
const context = useContext(ThemeProviderContext);
|
|
if (!context) throw new Error("useTheme must be used within a ThemeProvider");
|
|
return context;
|
|
};
|