57 lines
1.3 KiB
TypeScript
57 lines
1.3 KiB
TypeScript
|
import React, { createContext, FC, useContext, useEffect, useState } from "react";
|
||
|
|
||
|
const getInitialTheme = (): Mode => {
|
||
|
if (typeof window === "undefined") {
|
||
|
return 'auto';
|
||
|
}
|
||
|
const text = window?.localStorage?.getItem('theme');
|
||
|
switch (text) {
|
||
|
case 'dark': return 'dark';
|
||
|
case 'light': return 'light';
|
||
|
default: return 'auto';
|
||
|
}
|
||
|
};
|
||
|
|
||
|
type RawMode = "light" | "dark";
|
||
|
type Mode = RawMode | "auto";
|
||
|
|
||
|
interface Content {
|
||
|
mode: Mode;
|
||
|
setMode: (mode: Mode) => void;
|
||
|
}
|
||
|
|
||
|
export const ThemeContext = createContext<Content>({
|
||
|
mode: 'error' as Mode,
|
||
|
setMode: (_) => {console.log},
|
||
|
});
|
||
|
|
||
|
export const ThemeProvider: FC = ({ children }) => {
|
||
|
const [mode, setMode] = useState<Mode>(() => getInitialTheme());
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (window) {
|
||
|
localStorage.setItem("theme", mode);
|
||
|
let _mode = mode;
|
||
|
if (_mode === "auto") {
|
||
|
_mode =
|
||
|
window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||
|
}
|
||
|
if (_mode === "dark") {
|
||
|
window.document.documentElement.classList.add("dark");
|
||
|
} else {
|
||
|
window.document.documentElement.classList.remove("dark");
|
||
|
}
|
||
|
}
|
||
|
}, [mode]);
|
||
|
|
||
|
return (
|
||
|
<ThemeContext.Provider value={{ mode, setMode }}>
|
||
|
{children}
|
||
|
</ThemeContext.Provider>
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export function useTheme () {
|
||
|
return useContext(ThemeContext);
|
||
|
}
|