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 that will show above existing rendered content -->
|
||||||
<popup />
|
<popup />
|
||||||
|
|
||||||
<darkmode-toggle />
|
|
||||||
|
|
||||||
<!-- Command Palette -->
|
<!-- Command Palette -->
|
||||||
<command-palette />
|
<command-palette />
|
||||||
</div>
|
</div>
|
||||||
@@ -25,7 +23,6 @@
|
|||||||
import NavigationHeader from "@/components/header/NavigationHeader.vue";
|
import NavigationHeader from "@/components/header/NavigationHeader.vue";
|
||||||
import NavigationIcons from "@/components/header/NavigationIcons.vue";
|
import NavigationIcons from "@/components/header/NavigationIcons.vue";
|
||||||
import Popup from "@/components/Popup.vue";
|
import Popup from "@/components/Popup.vue";
|
||||||
import DarkmodeToggle from "@/components/ui/DarkmodeToggle.vue";
|
|
||||||
import CommandPalette from "@/components/ui/CommandPalette.vue";
|
import CommandPalette from "@/components/ui/CommandPalette.vue";
|
||||||
|
|
||||||
const router = useRouter();
|
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 router from "./routes";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
import Toast from "./plugins/Toast";
|
import Toast from "./plugins/Toast";
|
||||||
|
import { useTheme } from "./composables/useTheme";
|
||||||
|
|
||||||
import App from "./App.vue";
|
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
|
// Initialize theme before mounting
|
||||||
|
const { initTheme } = useTheme();
|
||||||
initTheme();
|
initTheme();
|
||||||
|
|
||||||
store.dispatch("darkmodeModule/findAndSetDarkmodeSupported");
|
|
||||||
store.dispatch("user/initUserFromCookie");
|
store.dispatch("user/initUserFromCookie");
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|||||||
Reference in New Issue
Block a user