Added | System Dark mode. Streamlined themes

This commit is contained in:
zackbcom 2021-01-05 23:26:59 -06:00
parent de17085e04
commit b099da573c
3 changed files with 190 additions and 93 deletions

View File

@ -2,7 +2,7 @@
<v-app>
<v-app-bar dense app color="primary" dark class="d-print-none">
<v-btn @click="$router.push('/')" icon class="d-flex align-center">
<v-icon size="40" >
<v-icon size="40">
mdi-silverware-variant
</v-icon>
</v-btn>
@ -44,32 +44,43 @@ export default {
Menu,
AddRecipe,
SearchHeader,
SnackBar,
SnackBar
},
watch: {
$route() {
this.search = false;
},
}
},
mounted() {
this.$store.dispatch("initCookies");
this.$store.dispatch("requestRecentRecipes");
this.darkModeAddEventListener();
},
data: () => ({
search: false,
search: false
}),
methods: {
/**
* This will monitor the OS level darkmode and call to update dark mode.
*/
darkModeAddEventListener() {
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
darkMediaQuery.addEventListener("change", () => {
this.$store.commit("setDarkMode", "system");
});
},
toggleSearch() {
if (this.search === true) {
this.search = false;
} else {
this.search = true;
}
},
},
}
}
};
</script>

View File

