Media
Video Share code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/video-share.blade.php
@props([
'url',
'title' => null,
'player' => 'default',
'startAt' => null,
'embed' => null,
'label' => 'Share',
])
@php
/*
| Share sheet for a video — copy link, copy link at current timestamp, and
| optional embed snippet. Listens to `video-time` so “copy at time” stays
| in sync with the paired player.
|
| Usage:
| <x-ui.video-share
| url="https://example.com/videos/1"
| title="Spring bloom"
| player="watch"
| embed='<iframe src="..."></iframe>'
| />
*/
@endphp
<div
x-data="{
open: false,
player: @js((string) $player),
url: @js((string) $url),
title: @js($title),
embed: @js($embed),
current: @js(is_numeric($startAt) ? (float) $startAt : 0),
copied: null,
timer: null,
get timestampUrl() {
const base = this.url.split('#')[0];
const seconds = Math.max(0, Math.floor(this.current || 0));
return `${base}#t=${seconds}`;
},
formatTime(seconds) {
const total = Math.max(0, Math.floor(seconds || 0));
const m = Math.floor(total / 60);
const s = String(total % 60).padStart(2, '0');
return `${m}:${s}`;
},
async copy(value, key) {
try {
await navigator.clipboard.writeText(value);
} catch (error) {
const area = document.createElement('textarea');
area.value = value;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.left = '-9999px';
document.body.appendChild(area);
area.select();
document.execCommand('copy');
document.body.removeChild(area);
}
this.copied = key;
clearTimeout(this.timer);
this.timer = setTimeout(() => { this.copied = null; }, 1600);
},
onTime(detail) {
if (detail?.player && detail.player !== this.player) { return; }
this.current = Number(detail?.time ?? this.current);
},
}"
@video-time.window="onTime($event.detail)"
@click.outside="open = false"
@keydown.escape.window="open = false"
{{ $attributes->merge(['class' => 'relative inline-flex']) }}
>
<button
type="button"
@click="open = ! open"
class="inline-flex h-9 items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800"
:aria-expanded="open.toString()"
aria-haspopup="dialog"
>
<x-ui.icon name="share-network" size="sm" />
{{ $label }}
</button>
<div
x-show="open"
x-cloak
x-transition.opacity.duration.150ms
class="absolute end-0 z-40 mt-2 w-[min(100vw-2rem,22rem)] overflow-hidden rounded-2xl border border-gray-200 bg-white p-4 shadow-lg ring-1 ring-black/5 dark:border-gray-800 dark:bg-gray-900"
role="dialog"
aria-label="{{ $label }}"
>
<div class="mb-3">
<p class="text-sm font-semibold text-gray-900 dark:text-white">{{ $label }}</p>
@if (filled($title))
<p class="mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400">{{ $title }}</p>
@endif
</div>
<div class="space-y-2">
<button
type="button"
class="flex w-full items-center justify-between gap-3 rounded-xl border border-gray-200 px-3 py-2.5 text-start text-sm transition hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:hover:bg-gray-800"
@click="copy(url, 'link')"
>
<span class="min-w-0">
<span class="block font-medium text-gray-900 dark:text-white">Copy link</span>
<span class="mt-0.5 block truncate text-xs text-gray-500 dark:text-gray-400" x-text="url"></span>
</span>
<span class="shrink-0 text-xs font-semibold text-brand-600 dark:text-brand-400" x-text="copied === 'link' ? 'Copied' : 'Copy'"></span>
</button>
<button
type="button"
class="flex w-full items-center justify-between gap-3 rounded-xl border border-gray-200 px-3 py-2.5 text-start text-sm transition hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:hover:bg-gray-800"
@click="copy(timestampUrl, 'time')"
>
<span class="min-w-0">
<span class="block font-medium text-gray-900 dark:text-white">Copy at current time</span>
<span class="mt-0.5 block text-xs text-gray-500 dark:text-gray-400">Starts at <span x-text="formatTime(current)"></span></span>
</span>
<span class="shrink-0 text-xs font-semibold text-brand-600 dark:text-brand-400" x-text="copied === 'time' ? 'Copied' : 'Copy'"></span>
</button>
<template x-if="embed">
<button
type="button"
class="flex w-full items-center justify-between gap-3 rounded-xl border border-gray-200 px-3 py-2.5 text-start text-sm transition hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-800 dark:hover:bg-gray-800"
@click="copy(embed, 'embed')"
>
<span class="min-w-0">
<span class="block font-medium text-gray-900 dark:text-white">Copy embed code</span>
<span class="mt-0.5 block text-xs text-gray-500 dark:text-gray-400">iframe snippet</span>
</span>
<span class="shrink-0 text-xs font-semibold text-brand-600 dark:text-brand-400" x-text="copied === 'embed' ? 'Copied' : 'Copy'"></span>
</button>
</template>
</div>
</div>
</div>