mealie/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageIngredientEditor.vue
Hoa (Kyle) Trinh c24d532608
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Docker Nightly Production / Backend Server Tests (push) Waiting to run
Docker Nightly Production / Frontend Tests (push) Waiting to run
Docker Nightly Production / Build Package (push) Waiting to run
Docker Nightly Production / Build Tagged Release (push) Blocked by required conditions
Docker Nightly Production / Notify Discord (push) Blocked by required conditions
Release Drafter / ✏️ Draft release (push) Waiting to run
feat: Migrate to Nuxt 3 framework (#5184)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2025-06-19 17:09:12 +00:00

168 lines
4.8 KiB
Vue

<!-- eslint-disable vue/no-mutating-props -->
<template>
<div>
<h2 class="mb-4 text-h5 font-weight-medium opacity-80">
{{ $t("recipe.ingredients") }}
</h2>
<VueDraggable
v-if="recipe.recipeIngredient.length > 0"
v-model="recipe.recipeIngredient"
handle=".handle"
:delay="250"
:delay-on-touch-only="true"
v-bind="{
animation: 200,
group: 'recipe-ingredients',
disabled: false,
ghostClass: 'ghost',
}"
@start="drag = true"
@end="drag = false"
>
<TransitionGroup
type="transition"
>
<RecipeIngredientEditor
v-for="(ingredient, index) in recipe.recipeIngredient"
:key="ingredient.referenceId"
v-model="recipe.recipeIngredient[index]"
class="list-group-item"
:disable-amount="recipe.settings.disableAmount"
@delete="recipe.recipeIngredient.splice(index, 1)"
@insert-above="insertNewIngredient(index)"
@insert-below="insertNewIngredient(index + 1)"
/>
</TransitionGroup>
</VueDraggable>
<v-skeleton-loader
v-else
boilerplate
elevation="2"
type="list-item"
/>
<div class="d-flex flex-wrap justify-center justify-sm-end mt-3">
<v-tooltip
top
color="accent"
>
<template #activator="{ props }">
<span>
<BaseButton
class="mb-1"
:disabled="recipe.settings.disableAmount || hasFoodOrUnit"
color="accent"
:to="`/g/${groupSlug}/r/${recipe.slug}/ingredient-parser`"
v-bind="props"
>
<template #icon>
{{ $globals.icons.foods }}
</template>
{{ $t('recipe.parse') }}
</BaseButton>
</span>
</template>
<span>{{ parserToolTip }}</span>
</v-tooltip>
<RecipeDialogBulkAdd
class="mx-1 mb-1"
@bulk-data="addIngredient"
/>
<BaseButton
class="mb-1"
@click="addIngredient"
>
{{ $t("general.add") }}
</BaseButton>
</div>
</div>
</template>
<script setup lang="ts">
import { VueDraggable } from "vue-draggable-plus";
import type { NoUndefinedField } from "~/lib/api/types/non-generated";
import type { Recipe } from "~/lib/api/types/recipe";
import RecipeIngredientEditor from "~/components/Domain/Recipe/RecipeIngredientEditor.vue";
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
import { uuid4 } from "~/composables/use-utils";
const recipe = defineModel<NoUndefinedField<Recipe>>({ required: true });
const i18n = useI18n();
const $auth = useMealieAuth();
const drag = ref(false);
const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
const hasFoodOrUnit = computed(() => {
if (!recipe.value) {
return false;
}
if (recipe.value.recipeIngredient) {
for (const ingredient of recipe.value.recipeIngredient) {
if (ingredient.food || ingredient.unit) {
return true;
}
}
}
return false;
});
const parserToolTip = computed(() => {
if (recipe.value.settings.disableAmount) {
return i18n.t("recipe.enable-ingredient-amounts-to-use-this-feature");
}
else if (hasFoodOrUnit.value) {
return i18n.t("recipe.recipes-with-units-or-foods-defined-cannot-be-parsed");
}
return i18n.t("recipe.parse-ingredients");
});
function addIngredient(ingredients: Array<string> | null = null) {
if (ingredients?.length) {
const newIngredients = ingredients.map((x) => {
return {
referenceId: uuid4(),
title: "",
note: x,
unit: undefined,
food: undefined,
disableAmount: true,
quantity: 1,
};
});
if (newIngredients) {
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
recipe.value.recipeIngredient.push(...newIngredients);
}
}
else {
recipe.value.recipeIngredient.push({
referenceId: uuid4(),
title: "",
note: "",
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
unit: undefined,
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
food: undefined,
disableAmount: true,
quantity: 1,
});
}
}
function insertNewIngredient(dest: number) {
recipe.value.recipeIngredient.splice(dest, 0, {
referenceId: uuid4(),
title: "",
note: "",
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
unit: undefined,
// @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set
food: undefined,
disableAmount: true,
quantity: 1,
});
}
</script>