This commit is contained in:
Denozordec committed 2026-05-03 11:16:07 +07:00
1 parent ce00c4c671
commit bdb9b72fac
66 files changed
+9553 -1547

No files matched your search

+15 -80
View File
@@ -1,90 +1,25 @@
"use client"
import { createContext, useContext, useEffect, useState } from "react"
// ── types ──────────────────────────────────────────────────────────────────────
type ThemeValue = "dark" | "light" | "system"
type ResolvedTheme = "dark" | "light"
interface ThemeContextValue {
theme: ThemeValue
resolvedTheme: ResolvedTheme
setTheme: (t: ThemeValue) => void
}
// ── context ────────────────────────────────────────────────────────────────────
const ThemeContext = createContext<ThemeContextValue>({
theme: "dark",
resolvedTheme: "dark",
setTheme: () => {},
})
// ── provider ───────────────────────────────────────────────────────────────────
export interface ThemeProviderProps {
children: React.ReactNode
defaultTheme?: ThemeValue
/** kept for API compat with next-themes — unused, class is always used */
attribute?: string
disableTransitionOnChange?: boolean
}
import { ThemeProvider as NextThemesProvider } from "next-themes"
import type { ComponentProps } from "react"
/** Обёртка над next-themes: тот же storageKey `rl-theme`, класс на `<html>`. */
export function ThemeProvider({
children,
defaultTheme = "dark",
disableTransitionOnChange = false,
}: ThemeProviderProps) {
const [theme, setThemeState] = useState<ThemeValue>(() => {
if (typeof window === "undefined") return defaultTheme
return (localStorage.getItem("rl-theme") as ThemeValue | null) ?? defaultTheme
})
const [systemDark, setSystemDark] = useState<boolean>(() => {
if (typeof window === "undefined") return true
return window.matchMedia("(prefers-color-scheme: dark)").matches
})
// Track system preference
useEffect(() => {
const mq = window.matchMedia("(prefers-color-scheme: dark)")
const handler = (e: MediaQueryListEvent) => setSystemDark(e.matches)
mq.addEventListener("change", handler)
return () => mq.removeEventListener("change", handler)
}, [])
const resolvedTheme: ResolvedTheme =
theme === "system" ? (systemDark ? "dark" : "light") : theme
// Apply class to <html>
useEffect(() => {
const root = document.documentElement
if (disableTransitionOnChange) {
root.style.setProperty("transition", "none")
void root.offsetHeight // force reflow
setTimeout(() => root.style.removeProperty("transition"), 0)
}
root.classList.toggle("dark", resolvedTheme === "dark")
root.classList.toggle("light", resolvedTheme === "light")
}, [resolvedTheme, disableTransitionOnChange])
const setTheme = (t: ThemeValue) => {
setThemeState(t)
try { localStorage.setItem("rl-theme", t) } catch {}
}
...props
}: ComponentProps<typeof NextThemesProvider>) {
return (
<ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
<NextThemesProvider
attribute="class"
defaultTheme="dark"
enableSystem
storageKey="rl-theme"
disableTransitionOnChange
{...props}
>
{children}
</ThemeContext.Provider>
</NextThemesProvider>
)
}
// ── hook (drop-in replacement for next-themes useTheme) ───────────────────────
export function useTheme(): ThemeContextValue {
return useContext(ThemeContext)
}
export { useTheme } from "next-themes"