mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
bottom-bar experiment
This commit is contained in:
parent
f7ed109ac6
commit
087de0739c
8 changed files with 180 additions and 67 deletions
|
@ -1,35 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<v-app-bar clipped-left dense app color="primary" dark class="d-print-none">
|
<TheAppBar />
|
||||||
<router-link v-if="!(isMobile && search)" to="/">
|
|
||||||
<v-btn icon>
|
|
||||||
<v-icon size="40"> mdi-silverware-variant </v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<div v-if="!isMobile" btn class="pl-2">
|
|
||||||
<v-toolbar-title style="cursor: pointer" @click="$router.push('/')"
|
|
||||||
>Mealie
|
|
||||||
</v-toolbar-title>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-expand-x-transition>
|
|
||||||
<SearchBar
|
|
||||||
ref="mainSearchBar"
|
|
||||||
v-if="search"
|
|
||||||
:show-results="true"
|
|
||||||
@selected="navigateFromSearch"
|
|
||||||
:max-width="isMobile ? '100%' : '450px'"
|
|
||||||
/>
|
|
||||||
</v-expand-x-transition>
|
|
||||||
<v-btn icon @click="search = !search">
|
|
||||||
<v-icon>mdi-magnify</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<SiteMenu />
|
|
||||||
<LanguageMenu />
|
|
||||||
</v-app-bar>
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-banner v-if="demo" sticky
|
<v-banner v-if="demo" sticky
|
||||||
><div class="text-center">
|
><div class="text-center">
|
||||||
|
@ -47,10 +18,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SiteMenu from "@/components/UI/SiteMenu";
|
import TheAppBar from "@/components/UI/TheAppBar";
|
||||||
import SearchBar from "@/components/UI/Search/SearchBar";
|
|
||||||
import AddRecipeFab from "@/components/UI/AddRecipeFab";
|
import AddRecipeFab from "@/components/UI/AddRecipeFab";
|
||||||
import LanguageMenu from "@/components/UI/LanguageMenu";
|
|
||||||
import Vuetify from "./plugins/vuetify";
|
import Vuetify from "./plugins/vuetify";
|
||||||
import { user } from "@/mixins/user";
|
import { user } from "@/mixins/user";
|
||||||
|
|
||||||
|
@ -58,23 +27,13 @@ export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
SiteMenu,
|
TheAppBar,
|
||||||
AddRecipeFab,
|
AddRecipeFab,
|
||||||
SearchBar,
|
|
||||||
LanguageMenu,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [user],
|
mixins: [user],
|
||||||
|
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
this.search = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
isMobile() {
|
|
||||||
return this.$vuetify.breakpoint.name === "xs";
|
|
||||||
},
|
|
||||||
demo() {
|
demo() {
|
||||||
const appInfo = this.$store.getters.getAppInfo;
|
const appInfo = this.$store.getters.getAppInfo;
|
||||||
return appInfo.demoStatus;
|
return appInfo.demoStatus;
|
||||||
|
@ -102,9 +61,6 @@ export default {
|
||||||
this.$store.dispatch("requestAppInfo");
|
this.$store.dispatch("requestAppInfo");
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
search: false,
|
|
||||||
}),
|
|
||||||
methods: {
|
methods: {
|
||||||
// For Later!
|
// For Later!
|
||||||
|
|
||||||
|
@ -126,9 +82,6 @@ export default {
|
||||||
this.darkModeSystemCheck();
|
this.darkModeSystemCheck();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
navigateFromSearch(slug) {
|
|
||||||
this.$router.push(`/recipe/${slug}`);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -5,27 +5,27 @@ import { store } from "@/store";
|
||||||
const prefix = baseURL + "categories";
|
const prefix = baseURL + "categories";
|
||||||
|
|
||||||
const categoryURLs = {
|
const categoryURLs = {
|
||||||
get_all: `${prefix}`,
|
getAll: `${prefix}`,
|
||||||
get_category: category => `${prefix}/${category}`,
|
getCategory: category => `${prefix}/${category}`,
|
||||||
delete_category: category => `${prefix}/${category}`,
|
deleteCategory: category => `${prefix}/${category}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const categoryAPI = {
|
export const categoryAPI = {
|
||||||
async getAll() {
|
async getAll() {
|
||||||
let response = await apiReq.get(categoryURLs.get_all);
|
let response = await apiReq.get(categoryURLs.getAll);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async create(name) {
|
async create(name) {
|
||||||
let response = await apiReq.post(categoryURLs.get_all, { name: name });
|
let response = await apiReq.post(categoryURLs.getAll, { name: name });
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async getRecipesInCategory(category) {
|
async getRecipesInCategory(category) {
|
||||||
let response = await apiReq.get(categoryURLs.get_category(category));
|
let response = await apiReq.get(categoryURLs.getCategory(category));
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async delete(category) {
|
async delete(category) {
|
||||||
let response = await apiReq.delete(categoryURLs.delete_category(category));
|
let response = await apiReq.delete(categoryURLs.deleteCategory(category));
|
||||||
store.dispatch("requestCategories");
|
store.dispatch("requestCategories");
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,9 +56,7 @@ export const recipeAPI = {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append("image", fileObject);
|
fd.append("image", fileObject);
|
||||||
fd.append("extension", fileObject.name.split(".").pop());
|
fd.append("extension", fileObject.name.split(".").pop());
|
||||||
|
|
||||||
let response = apiReq.put(recipeURLs.updateImage(recipeSlug), fd);
|
let response = apiReq.put(recipeURLs.updateImage(recipeSlug), fd);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<v-card hover :to="`/recipe/${slug}`" max-height="125">
|
<v-card
|
||||||
|
hover
|
||||||
|
:to="`/recipe/${slug}`"
|
||||||
|
max-height="125"
|
||||||
|
@click="$emit('selected')"
|
||||||
|
>
|
||||||
<v-list-item>
|
<v-list-item>
|
||||||
<v-list-item-avatar rounded size="125" class="mt-0 ml-n4">
|
<v-list-item-avatar rounded size="125" class="mt-0 ml-n4">
|
||||||
<v-img :src="getImage(image)"> </v-img>
|
<v-img :src="getImage(image)"> </v-img>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-center ">
|
<div class="text-center ">
|
||||||
<v-dialog v-model="dialog" class="search-dialog" width="600px" height="0">
|
<v-dialog v-model="dialog" width="600px" height="0" :fullscreen="isMobile">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-app-bar dark color="primary">
|
<v-app-bar dark color="primary">
|
||||||
<v-toolbar-title class="headline">Search a Recipe</v-toolbar-title>
|
<v-toolbar-title class="headline">Search a Recipe</v-toolbar-title>
|
||||||
|
@ -9,13 +9,27 @@
|
||||||
<SearchBar
|
<SearchBar
|
||||||
@results="updateResults"
|
@results="updateResults"
|
||||||
@selected="emitSelect"
|
@selected="emitSelect"
|
||||||
:show-results="true"
|
:show-results="!isMobile"
|
||||||
max-width="550px"
|
max-width="550px"
|
||||||
:dense="false"
|
:dense="false"
|
||||||
:nav-on-click="false"
|
:nav-on-click="false"
|
||||||
:reset-search="dialog"
|
:reset-search="dialog"
|
||||||
:solo="false"
|
:solo="false"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="isMobile">
|
||||||
|
<div v-for="recipe in searchResults.slice(0, 7)" :key="recipe.name">
|
||||||
|
<MobileRecipeCard
|
||||||
|
class="ma-1 px-0"
|
||||||
|
:name="recipe.item.name"
|
||||||
|
:description="recipe.item.description"
|
||||||
|
:slug="recipe.item.slug"
|
||||||
|
:rating="recipe.item.rating"
|
||||||
|
:image="recipe.item.image"
|
||||||
|
:route="true"
|
||||||
|
@selected="dialog = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
@ -24,16 +38,32 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SearchBar from "./SearchBar";
|
import SearchBar from "./SearchBar";
|
||||||
|
import MobileRecipeCard from "@/components/Recipe/MobileRecipeCard";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SearchBar,
|
SearchBar,
|
||||||
|
MobileRecipeCard,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
searchResults: null,
|
searchResults: [],
|
||||||
dialog: false,
|
dialog: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isMobile() {
|
||||||
|
return this.$vuetify.breakpoint.name === "xs";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
"$route.hash"(newHash, oldHash) {
|
||||||
|
if (newHash === "#mobile-search") {
|
||||||
|
this.dialog = true;
|
||||||
|
} else if (oldHash === "#mobile-search") {
|
||||||
|
this.dialog = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateResults(results) {
|
updateResults(results) {
|
||||||
this.searchResults = results;
|
this.searchResults = results;
|
||||||
|
@ -44,15 +74,22 @@ export default {
|
||||||
},
|
},
|
||||||
open() {
|
open() {
|
||||||
this.dialog = true;
|
this.dialog = true;
|
||||||
|
this.$router.push("#mobile-search");
|
||||||
|
},
|
||||||
|
toggleDialog(open) {
|
||||||
|
if (open) {
|
||||||
|
this.$router.push("#mobile-search");
|
||||||
|
} else {
|
||||||
|
this.$router.back(); // 😎 back button click
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scope>
|
<style scope>
|
||||||
.search-dialog {
|
.mobile-dialog {
|
||||||
margin-top: 10%;
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
113
frontend/src/components/UI/TheAppBar.vue
Normal file
113
frontend/src/components/UI/TheAppBar.vue
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-app-bar
|
||||||
|
v-if="!isMobile"
|
||||||
|
clipped-left
|
||||||
|
dense
|
||||||
|
app
|
||||||
|
color="primary"
|
||||||
|
dark
|
||||||
|
class="d-print-none"
|
||||||
|
>
|
||||||
|
<router-link v-if="!(isMobile && search)" to="/">
|
||||||
|
<v-btn icon>
|
||||||
|
<v-icon size="40"> mdi-silverware-variant </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div v-if="!isMobile" btn class="pl-2">
|
||||||
|
<v-toolbar-title style="cursor: pointer" @click="$router.push('/')"
|
||||||
|
>Mealie
|
||||||
|
</v-toolbar-title>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-expand-x-transition>
|
||||||
|
<SearchBar
|
||||||
|
ref="mainSearchBar"
|
||||||
|
v-if="search"
|
||||||
|
:show-results="true"
|
||||||
|
@selected="navigateFromSearch"
|
||||||
|
:max-width="isMobile ? '100%' : '450px'"
|
||||||
|
/>
|
||||||
|
</v-expand-x-transition>
|
||||||
|
<v-btn icon @click="search = !search">
|
||||||
|
<v-icon>mdi-magnify</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<SiteMenu />
|
||||||
|
</v-app-bar>
|
||||||
|
<v-app-bar
|
||||||
|
v-else
|
||||||
|
bottom
|
||||||
|
clipped-left
|
||||||
|
dense
|
||||||
|
app
|
||||||
|
color="primary"
|
||||||
|
dark
|
||||||
|
class="d-print-none"
|
||||||
|
>
|
||||||
|
<router-link to="/">
|
||||||
|
<v-btn icon>
|
||||||
|
<v-icon size="40"> mdi-silverware-variant </v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<div v-if="!isMobile" btn class="pl-2">
|
||||||
|
<v-toolbar-title style="cursor: pointer" @click="$router.push('/')"
|
||||||
|
>Mealie
|
||||||
|
</v-toolbar-title>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-expand-x-transition>
|
||||||
|
<SearchDialog ref="mainSearchDialog" />
|
||||||
|
</v-expand-x-transition>
|
||||||
|
<v-btn icon @click="$refs.mainSearchDialog.open()">
|
||||||
|
<v-icon>mdi-magnify</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<SiteMenu />
|
||||||
|
</v-app-bar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SiteMenu from "@/components/UI/SiteMenu";
|
||||||
|
import SearchBar from "@/components/UI/Search/SearchBar";
|
||||||
|
import SearchDialog from "@/components/UI/Search/SearchDialog";
|
||||||
|
import { user } from "@/mixins/user";
|
||||||
|
export default {
|
||||||
|
name: "AppBar",
|
||||||
|
|
||||||
|
mixins: [user],
|
||||||
|
components: {
|
||||||
|
SiteMenu,
|
||||||
|
SearchBar,
|
||||||
|
SearchDialog,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
search: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.search = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isMobile() {
|
||||||
|
return this.$vuetify.breakpoint.name === "xs";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
navigateFromSearch(slug) {
|
||||||
|
this.$router.push(`/recipe/${slug}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
</style>
|
7
frontend/src/mixins/utilMixins.js
Normal file
7
frontend/src/mixins/utilMixins.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export const utilMixins = {
|
||||||
|
commputed: {
|
||||||
|
isMobile() {
|
||||||
|
return this.$vuetify.breakpoint.name === "xs";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -70,7 +70,7 @@ const actions = {
|
||||||
|
|
||||||
async refreshToken({ commit, getters }) {
|
async refreshToken({ commit, getters }) {
|
||||||
if (!getters.getIsLoggedIn) {
|
if (!getters.getIsLoggedIn) {
|
||||||
commit("setIsLoggedIn", false); // This is to be here... for some reasons? ¯\_(ツ)_/¯
|
commit("setIsLoggedIn", false); // This has to be here... for some reasons? ¯\_(ツ)_/¯
|
||||||
console.log("Not Logged In");
|
console.log("Not Logged In");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue