36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
/* =====================================================================
|
|
useTheme — 라이트/다크 토글 상태.
|
|
Topbar 와 동일한 lib/theme 단일 경로를 사용한다.
|
|
===================================================================== */
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import { applyTheme, readInitialTheme } from "../../lib/theme";
|
|
|
|
/** [dark, setDark] — Topbar 토글과 같은 키/속성을 사용. */
|
|
export function useTheme(): [boolean, (next: boolean) => void] {
|
|
const [dark, setDark] = useState<boolean>(() => {
|
|
if (typeof document !== "undefined") {
|
|
const attr = document.documentElement.getAttribute("data-theme");
|
|
if (attr === "dark") return true;
|
|
if (attr === "light") return false;
|
|
}
|
|
return readInitialTheme() === "dark";
|
|
});
|
|
|
|
useEffect(() => {
|
|
applyTheme(dark ? "dark" : "light");
|
|
}, [dark]);
|
|
|
|
// 다른 곳(Topbar)에서 속성을 바꾸면 따라잡기 위해 마운트 시 1회 동기화
|
|
useEffect(() => {
|
|
const root = document.documentElement;
|
|
const observer = new MutationObserver(() => {
|
|
const isDark = root.getAttribute("data-theme") === "dark";
|
|
setDark((prev) => (prev === isDark ? prev : isDark));
|
|
});
|
|
observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
const set = useCallback((next: boolean) => setDark(next), []);
|
|
return [dark, set];
|
|
}
|