145 lines
3.0 KiB
Svelte
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>
|