wing-ops/frontend/src/tabs/prediction/components/InfoLayerSection.tsx
htlee dec066e8bb refactor(css): CSS 인프라 구축 + body default 인라인 스타일 1,055건 제거
Phase 0: CSS 인프라 구축
- Tailwind config 색상 불일치 수정 (t1/t2/t3 → CSS 변수 값으로 통일)
- index.css 1,302줄 → @import 엔트리포인트 7줄로 축소
- common/styles/base.css: @layer base 추출 (CSS 변수, 리셋, body 기본값)
- common/styles/components.css: @layer components + utilities 추출
- common/styles/wing.css: wing-* 디자인 시스템 클래스 신규 정의
- common/utils/cn.ts: className 조합 유틸리티
- App.css 삭제 (내용을 components.css로 통합)

Phase 1: body default 인라인 스타일 일괄 제거
- fontFamily: 'var(--fK)' 781건 제거 (body font-family 상속)
- color: 'var(--t1)' 274건 제거 (body color 상속)
- 빈 style={{}} 78건 정리
- 31개 파일, JS 번들 23KB 감소

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:45:01 +09:00

195 lines
6.6 KiB
TypeScript

import { useState, useMemo } from 'react'
import { LayerTree } from '@common/components/layer/LayerTree'
import { useLayerTree } from '@common/hooks/useLayers'
import { layerData } from '@common/data/layerData'
import type { LayerNode } from '@common/data/layerData'
import type { Layer } from '@common/services/layerService'
interface InfoLayerSectionProps {
expanded: boolean
onToggle: () => void
enabledLayers: Set<string>
onToggleLayer: (layerId: string, enabled: boolean) => void
layerOpacity: number
onLayerOpacityChange: (val: number) => void
layerBrightness: number
onLayerBrightnessChange: (val: number) => void
}
const InfoLayerSection = ({
expanded,
onToggle,
enabledLayers,
onToggleLayer,
layerOpacity,
onLayerOpacityChange,
layerBrightness,
onLayerBrightnessChange,
}: InfoLayerSectionProps) => {
// API에서 레이어 트리 데이터 가져오기
const { data: layerTree, isLoading } = useLayerTree()
const [layerColors, setLayerColors] = useState<Record<string, string>>({})
// 정적 데이터를 Layer 형식으로 변환 (API 실패 시 폴백)
const staticLayers = useMemo(() => {
const convert = (node: LayerNode): Layer => ({
id: node.code,
parentId: node.parentCode,
name: node.name,
fullName: node.fullName,
level: node.level,
wmsLayer: node.layerName,
icon: node.icon,
count: node.count,
children: node.children?.map(convert),
})
return layerData.map(convert)
}, [])
// API 데이터 우선, 실패 시 정적 데이터 폴백
const effectiveLayers = (layerTree && layerTree.length > 0) ? layerTree : staticLayers
return (
<div className="border-b border-border">
<div
className="flex items-center justify-between p-4 hover:bg-[rgba(255,255,255,0.02)]"
>
<h3
onClick={onToggle}
className="text-[13px] font-bold text-text-1 font-korean cursor-pointer"
>
📂
</h3>
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
<button
onClick={(e) => {
e.stopPropagation()
// Get all layer IDs from layerTree recursively
const getAllLayerIds = (layers: Layer[]): string[] => {
const ids: string[] = []
layers?.forEach(layer => {
ids.push(layer.id)
if (layer.children) {
ids.push(...getAllLayerIds(layer.children))
}
})
return ids
}
const allIds = getAllLayerIds(effectiveLayers)
allIds.forEach(id => onToggleLayer(id, true))
}}
style={{
padding: '4px 8px',
fontSize: '10px',
fontWeight: 600,
border: '1px solid var(--cyan)',
borderRadius: 'var(--rS)',
background: 'transparent',
color: 'var(--cyan)',
cursor: 'pointer',
transition: '0.15s'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(6,182,212,0.1)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent'
}}
>
</button>
<button
onClick={(e) => {
e.stopPropagation()
// Get all layer IDs from layerTree recursively
const getAllLayerIds = (layers: Layer[]): string[] => {
const ids: string[] = []
layers?.forEach(layer => {
ids.push(layer.id)
if (layer.children) {
ids.push(...getAllLayerIds(layer.children))
}
})
return ids
}
const allIds = getAllLayerIds(effectiveLayers)
allIds.forEach(id => onToggleLayer(id, false))
}}
style={{
padding: '4px 8px',
fontSize: '10px',
fontWeight: 600,
border: '1px solid var(--red)',
borderRadius: 'var(--rS)',
background: 'transparent',
color: 'var(--red)',
cursor: 'pointer',
transition: '0.15s'
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(239,68,68,0.1)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent'
}}
>
</button>
<span
onClick={onToggle}
className="text-[10px] text-text-3 cursor-pointer"
>
{expanded ? '▼' : '▶'}
</span>
</div>
</div>
{expanded && (
<div className="px-4 pb-2">
{isLoading && effectiveLayers.length === 0 ? (
<p className="text-[11px] text-text-3 py-2"> ...</p>
) : effectiveLayers.length === 0 ? (
<p className="text-[11px] text-text-3 py-2"> .</p>
) : (
<LayerTree
layers={effectiveLayers}
enabledLayers={enabledLayers}
onToggleLayer={onToggleLayer}
layerColors={layerColors}
onColorChange={(id, color) => setLayerColors(prev => ({ ...prev, [id]: color }))}
/>
)}
{/* 레이어 스타일 조절 */}
<div className="lyr-style-box">
<div className="lyr-style-label"> </div>
<div className="lyr-style-row">
<span className="lyr-style-name"></span>
<input
type="range"
className="lyr-style-slider"
min={0} max={100} value={layerOpacity}
onChange={e => onLayerOpacityChange(Number(e.target.value))}
/>
<span className="lyr-style-val">{layerOpacity}%</span>
</div>
<div className="lyr-style-row">
<span className="lyr-style-name"></span>
<input
type="range"
className="lyr-style-slider"
min={0} max={100} value={layerBrightness}
onChange={e => onLayerBrightnessChange(Number(e.target.value))}
/>
<span className="lyr-style-val">{layerBrightness}%</span>
</div>
</div>
</div>
)}
</div>
)
}
export default InfoLayerSection