First working POC
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<script lang="ts">
|
||||
import type { MediaItem } from "$lib/api/types";
|
||||
import MediaCard from "$lib/components/library/MediaCard.svelte";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
items: MediaItem[];
|
||||
onItemClick?: (item: MediaItem) => void;
|
||||
showAll?: () => void;
|
||||
}
|
||||
|
||||
let { title, items, onItemClick, showAll }: Props = $props();
|
||||
|
||||
let scrollContainer: HTMLDivElement | null = $state(null);
|
||||
let showLeftArrow = $state(false);
|
||||
let showRightArrow = $state(true);
|
||||
|
||||
function handleScroll() {
|
||||
if (!scrollContainer) return;
|
||||
showLeftArrow = scrollContainer.scrollLeft > 0;
|
||||
showRightArrow =
|
||||
scrollContainer.scrollLeft <
|
||||
scrollContainer.scrollWidth - scrollContainer.clientWidth - 10;
|
||||
}
|
||||
|
||||
function scrollLeft() {
|
||||
scrollContainer?.scrollBy({ left: -600, behavior: "smooth" });
|
||||
}
|
||||
|
||||
function scrollRight() {
|
||||
scrollContainer?.scrollBy({ left: 600, behavior: "smooth" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-3">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-4">
|
||||
<h2 class="text-2xl font-semibold text-white">{title}</h2>
|
||||
{#if showAll}
|
||||
<button
|
||||
onclick={showAll}
|
||||
class="text-sm text-gray-400 hover:text-white transition-colors"
|
||||
>
|
||||
See all
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Scrollable row -->
|
||||
<div class="relative group">
|
||||
<div
|
||||
bind:this={scrollContainer}
|
||||
onscroll={handleScroll}
|
||||
class="flex gap-4 overflow-x-auto scrollbar-hide scroll-smooth px-4 pb-4"
|
||||
>
|
||||
{#each items as item (item.id)}
|
||||
<MediaCard
|
||||
{item}
|
||||
size="medium"
|
||||
showProgress={true}
|
||||
onclick={() => onItemClick?.(item)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Navigation arrows (Spotify-style - only show on hover) -->
|
||||
{#if showLeftArrow}
|
||||
<button
|
||||
onclick={scrollLeft}
|
||||
class="absolute left-0 top-1/2 -translate-y-1/2 p-2 bg-black/80 hover:bg-black rounded-full opacity-0 group-hover:opacity-100 transition-opacity z-10 ml-2"
|
||||
aria-label="Scroll left"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if showRightArrow}
|
||||
<button
|
||||
onclick={scrollRight}
|
||||
class="absolute right-0 top-1/2 -translate-y-1/2 p-2 bg-black/80 hover:bg-black rounded-full opacity-0 group-hover:opacity-100 transition-opacity z-10 mr-2"
|
||||
aria-label="Scroll right"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.scrollbar-hide {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user