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
|
@ -13,14 +13,16 @@
|
|||
### General
|
||||
- Recipe Instructions now collapse when checked
|
||||
- Default recipe settings can be set through ENV variables
|
||||
- Recipe Ingredient lists can now container ingredient sections.
|
||||
|
||||
|
||||
### Localization
|
||||
|
||||
#### Huge thanks to [@sephrat](https://github.com/sephrat) for all his work on the project. He's very consistent in his contributions to the project and nearly every release has had some of his code in it! Here's some highlights from this release
|
||||
Huge thanks to [@sephrat](https://github.com/sephrat) for all his work on the project. He's very consistent in his contributions to the project and nearly every release has had some of his code in it! Here's some highlights from this release
|
||||
|
||||
- Lazy Load Translations (Huge Performance Increase!)
|
||||
- Tons of localization additions all around the site.
|
||||
- Tons of localization additions all around the site.
|
||||
- All of the work that goes into managing and making Crowdin a great feature the application
|
||||
|
||||
#### Here a list of contributors on Crowding who make Mealie possible in different locals
|
||||
|
||||
|
|
|
@ -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…
Reference in a new issue