Show small stitch-frame thumbs when a video collection is expanded.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="shrink-0">
|
||||
<span v-if="hidden" class="sensitive-glow block h-12 w-12 overflow-hidden rounded-lg" />
|
||||
<div v-else class="flex items-start gap-1.5">
|
||||
<button
|
||||
v-if="stillId && !failed.still"
|
||||
type="button"
|
||||
class="flex w-12 flex-col items-center gap-0.5"
|
||||
aria-label="Start still"
|
||||
@click.stop="openPreview(stillFull, 'Start')"
|
||||
>
|
||||
<span class="relative h-12 w-12 overflow-hidden rounded-lg bg-zinc-900">
|
||||
<img
|
||||
:src="stillThumb"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="h-full w-full object-cover"
|
||||
@error="failed.still = true"
|
||||
>
|
||||
</span>
|
||||
<span class="text-[10px] leading-none text-zinc-500">Start</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="fromClipId && !failed.from"
|
||||
type="button"
|
||||
class="flex w-12 flex-col items-center gap-0.5"
|
||||
aria-label="Last frame of previous clip"
|
||||
@click.stop="openPreview(fromFull, 'From prev')"
|
||||
>
|
||||
<span class="relative h-12 w-12 overflow-hidden rounded-lg bg-zinc-900">
|
||||
<img
|
||||
:src="fromThumb"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="h-full w-full object-cover"
|
||||
@error="failed.from = true"
|
||||
>
|
||||
</span>
|
||||
<span class="text-[10px] leading-none text-zinc-500">From prev</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="showFirstFrame && clipId && !failed.first"
|
||||
type="button"
|
||||
class="flex w-12 flex-col items-center gap-0.5"
|
||||
aria-label="First frame of this clip"
|
||||
@click.stop="openPreview(firstFull, 'This clip')"
|
||||
>
|
||||
<span class="relative h-12 w-12 overflow-hidden rounded-lg bg-zinc-900">
|
||||
<img
|
||||
:src="firstThumb"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="h-full w-full object-cover"
|
||||
@error="failed.first = true"
|
||||
>
|
||||
</span>
|
||||
<span class="text-[10px] leading-none text-zinc-500">This clip</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="preview"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
|
||||
@click.self="closePreview"
|
||||
>
|
||||
<img
|
||||
:src="preview.src"
|
||||
:alt="preview.label"
|
||||
class="max-h-[70vh] max-w-[min(90vw,56rem)] object-contain"
|
||||
@click.stop
|
||||
>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
hidden?: boolean
|
||||
stillId?: string
|
||||
fromClipId?: string
|
||||
clipId: string
|
||||
showFirstFrame?: boolean
|
||||
}>()
|
||||
|
||||
const failed = reactive({ still: false, from: false, first: false })
|
||||
const preview = ref<{ src: string; label: string } | null>(null)
|
||||
|
||||
const stillThumb = computed(() => props.stillId ? `/api/library/stills/${props.stillId}?thumb=1` : '')
|
||||
const stillFull = computed(() => props.stillId ? `/api/library/stills/${props.stillId}` : '')
|
||||
const fromThumb = computed(() => props.fromClipId ? `/api/library/clips/${props.fromClipId}/last-frame?thumb=1` : '')
|
||||
const fromFull = computed(() => props.fromClipId ? `/api/library/clips/${props.fromClipId}/last-frame` : '')
|
||||
const firstThumb = computed(() => props.clipId ? `/api/library/clips/${props.clipId}/first-frame?thumb=1` : '')
|
||||
const firstFull = computed(() => props.clipId ? `/api/library/clips/${props.clipId}/first-frame` : '')
|
||||
|
||||
watch(
|
||||
() => [props.hidden, props.stillId, props.fromClipId, props.clipId, props.showFirstFrame],
|
||||
() => {
|
||||
failed.still = false
|
||||
failed.from = false
|
||||
failed.first = false
|
||||
}
|
||||
)
|
||||
|
||||
function openPreview(src: string, label: string) {
|
||||
if (!src) return
|
||||
preview.value = { src, label }
|
||||
}
|
||||
|
||||
function closePreview() {
|
||||
preview.value = null
|
||||
}
|
||||
|
||||
function onKey(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') closePreview()
|
||||
}
|
||||
|
||||
watch(preview, (value) => {
|
||||
if (value) window.addEventListener('keydown', onKey)
|
||||
else window.removeEventListener('keydown', onKey)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', onKey)
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user