Files
20260512-skg-tk/web/components/storyboard-bar.tsx
2026-05-13 21:30:04 +08:00

81 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { LayoutGrid, ChevronDown, ChevronUp } from "lucide-react"
import { type Job, hasCutout } from "@/lib/api"
interface Props {
job: Job | null
selectedFrames: Set<number>
focusedFrame: number | null
onFocusFrame: (idx: number | null) => void
workbenchOpen?: boolean
onOpenWorkbench?: (frameIdx?: number) => void
onCloseWorkbench?: () => void
}
export function StoryboardBar({ job, selectedFrames, focusedFrame, onFocusFrame, workbenchOpen = false, onOpenWorkbench, onCloseWorkbench }: Props) {
if (!job) return null
const frames = job.frames
.filter((f) => selectedFrames.has(f.index))
.sort((a, b) => a.timestamp - b.timestamp)
const totalElements = frames.reduce(
(sum, f) => sum + (f.elements?.filter((e) => hasCutout(e)).length ?? 0),
0,
)
// focused 分镜数据
// focus 用来高亮分镜缩略图 + 顶部 indicator不再触发底部详情面板展开
const focusFrame = focusedFrame !== null
? job.frames.find((f) => f.index === focusedFrame) ?? null
: null
const focusSeq = focusFrame
? frames.findIndex((f) => f.index === focusFrame.index) + 1
: 0
return (
<div data-storyboard-bar="true" className="relative z-20 flex-shrink-0 border-b border-white/5 bg-black/30 backdrop-blur-xl">
{/* header */}
<div className="flex items-center justify-between px-4 py-2">
<div className="flex items-center gap-2 min-w-0">
<LayoutGrid className="h-3.5 w-3.5 text-violet-300 shrink-0" />
<span className="text-[12.5px] font-semibold text-white shrink-0"></span>
<span className="text-[10px] text-white/40 font-mono shrink-0">
{frames.length} · {totalElements}
</span>
{focusFrame ? (
<span className="text-[10px] text-violet-300/90 shrink-0 inline-flex items-center gap-1">
· <span className="text-white font-medium"> {focusSeq}</span>
</span>
) : (
<span className="text-[10px] text-white/30 truncate">
·
</span>
)}
</div>
<div className="flex items-center gap-2 shrink-0">
<button
onClick={() => {
if (workbenchOpen) {
onCloseWorkbench?.()
return
}
if (frames.length === 0) return
const nextFrame = focusedFrame ?? frames[0].index
if (focusedFrame === null) onFocusFrame(nextFrame)
onOpenWorkbench?.(nextFrame)
}}
disabled={frames.length === 0}
className="text-[11px] px-2.5 py-1 rounded-md bg-gradient-to-r from-violet-500 to-pink-500 hover:from-violet-400 hover:to-pink-400 text-white inline-flex items-center gap-1 disabled:opacity-40 disabled:cursor-not-allowed font-medium shadow"
title={frames.length === 0 ? "先到关键帧节点选用分镜" : workbenchOpen ? "收起分镜头编排明细" : "下拉展开分镜头编排"}
>
{workbenchOpen ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
{workbenchOpen ? "收起编排" : "展开编排"}
</button>
</div>
</div>
</div>
)
}