From 22361db2a9cda40d53b683672f649df0fc6563e3 Mon Sep 17 00:00:00 2001 From: Lightling <contact@lightling.xyz> Date: Sun, 20 Apr 2025 15:19:17 -0400 Subject: [PATCH] system theme support + as default --- .../src/components/shared/theme-picker.vue | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/projects/frontend/src/components/shared/theme-picker.vue b/projects/frontend/src/components/shared/theme-picker.vue index d156b32..b4ae4c7 100644 --- a/projects/frontend/src/components/shared/theme-picker.vue +++ b/projects/frontend/src/components/shared/theme-picker.vue @@ -3,20 +3,39 @@ import { computed, onMounted, ref } from 'vue' import PrimeVueSelect from 'primevue/select' -import type { SiteTheme } from '@goldenwere/mackenzii-types' +import type { SiteTheme, SiteThemeType } from '@goldenwere/mackenzii-types' import { injectStylesheet } from 'src/utilities/dom' import { storage } from 'src/utilities/fetch' import { useRouteStore } from 'src/routes' +type Theme = SiteTheme & { id: string } +const systemTheme = { + id: 'SYSTEM', + displayName: 'System', + type: null as any, + url: null as any, +} + const routeStore = useRouteStore() const globalConfig = routeStore._globals const options = ref(globalConfig.themes) -const optionsArray = computed(() => Object.keys(options.value).map(key => ({...options.value[key], id: key}))) +const optionsArray = computed<Theme[]>(() => { + const val = Object.keys(options.value).map(key => ({...options.value[key], id: key})) + if (val.find(other => other.type === 'dark') && val.find(other => other.type === 'light')) { + val.push(systemTheme) + } + return val +}) const currentTheme = ref<SiteTheme & { id: string }>(null as any) +const currentSystem = ref<SiteThemeType>(null as any) let node: HTMLLinkElement const setModeClass = () => { - switch (currentTheme.value.type) { + const type = currentTheme.value.id === 'SYSTEM' + ? currentSystem.value + : currentTheme.value.type + + switch (type) { case 'dark': document.body.parentElement!.classList.add('app-dark') document.body.parentElement!.classList.remove('app-dark-hc') @@ -50,29 +69,63 @@ const setModeClass = () => { } } -const onThemeChosen = (event: Event) => { +const getUrlOfTheme = (id: string) => { + if (id === 'SYSTEM') { + return optionsArray.value.find(other => other.type === currentSystem.value)!.url + } else { + return globalConfig.themes[id].url + } +} + +const onThemeChosen = (event?: Event) => { const themeId = currentTheme.value.id storage.write(`${globalConfig.id}::currentTheme`, themeId) - node.setAttribute('href', globalConfig.themes[themeId].url) + node.setAttribute('href', getUrlOfTheme(themeId)) setModeClass() } +const onSystemThemeChanged = (query: MediaQueryListEvent | MediaQueryList) => { + if (query.matches) { + currentSystem.value = 'dark' + } else { + currentSystem.value = 'light' + } + onThemeChosen() +} + +const setupSystemThemeListener = () => { + const themeListener = window.matchMedia('(prefers-color-scheme: dark)') + onSystemThemeChanged(themeListener) + themeListener.addEventListener('change', onSystemThemeChanged) +} + +const loadThemeFromStorage = () => { + const themeId = storage.read<string>(`${globalConfig.id}::currentTheme`) || ( + !!optionsArray.value.find(other => other.id === 'SYSTEM') ? 'SYSTEM' : Object.keys(globalConfig.themes)[0] + ) + currentTheme.value = themeId === 'SYSTEM' ? systemTheme : { ...options.value[themeId], id: themeId } + node = injectStylesheet(getUrlOfTheme(themeId), 'theme-stylesheet') +} + onMounted(async () => { - const themeId = storage.read<string>(`${globalConfig.id}::currentTheme`) || Object.keys(globalConfig.themes)[0] - currentTheme.value = { ...options.value[themeId], id: themeId } - node = injectStylesheet(globalConfig.themes[themeId].url, 'theme-stylesheet') + loadThemeFromStorage() + setupSystemThemeListener() setModeClass() }) </script> <template lang="pug"> .theme-picker + label( + for='site theme' + ) Theme PrimeVueSelect( v-model='currentTheme' :options='optionsArray' @change='onThemeChosen($event)' optionLabel='displayName' placeholder='theme' + name='site theme' ) </template>