Skip to content

Commit

Permalink
Add themeSwitcher for more >2 themes as OcDrop, further streamline th…
Browse files Browse the repository at this point in the history
…eming logic
  • Loading branch information
pascalwengerter committed Nov 7, 2023
1 parent a231701 commit 8ad955a
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 45 deletions.
28 changes: 21 additions & 7 deletions packages/web-pkg/src/composables/piniaStores/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,40 @@ export const useThemeStore = defineStore('theme', () => {
const hasOnlyTwoThemesForLightDarkMode = computed(
() =>
availableThemes.value.length === 2 &&
availableThemes.value.some((x) => x.isDark === true) &&
availableThemes.value.some((x) => x.isDark !== true)
availableThemes.value.some((t) => t.isDark === true) &&
availableThemes.value.some((t) => t.isDark !== true)
)

const initializeThemes = (themes: WebTheme[], newCommonTheme: CommonTheme) => {
availableThemes.value = themes
commonTheme.value = newCommonTheme

const currentThemeName = useLocalStorage('oc_currentThemeName', null) // note: use null as default so that we can fall back to system preferences
// Set default fallback theme names
// Set default theme name as fallback
if (unref(currentThemeName) === null) {
currentThemeName.value = usePreferredDark().value ? themeNameDark : themeNameLight
const isDark = usePreferredDark()
currentThemeName.value = isDark.value ? themeNameDark : themeNameLight
}

// TODO: Fix this by passing full theme?
// TODO: Discuss handling (former) default scenario
setCurrentTheme(availableThemes.value.find((x) => x.general.name === currentThemeName.value))
setAndApplyTheme(availableThemes.value.find((t) => t.general.name === currentThemeName.value))
}

const setCurrentTheme = (theme: WebTheme) => {
const setAndApplyTheme = (theme: WebTheme) => {
currentTheme.value = theme

for (const param in currentTheme.value.designTokens.colorPalette) {
;(document.querySelector(':root') as HTMLElement).style.setProperty(
`--oc-color-${param}`,
theme.designTokens.colorPalette[param]
)
}
}

// This should only be used with hasOnlyTwoThemesForLightDarkMode - we know there's exactly two themes, one with darkMode and one without
const toggleTheme = () => {
setAndApplyTheme(availableThemes.value.find((t) => t.isDark !== currentTheme.value.isDark))
}

return {
Expand All @@ -78,6 +91,7 @@ export const useThemeStore = defineStore('theme', () => {
hasOnlyOneTheme,
hasOnlyTwoThemesForLightDarkMode,
initializeThemes,
setCurrentTheme
setAndApplyTheme,
toggleTheme
}
})
76 changes: 42 additions & 34 deletions packages/web-runtime/src/components/Topbar/ThemeSwitcher.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<!-- TODO: Only show if themes.length === 2 && 1 theme isDark === true && 1 theme isDark === false -->
<oc-button
v-if="hasOnlyTwoThemesForLightDarkMode"
v-oc-tooltip="buttonLabel"
class="themeswitcher-btn"
:aria-label="buttonLabel"
Expand All @@ -11,55 +11,63 @@
<span class="oc-visible@s" :aria-label="switchLabel" />
<oc-icon :name="switchIcon" fill-type="line" variation="inherit" />
</oc-button>
<!-- TODO: Add button + ocdrop for >2 themes or (themes.length === 2 && both isDark || !isDark) -->
<template v-else-if="availableThemes.length > 2">
<oc-button
id="files-view-options-btn"
key="files-view-options-btn"
v-oc-tooltip="buttonLabel"
data-testid="files-view-options-btn"
:aria-label="buttonLabel"
appearance="raw"
class="oc-my-s oc-p-xs"
>
<oc-icon name="settings-3" fill-type="line" />
</oc-button>
<oc-drop
drop-id="files-view-options-drop"
toggle="#files-view-options-btn"
mode="click"
class="oc-width-auto"
padding-size="medium"
>
<oc-list>
<li
v-for="theme in availableThemes"
:key="theme.general.name"
class="files-view-options-list-item"
>
<oc-button @click="setAndApplyTheme(theme)">{{ theme.general.name }}</oc-button>
</li>
</oc-list>
</oc-drop>
</template>
</template>
<script lang="ts">
import { computed, unref, watch, defineComponent, ref } from 'vue'
import { computed, defineComponent } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useStore, useLocalStorage } from '@ownclouders/web-pkg'
import { themeNameDark, themeNameLight, useDefaultThemeName } from '../../composables'
// TODO: Consider using https://vueuse.org/core/useColorMode/#usecolormode
import { useThemeStore } from '@ownclouders/web-pkg'
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const { $gettext } = useGettext()
const store = useStore()
const themeStore = useThemeStore()
const currentThemeName = ref('')
const { setAndApplyTheme, toggleTheme } = themeStore
// TODO: Move to theme store?
currentThemeName.value = useLocalStorage('oc_currentThemeName', useDefaultThemeName())
const currentTheme = computed(() => store.getters.configuration.themes[unref(currentThemeName)])
const applyTheme = (theme) => {
for (const param in theme.designTokens.colorPalette) {
;(document.querySelector(':root') as HTMLElement).style.setProperty(
`--oc-color-${param}`,
theme.designTokens.colorPalette[param]
)
}
}
const { availableThemes, currentTheme, hasOnlyTwoThemesForLightDarkMode } =
storeToRefs(themeStore)
const buttonLabel = computed(() => $gettext('Click to switch theme'))
const switchLabel = computed(() => $gettext('Currently used theme'))
watch(currentThemeName, async () => {
await store.dispatch('loadTheme', { theme: unref(currentTheme) })
applyTheme(unref(currentTheme))
})
const isLightTheme = computed(() => [null, themeNameLight].includes(currentThemeName.value))
const switchIcon = computed(() => (isLightTheme.value ? 'sun' : 'moon-clear'))
const toggleTheme = () => {
currentThemeName.value = isLightTheme.value ? themeNameDark : themeNameLight
}
const switchIcon = computed(() => (currentTheme.value.isDark === false ? 'sun' : 'moon-clear'))
return {
availableThemes,
buttonLabel,
currentThemeName,
currentTheme,
hasOnlyTwoThemesForLightDarkMode,
setAndApplyTheme,
switchIcon,
switchLabel,
toggleTheme
Expand Down
1 change: 0 additions & 1 deletion packages/web-runtime/src/components/Topbar/TopBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,3 @@ export default {
}
}
</style>
@ownclouders/web-pkg/src/composables/piniaStores/theme
8 changes: 5 additions & 3 deletions packages/web-runtime/src/container/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { merge } from 'lodash-es'
import { AppConfigObject } from '@ownclouders/web-pkg'
import { MESSAGE_TYPE } from '@ownclouders/web-client/src/sse'
import { getQueryParam } from '../helpers/url'
import { storeToRefs } from 'pinia'

/**
* fetch runtime configuration, this step is optional, all later steps can use a static
Expand Down Expand Up @@ -283,15 +284,16 @@ export const announceTheme = async ({
designSystem: any
runtimeConfiguration?: RuntimeConfiguration
}): Promise<void> => {
// TODO: StoreToRefs, or nah?
const { currentTheme, initializeThemes } = useThemeStore()
const themeStore = useThemeStore()
const { initializeThemes } = themeStore
const { currentTheme } = storeToRefs(themeStore)

const { web, common } = await loadTheme(runtimeConfiguration?.theme)

await initializeThemes([web], common)

app.use(designSystem, {
tokens: currentTheme.designTokens
tokens: currentTheme.value.designTokens
})
}

Expand Down

0 comments on commit 8ad955a

Please sign in to comment.