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 { useRouter } from 'vue-router'
|
||||||
import { authClient } from '@/lib/auth-client.ts'
|
import { authClient } from '@/lib/auth-client.ts'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useTheme } from 'vuetify'
|
|
||||||
import ProjectMenu from '@/components/ProjectMenu.vue'
|
import ProjectMenu from '@/components/ProjectMenu.vue'
|
||||||
import AppBreadcrumbs from '@/components/AppBreadcrumbs.vue'
|
import AppBreadcrumbs from '@/components/AppBreadcrumbs.vue'
|
||||||
import BackButton from '@/components/BackButton.vue'
|
import BackButton from '@/components/BackButton.vue'
|
||||||
|
import { useThemeMode } from '@/composables/useThemeMode.ts'
|
||||||
|
import { THEME_ICONS } from '@/lib/theme.ts'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const session = authClient.useSession()
|
const session = authClient.useSession()
|
||||||
const theme = useTheme()
|
const { mode: themeMode, cycleThemeMode } = useThemeMode()
|
||||||
|
|
||||||
const title = 'APP'
|
const title = 'Ulitka'
|
||||||
const version = '0.0.1'
|
|
||||||
|
|
||||||
const isAuth = computed(() => !!session.value.data)
|
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 = () => {
|
const goToSettings = () => {
|
||||||
router.push({ name: 'settings' })
|
router.push({ name: 'settings' })
|
||||||
}
|
}
|
||||||
@@ -90,8 +84,9 @@ const goToSettings = () => {
|
|||||||
</template>
|
</template>
|
||||||
<v-divider class="mx-1" vertical />
|
<v-divider class="mx-1" vertical />
|
||||||
<v-app-bar-title>
|
<v-app-bar-title>
|
||||||
<router-link class="text-decoration-none text-link font-weight-bold" to="/">
|
<router-link class="text-decoration-none text-link font-weight-bold d-flex align-baseline ga-2" to="/">
|
||||||
{{ title }}
|
<v-icon icon="mdi-snail" />
|
||||||
|
<div>{{ title }}</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</v-app-bar-title>
|
</v-app-bar-title>
|
||||||
|
|
||||||
@@ -113,7 +108,12 @@ const goToSettings = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-divider class="mx-2" vertical />
|
<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 />
|
<v-divider class="mx-2" vertical />
|
||||||
|
|
||||||
<div v-if="session.data">
|
<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 { VAvatarGroup } from 'vuetify/labs/VAvatarGroup'
|
||||||
import * as directives from 'vuetify/directives'
|
import * as directives from 'vuetify/directives'
|
||||||
import { registerLayouts } from '@/layouts/register.ts'
|
import { registerLayouts } from '@/layouts/register.ts'
|
||||||
|
import { readStoredThemeMode } from '@/lib/theme.ts'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ const vuetify = createVuetify({
|
|||||||
components: { ...components, VAvatarGroup },
|
components: { ...components, VAvatarGroup },
|
||||||
directives,
|
directives,
|
||||||
theme: {
|
theme: {
|
||||||
defaultTheme: 'dark', // 'system' | 'light' | 'dark'
|
defaultTheme: readStoredThemeMode(),
|
||||||
themes: {
|
themes: {
|
||||||
dark: {
|
dark: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|||||||
Reference in new issue
Block a user