Files
website/src/lib/components/about/MediaCard.svelte
T

145 lines
3.0 KiB
Svelte

<script lang="ts">
import Icon from '@iconify/svelte';
import type { RatedTitle } from '#lib/content';
import type { MediaDetails } from '#lib/media/types';
let { item, details, kind }: { item: RatedTitle; details?: MediaDetails; kind: 'show' | 'movie' } = $props();
const placeholderIcon = $derived(kind === 'show' ? 'mdi:television-classic' : 'mdi:movie-open-outline');
const metaParts = $derived([details?.year, details?.runtime].filter(Boolean));
</script>
<a class="media-card" href={item.url} target="_blank" rel="noopener noreferrer" title={item.name}>
<div class="poster">
{#if details?.poster}
<img src={details.poster} alt="" loading="lazy" />
{:else}
<span class="placeholder"><Icon icon={placeholderIcon} width="36" /></span>
{/if}
{#if item.rating !== undefined}
<span class="rating"><Icon icon="mdi:star" width="13" />{item.rating}/10</span>
{/if}
{#if item.status === 'watching'}
<span class="status watching"><Icon icon="mdi:play" width="13" />watching</span>
{:else if item.status === 'planning'}
<span class="status planning"><Icon icon="mdi:bookmark-outline" width="13" />planning</span>
{/if}
</div>
<div class="text">
<span class="title">{item.name}</span>
{#if metaParts.length}
<span class="meta">{metaParts.join(' · ')}</span>
{/if}
{#if details?.genre}
<span class="genre">{details.genre}</span>
{/if}
</div>
</a>
<style>
.media-card {
display: flex;
flex-direction: column;
gap: 0.5rem;
min-width: 0;
}
.poster {
position: relative;
aspect-ratio: 2 / 3;
overflow: hidden;
background: var(--dark-background);
border: 1px solid color-mix(in srgb, var(--foreground) 20%, transparent);
transition: border-color 0.15s ease;
}
.media-card:hover .poster {
border-color: var(--accent);
}
.poster img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.media-card:hover .poster img {
transform: scale(1.05);
}
.placeholder {
display: grid;
place-items: center;
height: 100%;
color: var(--muted);
}
.rating {
position: absolute;
top: 0.4rem;
right: 0.4rem;
display: inline-flex;
align-items: center;
gap: 0.2rem;
padding: 0 0.4rem;
background: var(--background);
color: var(--yellow);
font-size: 0.78rem;
}
.status {
position: absolute;
bottom: 0.4rem;
left: 0.4rem;
display: inline-flex;
align-items: center;
gap: 0.2rem;
padding: 0 0.4rem;
color: var(--background);
font-size: 0.75rem;
font-weight: 700;
}
.status.watching {
background: var(--blue);
}
.status.planning {
background: var(--magenta);
}
.text {
display: flex;
flex-direction: column;
gap: 0.15rem;
min-width: 0;
}
.title {
overflow: hidden;
color: var(--accent);
font-weight: 700;
white-space: nowrap;
text-overflow: ellipsis;
}
.meta,
.genre {
overflow: hidden;
color: var(--muted);
font-size: 0.75rem;
white-space: nowrap;
text-overflow: ellipsis;
}
@media (prefers-reduced-motion: reduce) {
.poster img {
transition: none;
}
}
</style>