108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
import { useRef, useState } from 'react';
|
|
import { MapView } from '@common/components/map/MapView';
|
|
import type { OilReportPayload } from '@common/hooks/useSubMenu';
|
|
|
|
interface OilSpreadMapPanelProps {
|
|
mapData: OilReportPayload['mapData'];
|
|
capturedImage: string | null;
|
|
onCapture: (dataUrl: string) => void;
|
|
onReset: () => void;
|
|
}
|
|
|
|
const OilSpreadMapPanel = ({ mapData, capturedImage, onCapture, onReset }: OilSpreadMapPanelProps) => {
|
|
const captureRef = useRef<(() => Promise<string | null>) | null>(null);
|
|
const [isCapturing, setIsCapturing] = useState(false);
|
|
|
|
const handleCapture = async () => {
|
|
if (!captureRef.current) return;
|
|
setIsCapturing(true);
|
|
const dataUrl = await captureRef.current();
|
|
setIsCapturing(false);
|
|
if (dataUrl) {
|
|
onCapture(dataUrl);
|
|
}
|
|
};
|
|
|
|
if (!mapData) {
|
|
return (
|
|
<div className="w-full h-[280px] bg-bg-3 border border-border rounded-lg flex items-center justify-center text-text-3 text-[12px] font-korean mb-4">
|
|
확산 예측 데이터가 없습니다. 예측 탭에서 시뮬레이션을 실행 후 보고서를 생성하세요.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="mb-4">
|
|
{/* 지도 + 오버레이 컨테이너 — MapView 항상 마운트 유지 (deck.gl rAF race condition 방지) */}
|
|
<div className="relative w-full rounded-lg border border-border overflow-hidden" style={{ height: '620px' }}>
|
|
<MapView
|
|
center={mapData.center}
|
|
zoom={mapData.zoom}
|
|
incidentCoord={{ lat: mapData.center[0], lon: mapData.center[1] }}
|
|
oilTrajectory={mapData.trajectory}
|
|
externalCurrentTime={mapData.currentStep}
|
|
centerPoints={mapData.centerPoints}
|
|
showBeached={true}
|
|
showTimeLabel={true}
|
|
simulationStartTime={mapData.simulationStartTime || undefined}
|
|
mapCaptureRef={captureRef}
|
|
showOverlays={false}
|
|
lightMode
|
|
/>
|
|
|
|
{/* 캡처 이미지 오버레이 — 우측 상단 */}
|
|
{capturedImage && (
|
|
<div className="absolute top-3 right-3 z-10" style={{ width: '220px' }}>
|
|
<div
|
|
className="rounded-lg overflow-hidden"
|
|
style={{ border: '1px solid rgba(6,182,212,0.5)', boxShadow: '0 4px 16px rgba(0,0,0,0.5)' }}
|
|
>
|
|
<img src={capturedImage} alt="확산예측 지도 캡처" className="w-full block" />
|
|
<div
|
|
className="flex items-center justify-between px-2.5 py-1.5"
|
|
style={{ background: 'rgba(15,23,42,0.85)', borderTop: '1px solid rgba(6,182,212,0.3)' }}
|
|
>
|
|
<span className="text-[10px] font-korean font-semibold" style={{ color: '#06b6d4' }}>
|
|
📷 캡처 완료
|
|
</span>
|
|
<button
|
|
onClick={onReset}
|
|
className="text-[10px] font-korean hover:text-text-1 transition-colors"
|
|
style={{ color: 'rgba(148,163,184,0.8)' }}
|
|
>
|
|
다시 선택
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 하단 안내 + 캡처 버튼 */}
|
|
<div className="flex items-center justify-between mt-2">
|
|
<p className="text-[10px] text-text-3 font-korean">
|
|
{capturedImage
|
|
? 'PDF 다운로드 시 캡처된 이미지가 포함됩니다.'
|
|
: '지도를 이동/확대하여 원하는 범위를 선택한 후 캡처하세요.'}
|
|
</p>
|
|
<button
|
|
onClick={handleCapture}
|
|
disabled={isCapturing || !!capturedImage}
|
|
className="px-3 py-1.5 text-[11px] font-semibold rounded transition-all font-korean flex items-center gap-1.5"
|
|
style={{
|
|
background: capturedImage ? 'rgba(6,182,212,0.06)' : 'rgba(6,182,212,0.12)',
|
|
border: '1px solid rgba(6,182,212,0.4)',
|
|
color: capturedImage ? 'rgba(6,182,212,0.5)' : '#06b6d4',
|
|
opacity: isCapturing ? 0.6 : 1,
|
|
cursor: capturedImage ? 'default' : 'pointer',
|
|
}}
|
|
>
|
|
{capturedImage ? '✓ 캡처됨' : '📷 이 범위로 캡처'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default OilSpreadMapPanel;
|