mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-25 08:09:41 +02:00
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>
This commit is contained in:
parent
89ab7fac25
commit
c24d532608
403 changed files with 23959 additions and 19557 deletions
|
@ -1,41 +1,74 @@
|
|||
<template>
|
||||
<v-container v-if="!edit" class="pa-0">
|
||||
<v-row no-gutters class="flex-nowrap align-center">
|
||||
<v-container
|
||||
v-if="!edit"
|
||||
class="pa-0"
|
||||
>
|
||||
<v-row
|
||||
no-gutters
|
||||
class="flex-nowrap align-center"
|
||||
>
|
||||
<v-col :cols="itemLabelCols">
|
||||
<v-checkbox
|
||||
v-model="listItem.checked"
|
||||
class="mt-0"
|
||||
color="null"
|
||||
hide-details
|
||||
dense
|
||||
:label="listItem.note"
|
||||
density="compact"
|
||||
:label="listItem.note!"
|
||||
@change="$emit('checked', listItem)"
|
||||
>
|
||||
<template #label>
|
||||
<div :class="listItem.checked ? 'strike-through' : ''">
|
||||
<RecipeIngredientListItem :ingredient="listItem" :disable-amount="!(listItem.isFood || listItem.quantity !== 1)" />
|
||||
<RecipeIngredientListItem
|
||||
:ingredient="listItem"
|
||||
:disable-amount="!(listItem.isFood || listItem.quantity !== 1)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-col>
|
||||
<v-spacer />
|
||||
<v-col v-if="label && showLabel" cols="3" class="text-right">
|
||||
<MultiPurposeLabel :label="label" small />
|
||||
<v-col
|
||||
v-if="label && showLabel"
|
||||
cols="3"
|
||||
class="text-right"
|
||||
>
|
||||
<MultiPurposeLabel
|
||||
:label="label"
|
||||
size="small"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="auto" class="text-right">
|
||||
<div v-if="!listItem.checked" style="min-width: 72px">
|
||||
<v-menu offset-x left min-width="125px">
|
||||
<template #activator="{ on, attrs }">
|
||||
<v-col
|
||||
cols="auto"
|
||||
class="text-right"
|
||||
>
|
||||
<div
|
||||
v-if="!listItem.checked"
|
||||
style="min-width: 72px"
|
||||
>
|
||||
<v-menu
|
||||
offset-x
|
||||
start
|
||||
min-width="125px"
|
||||
>
|
||||
<template #activator="{ props }">
|
||||
<v-tooltip
|
||||
v-if="recipeList && recipeList.length"
|
||||
open-delay="200"
|
||||
transition="slide-x-reverse-transition"
|
||||
dense
|
||||
density="compact"
|
||||
right
|
||||
content-class="text-caption"
|
||||
>
|
||||
<template #activator="{ on: onBtn, attrs: attrsBtn }">
|
||||
<v-btn small class="ml-2" icon v-bind="attrsBtn" v-on="onBtn" @click="displayRecipeRefs = !displayRecipeRefs">
|
||||
<template #activator="{ props: tooltipProps }">
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
class="ml-2"
|
||||
icon
|
||||
v-bind="tooltipProps"
|
||||
@click="displayRecipeRefs = !displayRecipeRefs"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.potSteam }}
|
||||
</v-icon>
|
||||
|
@ -44,43 +77,91 @@
|
|||
<span>Toggle Recipes</span>
|
||||
</v-tooltip>
|
||||
<!-- Dummy button so the spacing is consistent when labels are enabled -->
|
||||
<v-btn v-else small class="ml-2" icon disabled>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-else
|
||||
size="small"
|
||||
variant="text"
|
||||
class="ml-2"
|
||||
icon
|
||||
disabled
|
||||
/>
|
||||
|
||||
<v-btn small class="ml-2 handle" icon v-bind="attrs" v-on="on">
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
class="ml-2 handle"
|
||||
icon
|
||||
v-bind="props"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.arrowUpDown }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<v-btn small class="ml-2" icon @click="toggleEdit(true)">
|
||||
<v-btn
|
||||
size="small"
|
||||
variant="text"
|
||||
class="ml-2"
|
||||
icon
|
||||
@click="toggleEdit(true)"
|
||||
>
|
||||
<v-icon>
|
||||
{{ $globals.icons.edit }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item v-for="action in contextMenu" :key="action.event" dense @click="contextHandler(action.event)">
|
||||
<v-list-item-title>{{ action.text }}</v-list-item-title>
|
||||
<v-list density="compact">
|
||||
<v-list-item
|
||||
v-for="action in contextMenu"
|
||||
:key="action.event"
|
||||
density="compact"
|
||||
@click="contextHandler(action.event)"
|
||||
>
|
||||
<v-list-item-title>
|
||||
{{ action.text }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="!listItem.checked && recipeList && recipeList.length && displayRecipeRefs" no-gutters class="mb-2">
|
||||
<v-col cols="auto" style="width: 100%;">
|
||||
<RecipeList :recipes="recipeList" :list-item="listItem" :disabled="$nuxt.isOffline" small tile />
|
||||
<v-row
|
||||
v-if="!listItem.checked && recipeList && recipeList.length && displayRecipeRefs"
|
||||
no-gutters
|
||||
class="mb-2"
|
||||
>
|
||||
<v-col
|
||||
cols="auto"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<RecipeList
|
||||
:recipes="recipeList"
|
||||
:list-item="listItem"
|
||||
:disabled="isOffline"
|
||||
size="small"
|
||||
tile
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="listItem.checked" no-gutters class="mb-2">
|
||||
<v-row
|
||||
v-if="listItem.checked"
|
||||
no-gutters
|
||||
class="mb-2"
|
||||
>
|
||||
<v-col cols="auto">
|
||||
<div class="text-caption font-weight-light font-italic">
|
||||
{{ $t("shopping-list.completed-on", {date: new Date(listItem.updatedAt || "").toLocaleDateString($i18n.locale)}) }}
|
||||
{{ $t("shopping-list.completed-on", {
|
||||
date: new Date(listItem.updatedAt
|
||||
|| "").toLocaleDateString($i18n.locale) })
|
||||
}}
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<div v-else class="mb-1 mt-6">
|
||||
<div
|
||||
v-else
|
||||
class="mb-1 mt-6"
|
||||
>
|
||||
<ShoppingListItemEditor
|
||||
v-model="localListItem"
|
||||
:labels="labels"
|
||||
|
@ -95,13 +176,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import { useOnline } from "@vueuse/core";
|
||||
import RecipeIngredientListItem from "../Recipe/RecipeIngredientListItem.vue";
|
||||
import ShoppingListItemEditor from "./ShoppingListItemEditor.vue";
|
||||
import MultiPurposeLabel from "./MultiPurposeLabel.vue";
|
||||
import { ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
|
||||
import { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
|
||||
import type { ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import type { MultiPurposeLabelOut, MultiPurposeLabelSummary } from "~/lib/api/types/labels";
|
||||
import type { IngredientFood, IngredientUnit, RecipeSummary } from "~/lib/api/types/recipe";
|
||||
import RecipeList from "~/components/Domain/Recipe/RecipeList.vue";
|
||||
|
||||
interface actions {
|
||||
|
@ -109,10 +190,10 @@ interface actions {
|
|||
event: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
export default defineNuxtComponent({
|
||||
components: { ShoppingListItemEditor, MultiPurposeLabel, RecipeList, RecipeIngredientListItem },
|
||||
props: {
|
||||
value: {
|
||||
modelValue: {
|
||||
type: Object as () => ShoppingListItemOut,
|
||||
required: true,
|
||||
},
|
||||
|
@ -137,10 +218,12 @@ export default defineComponent({
|
|||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ["checked", "update:modelValue", "save", "delete"],
|
||||
setup(props, context) {
|
||||
const { i18n } = useContext();
|
||||
const i18n = useI18n();
|
||||
const displayRecipeRefs = ref(false);
|
||||
const itemLabelCols = ref<string>(props.value.checked ? "auto" : props.showLabel ? "4" : "6");
|
||||
const itemLabelCols = ref<string>(props.modelValue.checked ? "auto" : props.showLabel ? "4" : "6");
|
||||
const isOffline = computed(() => useOnline().value === false);
|
||||
|
||||
const contextMenu: actions[] = [
|
||||
{
|
||||
|
@ -154,15 +237,15 @@ export default defineComponent({
|
|||
];
|
||||
|
||||
// copy prop value so a refresh doesn't interrupt the user
|
||||
const localListItem = ref(Object.assign({}, props.value));
|
||||
const localListItem = ref(Object.assign({}, props.modelValue));
|
||||
const listItem = computed({
|
||||
get: () => {
|
||||
return props.value;
|
||||
return props.modelValue;
|
||||
},
|
||||
set: (val) => {
|
||||
// keep local copy in sync
|
||||
localListItem.value = val;
|
||||
context.emit("input", val);
|
||||
context.emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
const edit = ref(false);
|
||||
|
@ -173,7 +256,7 @@ export default defineComponent({
|
|||
|
||||
if (val) {
|
||||
// update local copy of item with the current value
|
||||
localListItem.value = props.value;
|
||||
localListItem.value = props.modelValue;
|
||||
}
|
||||
|
||||
edit.value = val;
|
||||
|
@ -182,7 +265,8 @@ export default defineComponent({
|
|||
function contextHandler(event: string) {
|
||||
if (event === "edit") {
|
||||
toggleEdit(true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
context.emit(event);
|
||||
}
|
||||
}
|
||||
|
@ -205,9 +289,7 @@ export default defineComponent({
|
|||
* or the label of the food applied.
|
||||
*/
|
||||
const label = computed<MultiPurposeLabelSummary | undefined>(() => {
|
||||
// @ts-ignore - it _might_ exists
|
||||
if (listItem.value.label) {
|
||||
// @ts-ignore - it _might_ exists
|
||||
return listItem.value.label as MultiPurposeLabelSummary;
|
||||
}
|
||||
|
||||
|
@ -225,7 +307,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
listItem.value.recipeReferences.forEach((ref) => {
|
||||
const recipe = props.recipes.get(ref.recipeId)
|
||||
const recipe = props.recipes.get(ref.recipeId);
|
||||
if (recipe) {
|
||||
recipeList.push(recipe);
|
||||
}
|
||||
|
@ -247,6 +329,7 @@ export default defineComponent({
|
|||
label,
|
||||
recipeList,
|
||||
toggleEdit,
|
||||
isOffline,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue