Migrate to nuxt content v3. Change articles layout.

This commit is contained in:
2025-03-15 19:57:25 +02:00
parent 5ad3e7b393
commit 64afeb8091
7 changed files with 947 additions and 510 deletions

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ParsedContent } from "@nuxt/content/dist/runtime/types"; import { formatDate } from "@vueuse/core";
interface Props { interface Props {
article: ParsedContent; article: any;
} }
const { article } = defineProps<Props>(); const { article } = defineProps<Props>();
@@ -10,8 +10,10 @@ const date = useDateFormat(article.date, "Do of MMMM YYYY");
</script> </script>
<template> <template>
<ULink class="font-semibold hover:underline" :to="article._path">{{ <ULink class="font-semibold hover:underline" :to="article.path">{{
article.title article.title
}}</ULink> }}</ULink>
<div class="text-xs text-slate-500">{{ date }}</div> <div class="text-xs text-slate-500">
{{ date }}
</div>
</template> </template>

19
content.config.ts Normal file
View File

@@ -0,0 +1,19 @@
import { defineCollection, defineContentConfig, z } from "@nuxt/content";
export default defineContentConfig({
collections: {
articles: defineCollection({
source: "articles/**/*.md",
type: "page",
schema: z.object({
title: z.string(),
date: z.date(),
coverImage: z.object({
author: z.string(),
authorUrl: z.string().url(),
url: z.string().url(),
}),
}),
}),
},
});

View File

@@ -12,10 +12,10 @@ export default defineNuxtConfig({
], ],
content: { content: {
markdown: { renderer: {
anchorLinks: false, anchorLinks: false,
}, },
}, },
compatibilityDate: "2025-03-15", compatibilityDate: "2025-03-15",
}); });

1277
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,12 +17,12 @@
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
"dependencies": { "dependencies": {
"@nuxt/content": "^2.9.0", "@nuxt/content": "^3.3.0",
"@nuxt/ui": "^2.11.0", "@nuxt/ui": "^2.11.0",
"@nuxtjs/robots": "^3.0.0", "@nuxtjs/robots": "^3.0.0",
"@vueuse/core": "^10.7.0", "@vueuse/core": "^10.7.0",
"@vueuse/motion": "^2.0.0", "@vueuse/motion": "^3.0.3",
"@vueuse/nuxt": "^10.7.0", "@vueuse/nuxt": "^13.0.0",
"sharp": "^0.33.0" "sharp": "^0.33.0"
} }
} }

View File

@@ -6,53 +6,71 @@ definePageMeta({
}); });
const { path } = useRoute(); const { path } = useRoute();
const { data: article } = useAsyncData(path, async () => {
const article = await queryCollection("articles").path(path).first();
return article;
});
</script> </script>
<template> <template>
<ContentDoc :path="path" v-slot="{ doc }"> <UContainer
<UContainer v-motion-fade
v-motion-fade class="flex flex-col gap-3 prose dark:prose-invert"
class="flex flex-col gap-3 prose dark:prose-invert" as="article"
as="article" >
> <div class="text-sm text-slate-500">
<div class="text-sm text-slate-500"> {{ formatDate(new Date(article.date), "Do of MMMM YYYY") }}
{{ formatDate(new Date(doc.date), "Do of MMMM YYYY") }} </div>
</div> <h1>{{ article.title }}</h1>
<h1>{{ doc.title }}</h1> </UContainer>
</UContainer>
<UContainer <UContainer
class="hidden md:block" class="hidden md:block"
as="figure" as="figure"
v-if="doc.coverImage?.url" v-if="article.coverImage?.url"
v-motion-fade v-motion-fade
:delay="500" :delay="500"
>
<NuxtImg
placeholder
:src="article.coverImage?.url ?? ''"
:alt="article.title"
height="1000"
width="1700"
class="rounded-lg"
/>
<ULink
:to="article.coverImage.authorUrl"
target="_blank"
class="text-xs text-slate-500 italic font-serif hover:underline"
> >
<NuxtImg Photo by {{ article.coverImage.author }}
placeholder </ULink>
:src="doc.coverImage.url ?? ''" </UContainer>
:alt="doc.title"
height="1000" <UContainer
width="1700" v-motion-fade
class="rounded-lg" :delay="500"
/> class="flex flex-col gap-3 prose dark:prose-invert !pt-0"
<ULink as="article"
:to="doc.coverImage.authorUrl" >
target="_blank" <ContentRenderer :value="article" />
class="text-xs text-slate-500 italic font-serif hover:underline" </UContainer>
>
Photo by {{ doc.coverImage.author }}
</ULink>
</UContainer>
<UContainer
v-motion-fade
:delay="500"
class="flex flex-col gap-3 prose dark:prose-invert !pt-0"
as="article"
>
<ContentRenderer :value="doc" />
</UContainer>
</ContentDoc>
<Footer v-motion-fade :delay="500" /> <Footer v-motion-fade :delay="500" />
</template> </template>
<style scoped>
article.prose {
h2 {
font-weight: bold !important;
a {
font-weight: bold !important;
}
}
}
</style>
s

View File

@@ -1,7 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { formatDate } from "@vueuse/core";
definePageMeta({ definePageMeta({
layout: "content", layout: "content",
}); });
const articles = useAsyncData("articles", async () => {
return queryCollection("articles").order("date", "DESC").all();
});
</script> </script>
<template> <template>
@@ -9,19 +15,30 @@ definePageMeta({
<h1>Articles</h1> <h1>Articles</h1>
</UContainer> </UContainer>
<UContainer <UContainer v-motion-fade :delay="500" class="grid grid-cols-12 gap-8">
v-motion-fade <UCard class="col-span-6" v-for="article in articles.data.value">
:delay="500" <div class="grid gap-4">
class="flex flex-col md:gap-10 gap-5 !pt-0" <NuxtImg
> class="rounded rounded-lg cursor-pointer"
<ContentList :query="{ sort: [{ date: -1 }] }" v-slot="{ list }"> :src="article.coverImage.url"
<div v-for="article in list" :key="article._path"> @click="navigateTo(article.path)"
<ArticleListing
:article="article"
v-if="article._path?.startsWith('/articles')"
/> />
<div class="w-full">
<NuxtLink :to="article.path" class="font-semibold">{{
article.title
}}</NuxtLink>
<div class="text-sm text-slate-500">
{{ formatDate(new Date(article.date), "Do of MMMM YYYY") }}
</div>
</div>
<div>
<UButton variant="soft" color="gray" :to="article.path"
>Read article</UButton
>
</div>
</div> </div>
</ContentList> </UCard>
</UContainer> </UContainer>
<Footer v-motion-fade :delay="500" /> <Footer v-motion-fade :delay="500" />