Files
2026-09-12 20:50:06 +02:00

56 lines
1.7 KiB
Vue

<script setup lang="ts">
interface OgData {
image?: string
title?: string
description?: string
url?: string
}
const props = defineProps<{
link: string
}>()
// Unique key per component instance so multiple BlogLink usages (even of the
// same URL) don't share/collide their async data state.
const instanceId = useId()
const { data: og } = await useAsyncData<Partial<OgData>>(
`blog-link-${instanceId}`,
() => $fetch('/api/ogimage', { params: { url: props.link } }),
{ default: () => ({}) }
)
</script>
<template>
<a
:href="props.link" target="_blank" rel="noopener noreferrer"
class="block ml-auto mr-auto mt-2 mb-2 sm:max-w-[55%] max-w-[75%] rounded-xl overflow-hidden border border-slate-200 hover:border-slate-400 transition-colors bg-white group no-underline"
>
<img
v-if="og?.image" :src="og.image" :alt="og?.title || props.link"
class="w-full max-h-[320px] object-cover block" loading="lazy"
>
<div v-if="og?.title || og?.description" class="p-4">
<h3 v-if="og?.title" class="text-lg font-semibold text-slate-800 group-hover:underline">{{ og.title }}</h3>
<p v-if="og?.description" class="text-sm text-slate-500 mt-1 line-clamp-2">{{ og.description }}</p>
<p class="text-xs text-slate-400 mt-2 pb-1 leading-relaxed truncate">{{ props.link }}</p>
</div>
<!-- Fallback when no metadata could be retrieved -->
<div v-else class="p-4">
<h3 class="text-lg text-center font-semibold text-slate-800 group-hover:underline break-all">{{ props.link }}</h3>
</div>
<div v-if="$slots.default" class="ml-auto mr-auto mt-2 w-fit max-w-[80%] slot pb-4">
<slot />
</div>
</a>
</template>
<style lang="css" scoped>
.slot > * {
font-size: 16px;
}
</style>