39 lines
1.6 KiB
Vue
39 lines
1.6 KiB
Vue
<script setup lang="ts">
|
|
const props = defineProps<{ book: WishlistBook }>()
|
|
|
|
const { url: cover, loading } = useWishlistCover(() => props.book)
|
|
const imageFailed = ref(false)
|
|
const authors = computed(() => props.book.authors.map(wishlistAuthorName).join(', '))
|
|
</script>
|
|
|
|
<template>
|
|
<component
|
|
:is="book.thalia ? 'a' : 'div'"
|
|
:href="book.thalia ?? undefined"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="group block focus-visible:outline-none"
|
|
>
|
|
<div
|
|
class="relative aspect-[2/3] overflow-hidden rounded-md bg-elevated shadow-sm ring-1 ring-default transition duration-200 group-hover:-translate-y-1 group-hover:shadow-lg group-focus-visible:ring-2 group-focus-visible:ring-primary"
|
|
>
|
|
<img
|
|
v-if="cover && !imageFailed"
|
|
:src="cover"
|
|
:alt="`Cover von ${book.title}`"
|
|
loading="lazy"
|
|
class="size-full object-cover"
|
|
@error="imageFailed = true"
|
|
>
|
|
<div v-else-if="loading" class="size-full animate-pulse bg-accented" />
|
|
<div v-else class="flex size-full flex-col justify-between bg-gradient-to-br from-primary-100 to-primary-200 p-3 text-primary-950 dark:from-primary-900 dark:to-primary-950 dark:text-primary-50">
|
|
<p class="font-serif text-base leading-snug font-semibold line-clamp-5">{{ book.title }}</p>
|
|
<p class="text-xs opacity-75 line-clamp-2">{{ authors }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="mt-2 text-sm leading-snug font-medium text-highlighted line-clamp-2">{{ book.title }}</p>
|
|
<p class="text-xs text-muted line-clamp-1">{{ authors }}</p>
|
|
</component>
|
|
</template>
|