First commit
This commit is contained in:
77
frontend/app/components/ArticleCard.vue
Normal file
77
frontend/app/components/ArticleCard.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<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: language.value, slug: props.article.slug })
|
||||
)
|
||||
|
||||
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>
|
||||
Reference in New Issue
Block a user