mirror of
https://github.com/KevinMidboe/seasoned.git
synced 2026-03-10 03:19:32 +00:00
Create centralized theme management with useTheme composable
- Extract theme initialization logic from main.ts into useTheme composable - Add setTheme() and initTheme() functions for consistent theme handling - Update ThemePreferences to use useTheme instead of duplicate logic - Remove unused DarkmodeToggle component - Clean up main.ts from ~49 to 28 lines (theme logic now in composable) - Establish single source of truth for theme management - Follow Vue 3 composables pattern for better organization and testability
This commit is contained in:
@@ -13,8 +13,6 @@
|
||||
<!-- Popup that will show above existing rendered content -->
|
||||
<popup />
|
||||
|
||||
<darkmode-toggle />
|
||||
|
||||
<!-- Command Palette -->
|
||||
<command-palette />
|
||||
</div>
|
||||
@@ -25,7 +23,6 @@
|
||||
import NavigationHeader from "@/components/header/NavigationHeader.vue";
|
||||
import NavigationIcons from "@/components/header/NavigationIcons.vue";
|
||||
import Popup from "@/components/Popup.vue";
|
||||
import DarkmodeToggle from "@/components/ui/DarkmodeToggle.vue";
|
||||
import CommandPalette from "@/components/ui/CommandPalette.vue";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<template>
|
||||
<div class="darkToggle">
|
||||
<span @click="toggleDarkmode" @keydown.enter="toggleDarkmode">{{
|
||||
darkmodeToggleIcon
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
function systemDarkModeEnabled() {
|
||||
const computedStyle = window.getComputedStyle(document.body);
|
||||
if (computedStyle?.colorScheme != null) {
|
||||
return computedStyle.colorScheme.includes("dark");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const darkmode = ref(systemDarkModeEnabled());
|
||||
const darkmodeToggleIcon = computed(() => {
|
||||
return darkmode.value ? "🌝" : "🌚";
|
||||
});
|
||||
|
||||
function toggleDarkmode() {
|
||||
darkmode.value = !darkmode.value;
|
||||
document.body.className = darkmode.value ? "dark" : "light";
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.darkToggle {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-right: 2px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
56
src/composables/useTheme.ts
Normal file
56
src/composables/useTheme.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
type Theme = "light" | "dark" | "auto";
|
||||
|
||||
const currentTheme = ref<Theme>("auto");
|
||||
|
||||
function systemDarkModeEnabled(): boolean {
|
||||
const computedStyle = window.getComputedStyle(document.body);
|
||||
if (computedStyle?.colorScheme != null) {
|
||||
return computedStyle.colorScheme.includes("dark");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function applyTheme(theme: Theme) {
|
||||
if (theme === "auto") {
|
||||
const systemDark = systemDarkModeEnabled();
|
||||
document.body.className = systemDark ? "dark" : "light";
|
||||
} else {
|
||||
document.body.className = theme;
|
||||
}
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const savedTheme = computed(() => {
|
||||
return (localStorage.getItem("theme-preference") as Theme) || "auto";
|
||||
});
|
||||
|
||||
function initTheme() {
|
||||
const theme = savedTheme.value;
|
||||
currentTheme.value = theme;
|
||||
applyTheme(theme);
|
||||
|
||||
// Listen for system theme changes when in auto mode
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mediaQuery.addEventListener("change", e => {
|
||||
const currentSetting = localStorage.getItem("theme-preference") as Theme;
|
||||
if (currentSetting === "auto") {
|
||||
document.body.className = e.matches ? "dark" : "light";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setTheme(theme: Theme) {
|
||||
currentTheme.value = theme;
|
||||
localStorage.setItem("theme-preference", theme);
|
||||
applyTheme(theme);
|
||||
}
|
||||
|
||||
return {
|
||||
currentTheme,
|
||||
savedTheme,
|
||||
initTheme,
|
||||
setTheme
|
||||
};
|
||||
}
|
||||
32
src/main.ts
32
src/main.ts
@@ -2,42 +2,14 @@ import { createApp } from "vue";
|
||||
import router from "./routes";
|
||||
import store from "./store";
|
||||
import Toast from "./plugins/Toast";
|
||||
import { useTheme } from "./composables/useTheme";
|
||||
|
||||
import App from "./App.vue";
|
||||
|
||||
// Initialize theme from localStorage
|
||||
function initTheme() {
|
||||
const savedTheme = localStorage.getItem("theme-preference") || "auto";
|
||||
|
||||
function systemDarkModeEnabled() {
|
||||
const computedStyle = window.getComputedStyle(document.body);
|
||||
if (computedStyle?.colorScheme != null) {
|
||||
return computedStyle.colorScheme.includes("dark");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (savedTheme === "auto") {
|
||||
const systemDark = systemDarkModeEnabled();
|
||||
document.body.className = systemDark ? "dark" : "light";
|
||||
|
||||
// Listen for system theme changes
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mediaQuery.addEventListener("change", e => {
|
||||
const currentTheme = localStorage.getItem("theme-preference");
|
||||
if (currentTheme === "auto") {
|
||||
document.body.className = e.matches ? "dark" : "light";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.body.className = savedTheme;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize theme before mounting
|
||||
const { initTheme } = useTheme();
|
||||
initTheme();
|
||||
|
||||
store.dispatch("darkmodeModule/findAndSetDarkmodeSupported");
|
||||
store.dispatch("user/initUserFromCookie");
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
Reference in New Issue
Block a user