Zoeken...  ⌘K GitHub

VideoPlaylist video

Afspeellijst naast een actieve video-player.

/video-playlist
src/components/video/VideoPlaylist.astro
---
interface PlaylistItem {
  poster?: string;
  alt: string;
  title: string;
  duration?: string;
  active?: boolean;
}

interface Props {
  items?: PlaylistItem[];
  heading?: string;
}

const {
  items = [
    { alt: 'Video 1', title: 'Introductie BLURR methode', duration: '3:28', active: true },
    { alt: 'Video 2', title: 'Stap 1: Strategie & positionering', duration: '8:14' },
    { alt: 'Video 3', title: 'Stap 2: Brand identity bouwen', duration: '6:55' },
    { alt: 'Video 4', title: 'Stap 3: Campagnes opzetten', duration: '10:02' },
    { alt: 'Video 5', title: 'Stap 4: Meten en optimaliseren', duration: '7:40' },
  ],
  heading = 'Videoserie: de BLURR methode',
} = Astro.props;

const activeItem = items.find(i => i.active) ?? items[0];
---

<section class="vpl">
  {heading && <h2 class="vpl__heading">{heading}</h2>}
  <div class="vpl__layout">
    <div class="vpl__main">
      <div class="vpl__player">
        {activeItem?.poster ? (
          <img class="vpl__active-poster" src={activeItem.poster} alt={activeItem.alt} />
        ) : (
          <div class="vpl__active-placeholder" aria-label={activeItem?.alt}>
            <svg width="60" height="60" viewBox="0 0 60 60">
              <circle cx="30" cy="30" r="30" fill="rgba(99,102,241,0.85)"/>
              <polygon points="24,16 50,30 24,44" fill="#fff"/>
            </svg>
          </div>
        )}
      </div>
      <p class="vpl__active-title">{activeItem?.title}</p>
    </div>
    <div class="vpl__sidebar">
      {items.map((item) => (
        <button
          class:list={['vpl__item', item.active && 'vpl__item--active']}
          type="button"
        >
          <div class="vpl__thumb">
            {item.poster ? (
              <img class="vpl__thumb-img" src={item.poster} alt={item.alt} />
            ) : (
              <div class="vpl__thumb-placeholder" aria-label={item.alt}></div>
            )}
            {item.active && <span class="vpl__playing-dot" aria-hidden="true"></span>}
          </div>
          <div class="vpl__item-info">
            <p class="vpl__item-title">{item.title}</p>
            {item.duration && <p class="vpl__item-duration">{item.duration}</p>}
          </div>
        </button>
      ))}
    </div>
  </div>
</section>

<style>
  :root {
    --color-accent: #6366f1;
    --color-primary: #0a0a0a;
  }
  .vpl { padding: 2rem 0; }
  .vpl__heading {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--color-primary, #0a0a0a);
    margin: 0 0 2rem;
  }
  .vpl__layout {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 1.5rem;
    align-items: start;
  }
  .vpl__player {
    aspect-ratio: 16/9;
    border-radius: 10px;
    overflow: hidden;
    background: #111;
  }
  .vpl__active-poster { width: 100%; height: 100%; object-fit: cover; }
  .vpl__active-placeholder {
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg, #0a0a1a 0%, #1a1a3a 100%);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .vpl__active-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-primary, #0a0a0a);
    margin: 0.75rem 0 0;
  }
  .vpl__sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-height: 400px;
    overflow-y: auto;
  }
  .vpl__item {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.6rem;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    text-align: left;
    transition: background 0.15s;
  }
  .vpl__item:hover { background: #f5f5f5; }
  .vpl__item--active { background: #eeeef8; }
  .vpl__thumb {
    position: relative;
    width: 80px;
    aspect-ratio: 16/9;
    border-radius: 4px;
    overflow: hidden;
    flex-shrink: 0;
  }
  .vpl__thumb-img { width: 100%; height: 100%; object-fit: cover; }
  .vpl__thumb-placeholder {
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg, #1a1a2e 0%, #2a2a4e 100%);
  }
  .vpl__playing-dot {
    position: absolute;
    inset: 0;
    background: rgba(99,102,241,0.5);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .vpl__playing-dot::after {
    content: '▶';
    color: #fff;
    font-size: 0.8rem;
  }
  .vpl__item-info { flex: 1; min-width: 0; }
  .vpl__item-title {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-primary, #0a0a0a);
    margin: 0 0 0.2rem;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .vpl__item-duration {
    font-size: 0.72rem;
    color: #999;
    margin: 0;
    font-weight: 500;
  }
  @media (max-width: 768px) {
    .vpl__layout { grid-template-columns: 1fr; }
    .vpl__sidebar { max-height: none; }
  }
</style>

Props

Prop Type Default Beschrijving
items { poster?: string; alt: string; title: string; duration?: string; active?: boolean }[] Playlist items
heading string Sectietitel

* = verplicht