1 parent
c03f9826af
commit
1274dece67
4 files changed
+56
-14
No files matched your search
@@ -0,0 +1,20 @@
|
||||
import { watch } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { THEME_MODES, THEME_STORAGE_KEY, type ThemeMode } from '@/lib/theme.ts'
|
||||
|
||||
/** Persisted light/dark/system theme mode, cycled in that order on each call to `cycleThemeMode`. */
|
||||
export function useThemeMode() {
|
||||
const theme = useTheme()
|
||||
const mode = useStorage<ThemeMode>(THEME_STORAGE_KEY, 'system')
|
||||
|
||||
watch(mode, (value) => theme.change(value), { immediate: true })
|
||||
|
||||
function cycleThemeMode(e?: MouseEvent) {
|
||||
if (e) theme.setTransitionOrigin(e.target as HTMLElement)
|
||||
const nextIndex = (THEME_MODES.indexOf(mode.value) + 1) % THEME_MODES.length
|
||||
mode.value = THEME_MODES[nextIndex]
|
||||
}
|
||||
|
||||
return { mode, cycleThemeMode }
|
||||
}
|
||||
@@ -2,17 +2,17 @@
|
||||
import { useRouter } from 'vue-router'
|
||||
import { authClient } from '@/lib/auth-client.ts'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import ProjectMenu from '@/components/ProjectMenu.vue'
|
||||
import AppBreadcrumbs from '@/components/AppBreadcrumbs.vue'
|
||||
import BackButton from '@/components/BackButton.vue'
|
||||
import { useThemeMode } from '@/composables/useThemeMode.ts'
|
||||
import { THEME_ICONS } from '@/lib/theme.ts'
|
||||
|
||||
const router = useRouter()
|
||||
const session = authClient.useSession()
|
||||
const theme = useTheme()
|
||||
const { mode: themeMode, cycleThemeMode } = useThemeMode()
|
||||
|
||||
const title = 'APP'
|
||||
const version = '0.0.1'
|
||||
const title = 'Ulitka'
|
||||
|
||||
const isAuth = computed(() => !!session.value.data)
|
||||
|
||||
@@ -56,12 +56,6 @@ async function goToSignOut() {
|
||||
})
|
||||
}
|
||||
|
||||
function toggleTheme(e: MouseEvent) {
|
||||
const target = e.target as HTMLElement
|
||||
theme.setTransitionOrigin(target)
|
||||
theme.toggle()
|
||||
}
|
||||
|
||||
const goToSettings = () => {
|
||||
router.push({ name: 'settings' })
|
||||
}
|
||||
@@ -90,8 +84,9 @@ const goToSettings = () => {
|
||||
</template>
|
||||
<v-divider class="mx-1" vertical />
|
||||
<v-app-bar-title>
|
||||
<router-link class="text-decoration-none text-link font-weight-bold" to="/">
|
||||
{{ title }}
|
||||
<router-link class="text-decoration-none text-link font-weight-bold d-flex align-baseline ga-2" to="/">
|
||||
<v-icon icon="mdi-snail" />
|
||||
<div>{{ title }}</div>
|
||||
</router-link>
|
||||
</v-app-bar-title>
|
||||
|
||||
@@ -113,7 +108,12 @@ const goToSettings = () => {
|
||||
</div>
|
||||
|
||||
<v-divider class="mx-2" vertical />
|
||||
<v-btn icon="mdi-brightness-6" density="comfortable" @click="toggleTheme" />
|
||||
<v-btn
|
||||
:icon="THEME_ICONS[themeMode]"
|
||||
:title="`Theme: ${themeMode}`"
|
||||
density="comfortable"
|
||||
@click="cycleThemeMode"
|
||||
/>
|
||||
<v-divider class="mx-2" vertical />
|
||||
|
||||
<div v-if="session.data">
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
export type ThemeMode = 'light' | 'dark' | 'system'
|
||||
|
||||
export const THEME_MODES: ThemeMode[] = ['light', 'dark', 'system']
|
||||
export const THEME_STORAGE_KEY = 'theme-mode'
|
||||
|
||||
export const THEME_ICONS: Record<ThemeMode, string> = {
|
||||
light: 'mdi-white-balance-sunny',
|
||||
dark: 'mdi-weather-night',
|
||||
system: 'mdi-theme-light-dark',
|
||||
}
|
||||
|
||||
export function isThemeMode(value: unknown): value is ThemeMode {
|
||||
return THEME_MODES.includes(value as ThemeMode)
|
||||
}
|
||||
|
||||
/** Read the persisted theme mode synchronously, before Vuetify is created, to avoid a flash of the wrong theme. */
|
||||
export function readStoredThemeMode(): ThemeMode {
|
||||
if (typeof localStorage === 'undefined') return 'system'
|
||||
const stored = localStorage.getItem(THEME_STORAGE_KEY)
|
||||
return isThemeMode(stored) ? stored : 'system'
|
||||
}
|
||||
+2
-1
@@ -14,6 +14,7 @@ import * as components from 'vuetify/components'
|
||||
import { VAvatarGroup } from 'vuetify/labs/VAvatarGroup'
|
||||
import * as directives from 'vuetify/directives'
|
||||
import { registerLayouts } from '@/layouts/register.ts'
|
||||
import { readStoredThemeMode } from '@/lib/theme.ts'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -24,7 +25,7 @@ const vuetify = createVuetify({
|
||||
components: { ...components, VAvatarGroup },
|
||||
directives,
|
||||
theme: {
|
||||
defaultTheme: 'dark', // 'system' | 'light' | 'dark'
|
||||
defaultTheme: readStoredThemeMode(),
|
||||
themes: {
|
||||
dark: {
|
||||
colors: {
|
||||
|
||||
Reference in new issue
Block a user