Yimaru-Admin/src/lib/theme.ts
Yared Yemane 2c3f0da6f7 feat(admin): payments, settings tabs, theme, and navigation refresh
Add admin payments with status, provider, and plan category filters. Introduce app versions and subscription plan management in settings, change-password security flow, and dark theme support. Reorganize sidebar, improve activity log actor details, analytics, and related UI polish.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 06:54:58 -07:00

65 lines
2.0 KiB
TypeScript

export type ThemeMode = "light" | "dark" | "system"
export type ResolvedTheme = "light" | "dark"
export const THEME_STORAGE_KEY = "yimaru-admin-theme"
const MEDIA_QUERY = "(prefers-color-scheme: dark)"
export function getSystemTheme(): ResolvedTheme {
if (typeof window === "undefined") return "light"
return window.matchMedia(MEDIA_QUERY).matches ? "dark" : "light"
}
/** Resolved appearance: light mode always forces light; dark forces dark; system follows OS. */
export function resolveTheme(mode: ThemeMode): ResolvedTheme {
if (mode === "light") return "light"
if (mode === "dark") return "dark"
return getSystemTheme()
}
export function getStoredTheme(): ThemeMode {
if (typeof window === "undefined") return "light"
const value = localStorage.getItem(THEME_STORAGE_KEY)
if (value === "light" || value === "dark" || value === "system") return value
return "light"
}
function syncMetaThemeColor(resolved: ResolvedTheme) {
if (typeof document === "undefined") return
let meta = document.querySelector('meta[name="theme-color"]') as HTMLMetaElement | null
if (!meta) {
meta = document.createElement("meta")
meta.name = "theme-color"
document.head.appendChild(meta)
}
meta.content = resolved === "dark" ? "#12121a" : "#f5f5f5"
}
export function applyTheme(mode: ThemeMode): ResolvedTheme {
const resolved = resolveTheme(mode)
const root = document.documentElement
root.classList.remove("dark")
if (resolved === "dark") {
root.classList.add("dark")
}
root.dataset.theme = resolved
root.dataset.themePreference = mode
root.style.colorScheme = resolved
syncMetaThemeColor(resolved)
return resolved
}
export function watchSystemTheme(onChange: (resolved: ResolvedTheme) => void): () => void {
if (typeof window === "undefined") return () => undefined
const media = window.matchMedia(MEDIA_QUERY)
const handler = () => onChange(getSystemTheme())
media.addEventListener("change", handler)
return () => media.removeEventListener("change", handler)
}