auto-save 2026-05-12 18:57 (~2)
This commit is contained in:
@@ -202,6 +202,13 @@
|
||||
"message": "auto-save 2026-05-12 18:46 (~3)",
|
||||
"hash": "5a914b9",
|
||||
"files_changed": 3
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-12T18:52:21+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-12 18:52 (~2)",
|
||||
"hash": "f6f8212",
|
||||
"files_changed": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ export function Dashboard({ data }: Props) {
|
||||
const [videoT, setVideoT] = useState(0)
|
||||
const [addingFrame, setAddingFrame] = useState(false)
|
||||
const [expanded, setExpanded] = useState<Set<string>>(new Set())
|
||||
const [panelPos, setPanelPos] = useState<{ left: number; top: number } | null>(null)
|
||||
const tileRefs = useRef<Record<string, HTMLButtonElement | null>>({})
|
||||
const fileRef = useRef<HTMLInputElement>(null)
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
|
||||
@@ -133,9 +135,21 @@ export function Dashboard({ data }: Props) {
|
||||
{ key: "compose", title: "合成", type: "output", icon: <FileVideo className="h-3.5 w-3.5" />, step: 10 },
|
||||
]
|
||||
|
||||
// 单选展开:toggle 同一 key = 收起;点其他 key = 切换
|
||||
const toggleTile = (key: string) => setExpanded((prev) => (prev.has(key) ? new Set() : new Set([key])))
|
||||
const closeTile = (_key: string) => setExpanded(new Set())
|
||||
// 单选展开:toggle 同一 key = 收起;点其他 key = 切换;并记录 tile 位置让 panel 跟随
|
||||
const toggleTile = (key: string) => {
|
||||
if (expanded.has(key)) {
|
||||
setExpanded(new Set())
|
||||
setPanelPos(null)
|
||||
return
|
||||
}
|
||||
setExpanded(new Set([key]))
|
||||
const el = tileRefs.current[key]
|
||||
if (el) {
|
||||
const r = el.getBoundingClientRect()
|
||||
setPanelPos({ left: r.left, top: r.bottom + 6 })
|
||||
}
|
||||
}
|
||||
const closeTile = (_key: string) => { setExpanded(new Set()); setPanelPos(null) }
|
||||
|
||||
const Tile = ({ tkey, rowSpan }: { tkey: string; rowSpan?: boolean }) => {
|
||||
const t = TILES.find((x) => x.key === tkey)!
|
||||
@@ -143,6 +157,7 @@ export function Dashboard({ data }: Props) {
|
||||
const isOpen = expanded.has(t.key)
|
||||
return (
|
||||
<button
|
||||
ref={(el) => { tileRefs.current[t.key] = el }}
|
||||
type="button"
|
||||
onClick={() => toggleTile(t.key)}
|
||||
title={colSummary[t.key]}
|
||||
@@ -187,14 +202,14 @@ export function Dashboard({ data }: Props) {
|
||||
<Tile tkey="compose" />
|
||||
</div>
|
||||
|
||||
{/* 展开面板 — 单列宽度,居中,卡片竖向堆叠 */}
|
||||
{expanded.size > 0 && (
|
||||
<div className="flex justify-center px-4 pb-3" style={{ maxHeight: "52vh" }}>
|
||||
{/* 展开面板 — 跟随被点 tile 的位置,从 tile 正下方冒出(fixed 定位) */}
|
||||
{expanded.size > 0 && panelPos && (
|
||||
<div className="fixed z-40" style={{ left: Math.max(8, Math.min(panelPos.left, window.innerWidth - 376)), top: panelPos.top, maxHeight: "calc(100vh - " + panelPos.top + "px - 16px)" }}>
|
||||
{TILES.filter((t) => expanded.has(t.key)).map((t) => (
|
||||
<section
|
||||
key={t.key}
|
||||
className="rounded-xl border border-white/10 bg-black/30 backdrop-blur-xl overflow-hidden flex flex-col"
|
||||
style={{ width: 360, maxHeight: "52vh" }}
|
||||
className="rounded-xl border border-white/10 bg-black/40 backdrop-blur-xl overflow-hidden flex flex-col shadow-2xl"
|
||||
style={{ width: 360, maxHeight: "calc(100vh - " + panelPos.top + "px - 16px)" }}
|
||||
>
|
||||
<div className="flex items-center justify-between px-3 py-2" style={{ background: TYPE_GRAD[t.type] }}>
|
||||
<div className="flex items-center gap-1.5">
|
||||
@@ -211,7 +226,7 @@ export function Dashboard({ data }: Props) {
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-y-auto p-3 bg-black/20">
|
||||
<div className="overflow-y-auto p-3 bg-black/20 flex-1">
|
||||
{renderSection(t.key)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user