mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 05:25:26 +02:00
Feature/ingredient sections (#624)
* add ingredient sections to UI * update changelog * move recipe favorite to action bar * fix button position on meal-planner Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
9b5cf36981
commit
458ba2964f
7 changed files with 77 additions and 34 deletions
|
@ -18,14 +18,16 @@
|
|||
<v-hover v-slot="{ hover }" :open-delay="50">
|
||||
<v-card :class="{ 'on-hover': hover }" :elevation="hover ? 12 : 2">
|
||||
<CardImage large :slug="planDay.meals[0].slug" icon-size="200" @click="openSearch(index, modes.primary)">
|
||||
<v-fade-transition>
|
||||
<v-btn v-if="hover" small color="info" class="ma-1" @click.stop="addCustomItem(index, modes.primary)">
|
||||
<v-icon left>
|
||||
{{ $globals.icons.edit }}
|
||||
</v-icon>
|
||||
{{ $t('reicpe.no-recipe') }}
|
||||
</v-btn>
|
||||
</v-fade-transition>
|
||||
<div>
|
||||
<v-fade-transition>
|
||||
<v-btn v-if="hover" small color="info" class="ma-1" @click.stop="addCustomItem(index, modes.primary)">
|
||||
<v-icon left>
|
||||
{{ $globals.icons.edit }}
|
||||
</v-icon>
|
||||
{{ $t("reicpe.no-recipe") }}
|
||||
</v-btn>
|
||||
</v-fade-transition>
|
||||
</div>
|
||||
</CardImage>
|
||||
|
||||
<v-card-title class="my-n3 mb-n6">
|
||||
|
@ -40,14 +42,14 @@
|
|||
<v-icon left>
|
||||
{{ $globals.icons.edit }}
|
||||
</v-icon>
|
||||
{{ $t('reicpe.no-recipe') }}
|
||||
{{ $t("reicpe.no-recipe") }}
|
||||
</v-btn>
|
||||
</v-fade-transition>
|
||||
<v-btn color="info" outlined small @click="openSearch(index, modes.sides)">
|
||||
<v-icon small class="mr-1">
|
||||
{{ $globals.icons.create }}
|
||||
</v-icon>
|
||||
{{ $t('meal-plan.side') }}
|
||||
{{ $t("meal-plan.side") }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-hover>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<v-icon color="primary" class="icon-position" :size="iconSize">
|
||||
{{ $globals.icons.primary }}
|
||||
</v-icon>
|
||||
<slot> </slot>
|
||||
<slot> </slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -85,6 +85,7 @@ export default {
|
|||
}
|
||||
|
||||
.icon-slot > div {
|
||||
top: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<v-tooltip bottom nudge-right="50" :color="buttonStyle ? 'primary' : 'secondary'">
|
||||
<v-tooltip bottom nudge-right="50" :color="buttonStyle ? 'info' : 'secondary'">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
small
|
||||
@click.prevent="toggleFavorite"
|
||||
v-if="isFavorite || showAlways"
|
||||
:color="buttonStyle ? 'primary' : 'secondary'"
|
||||
:color="buttonStyle ? 'info' : 'secondary'"
|
||||
:icon="!buttonStyle"
|
||||
:fab="buttonStyle"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon :small="!buttonStyle" color="secondary">
|
||||
<v-icon :small="!buttonStyle" :color="buttonStyle ? 'white' : 'secondary'">
|
||||
{{ isFavorite ? $globals.icons.heart : $globals.icons.heartOutline }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
<template>
|
||||
<div v-if="edit || ( value && value.length > 0 )">
|
||||
<div v-if="edit || (value && value.length > 0)">
|
||||
<h2 class="mb-4">{{ $t("recipe.ingredients") }}</h2>
|
||||
<div v-if="edit">
|
||||
<draggable :value="value" @input="updateIndex" @start="drag = true" @end="drag = false" handle=".handle">
|
||||
<transition-group type="transition" :name="!drag ? 'flip-list' : null">
|
||||
<div v-for="(ingredient, index) in value" :key="generateKey('ingredient', index)">
|
||||
<v-row align="center">
|
||||
<v-text-field
|
||||
v-if="edit && showTitleEditor[index]"
|
||||
class="mx-3 mt-3"
|
||||
v-model="value[index].title"
|
||||
dense
|
||||
:label="$t('recipe.section-title')"
|
||||
>
|
||||
</v-text-field>
|
||||
|
||||
<v-textarea
|
||||
class="mr-2"
|
||||
:label="$t('recipe.ingredient')"
|
||||
|
@ -15,6 +24,18 @@
|
|||
dense
|
||||
rows="1"
|
||||
>
|
||||
<template slot="append">
|
||||
<v-tooltip right nudge-right="10">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn icon small class="mt-n1" v-bind="attrs" v-on="on" @click="toggleShowTitle(index)">
|
||||
<v-icon>{{ showTitleEditor[index] ? $globals.icons.minus : $globals.icons.createAlt }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>{{
|
||||
showTitleEditor[index] ? $t("recipe.remove-section") : $t("recipe.insert-section")
|
||||
}}</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<template slot="append-outer">
|
||||
<v-icon class="handle">{{ $globals.icons.arrowUpDown }}</v-icon>
|
||||
</template>
|
||||
|
@ -35,18 +56,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-list-item
|
||||
dense
|
||||
v-for="(ingredient, index) in value"
|
||||
:key="generateKey('ingredient', index)"
|
||||
@click="toggleChecked(index)"
|
||||
>
|
||||
<v-checkbox hide-details :value="checked[index]" class="pt-0 my-auto py-auto" color="secondary"> </v-checkbox>
|
||||
|
||||
<v-list-item-content>
|
||||
<vue-markdown class="ma-0 pa-0 text-subtitle-1 dense-markdown" :source="ingredient.note"> </vue-markdown>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<div v-for="(ingredient, index) in value" :key="generateKey('ingredient', index)">
|
||||
<h3 class="mt-2" v-if="showTitleEditor[index]">{{ ingredient.title }}</h3>
|
||||
<v-divider v-if="showTitleEditor[index]"></v-divider>
|
||||
<v-list-item dense @click="toggleChecked(index)">
|
||||
<v-checkbox hide-details :value="checked[index]" class="pt-0 my-auto py-auto" color="secondary"> </v-checkbox>
|
||||
<v-list-item-content>
|
||||
<vue-markdown class="ma-0 pa-0 text-subtitle-1 dense-markdown" :source="ingredient.note"> </vue-markdown>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -76,10 +95,19 @@ export default {
|
|||
return {
|
||||
drag: false,
|
||||
checked: [],
|
||||
showTitleEditor: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.checked = this.value.map(() => false);
|
||||
this.showTitleEditor = this.value.map(x => this.validateTitle(x.title));
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler() {
|
||||
this.showTitleEditor = this.value.map(x => this.validateTitle(x.title));
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addIngredient(ingredients = null) {
|
||||
|
@ -118,6 +146,16 @@ export default {
|
|||
removeByIndex(list, index) {
|
||||
list.splice(index, 1);
|
||||
},
|
||||
validateTitle(title) {
|
||||
return !(title === null || title === "");
|
||||
},
|
||||
toggleShowTitle(index) {
|
||||
const newVal = !this.showTitleEditor[index];
|
||||
if (!newVal) {
|
||||
this.value[index].title = "";
|
||||
}
|
||||
this.$set(this.showTitleEditor, index, newVal);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
/>
|
||||
<v-spacer></v-spacer>
|
||||
<div v-if="!value" class="custom-btn-group ma-1">
|
||||
<FavoriteBadge class="mx-1" color="info" button-style v-if="loggedIn" :slug="slug" show-always />
|
||||
<v-tooltip bottom color="info">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
|
@ -66,13 +67,15 @@
|
|||
<script>
|
||||
import ConfirmationDialog from "@/components/UI/Dialogs/ConfirmationDialog.vue";
|
||||
import ContextMenu from "@/components/Recipe/ContextMenu.vue";
|
||||
import FavoriteBadge from "@/components/Recipe/FavoriteBadge.vue";
|
||||
|
||||
const SAVE_EVENT = "save";
|
||||
const DELETE_EVENT = "delete";
|
||||
const CLOSE_EVENT = "close";
|
||||
const JSON_EVENT = "json";
|
||||
|
||||
export default {
|
||||
components: { ConfirmationDialog, ContextMenu },
|
||||
components: { ConfirmationDialog, ContextMenu, FavoriteBadge },
|
||||
props: {
|
||||
slug: {
|
||||
type: String,
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
class="d-print-none"
|
||||
:key="imageKey"
|
||||
>
|
||||
<FavoriteBadge class="ma-1" button-style v-if="loggedIn" :slug="recipeDetails.slug" show-always />
|
||||
<RecipeTimeCard
|
||||
:class="isMobile ? undefined : 'force-bottom'"
|
||||
:prepTime="recipeDetails.prepTime"
|
||||
|
@ -69,7 +68,6 @@
|
|||
<script>
|
||||
import RecipePageActionMenu from "@/components/Recipe/RecipePageActionMenu.vue";
|
||||
import { api } from "@/api";
|
||||
import FavoriteBadge from "@/components/Recipe/FavoriteBadge";
|
||||
import RecipeViewer from "@/components/Recipe/RecipeViewer";
|
||||
import PrintView from "@/components/Recipe/PrintView";
|
||||
import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
||||
|
@ -88,7 +86,6 @@ export default {
|
|||
RecipePageActionMenu,
|
||||
PrintView,
|
||||
NoRecipe,
|
||||
FavoriteBadge,
|
||||
CommentsSection,
|
||||
},
|
||||
mixins: [user],
|
||||
|
@ -233,7 +230,7 @@ export default {
|
|||
async saveRecipe() {
|
||||
if (this.validateRecipe()) {
|
||||
let slug = await api.recipes.update(this.recipeDetails);
|
||||
if(!slug) return;
|
||||
if (!slug) return;
|
||||
|
||||
if (this.fileObject) {
|
||||
this.saveImage(true);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue