76 lines
3.0 KiB
Vue
76 lines
3.0 KiB
Vue
<script setup lang="ts">
|
|
/* One language version of an article, as the version switcher lists them. */
|
|
export interface ArticleVersion {
|
|
language: string
|
|
slug: string
|
|
title: string
|
|
updatedAt?: string
|
|
}
|
|
|
|
export interface ArticleMeta {
|
|
_id: string
|
|
slug: string
|
|
title: string
|
|
tags?: string[]
|
|
language?: string
|
|
/* set on translations: the canonical (original) version's slug */
|
|
baseSlug?: string | null
|
|
/* every language version the article exists in, from the single-article GET */
|
|
versions?: ArticleVersion[]
|
|
createdAt?: string
|
|
updatedAt?: string
|
|
}
|
|
|
|
const props = defineProps<{ article: ArticleMeta }>()
|
|
|
|
const { t, locale } = useI18n()
|
|
|
|
/* shown only when an article steps off the English default — a quiet badge
|
|
so the shelf's other languages are visible at a glance; when the article
|
|
exists in several languages, its codes ride together instead */
|
|
const language = computed(() => articleLanguage(props.article))
|
|
const languageLabel = computed(() => languageName(language.value))
|
|
const langs = computed(() => props.article.versions ?? [])
|
|
const multiLang = computed(() => langs.value.length > 1)
|
|
const langCodes = computed(() => langs.value.map((v) => v.language.toUpperCase()).join(' · '))
|
|
const langNames = computed(() => langs.value.map((v) => languageName(v.language)).join(', '))
|
|
/* where the card leads: the language address of the version it shows */
|
|
const href = computed(() => versionHref(identityOf(props.article), language.value))
|
|
|
|
const updated = computed(() => {
|
|
const d = props.article.updatedAt ? new Date(props.article.updatedAt) : null
|
|
if (!d || Number.isNaN(d.getTime())) return ''
|
|
return d.toLocaleDateString(locale.value, { year: 'numeric', month: 'short', day: 'numeric' })
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<NuxtLink :to="href" class="article-card">
|
|
<div class="card-top">
|
|
<span class="card-kicker">{{ article.baseSlug ? t('card.kickerTranslation') : t('card.kickerArticle') }}</span>
|
|
<span v-if="multiLang" class="card-langs" :title="t('card.existsIn', { names: langNames })">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="10" />
|
|
<path d="M2 12h20" />
|
|
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
|
</svg>
|
|
{{ langCodes }}
|
|
</span>
|
|
<span
|
|
v-else-if="language !== 'en'"
|
|
class="lang-badge"
|
|
:title="t('common.writtenIn', { lang: languageLabel })"
|
|
>{{ languageLabel }}</span
|
|
>
|
|
</div>
|
|
<h3 class="card-title" :lang="language">{{ article.title }}</h3>
|
|
<div v-if="article.tags?.length" class="tags">
|
|
<span v-for="tag in article.tags.slice(0, 3)" :key="tag" class="tag">{{ tag }}</span>
|
|
</div>
|
|
<div class="card-meta">
|
|
<span>{{ href }}</span>
|
|
<span v-if="updated">{{ t('card.updated', { date: updated }) }}</span>
|
|
</div>
|
|
</NuxtLink>
|
|
</template>
|