// FloatOverlayContent.tsx — Map Overlay + Map Popup 카탈로그 import type { DesignTheme } from '../designTheme'; interface FloatOverlayContentProps { theme: DesignTheme; } const OVERLAY_CASES = [ { component: 'BacktrackReplayBar', position: '하단 중앙', zIndex: 'z-40', pointerEvents: 'auto', desc: '역추적 재생 컨트롤 바. 재생/일시정지/슬라이더.', source: 'common/components/map/BacktrackReplayBar.tsx', }, { component: 'MeasureOverlay', position: '마커 위치', zIndex: 'z-40', pointerEvents: 'auto', desc: '거리 측정 마커 "지우기" 버튼. MapLibre Marker 컴포넌트 활용.', source: 'common/components/map/MeasureOverlay.tsx', }, { component: 'OilDetectionOverlay', position: 'inset-0 + 우하단 정보', zIndex: 'z-[15]', pointerEvents: 'none', desc: '유류 탐지 결과 마스크 렌더링. OffscreenCanvas 기반. 정보 패널만 클릭 가능.', source: 'tabs/aerial/components/OilDetectionOverlay.tsx', }, { component: 'WeatherMapOverlay', position: 'absolute inset-0', zIndex: 'map layer', pointerEvents: 'none', desc: '기상 데이터 레이어 오버레이.', source: 'tabs/weather/components/WeatherMapOverlay.tsx', }, { component: 'OceanForecastOverlay', position: 'absolute inset-0', zIndex: 'map layer', pointerEvents: 'none', desc: '해양 예측 레이어 오버레이.', source: 'tabs/weather/components/OceanForecastOverlay.tsx', }, ]; export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => { const t = theme; const isDark = t.mode === 'dark'; const mapMockBg = isDark ? '#0f1a2e' : '#c8d8e8'; const mapGridColor = isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.06)'; return (
{/* ── 개요 ── */}

Overlay

지도 컨테이너 위에 position: absolute 로 레이어되는 UI. 백드롭 없이 지도 위에 기능 UI를 표시한다. Modal과 달리 화면 상호작용을 차단하지 않으며, 지도 컨테이너의 크기 변화에 반응한다.

{/* ── Overlay vs Modal 비교 ── */}

Overlay vs Modal

{['속성', 'Overlay', 'Modal'].map((col) => (
{col}
))}
{[ { attr: 'position', overlay: 'absolute (지도 기준)', modal: 'fixed (뷰포트 기준)' }, { attr: '백드롭', overlay: '없음', modal: 'rgba(0,0,0,0.65) + blur' }, { attr: '클릭 차단', overlay: 'pointer-events: none (일반)', modal: '전체 화면 차단' }, { attr: 'z-index', overlay: 'z-40 (지도 UI 위)', modal: 'z-[9999] (최상위)' }, { attr: '크기 기준', overlay: '지도 컨테이너 = 100%', modal: '고정 너비 (380~720px)' }, { attr: '닫기 방식', overlay: '기능 비활성화 시 사라짐', modal: '닫기 버튼 / 백드롭 클릭', }, ].map((row, idx) => (
{row.attr}
{row.overlay}
{row.modal}
))}
{/* ── 지도 목업 다이어그램 ── */}

Overlay 배치 다이어그램

지도 컨테이너 기준 절대 위치 {/* 지도 목업 */}
{/* 격자 배경 (지도 모사) */}
{/* 지도 레이블 */}
MapView (position: relative)
{/* OilDetectionOverlay — 전체 영역 */}
OilDetectionOverlay — inset-0, z-[15], pointer-events:none
{/* MeasureOverlay 마커 */}
지우기
MeasureOverlay — Marker 위치
{/* BacktrackReplayBar — 하단 중앙 */}
◀◀
▶▶
BacktrackReplayBar — bottom-3 center, z-40
{/* ── Map Popup 서브패턴 ── */}

Map Popup — 위치 앵커드 패턴

ScatPopup은 지도 마커에 앵커된 컨텍스트 팝업이다. Modal(fixed 뷰포트 중앙)과 달리 마커 위치에서 동적으로 좌표를 계산하며, 지도 패닝·줌 시 위치가 함께 업데이트된다.

{[ { label: '위치 계산', value: 'map.project(lngLat)', desc: '지도 좌표 → 픽셀 좌표 변환', }, { label: '위치 업데이트', value: 'map.on("move")', desc: '패닝/줌 시 재계산' }, { label: 'z-index', value: 'z-[9999]', desc: '다른 오버레이 위' }, ].map((item) => (
{item.label} {item.value} {item.desc}
))}
주의: ScatPopup은 MapLibre GL JS의 Popup/Marker 컴포넌트가 아닌 React DOM으로 구현됨. 지도 컨테이너 내부에 position: absolute로 렌더링된다.
Source: tabs/scat/components/ScatPopup.tsx
{/* ── 사용 사례 목록 ── */}

현재 사용 사례

{['Component', 'Position', 'Z-Index', 'Events', 'Description'].map((col) => (
{col}
))}
{OVERLAY_CASES.map((item, idx) => (
{item.component}
{item.position}
{item.zIndex}
{item.pointerEvents}
{item.desc}
))}
); }; export default FloatOverlayContent;