refactor(TimelinePanel): 抽取 ClipTrack 组件

This commit is contained in:
2026-07-30 07:13:33 +08:00
parent fb8eac470a
commit 0687b36fb8
@@ -0,0 +1,147 @@
import React from "react"
import type { ClipData, TrimConfig } from "../../types"
import { ClipCard } from "./ClipCard"
interface ClipTrackProps {
/** 片段列表 */
clips: ClipData[]
/** 选中的片段ID */
selectedClipId: string | null
/** 缩放:每秒像素数 */
pps: number
/** 当前播放时间(秒) */
currentTime: number
/** 总时长(秒) */
totalDuration: number
/** 正在拖拽的片段索引 */
dragIdx: number | null
/** 拖拽悬停的片段索引 */
dragOverIdx: number | null
/** 悬停的片段ID */
hoveredClipId: string | null
/** 是否正在裁剪拖拽 */
trimDragActive: boolean
/** 是否显示裁剪手柄 */
showTrimHandles: boolean
/** 轨道ref */
trackRef: React.RefObject<HTMLDivElement>
/** 添加卡片ref */
addCardRef: React.RefObject<HTMLDivElement>
/** 播放头拖拽开始 */
onPlayheadMouseDown: (e: React.MouseEvent) => void
/** 片段选中 */
onClipSelect: (clipId: string) => void
/** 拖拽开始 */
onDragStart: (idx: number, e: React.DragEvent) => void
/** 拖拽悬停 */
onDragOver: (idx: number, e: React.DragEvent) => void
/** 拖拽结束 */
onDragEnd: () => void
/** 放置 */
onDrop: (idx: number, e: React.DragEvent) => void
/** 空区域拖拽悬停 */
onEmptyDragOver: (e: React.DragEvent) => void
/** 右键菜单 */
onContextMenu: (clipId: string, e: React.MouseEvent) => void
/** 片段悬停进入 */
onClipMouseEnter: (clipId: string) => void
/** 片段悬停离开 */
onClipMouseLeave: () => void
/** 裁剪手柄按下 */
onTrimHandleMouseDown: (clipId: string, side: "start" | "end", e: React.MouseEvent) => void
/** 删除片段 */
onClipRemove: (clipId: string) => void
/** 点击添加卡片 */
onTogglePicker: () => void
}
/**
* 片段轨道组件
* 包含播放头、片段列表、空状态和添加卡片
*/
export const ClipTrack: React.FC<ClipTrackProps> = ({
clips,
selectedClipId,
pps,
currentTime,
totalDuration,
dragIdx,
dragOverIdx,
hoveredClipId,
trimDragActive,
showTrimHandles,
trackRef,
addCardRef,
onPlayheadMouseDown,
onClipSelect,
onDragStart,
onDragOver,
onDragEnd,
onDrop,
onEmptyDragOver,
onContextMenu,
onClipMouseEnter,
onClipMouseLeave,
onTrimHandleMouseDown,
onClipRemove,
onTogglePicker,
}) => {
return (
<div className="ep-clip-track" ref={trackRef} onDragOver={onEmptyDragOver}>
{/* 播放头 */}
{totalDuration > 0 && (
<div
className="ep-playhead"
style={{ left: currentTime * pps }}
onMouseDown={onPlayheadMouseDown}
>
<div className="ep-playhead-handle" />
</div>
)}
{clips.length === 0 ? (
<div className="ep-track-empty">
<div className="ep-track-empty-icon">🎬</div>
<div className="ep-track-empty-text">点击右侧 + 添加片段</div>
</div>
) : (
clips.map((clip, idx) => (
<ClipCard
key={clip.id}
clip={clip}
idx={idx}
isSelected={selectedClipId === clip.id}
isDragging={dragIdx === idx}
isDragOver={dragOverIdx === idx}
isHovered={hoveredClipId === clip.id}
pps={pps}
trimDragActive={trimDragActive}
showTrimHandles={showTrimHandles}
onDragStart={onDragStart}
onDragOver={onDragOver}
onDragEnd={onDragEnd}
onDrop={onDrop}
onSelect={onClipSelect}
onContextMenu={onContextMenu}
onMouseEnter={() => onClipMouseEnter(clip.id)}
onMouseLeave={onClipMouseLeave}
onTrimHandleMouseDown={onTrimHandleMouseDown}
onRemove={onClipRemove}
/>
))
)}
{/* ── 轨道末尾 "+" 添加卡片 ── */}
<div className="ep-track-add-card-wrapper">
<div
ref={addCardRef}
className="ep-track-add-card"
onClick={onTogglePicker}
title="添加片段"
>
+
</div>
</div>
</div>
)
}