mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
feat: refactor recipe scaling (#4298)
This commit is contained in:
parent
1bd3d38dfc
commit
14dbd79c7f
@ -33,8 +33,6 @@ Do the following for each recipe you want to intelligently handle ingredients.
|
|||||||
|
|
||||||
Scaling up this recipe or adding it to a Shopping List will now smartly take care of ingredient amounts and duplicate combinations.
|
Scaling up this recipe or adding it to a Shopping List will now smartly take care of ingredient amounts and duplicate combinations.
|
||||||
|
|
||||||
Note: Each recipe must have a servings count set for recipe scaling to work.
|
|
||||||
|
|
||||||
## Is it safe to upgrade Mealie?
|
## Is it safe to upgrade Mealie?
|
||||||
|
|
||||||
Yes. If you are using the v1 branches (including beta), you can upgrade to the latest version of Mealie without performing a site Export/Restore. This process was required in previous versions of Mealie, however we've automated the database migration process to make it easier to upgrade. Note that if you were using the v0.5.x version, you CANNOT upgrade to the latest version automatically. You must follow the migration instructions in the documentation.
|
Yes. If you are using the v1 branches (including beta), you can upgrade to the latest version of Mealie without performing a site Export/Restore. This process was required in previous versions of Mealie, however we've automated the database migration process to make it easier to upgrade. Note that if you were using the v0.5.x version, you CANNOT upgrade to the latest version automatically. You must follow the migration instructions in the documentation.
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-flex justify-space-between align-center pt-2 pb-3">
|
<div class="d-flex justify-space-between align-center pt-2 pb-3">
|
||||||
<v-tooltip v-if="!isEditMode && recipe.recipeYield" small top color="secondary darken-1">
|
<v-tooltip v-if="!isEditMode" small top color="secondary darken-1">
|
||||||
<template #activator="{ on, attrs }">
|
<template #activator="{ on, attrs }">
|
||||||
<RecipeScaleEditButton
|
<RecipeScaleEditButton
|
||||||
v-model.number="scaleValue"
|
v-model.number="scaleValue"
|
||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
:recipe-yield="recipe.recipeYield"
|
:recipe-yield="recipe.recipeYield"
|
||||||
:basic-yield="basicYield"
|
|
||||||
:scaled-yield="scaledYield"
|
:scaled-yield="scaledYield"
|
||||||
|
:basic-yield-num="basicYieldNum"
|
||||||
:edit-scale="!recipe.settings.disableAmount && !isEditMode"
|
:edit-scale="!recipe.settings.disableAmount && !isEditMode"
|
||||||
v-on="on"
|
v-on="on"
|
||||||
/>
|
/>
|
||||||
@ -27,13 +27,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from "@nuxtjs/composition-api";
|
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
|
||||||
import RecipeScaleEditButton from "~/components/Domain/Recipe/RecipeScaleEditButton.vue";
|
import RecipeScaleEditButton from "~/components/Domain/Recipe/RecipeScaleEditButton.vue";
|
||||||
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
import RecipeRating from "~/components/Domain/Recipe/RecipeRating.vue";
|
||||||
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||||
import { Recipe } from "~/lib/api/types/recipe";
|
import { Recipe } from "~/lib/api/types/recipe";
|
||||||
import { usePageState } from "~/composables/recipe-page/shared-state";
|
import { usePageState } from "~/composables/recipe-page/shared-state";
|
||||||
import { useExtractRecipeYield } from "~/composables/recipe-page/use-extract-recipe-yield";
|
import { useExtractRecipeYield, findMatch } from "~/composables/recipe-page/use-extract-recipe-yield";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -70,14 +70,13 @@ export default defineComponent({
|
|||||||
return useExtractRecipeYield(props.recipe.recipeYield, scaleValue.value);
|
return useExtractRecipeYield(props.recipe.recipeYield, scaleValue.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const basicYield = computed(() => {
|
const match = findMatch(props.recipe.recipeYield);
|
||||||
return useExtractRecipeYield(props.recipe.recipeYield, 1);
|
const basicYieldNum = ref<number |null>(match ? match[1] : null);
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scaleValue,
|
scaleValue,
|
||||||
basicYield,
|
|
||||||
scaledYield,
|
scaledYield,
|
||||||
|
basicYieldNum,
|
||||||
isEditMode,
|
isEditMode,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -5,17 +5,22 @@
|
|||||||
<v-menu v-model="menu" :disabled="!editScale" offset-y top nudge-top="6" :close-on-content-click="false">
|
<v-menu v-model="menu" :disabled="!editScale" offset-y top nudge-top="6" :close-on-content-click="false">
|
||||||
<template #activator="{ on, attrs }">
|
<template #activator="{ on, attrs }">
|
||||||
<v-card class="pa-1 px-2" dark color="secondary darken-1" small v-bind="attrs" v-on="on">
|
<v-card class="pa-1 px-2" dark color="secondary darken-1" small v-bind="attrs" v-on="on">
|
||||||
<span v-if="recipeYield"> {{ scaledYield }} </span>
|
|
||||||
<span v-if="!recipeYield"> x {{ scale }} </span>
|
<span v-if="!recipeYield"> x {{ scale }} </span>
|
||||||
|
<div v-else-if="!numberParsed && recipeYield">
|
||||||
|
<span v-if="numerator === 1"> {{ recipeYield }} </span>
|
||||||
|
<span v-else> {{ numerator }}x {{ scaledYield }} </span>
|
||||||
|
</div>
|
||||||
|
<span v-else> {{ scaledYield }} </span>
|
||||||
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
<v-card min-width="300px">
|
<v-card min-width="300px">
|
||||||
<v-card-title class="mb-0">
|
<v-card-title class="mb-0">
|
||||||
{{ $t("recipe.edit-scale") }}
|
{{ $t("recipe.servings") }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text class="mt-n5">
|
<v-card-text class="mt-n5">
|
||||||
<div class="mt-4 d-flex align-center">
|
<div class="mt-4 d-flex align-center">
|
||||||
<v-text-field v-model.number="scale" type="number" :min="0" :label="$t('recipe.edit-scale')" />
|
<v-text-field v-model="numerator" type="number" :min="0" hide-spin-buttons />
|
||||||
<v-tooltip right color="secondary darken-1">
|
<v-tooltip right color="secondary darken-1">
|
||||||
<template #activator="{ on, attrs }">
|
<template #activator="{ on, attrs }">
|
||||||
<v-btn v-bind="attrs" icon class="mx-1" small v-on="on" @click="scale = 1">
|
<v-btn v-bind="attrs" icon class="mx-1" small v-on="on" @click="scale = 1">
|
||||||
@ -24,7 +29,7 @@
|
|||||||
</v-icon>
|
</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<span> {{ $t("recipe.reset-scale") }} </span>
|
<span> {{ $t("recipe.reset-servings-count") }} </span>
|
||||||
</v-tooltip>
|
</v-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@ -38,24 +43,25 @@
|
|||||||
:buttons="[
|
:buttons="[
|
||||||
{
|
{
|
||||||
icon: $globals.icons.minus,
|
icon: $globals.icons.minus,
|
||||||
text: $t('recipe.decrease-scale-label'),
|
text: $tc('recipe.decrease-scale-label'),
|
||||||
event: 'decrement',
|
event: 'decrement',
|
||||||
|
disabled: disableDecrement,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: $globals.icons.createAlt,
|
icon: $globals.icons.createAlt,
|
||||||
text: $t('recipe.increase-scale-label'),
|
text: $tc('recipe.increase-scale-label'),
|
||||||
event: 'increment',
|
event: 'increment',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
@decrement="scale > 1 ? scale-- : null"
|
@decrement="numerator--"
|
||||||
@increment="scale++"
|
@increment="numerator++"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, toRefs, computed } from "@nuxtjs/composition-api";
|
import { defineComponent, ref, computed, watch } from "@nuxtjs/composition-api";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -63,12 +69,12 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
basicYield: {
|
scaledYield: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
scaledYield: {
|
basicYieldNum: {
|
||||||
type: String,
|
type: Number,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
editScale: {
|
editScale: {
|
||||||
@ -81,10 +87,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const state = reactive({
|
const menu = ref<boolean>(false);
|
||||||
tempScale: 1,
|
|
||||||
menu: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const scale = computed({
|
const scale = computed({
|
||||||
get: () => props.value,
|
get: () => props.value,
|
||||||
@ -94,9 +97,24 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numerator = ref<number>(parseFloat(props.basicYieldNum.toFixed(3)) ?? 1);
|
||||||
|
const denominator = parseFloat(props.basicYieldNum.toFixed(32)) ?? 1;
|
||||||
|
const numberParsed = !!props.basicYieldNum;
|
||||||
|
|
||||||
|
watch(() => numerator.value, () => {
|
||||||
|
scale.value = parseFloat((numerator.value / denominator).toFixed(3));
|
||||||
|
});
|
||||||
|
const disableDecrement = computed(() => {
|
||||||
|
return numerator.value <= 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
menu,
|
||||||
scale,
|
scale,
|
||||||
...toRefs(state),
|
numerator,
|
||||||
|
disableDecrement,
|
||||||
|
numberParsed,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -39,7 +39,7 @@ function extractServingsFromFraction(fractionString: string): number | undefined
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function findMatch(yieldString: string): [matchString: string, servings: number, isFraction: boolean] | null {
|
export function findMatch(yieldString: string): [matchString: string, servings: number, isFraction: boolean] | null {
|
||||||
if (!yieldString) {
|
if (!yieldString) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -652,7 +652,8 @@
|
|||||||
"missing-unit": "Create missing unit: {unit}",
|
"missing-unit": "Create missing unit: {unit}",
|
||||||
"missing-food": "Create missing food: {food}",
|
"missing-food": "Create missing food: {food}",
|
||||||
"no-food": "No Food"
|
"no-food": "No Food"
|
||||||
}
|
},
|
||||||
|
"reset-servings-count": "Reset Servings Count"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"advanced-search": "Advanced Search",
|
"advanced-search": "Advanced Search",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user