diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 93c5a17..004a7c4 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -2,7 +2,9 @@ import THEMES_JSON from "assets/themes.json"; import { hexToHSL } from "lib/colors"; import { camelToKebabCase } from "lib/util"; -export const THEMES: Record = THEMES_JSON; +export type ThemeName = keyof typeof THEMES_JSON; +export const THEMES: Record = THEMES_JSON; +export const THEME_NAMES = Object.keys(THEMES) as ThemeName[]; export interface Theme { base: string; @@ -18,19 +20,35 @@ export interface Theme { onMuted: string; } -export function applyTheme(theme: Theme) { +const LOCAL_STORAGE_THEME_KEY = "theme"; + +export function saveTheme(themeName?: ThemeName) { + if (!themeName) { + localStorage.removeItem(LOCAL_STORAGE_THEME_KEY); + return; + } + + localStorage.setItem(LOCAL_STORAGE_THEME_KEY, themeName); +} + +export function loadTheme(): ThemeName | null { + return localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as ThemeName | null; +} + +export function applyTheme(themeName: ThemeName, persistent = false) { + console.assert(THEME_NAMES.includes(themeName), "Set Theme does not exist"); + const style = document.documentElement.style; + const theme = THEMES[themeName]; + + type ThemeEntry = [keyof Theme, string]; - for (const [name, val] of Object.entries(theme) as [ - keyof Theme, - string, - ][]) { + for (const [name, val] of Object.entries(theme) as ThemeEntry[]) { let color = val; if (val.startsWith("@")) { const link = val.substring(1) as keyof Theme; color = theme[link]; - console.log(name, "links to " + link + " --> " + color); } const { h, s, l } = hexToHSL(color); @@ -38,4 +56,8 @@ export function applyTheme(theme: Theme) { style.setProperty("--color-" + camelToKebabCase(name), hsl); } + + if (persistent) { + saveTheme(themeName); + } } diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index 9224a6c..9483b57 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -13,7 +13,7 @@ import LanguageDetector from "i18next-browser-languagedetector"; import en_translation from "i18n/en.json"; import de_translation from "i18n/de.json"; -import { THEMES, applyTheme } from "lib/theme"; +import { THEMES, ThemeName, applyTheme, loadTheme } from "lib/theme"; i18n.use(LanguageDetector) .use(initReactI18next) @@ -33,7 +33,11 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); -applyTheme(Object.values(THEMES)[0]); +const defaultTheme = Object.keys(THEMES)[0] as ThemeName; +const setTheme = loadTheme(); +console.log("Default Theme:", defaultTheme); +console.log("Set Theme:", defaultTheme); +applyTheme(setTheme ?? defaultTheme); const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index f7a4fb0..26cb357 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -6,7 +6,7 @@ import { ButtonVariant, } from "components/lila/button"; import { TextInput } from "components/lila/input"; -import { THEMES, applyTheme } from "lib/theme"; +import { THEMES, ThemeName, applyTheme } from "lib/theme"; import { useState } from "react"; export function Debug() { @@ -14,7 +14,6 @@ export function Debug() { const variants = Object.keys(BUTTON_VARIANTS) as ButtonVariant[]; const [text, setText] = useState("Test"); - return (
setText(e.target.value || "Test")} > - applyTheme(e.target.value as ThemeName, true)} + > {Object.keys(THEMES).map((theme) => ( ))}