Media
Video Meta code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/video-meta.blade.php
@props([
'title' => null,
'description' => null,
'uploader' => null,
'uploaderAvatar' => null,
'uploaderHref' => null,
'uploaderLabel' => 'Uploaded by',
'uploadedAt' => null,
'uploadedAtDatetime' => null,
'views' => null,
'resolution' => null,
'bitrate' => null,
'duration' => null,
'size' => null,
'codec' => null,
'format' => null,
'fps' => null,
'items' => [],
'meta' => [],
'tags' => [],
'layout' => 'bar',
])
@php
/*
| Video metadata companion for <x-ui.video-player>. Renders the uploader
| byline, published date, and technical facts (views, resolution, bitrate,
| duration, size, codec, format, fps). Sit it directly under the player.
|
| layout:
| bar — icon + value status strip (default)
| chips — badge-split key/value chips
| list — compact description list
|
| Usage:
| <x-ui.video-meta
| title="Spring bloom"
| uploader="Ada Lovelace"
| uploaded-at="Jun 12, 2026"
| views="12.4k"
| resolution="1920 × 1080"
| bitrate="4.2 Mbps"
| duration="0:05"
| />
*/
$layout = in_array($layout, ['bar', 'chips', 'list'], true) ? $layout : 'bar';
$factDefs = [
['key' => 'views', 'label' => 'Views', 'icon' => 'eye', 'value' => $views, 'variant' => 'brand'],
['key' => 'uploadedAt', 'label' => 'Published', 'icon' => 'calendar-blank', 'value' => $uploadedAt, 'variant' => 'gray', 'datetime' => $uploadedAtDatetime],
['key' => 'duration', 'label' => 'Duration', 'icon' => 'timer', 'value' => $duration, 'variant' => 'gray'],
['key' => 'resolution', 'label' => 'Resolution', 'icon' => 'frame-corners', 'value' => $resolution, 'variant' => 'blue'],
['key' => 'bitrate', 'label' => 'Bitrate', 'icon' => 'waveform', 'value' => $bitrate, 'variant' => 'amber'],
['key' => 'fps', 'label' => 'Frame rate', 'icon' => 'film-strip', 'value' => $fps, 'variant' => 'gray'],
['key' => 'codec', 'label' => 'Codec', 'icon' => 'cpu', 'value' => $codec, 'variant' => 'gray'],
['key' => 'format', 'label' => 'Format', 'icon' => 'file-video', 'value' => $format, 'variant' => 'gray'],
['key' => 'size', 'label' => 'Size', 'icon' => 'hard-drive', 'value' => $size, 'variant' => 'gray'],
];
$facts = collect($factDefs)
->filter(fn (array $fact) => filled($fact['value']))
->values();
$statusItems = $facts
->map(function (array $fact) {
$item = [
'icon' => $fact['icon'],
'value' => $fact['value'],
'title' => $fact['label'],
];
if ($fact['key'] === 'uploadedAt' && filled($fact['datetime'] ?? null)) {
$item['label'] = null;
}
return $item;
})
->merge($items)
->filter(fn ($item) => filled($item['icon'] ?? null) || filled($item['label'] ?? null) || filled($item['value'] ?? null))
->values()
->all();
$detailItems = $facts
->mapWithKeys(fn (array $fact) => [$fact['label'] => $fact['value']])
->merge($meta)
->filter(fn ($value) => filled($value))
->all();
$hasUploader = filled($uploader) || filled($uploaderAvatar);
$hasHeader = filled($title) || filled($description) || $hasUploader || isset($actions);
$hasFacts = $layout === 'bar'
? $statusItems !== []
: ($layout === 'chips' ? $facts->isNotEmpty() || $items !== [] : $detailItems !== []);
$hasTags = $tags !== [];
@endphp
<section
{{ $attributes->merge(['class' => 'min-w-0 max-w-full rounded-2xl border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-800 dark:bg-gray-900 sm:p-5']) }}
@if (filled($title)) aria-label="{{ $title }} metadata" @else aria-label="Video metadata" @endif
>
@if ($hasHeader)
<div @class(['flex flex-col gap-4', 'mb-4' => $hasFacts || $hasTags || ! $slot->isEmpty()])>
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="min-w-0 flex-1 space-y-3">
@if (filled($title) || filled($description))
<div class="min-w-0">
@if (filled($title))
<h3 class="text-base font-semibold tracking-tight text-gray-900 dark:text-white sm:truncate">{{ $title }}</h3>
@endif
@if (filled($description))
<p class="mt-1 text-sm leading-6 text-gray-600 dark:text-gray-400">{{ $description }}</p>
@endif
</div>
@endif
@if ($hasUploader || filled($uploadedAt))
<div class="flex flex-wrap items-center gap-x-4 gap-y-2">
@if ($hasUploader)
@if ($uploaderHref)
<a href="{{ $uploaderHref }}" class="group flex min-w-0 items-center gap-3 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500">
<x-ui.avatar :src="$uploaderAvatar" :name="$uploader" size="sm" />
<div class="min-w-0">
<p class="truncate text-sm font-semibold text-gray-900 group-hover:text-brand-600 dark:text-white dark:group-hover:text-brand-400">{{ $uploader }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">{{ $uploaderLabel }}</p>
</div>
</a>
@else
<div class="flex min-w-0 items-center gap-3">
<x-ui.avatar :src="$uploaderAvatar" :name="$uploader" size="sm" />
<div class="min-w-0">
<p class="truncate text-sm font-semibold text-gray-900 dark:text-white">{{ $uploader }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">{{ $uploaderLabel }}</p>
</div>
</div>
@endif
@endif
@if (filled($uploadedAt) && ! $hasFacts)
<div class="flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400">
<x-ui.icon name="calendar-blank" size="sm" class="text-gray-400 dark:text-gray-500" />
<time @if (filled($uploadedAtDatetime)) datetime="{{ $uploadedAtDatetime }}" @endif>{{ $uploadedAt }}</time>
</div>
@endif
</div>
@endif
</div>
@isset($actions)
<div class="flex w-full min-w-0 flex-wrap items-center gap-2 sm:w-auto sm:max-w-[min(100%,24rem)] sm:shrink-0 sm:justify-end">
{{ $actions }}
</div>
@endisset
</div>
</div>
@endif
@if ($hasFacts)
@if ($layout === 'bar')
<x-ui.status-bar :items="$statusItems" @class(['mb-4' => $hasTags || ! $slot->isEmpty()]) />
@elseif ($layout === 'chips')
<div @class(['flex flex-wrap items-center gap-2', 'mb-4' => $hasTags || ! $slot->isEmpty()])>
@foreach ($facts as $fact)
<x-ui.badge-split :label="$fact['label']" :variant="$fact['variant']" :mono="in_array($fact['key'], ['bitrate', 'resolution', 'fps', 'codec', 'format', 'size', 'duration'], true)">
@if ($fact['key'] === 'uploadedAt' && filled($fact['datetime'] ?? null))
<time datetime="{{ $fact['datetime'] }}">{{ $fact['value'] }}</time>
@else
{{ $fact['value'] }}
@endif
</x-ui.badge-split>
@endforeach
@foreach ($items as $item)
@if (filled($item['label'] ?? null) && filled($item['value'] ?? null))
<x-ui.badge-split :label="$item['label']" variant="gray" mono>
{{ $item['value'] }}
</x-ui.badge-split>
@endif
@endforeach
</div>
@else
<div @class(['mb-4' => $hasTags || ! $slot->isEmpty()])>
<x-ui.description-list compact :items="$detailItems" />
</div>
@endif
@endif
@if ($hasTags)
<div @class(['flex flex-wrap gap-1.5', 'mb-4' => ! $slot->isEmpty()])>
@foreach ($tags as $tag)
<x-ui.badge variant="gray" size="sm">{{ $tag }}</x-ui.badge>
@endforeach
</div>
@endif
@if (! $slot->isEmpty())
<div>
{{ $slot }}
</div>
@endif
</section>