136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import React from 'react'
|
|
|
|
interface TrackSegment {
|
|
id: string
|
|
type: 'video' | 'audio' | 'image' | 'text' | 'effect'
|
|
name: string
|
|
start_time: number
|
|
end_time: number
|
|
duration: number
|
|
resource_path?: string
|
|
properties?: any
|
|
effects?: any[]
|
|
}
|
|
|
|
interface SegmentTooltipProps {
|
|
segment: TrackSegment | null
|
|
position: { x: number; y: number }
|
|
isVisible: boolean
|
|
}
|
|
|
|
export const SegmentTooltip: React.FC<SegmentTooltipProps> = ({
|
|
segment,
|
|
position,
|
|
isVisible
|
|
}) => {
|
|
const formatTime = (seconds: number): string => {
|
|
const minutes = Math.floor(seconds / 60)
|
|
const secs = (seconds % 60).toFixed(2)
|
|
return `${minutes}:${secs.padStart(5, '0')}`
|
|
}
|
|
|
|
const getTypeLabel = (type: string) => {
|
|
switch (type) {
|
|
case 'video': return '视频'
|
|
case 'audio': return '音频'
|
|
case 'image': return '图片'
|
|
case 'text': return '文本'
|
|
case 'effect': return '特效'
|
|
default: return '未知'
|
|
}
|
|
}
|
|
|
|
const getTypeColor = (type: string) => {
|
|
switch (type) {
|
|
case 'video': return 'bg-blue-100 text-blue-800'
|
|
case 'audio': return 'bg-green-100 text-green-800'
|
|
case 'image': return 'bg-yellow-100 text-yellow-800'
|
|
case 'text': return 'bg-purple-100 text-purple-800'
|
|
case 'effect': return 'bg-gray-100 text-gray-800'
|
|
default: return 'bg-gray-100 text-gray-800'
|
|
}
|
|
}
|
|
|
|
if (!isVisible || !segment) return null
|
|
|
|
return (
|
|
<div
|
|
className="fixed bg-gray-900 text-white rounded-lg shadow-xl p-4 z-50 max-w-sm pointer-events-none"
|
|
style={{
|
|
left: position.x + 10,
|
|
top: position.y - 10,
|
|
transform: 'translateY(-100%)'
|
|
}}
|
|
>
|
|
{/* Segment Name */}
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<span className={`px-2 py-1 rounded text-xs font-medium ${getTypeColor(segment.type)}`}>
|
|
{getTypeLabel(segment.type)}
|
|
</span>
|
|
<h3 className="font-semibold text-white">{segment.name}</h3>
|
|
</div>
|
|
|
|
{/* Time Information */}
|
|
<div className="space-y-2 text-sm">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<span className="text-gray-300">开始时间:</span>
|
|
<div className="font-mono text-white">{formatTime(segment.start_time)}</div>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-300">结束时间:</span>
|
|
<div className="font-mono text-white">{formatTime(segment.end_time)}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<span className="text-gray-300">持续时长:</span>
|
|
<div className="font-mono text-white">{formatTime(segment.duration)}</div>
|
|
</div>
|
|
|
|
{/* Resource Path */}
|
|
{segment.resource_path && (
|
|
<div>
|
|
<span className="text-gray-300">资源文件:</span>
|
|
<div className="text-white text-xs break-all">
|
|
{segment.resource_path.split('/').pop() || segment.resource_path}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Effects */}
|
|
{segment.effects && segment.effects.length > 0 && (
|
|
<div>
|
|
<span className="text-gray-300">特效:</span>
|
|
<div className="text-white text-xs">
|
|
{segment.effects.length} 个特效
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Properties */}
|
|
{segment.properties && Object.keys(segment.properties).length > 0 && (
|
|
<div>
|
|
<span className="text-gray-300">属性:</span>
|
|
<div className="text-white text-xs">
|
|
{Object.keys(segment.properties).length} 个属性
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Tooltip Arrow */}
|
|
<div
|
|
className="absolute bottom-0 left-4 transform translate-y-full"
|
|
style={{
|
|
width: 0,
|
|
height: 0,
|
|
borderLeft: '6px solid transparent',
|
|
borderRight: '6px solid transparent',
|
|
borderTop: '6px solid rgb(17, 24, 39)' // gray-900
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|