@flanksource/clicky-ui
Version:
Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.
69 lines (68 loc) • 2.32 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { useContext, createContext, useState, useEffect, useCallback, useMemo } from "react";
const STORAGE_KEY = "clicky-ui-theme";
const DATA_ATTR = "data-theme";
const ThemeContext = createContext(null);
function prefersDark() {
if (typeof window === "undefined") return false;
return window.matchMedia("(prefers-color-scheme: dark)").matches;
}
function readStored() {
if (typeof window === "undefined") return "system";
const raw = window.localStorage.getItem(STORAGE_KEY);
return raw === "light" || raw === "dark" || raw === "system" ? raw : "system";
}
function resolve(theme) {
if (theme === "system") return prefersDark() ? "dark" : "light";
return theme;
}
function apply(resolved) {
if (typeof document === "undefined") return;
document.documentElement.setAttribute(DATA_ATTR, resolved);
}
function ThemeProvider({
children,
defaultTheme = "system",
storageKey = STORAGE_KEY
}) {
const [theme, setThemeState] = useState(() => {
if (typeof window === "undefined") return defaultTheme;
return readStored() ?? defaultTheme;
});
const [resolvedTheme, setResolvedTheme] = useState(() => resolve(theme));
useEffect(() => {
const next = resolve(theme);
setResolvedTheme(next);
apply(next);
if (typeof window !== "undefined") {
window.localStorage.setItem(storageKey, theme);
}
}, [theme, storageKey]);
useEffect(() => {
if (theme !== "system" || typeof window === "undefined") return;
const mq = window.matchMedia("(prefers-color-scheme: dark)");
const onChange = () => {
const next = mq.matches ? "dark" : "light";
setResolvedTheme(next);
apply(next);
};
mq.addEventListener("change", onChange);
return () => mq.removeEventListener("change", onChange);
}, [theme]);
const setTheme = useCallback((next) => setThemeState(next), []);
const value = useMemo(
() => ({ theme, resolvedTheme, setTheme }),
[theme, resolvedTheme, setTheme]
);
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
}
function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("useTheme must be used inside <ThemeProvider>");
return ctx;
}
export {
ThemeProvider,
useTheme
};
//# sourceMappingURL=use-theme.js.map