1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-24 15:49:42 +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:
Hoa (Kyle) Trinh 2025-06-20 00:09:12 +07:00 committed by GitHub
parent 89ab7fac25
commit c24d532608
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
403 changed files with 23959 additions and 19557 deletions

View file

@ -1,81 +1,121 @@
<template>
<div :style="`height: ${height}`">
<div :style="`height: ${height}px;`">
<v-expand-transition>
<v-card
:ripple="false"
:class="isFlat ? 'mx-auto flat' : 'mx-auto'"
:style="{ cursor }"
hover
:to="$listeners.selected ? undefined : recipeRoute"
height="100%"
:to="$attrs.selected ? undefined : recipeRoute"
@click="$emit('selected')"
>
<v-img v-if="vertical" class="rounded-sm">
<v-img
v-if="vertical"
class="rounded-sm"
cover
>
<RecipeCardImage
:icon-size="100"
:height="height"
:slug="slug"
:recipe-id="recipeId"
small
size="small"
:image-version="image"
:height="height"
/>
</v-img>
<v-list-item three-line :class="vertical ? 'px-2' : 'px-0'">
<slot v-if="!vertical" name="avatar">
<v-list-item-avatar tile :height="height" width="125" class="v-mobile-img rounded-sm my-0">
<v-list-item
lines="two"
class="py-0"
:class="vertical ? 'px-2' : 'px-0'"
item-props
height="100%"
density="compact"
>
<template #prepend>
<slot
v-if="!vertical"
name="avatar"
>
<RecipeCardImage
:icon-size="100"
:height="height"
:slug="slug"
:recipe-id="recipeId"
:image-version="image"
size="small"
width="125"
:height="height"
/>
</slot>
</template>
<div class="pl-4 d-flex flex-column justify-space-between align-stretch pr-2">
<v-list-item-title class="mt-3 mb-1 text-top text-truncate w-100">
{{ name }}
</v-list-item-title>
<v-list-item-subtitle class="ma-0 text-top">
<SafeMarkdown v-if="description" :source="description" />
<p v-else>
<br>
<br>
<br>
</p>
</v-list-item-subtitle>
<div
class="d-flex flex-nowrap justify-start ma-0 pt-2 pb-0"
style="overflow-x: hidden; overflow-y: hidden; white-space: nowrap;"
>
<RecipeChips
:truncate="true"
:items="tags"
:title="false"
:limit="2"
small
url-prefix="tags"
v-bind="$attrs"
/>
</div>
</div>
<slot name="actions">
<v-card-actions class="w-100 my-0 px-1 py-0">
<RecipeFavoriteBadge
v-if="isOwnGroup && showRecipeContent"
:recipe-id="recipeId"
show-always
class="ma-0 pa-0"
/>
<div v-else class="my-0 px-1 py-0" /> <!-- Empty div to keep the layout consistent -->
<RecipeRating
v-if="showRecipeContent"
:class="[{ 'pb-2': !isOwnGroup }, 'ml-n2']"
:value="rating"
:recipe-id="recipeId"
:slug="slug"
small
/>
</v-list-item-avatar>
</slot>
<v-list-item-content class="py-0">
<v-list-item-title class="mt-1 mb-1 text-top">{{ name }}</v-list-item-title>
<v-list-item-subtitle class="ma-0 text-top">
<SafeMarkdown :source="description" />
</v-list-item-subtitle>
<div class="d-flex flex-wrap justify-start ma-0">
<RecipeChips :truncate="true" :items="tags" :title="false" :limit="2" :small="true" url-prefix="tags" v-on="$listeners" />
</div>
<div class="d-flex flex-wrap justify-end align-center">
<slot name="actions">
<RecipeFavoriteBadge v-if="isOwnGroup && showRecipeContent" :recipe-id="recipeId" show-always />
<RecipeRating
v-if="showRecipeContent"
:class="isOwnGroup ? 'ml-auto' : 'ml-auto pb-2'"
:value="rating"
:recipe-id="recipeId"
:slug="slug"
:small="true"
/>
<v-spacer></v-spacer>
<!-- If we're not logged-in, no items display, so we hide this menu -->
<!-- We also add padding to the v-rating above to compensate -->
<RecipeContextMenu
v-if="isOwnGroup && showRecipeContent"
:slug="slug"
:menu-icon="$globals.icons.dotsHorizontal"
:name="name"
:recipe-id="recipeId"
:use-items="{
delete: false,
edit: false,
download: true,
mealplanner: true,
shoppingList: true,
print: false,
printPreferences: false,
share: true,
}"
@deleted="$emit('delete', slug)"
/>
</slot>
</div>
</v-list-item-content>
<!-- If we're not logged-in, no items display, so we hide this menu -->
<!-- We also add padding to the v-rating above to compensate -->
<RecipeContextMenu
v-if="isOwnGroup && showRecipeContent"
:slug="slug"
:menu-icon="$globals.icons.dotsHorizontal"
:name="name"
:recipe-id="recipeId"
class="ml-auto"
:use-items="{
delete: false,
edit: false,
download: true,
mealplanner: true,
shoppingList: true,
print: false,
printPreferences: false,
share: true,
}"
@deleted="$emit('delete', slug)"
/>
</v-card-actions>
</slot>
</v-list-item>
<slot />
</v-card>
@ -84,7 +124,6 @@
</template>
<script lang="ts">
import { computed, defineComponent, useContext, useRoute } from "@nuxtjs/composition-api";
import RecipeFavoriteBadge from "./RecipeFavoriteBadge.vue";
import RecipeContextMenu from "./RecipeContextMenu.vue";
import RecipeCardImage from "./RecipeCardImage.vue";
@ -92,7 +131,7 @@ import RecipeRating from "./RecipeRating.vue";
import RecipeChips from "./RecipeChips.vue";
import { useLoggedInState } from "~/composables/use-logged-in-state";
export default defineComponent({
export default defineNuxtComponent({
components: {
RecipeFavoriteBadge,
RecipeContextMenu,
@ -139,27 +178,23 @@ export default defineComponent({
default: false,
},
height: {
type: [Number, String],
type: [Number],
default: 150,
},
imageHeight: {
type: [Number, String],
default: "fill-height",
},
},
emits: ["selected", "delete"],
setup(props) {
const { $auth } = useContext();
const $auth = useMealieAuth();
const { isOwnGroup } = useLoggedInState();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const groupSlug = computed(() => route.params.groupSlug || $auth.user.value?.groupSlug || "");
const showRecipeContent = computed(() => props.recipeId && props.slug);
const recipeRoute = computed<string>(() => {
return showRecipeContent.value ? `/g/${groupSlug.value}/r/${props.slug}` : "";
});
const cursor = computed(() => showRecipeContent.value ? "pointer" : "auto");
return {
isOwnGroup,
recipeRoute,
@ -170,7 +205,10 @@ export default defineComponent({
});
</script>
<style>
<style scoped>
:deep(.v-list-item__prepend) {
height: 100%;
}
.v-mobile-img {
padding-top: 0;
padding-bottom: 0;
@ -198,8 +236,9 @@ export default defineComponent({
align-self: start !important;
}
.flat, .theme--dark .flat {
box-shadow: none!important;
background-color: transparent!important;
.flat,
.theme--dark .flat {
box-shadow: none !important;
background-color: transparent !important;
}
</style>