From dd1ede93d914563e02d1ed8f21574e0dd5c0d845 Mon Sep 17 00:00:00 2001 From: JY Hsu Date: Sun, 1 Sep 2024 00:38:50 +0800 Subject: [PATCH] feat: Add basic theme feature --- assets/scripts/features/index.js | 4 + assets/scripts/features/theme/index.js | 91 +++++++++++++++++++ .../partials/helpers/get-esbuild-options.html | 11 ++- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 assets/scripts/features/theme/index.js diff --git a/assets/scripts/features/index.js b/assets/scripts/features/index.js index 9682acf..9e4fb4f 100644 --- a/assets/scripts/features/index.js +++ b/assets/scripts/features/index.js @@ -10,6 +10,10 @@ if (process.env.FEATURE_DARKMODE === '1') { import('./darkmode') } +if (process.env.FEATURE_THEME === '1') { + import('./theme') +} + if (process.env.FEATURE_FLOWCHART === '1') { import('./flowchart') } diff --git a/assets/scripts/features/theme/index.js b/assets/scripts/features/theme/index.js new file mode 100644 index 0000000..e0da3eb --- /dev/null +++ b/assets/scripts/features/theme/index.js @@ -0,0 +1,91 @@ +const PERSISTENCE_KEY = 'lightmode:color-scheme' + +const THEME_DARK = typeof process.env.FEATURE_THEME_DARK === 'undefined' ? false : process.env.FEATURE_THEME_DARK; +const THEME_LIGHT = typeof process.env.FEATURE_THEME_LIGHT === 'undefined' ? false : process.env.FEATURE_THEME_LIGHT; +const THEME_DEFAULT = typeof process.env.FEATURE_THEME_DEFAULT === 'system' ? false : process.env.FEATURE_THEME_DEFAULT; + +window.addEventListener('load', async () => { + const menu = document.getElementById('themeMenu') + const $icon = document.getElementById('navbar-theme-icon-svg') + if (menu == null || $icon == null) return + + const btns = menu.getElementsByTagName('a') + const iconMap = Array.from(btns).reduce((map, btn) => { + const $img = btn.getElementsByTagName('img')[0] + map[btn.dataset.scheme] = $img.src + return map + }, {}) + + + function checkScheme(scheme) { + if (scheme === "light" && THEME_LIGHT) { + return "light" + } + if (scheme === "dark" && THEME_DARK) { + return "dark" + } + return "system" + } + + function loadScheme() { + return localStorage.getItem(PERSISTENCE_KEY) || loadDefaultScheme() + } + + function loadDefaultScheme() { + return THEME_DEFAULT || "system" + } + + function saveScheme(scheme) { + localStorage.setItem(PERSISTENCE_KEY, scheme) + } + + function getPreferredColorScheme() { + const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; + return isDarkMode ? "dark" : "light"; + } + + function setScheme(newScheme) { + let theme = newScheme + if (newScheme === 'system') { + theme = getPreferredColorScheme() + } + // set data-theme attribute on html tag + document.querySelector("html").dataset.theme = theme; + + // update icon + $icon.src = iconMap[newScheme] + + // save preference to local storage + saveScheme(newScheme) + + setImages(theme) + } + + setScheme(loadScheme()) + + Array.from(menu.getElementsByTagName('a')).forEach((btn) => { + btn.addEventListener('click', () => { + const { scheme } = btn.dataset + setScheme(scheme) + }) + }) +}) + +function setImages(newScheme) { + const els = Array.from(document.getElementsByClassName('logo-holder')); + for (const el of els) { + const light = el.querySelector('.light-logo'); + const dark = el.querySelector('.dark-logo'); + + + + if (newScheme === "dark" && dark !== null) { + if (light !== null) light.style.display = 'none' + dark.style.display = 'inline' + } + else { + if (light !== null) light.style.display = 'inline' + if (dark !== null) dark.style.display = 'none' + } + } +} \ No newline at end of file diff --git a/layouts/partials/helpers/get-esbuild-options.html b/layouts/partials/helpers/get-esbuild-options.html index e582786..bdc4d32 100644 --- a/layouts/partials/helpers/get-esbuild-options.html +++ b/layouts/partials/helpers/get-esbuild-options.html @@ -69,15 +69,24 @@ params: # id: foo # name: bar - # The `darkMode` feature + # The `darkMode` feature. It's a deprecated setting but keep backward compatibility darkmode: enable: true + # Configure theme settings + theme: + enable: true + services: + light: true # enable light theme. default "true" + dark: true # enable dark theme. default "true" + defaultTheme: light # can be either light, dark or system. default "system" + This helper will convert the above config into the following env vars: * `FEATURE_ANALYTICS=1` * `FEATURE_ANALYTICS_GOOGLE=1` * `FEATURE_DARKMODE=1` + * `FEATURE_THEME=1` In JS, you can use it like this: