feat: add color scheme setting

This commit is contained in:
Dominik Pschenitschni 2021-11-12 14:34:36 +01:00
parent b1425e8c4a
commit 817845a70b
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
8 changed files with 132 additions and 2 deletions

View File

@ -21,6 +21,7 @@
"@sentry/tracing": "6.14.1",
"@sentry/vue": "6.14.1",
"@vue/compat": "3.2.21",
"@vueuse/core": "^6.8.0",
"bulma-css-variables": "^0.9.33",
"camel-case": "4.1.2",
"codemirror": "5.63.3",

View File

@ -36,6 +36,7 @@ import ContentLinkShare from './components/home/contentLinkShare'
import ContentNoAuth from './components/home/contentNoAuth'
import {setLanguage} from './i18n'
import AccountDeleteService from '@/services/accountDelete'
import {useColorScheme} from '@/composables/useColorScheme'
export default defineComponent({
name: 'app',
@ -63,6 +64,9 @@ export default defineComponent({
setLanguage()
},
setup() {
useColorScheme()
},
created() {
// Make sure to always load the home route when running with electron
if (this.$route.fullPath.endsWith('frontend/index.html')) {

View File

@ -0,0 +1,48 @@
import {computed, watch, readonly} from 'vue'
import {useStorage, createSharedComposable, ColorSchemes, usePreferredColorScheme, tryOnMounted} from '@vueuse/core'
const STORAGE_KEY = 'color-scheme'
const DEFAULT_COLOR_SCHEME_SETTING: ColorSchemes = 'light'
const CLASS_DARK = 'dark'
const CLASS_LIGHT = 'light'
// This is built upon the vueuse useDark
// Main differences:
// - usePreferredColorScheme
// - doesn't allow setting via the `isDark` ref.
// - instead the store is exposed
// - value is synced via `createSharedComposable`
// https://github.com/vueuse/vueuse/blob/main/packages/core/useDark/index.ts
export const useColorScheme = createSharedComposable(() => {
const store = useStorage<ColorSchemes>(STORAGE_KEY, DEFAULT_COLOR_SCHEME_SETTING)
const preferredColorScheme = usePreferredColorScheme()
const isDark = computed<boolean>(() => {
if (store.value !== 'auto') {
return store.value === 'dark'
}
const autoColorScheme = preferredColorScheme.value === 'no-preference'
? DEFAULT_COLOR_SCHEME_SETTING
: preferredColorScheme.value
return autoColorScheme === 'dark'
})
function onChanged(v: boolean) {
const el = window?.document.querySelector('html')
el?.classList.toggle(CLASS_DARK, v)
el?.classList.toggle(CLASS_LIGHT, !v)
}
watch(isDark, onChanged, { flush: 'post' })
tryOnMounted(() => onChanged(isDark.value))
return {
store,
isDark: readonly(isDark),
}
})

View File

@ -102,6 +102,15 @@
"disabled": "Disabled",
"todoist": "Todoist",
"vikunja": "Vikunja"
},
"appearance": {
"title": "Color Scheme",
"setSuccess": "Saved change of color scheme to {colorScheme}",
"colorScheme": {
"light": "Light",
"system": "System",
"dark": "Dark"
}
}
},
"deletion": {

View File

@ -81,7 +81,7 @@
--card-border-color: var(--grey-200);
@media (prefers-color-scheme: dark) {
&.dark {
// Light mode colours reversed for dark mode
--grey-900-hsl: 210, 20%, 98%;
--grey-900: hsl(var(--grey-900-hsl));

View File

@ -8,7 +8,7 @@
--shadow-lg: 0 15px 25px hsla(var(--grey-500-hsl), .12),
0 5px 10px hsla(var(--grey-500-hsl), .05);
@media (prefers-color-scheme: dark) {
&.dark {
// Even darker shadows for dark mode
--shadow-xs: 0 1px 3px hsla(var(--grey-100-hsl),.12),
0 1px 2px hsla(var(--grey-100-hsl), .24);

View File

@ -90,6 +90,21 @@
</div>
</label>
</div>
<div class="field">
<label class="is-flex is-align-items-center">
<span>
{{ $t('user.settings.appearance.title') }}
</span>
<div class="select ml-2">
<select v-model="activeColorSchemeSetting">
<!-- TODO: use the Vikunja logo in color scheme as option buttons -->
<option v-for="(title, schemeId) in colorSchemeSettings" :key="schemeId" :value="schemeId">
{{ title }}
</option>
</select>
</div>
</label>
</div>
<x-button
:loading="userSettingsService.loading"
@ -102,6 +117,9 @@
</template>
<script>
import {computed, watch} from 'vue'
import {useI18n} from 'vue-i18n'
import {playSoundWhenDoneKey} from '@/helpers/playPop'
import {availableLanguages, saveLanguage, getCurrentLanguage} from '@/i18n'
import {getQuickAddMagicMode, setQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
@ -110,6 +128,29 @@ import {PrefixMode} from '@/modules/parseTaskText'
import ListSearch from '@/components/tasks/partials/listSearch'
import {createRandomID} from '@/helpers/randomId'
import {playPop} from '@/helpers/playPop'
import {useColorScheme} from '@/composables/useColorScheme'
import {success} from '@/message'
function useColorSchemeSetting() {
const { t } = useI18n()
const colorSchemeSettings = computed(() => ({
light: t('user.settings.appearance.colorScheme.light'),
auto: t('user.settings.appearance.colorScheme.system'),
dark: t('user.settings.appearance.colorScheme.dark'),
}))
const {store} = useColorScheme()
watch(store, (schemeId) => {
success({message: t('user.settings.appearance.setSuccess', {
colorScheme: colorSchemeSettings.value[schemeId],
})})
})
return {
colorSchemeSettings,
activeColorSchemeSetting: store,
}
}
function getPlaySoundWhenDoneSetting() {
return localStorage.getItem(playSoundWhenDoneKey) === 'true' || localStorage.getItem(playSoundWhenDoneKey) === null
@ -141,6 +182,13 @@ export default {
return this.$store.getters['lists/getListById'](this.settings.defaultListId)
},
},
setup() {
return {
...useColorSchemeSetting(),
}
},
mounted() {
this.setTitle(`${this.$t('user.settings.general.title')} - ${this.$t('user.settings.title')}`)
},

View File

@ -2844,6 +2844,21 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.21.tgz#4cd80c0e62cf65a7adab2449e86b6f0cb33a130b"
integrity sha512-5EQmIPK6gw4UVYUbM959B0uPsJ58+xoMESCZs3N89XyvJ9e+fX4pqEPrOGV8OroIk3SbEvJcC+eYc8BH9JQrHA==
"@vueuse/core@^6.8.0":
version "6.8.0"
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-6.8.0.tgz#dc004bc30031e053e9ef5011203c4a80f00986ed"
integrity sha512-C6KMBus29L/mVtA5eK26WAqj6tyPlugrKaPLi2uLtbV//BHjbxe1uo3gVXCc5SwouDEdc7zswlGPw/l0/++NRg==
dependencies:
"@vueuse/shared" "6.8.0"
vue-demi "*"
"@vueuse/shared@6.8.0":
version "6.8.0"
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-6.8.0.tgz#50713b770941293e557a4eae9424cad2aad44a85"
integrity sha512-+YjehQ8Qe4Qgyq8iTToVOzp4sZBAZvScv3AGJSMi6HYbe54+nyjrRfS8DN4fA0eUahyftHKZ00WKgMe7TS5N0w==
dependencies:
vue-demi "*"
abab@^2.0.3, abab@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
@ -12097,6 +12112,11 @@ vue-advanced-cropper@2.6.3:
debounce "^1.2.0"
easy-bem "^1.0.2"
vue-demi@*:
version "0.12.1"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.1.tgz#f7e18efbecffd11ab069d1472d7a06e319b4174c"
integrity sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==
vue-drag-resize@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/vue-drag-resize/-/vue-drag-resize-2.0.3.tgz#1faf0813f43304205bb355fbb3dacc548dd9398a"