import { useState } from 'react'; import type { MapStyleSettings, MapLabelLanguage, DepthFontSize, DepthColorStop } from './types'; import { DEFAULT_MAP_STYLE_SETTINGS } from './types'; interface MapSettingsPanelProps { value: MapStyleSettings; onChange: (next: MapStyleSettings) => void; } const LANGUAGES: { value: MapLabelLanguage; label: string }[] = [ { value: 'ko', label: '한국어' }, { value: 'en', label: 'English' }, { value: 'ja', label: '日本語' }, { value: 'zh', label: '中文' }, { value: 'local', label: '현지어' }, ]; const FONT_SIZES: { value: DepthFontSize; label: string }[] = [ { value: 'small', label: '소' }, { value: 'medium', label: '중' }, { value: 'large', label: '대' }, ]; function depthLabel(depth: number): string { return `${Math.abs(depth).toLocaleString()}m`; } function hexToRgb(hex: string): [number, number, number] { return [ parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16), ]; } function rgbToHex(r: number, g: number, b: number): string { return `#${[r, g, b].map((c) => Math.round(Math.max(0, Math.min(255, c))).toString(16).padStart(2, '0')).join('')}`; } function interpolateGradient(stops: DepthColorStop[]): DepthColorStop[] { if (stops.length < 2) return stops; const sorted = [...stops].sort((a, b) => a.depth - b.depth); const first = sorted[0]; const last = sorted[sorted.length - 1]; const [r1, g1, b1] = hexToRgb(first.color); const [r2, g2, b2] = hexToRgb(last.color); return sorted.map((stop, i) => { if (i === 0 || i === sorted.length - 1) return stop; const t = i / (sorted.length - 1); return { depth: stop.depth, color: rgbToHex( r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t, ), }; }); } export function MapSettingsPanel({ value, onChange }: MapSettingsPanelProps) { const [open, setOpen] = useState(false); const [autoGradient, setAutoGradient] = useState(false); const update = (key: K, val: MapStyleSettings[K]) => { onChange({ ...value, [key]: val }); }; const updateDepthStop = (index: number, color: string) => { const next = value.depthStops.map((s, i) => (i === index ? { ...s, color } : s)); if (autoGradient && (index === 0 || index === next.length - 1)) { update('depthStops', interpolateGradient(next)); } else { update('depthStops', next); } }; const toggleAutoGradient = () => { const next = !autoGradient; setAutoGradient(next); if (next) { update('depthStops', interpolateGradient(value.depthStops)); } }; return ( <> {open && (
지도 설정
{/* ── Language ──────────────────────────────────── */}
레이블 언어
{/* ── Land color ────────────────────────────────── */}
육지 색상
update('landColor', e.target.value)} /> {value.landColor}
{/* ── Water color ───────────────────────────────── */}
물 기본색
update('waterBaseColor', e.target.value)} /> {value.waterBaseColor}
{/* ── Depth gradient ────────────────────────────── */}
수심 구간 색상 자동채우기
{value.depthStops.map((stop, i) => { const isEdge = i === 0 || i === value.depthStops.length - 1; const dimmed = autoGradient && !isEdge; return (
{depthLabel(stop.depth)} updateDepthStop(i, e.target.value)} disabled={dimmed} /> {stop.color}
); })}
{/* ── Depth font size ───────────────────────────── */}
수심 폰트 크기
{FONT_SIZES.map((fs) => (
update('depthFontSize', fs.value)} > {fs.label}
))}
{/* ── Depth font color ──────────────────────────── */}
수심 폰트 색상
update('depthFontColor', e.target.value)} /> {value.depthFontColor}
{/* ── Reset ─────────────────────────────────────── */}
)} ); }