@ -1,69 +1,114 @@
<template>
<v-card>
<v-card-title class="secondary white--text"> Theme Settings </v-card-title>
<v-card-text>
<h2 class="mt-4 mb-1">Dark Mode</h2>
<p>
Choose how Mealie looks to you. Set your theme preference to follow your
system settings, or choose to use the light or dark theme.
</p>
<v-row dense align="center">
<v-col cols="12">
<v-btn-toggle
v-model="darkMode"
color="primary "
mandatory
@change="setDarkMode"
>
<v-btn value="system">
Default to system
</v-btn>
<v-btn value="light">
Light
</v-btn>
<v-btn value="dark">
Dark
</v-btn>
</v-btn-toggle>
</v-col>
</v-row></v-card-text
>
<v-divider class=""></v-divider>
<v-card-text>
<h2 class="mt-1 mb-1">Theme</h2>
<p>
Select a theme from the dropdown or create a new theme. Note that the
default theme will be served to all users who have not set a theme
preference.
</p>
<v-row dense align="center">
<v-col cols="12" md="2" sm="5">
<v-switch
v-model="darkMode"
inset
label="Dark Mode"
class="my-n3"
@change="toggleDarkMode"
></v-switch>
</v-col>
<v-col cols="12" md="4" sm="3">
<v-form ref="form" lazy-validation>
<v-form ref="form" lazy-validation>
<v-row dense align="center">
<v-col cols="12" md="4" sm="3">
<v-select
label="Saved Color Schemes"
label="Saved Color Theme"
:items="availableThemes"
item-text="name"
item-value="colors"
return-object
v-model="selectedScheme"
v-model="activeTheme"
@change="themeSelected"
:rules="[(v) => !!v || 'Theme is required']"
:rules="[v => !!v || 'Theme is required']"
required
>
</v-select>
</v-form>
</v-col>
<v-col cols="12" sm="1">
<NewTheme @new-theme="appendTheme" />
</v-col>
<v-col cols="12" sm="1">
<v-btn text color="error" @click="deleteSelected"> Delete </v-btn>
</v-col>
</v-row>
<v-row dense align-content="center" v-if="activeTheme">
</v-col>
<v-col cols="12" sm="1">
<NewTheme @new-theme="appendTheme" />
</v-col>
<v-col cols="12" sm="1">
<v-btn text color="error" @click="deleteSelectedThemeValidation">
Delete
</v-btn>
<Confirmation
title="Delete Theme"
message="Are you sure you want to delete this theme?"
color="error"
icon="mdi-alert-circle"
ref="deleteThemeConfirm"
v-on:confirm="deleteSelectedTheme()"
/>
</v-col>
</v-row>
</v-form>
<v-row dense align-content="center" v-if="activeTheme.colors">
<v-col>
<ColorPicker button-text="Primary" v-model="activeTheme.primary" />
<ColorPicker
button-text="Primary"
v-model="activeTheme.colors.primary"
/>
</v-col>
<v-col>
<ColorPicker
button-text="Secondary"
v-model="activeTheme.secondary"
v-model="activeTheme.colors.secondary"
/>
</v-col>
<v-col>
<ColorPicker button-text="Accent" v-model="activeTheme.accent" />
<ColorPicker
button-text="Accent"
v-model="activeTheme.colors.accent"
/>
</v-col>
<v-col>
<ColorPicker button-text="Success" v-model="activeTheme.success" />
<ColorPicker
button-text="Success"
v-model="activeTheme.colors.success"
/>
</v-col>
<v-col>
<ColorPicker button-text="Info" v-model="activeTheme.info" />
<ColorPicker button-text="Info" v-model="activeTheme.colors.info" />
</v-col>
<v-col>
<ColorPicker button-text="Warning" v-model="activeTheme.warning" />
<ColorPicker
button-text="Warning"
v-model="activeTheme.colors.warning"
/>
</v-col>
<v-col>
<ColorPicker button-text="Error" v-model="activeTheme.error" />
<ColorPicker button-text="Error" v-model="activeTheme.colors.error" />
</v-col>
</v-row>
</v-card-text>
@ -84,73 +129,74 @@
import api from "../../api";
import ColorPicker from "./ThemeUI/ColorPicker";
import NewTheme from "./ThemeUI/NewTheme";
import Confirmation from "../UI/Confirmation";
export default {
components: {
ColorPicker,
NewTheme,
Confirmation,
NewTheme
},
data() {
return {
themes: null,
activeTheme: {},
darkMode: false,
availableThemes: [],
selectedScheme: "",
selectedLight: "",
darkMode: "system",
availableThemes: []
};
},
async mounted() {
this.availableThemes = await api.themes.requestAll();
this.activeTheme = this.$store.getters.getActiveTheme;
this.darkMode = this.$store.getters.getDarkMode;
this.themes = this.$store.getters.getThemes;
this.setThemeEditor();
},
methods: {
async deleteSelected() {
deleteSelectedThemeValidation() {
if (this.$refs.form.validate()) {
if (this.selectedScheme === "default") {
if (this.activeTheme.name === "default") {
// Notify User Can't Delete Default
} else if (this.selectedScheme !== "") {
api.themes.delete(this.selectedScheme.name);
} else if (this.activeTheme !== {}) {
this.$refs.deleteThemeConfirm.open();
}
this.availableThemes = await api.themes.requestAll();
}
},
async deleteSelectedTheme() {
api.themes.delete(this.activeTheme.name);
this.availableThemes = await api.themes.requestAll();
//Change to default if deleting current theme.
if (
!this.availableThemes.some(
theme => theme.name === this.activeTheme.name
)
) {
this.$store.commit("setActiveTheme", null);
this.activeTheme = this.$store.getters.getActiveTheme;
}
},
async appendTheme(newTheme) {
api.themes.create(newTheme);
this.availableThemes.push(newTheme);
this.activeTheme = newTheme;
},
themeSelected() {
this.activeTheme = this.selectedScheme.colors;
console.log("this.activeTheme", this.activeTheme);
},
setThemeEditor() {
if (this.darkMode) {
this.activeTheme = this.themes.dark;
} else {
this.activeTheme = this.themes.light;
}
},
toggleDarkMode() {
this.$store.commit("setDarkMode", this.darkMode);
this.selectedScheme = "";
this.setThemeEditor();
setDarkMode() {
this.$store.commit("setDarkMode", this.darkMode);
},
saveThemes() {
/**
* This will save the current colors and make the selected theme live.
*/
async saveThemes() {
if (this.$refs.form.validate()) {
if (this.darkMode) {
this.themes.dark = this.activeTheme;
} else {
this.themes.light = this.activeTheme;
}
this.$store.commit("setThemes", this.themes);
this.$store.commit("setActiveTheme", this.activeTheme);
this.$store.dispatch("initCookies");
api.themes.update(this.selectedScheme.name, this.activeTheme);
api.themes.update(this.activeTheme.name, this.activeTheme);
} else;
},
},
}
}
};
</script>

View File

@ -17,25 +17,37 @@ const store = new Vuex.Store({
allRecipes: [],
// Site Settings
darkMode: false,
darkMode: 'system',
activeTheme: {
name: 'default',
colors: {
primary: "#E58325",
accent: "#00457A",
secondary: "#973542",
success: "#5AB1BB",
info: "#4990BA",
warning: "#FF4081",
error: "#EF5350",
}
},
themes: {
light: {
primary: "#E58325",
accent: "#00457A",
secondary: "#973542",
success: "#43A047",
info: "#FFFD99",
success: "#5AB1BB",
info: "#4990BA",
warning: "#FF4081",
error: "#EF5350",
},
dark: {
primary: "#4527A0",
accent: "#FF4081",
secondary: "#26C6DA",
success: "#43A047",
info: "#2196F3",
warning: "#FB8C00",
error: "#FF5252",
primary: "#E58325",
accent: "#00457A",
secondary: "#973542",
success: "#5AB1BB",
info: "#4990BA",
warning: "#FF4081",
error: "#EF5350",
},
},
},
@ -55,35 +67,62 @@ const store = new Vuex.Store({
},
setDarkMode(state, payload) {
let isDark;
state.darkMode = payload;
Vue.$cookies.set("darkMode", payload);
Vuetify.framework.theme.dark = payload;
if (payload === 'system') {
//Get System Preference from browser
const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
isDark = darkMediaQuery.matches;
}
else if (payload === 'dark')
isDark = true;
else
isDark = false;
Vuetify.framework.theme.dark = isDark;
},
setThemes(state, payload) {
state.themes = payload;
Vue.$cookies.set("themes", payload);
Vuetify.framework.theme.themes = payload;
setActiveTheme(state, payload) {
state.activeTheme = payload;
Vue.$cookies.set("activeTheme", payload);
const themes = payload ? { dark: payload.colors, light: payload.colors } : null
console.log("themes", themes)
state.themes = themes;
Vue.$cookies.set("themes", themes);
Vuetify.framework.theme.themes = themes;
},
},
actions: {
async initCookies() {
if (!Vue.$cookies.isKey("themes")) {
//TODO if has no value set to default.
if (!Vue.$cookies.isKey("themes") || !Vue.$cookies.isKey("activeTheme")) {
const DEFAULT_THEME = await api.themes.requestByName("default");
Vue.$cookies.set("themes", {
light: DEFAULT_THEME.colors,
dark: DEFAULT_THEME.colors,
});
Vue.$cookies.set("activeTheme", {
name: DEFAULT_THEME.name,
colors: DEFAULT_THEME.colors
});
}
this.commit("setThemes", Vue.$cookies.get("themes"));
this.commit("setActiveTheme", Vue.$cookies.get("activeTheme"));
//https://csabaszabo.dev/blog/dark-mode-for-website-with-nuxtjs-and-vuetify/
//https://github.com/settings/appearance
// Dark Mode
if (!Vue.$cookies.isKey("darkMode")) {
Vue.$cookies.set("darkMode", false);
Vue.$cookies.set("darkMode", 'system');
}
this.commit("setDarkMode", JSON.parse(Vue.$cookies.get("darkMode")));
this.commit("setDarkMode", Vue.$cookies.get("darkMode"));
},
async requestRecentRecipes() {
@ -112,6 +151,7 @@ const store = new Vuex.Store({
// Site Settings
getDarkMode: (state) => state.darkMode,
getThemes: (state) => state.themes,
getActiveTheme: (state) => state.activeTheme
},
});