Media
Video Card code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/video-card.blade.php
@props([
'title',
'href' => null,
'src' => null,
'poster' => null,
'uploader' => null,
'uploaderAvatar' => null,
'duration' => null,
'views' => null,
'uploadedAt' => null,
'player' => 'default',
'active' => false,
'orientation' => 'vertical',
])
@php
/*
| Thumbnail card for video grids, playlists and “up next” rails.
| When `href` is omitted and `src` is set, clicking dispatches
| `video-source` for a paired <x-ui.video-player :name="$player">.
|
| orientation: vertical (default), horizontal
|
| Usage:
| <x-ui.video-card title="Spring bloom" poster="..." duration="0:05" views="12.4k" />
*/
$orientation = in_array($orientation, ['vertical', 'horizontal'], true) ? $orientation : 'vertical';
$isHorizontal = $orientation === 'horizontal';
$isButton = blank($href);
@endphp
@if ($isButton)
<button
type="button"
@if (filled($src))
@click="$dispatch('video-source', {
player: @js((string) $player),
src: @js((string) $src),
poster: @js($poster),
title: @js((string) $title),
play: true,
})"
@endif
{{ $attributes->merge([
'class' => ($isHorizontal
? 'group flex w-full items-stretch gap-3 rounded-2xl border border-gray-200 bg-white p-2 text-start shadow-sm transition hover:border-brand-300 hover:bg-brand-50/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-brand-700 dark:hover:bg-brand-950/30'
: 'group flex w-full flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white text-start shadow-sm transition hover:border-brand-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-brand-700'
).($active ? ' border-brand-500 ring-1 ring-brand-500/30 dark:border-brand-400' : ''),
]) }}
>
@else
<a
href="{{ $href }}"
{{ $attributes->merge([
'class' => ($isHorizontal
? 'group flex w-full items-stretch gap-3 rounded-2xl border border-gray-200 bg-white p-2 text-start shadow-sm transition hover:border-brand-300 hover:bg-brand-50/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-brand-700 dark:hover:bg-brand-950/30'
: 'group flex w-full flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white text-start shadow-sm transition hover:border-brand-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-brand-700'
).($active ? ' border-brand-500 ring-1 ring-brand-500/30 dark:border-brand-400' : ''),
]) }}
>
@endif
<div @class([
'relative overflow-hidden bg-gray-100 dark:bg-gray-800',
'aspect-video w-full' => ! $isHorizontal,
'aspect-video w-32 shrink-0 rounded-xl sm:w-40' => $isHorizontal,
])>
@if (filled($poster))
<img src="{{ $poster }}" alt="" class="h-full w-full object-cover transition duration-300 group-hover:scale-[1.02]" loading="lazy">
@else
<div class="flex h-full w-full items-center justify-center text-gray-400 dark:text-gray-500">
<x-ui.icon name="play" weight="fill" size="2xl" />
</div>
@endif
@if (filled($duration))
<span class="absolute bottom-2 end-2 rounded-md bg-black/75 px-1.5 py-0.5 font-mono text-[11px] font-medium tabular-nums text-white">{{ $duration }}</span>
@endif
@if ($active)
<span class="absolute start-2 top-2 inline-flex items-center gap-1 rounded-md bg-brand-600 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-white">
<x-ui.icon name="play" weight="fill" size="xs" />
Now
</span>
@endif
</div>
<div @class(['min-w-0 flex-1', 'p-3 sm:p-4' => ! $isHorizontal, 'py-1 pe-1' => $isHorizontal])>
<p class="line-clamp-2 text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</p>
@if (filled($uploader) || filled($views) || filled($uploadedAt))
<div class="mt-1.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-gray-500 dark:text-gray-400">
@if (filled($uploader))
<span class="inline-flex min-w-0 items-center gap-1.5">
<x-ui.avatar :src="$uploaderAvatar" :name="$uploader" size="xs" />
<span class="truncate font-medium text-gray-700 dark:text-gray-300">{{ $uploader }}</span>
</span>
@endif
@if (filled($views))
<span class="tabular-nums">{{ $views }} views</span>
@endif
@if (filled($uploadedAt))
<span>{{ $uploadedAt }}</span>
@endif
</div>
@endif
{{ $slot }}
</div>
@if ($isButton)
</button>
@else
</a>
@endif