AllAi/packages/ui/hooks/use-theme.tsx
2025-11-14 21:54:04 +03:00

76 lines
1.9 KiB
TypeScript

"use client";
import type { ReactNode } from "react";
import { createContext, useContext, useEffect, useState } from "react";
import { ThemeMode, themeVariables } from "../theme";
type ThemeContextValue = {
theme: ThemeMode;
setTheme: (theme: ThemeMode) => void;
toggle: () => void;
};
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
function applyTheme(theme: ThemeMode) {
if (typeof document === "undefined") return;
const root = document.documentElement;
const variables = themeVariables[theme];
Object.entries(variables).forEach(([key, value]) => {
root.style.setProperty(key, String(value));
});
root.dataset.theme = theme;
root.style.colorScheme = theme;
}
function detectTheme(): ThemeMode {
if (typeof window === "undefined") return "dark";
const stored = window.localStorage.getItem("allai-theme");
if (stored === "light" || stored === "dark") {
return stored;
}
return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
}
export function ThemeProvider({ initialTheme = "dark", children }: { initialTheme?: ThemeMode; children: ReactNode }) {
const [theme, setThemeState] = useState<ThemeMode>(initialTheme);
useEffect(() => {
const next = detectTheme();
setThemeState(next);
applyTheme(next);
}, []);
function setTheme(next: ThemeMode) {
setThemeState(next);
if (typeof window !== "undefined") {
window.localStorage.setItem("allai-theme", next);
applyTheme(next);
}
}
function toggle() {
setTheme(theme === "light" ? "dark" : "light");
}
return (
<ThemeContext.Provider
value={{
theme,
setTheme,
toggle
}}
>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}