wing-ops/frontend/src/components/common/map/TimelineControl.tsx
leedano 38d931db65 refactor(mpa): 탭 디렉토리를 MPA 컴포넌트 구조로 재편
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 17:38:49 +09:00

153 lines
4.8 KiB
TypeScript
Raw Blame 히스토리

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.

import type { MouseEvent } from 'react';
export interface TimelineControlProps {
currentTime: number;
maxTime: number;
isPlaying: boolean;
playbackSpeed: number;
onTimeChange: (time: number) => void;
onPlayPause: () => void;
onSpeedChange: (speed: number) => void;
simulationStartTime?: string;
stepSize?: number;
tickInterval?: number;
majorTickEvery?: number;
timeUnitLabel?: string;
formatOffset?: (t: number) => string;
formatAbsolute?: (t: number, base: Date) => string;
showSpeedToggle?: boolean;
}
export function TimelineControl({
currentTime,
maxTime,
isPlaying,
playbackSpeed,
onTimeChange,
onPlayPause,
onSpeedChange,
simulationStartTime,
stepSize = 6,
tickInterval = 6,
majorTickEvery = 12,
timeUnitLabel = 'h',
formatOffset,
formatAbsolute,
showSpeedToggle = true,
}: TimelineControlProps) {
const progressPercent = maxTime > 0 ? (currentTime / maxTime) * 100 : 0;
const handleRewind = () => onTimeChange(Math.max(0, currentTime - stepSize));
const handleForward = () => onTimeChange(Math.min(maxTime, currentTime + stepSize));
const handleStart = () => onTimeChange(0);
const handleEnd = () => onTimeChange(maxTime);
const toggleSpeed = () => {
const speeds = [1, 2, 4];
const currentIndex = speeds.indexOf(playbackSpeed);
onSpeedChange(speeds[(currentIndex + 1) % speeds.length]);
};
const handleTimelineClick = (e: MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
onTimeChange(Math.max(0, Math.min(maxTime, Math.round(percent * maxTime))));
};
const timeLabels: number[] = [];
for (let t = 0; t <= maxTime; t += tickInterval) {
timeLabels.push(t);
}
const defaultOffset = (t: number) => `+${t.toFixed(0)}${timeUnitLabel}`;
const defaultAbsolute = (t: number, base: Date) => {
const d = new Date(base.getTime() + t * 3600 * 1000);
return `${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')} KST`;
};
const offsetStr = (formatOffset ?? defaultOffset)(currentTime);
const baseDate = simulationStartTime ? new Date(simulationStartTime) : new Date();
const absoluteStr = (formatAbsolute ?? defaultAbsolute)(currentTime, baseDate);
return (
<div className="tlb">
<div className="tlc">
<div className="tb" onClick={handleStart}>
</div>
<div className="tb" onClick={handleRewind}>
</div>
<div className={`tb ${isPlaying ? 'on' : ''}`} onClick={onPlayPause}>
{isPlaying ? '⏸' : '▶'}
</div>
<div className="tb" onClick={handleForward}>
</div>
<div className="tb" onClick={handleEnd}>
</div>
{showSpeedToggle && (
<>
<div className="w-2" />
<div className="tb" onClick={toggleSpeed}>
{playbackSpeed}×
</div>
</>
)}
</div>
<div className="tlt">
<div className="tlls">
{timeLabels.map((t) => (
<span
key={t}
className={`tll ${Math.abs(currentTime - t) < 1 ? 'on' : ''}`}
style={{ left: `${(t / maxTime) * 100}%` }}
>
{t}
{timeUnitLabel}
</span>
))}
</div>
<div className="tlsw" onClick={handleTimelineClick}>
<div className="tlr">
<div className="tlp" style={{ width: `${progressPercent}%` }} />
{timeLabels.map((t) => (
<div
key={`marker-${t}`}
className={`tlm ${t % majorTickEvery === 0 ? 'mj' : ''}`}
style={{ left: `${(t / maxTime) * 100}%` }}
/>
))}
</div>
<div className="tlth" style={{ left: `${progressPercent}%` }} />
</div>
</div>
<div className="tli">
<div className="tlct">
{offsetStr} {absoluteStr}
</div>
<div className="tlss">
<div className="tls">
<span className="tlsl"></span>
<span className="tlsv">{progressPercent.toFixed(0)}%</span>
</div>
{showSpeedToggle && (
<div className="tls">
<span className="tlsl"></span>
<span className="tlsv">{playbackSpeed}×</span>
</div>
)}
<div className="tls">
<span className="tlsl"></span>
<span className="tlsv">
{currentTime.toFixed(0)}/{maxTime}
{timeUnitLabel}
</span>
</div>
</div>
</div>
</div>
);
}