2026-02-05 09:59:30 +09:00
|
|
|
/**
|
|
|
|
|
* 리플레이 전용 범례 컴포넌트
|
|
|
|
|
* - 리플레이 모드에서 선종별 카운트 표시
|
|
|
|
|
* - ShipLegend 디자인 재사용, replayStore 연동
|
|
|
|
|
*/
|
|
|
|
|
import { memo } from 'react';
|
|
|
|
|
import { shallow } from 'zustand/shallow';
|
|
|
|
|
import useReplayStore from '../../replay/stores/replayStore';
|
|
|
|
|
import {
|
|
|
|
|
SIGNAL_KIND_CODE_FISHING,
|
|
|
|
|
SIGNAL_KIND_CODE_KCGV,
|
|
|
|
|
SIGNAL_KIND_CODE_PASSENGER,
|
|
|
|
|
SIGNAL_KIND_CODE_CARGO,
|
|
|
|
|
SIGNAL_KIND_CODE_TANKER,
|
|
|
|
|
SIGNAL_KIND_CODE_GOV,
|
|
|
|
|
SIGNAL_KIND_CODE_NORMAL,
|
|
|
|
|
SIGNAL_KIND_CODE_BUOY,
|
|
|
|
|
} from '../../types/constants';
|
|
|
|
|
import './ShipLegend.scss';
|
|
|
|
|
|
|
|
|
|
// 선박 종류별 SVG 아이콘
|
|
|
|
|
import fishingIcon from '../../assets/img/shipKindIcons/fishing.svg';
|
|
|
|
|
import passIcon from '../../assets/img/shipKindIcons/pass.svg';
|
|
|
|
|
import cargoIcon from '../../assets/img/shipKindIcons/cargo.svg';
|
|
|
|
|
import hazardIcon from '../../assets/img/shipKindIcons/hazard.svg';
|
|
|
|
|
import govIcon from '../../assets/img/shipKindIcons/gov.svg';
|
|
|
|
|
import kcgvIcon from '../../assets/img/shipKindIcons/kcgv.svg';
|
|
|
|
|
import bouyIcon from '../../assets/img/shipKindIcons/bouy.svg';
|
|
|
|
|
import etcIcon from '../../assets/img/shipKindIcons/etc.svg';
|
|
|
|
|
|
|
|
|
|
/**
|
2026-02-15 10:28:27 +09:00
|
|
|
* 선박 종류 코드 -> 아이콘 매핑
|
2026-02-05 09:59:30 +09:00
|
|
|
*/
|
2026-02-15 10:28:27 +09:00
|
|
|
const SHIP_KIND_ICONS: Record<string, string> = {
|
2026-02-05 09:59:30 +09:00
|
|
|
[SIGNAL_KIND_CODE_FISHING]: fishingIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_KCGV]: kcgvIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_PASSENGER]: passIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_CARGO]: cargoIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_TANKER]: hazardIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_GOV]: govIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_NORMAL]: etcIcon,
|
|
|
|
|
[SIGNAL_KIND_CODE_BUOY]: bouyIcon,
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-15 10:28:27 +09:00
|
|
|
interface LegendItemConfig {
|
|
|
|
|
code: string;
|
|
|
|
|
label: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 09:59:30 +09:00
|
|
|
/**
|
|
|
|
|
* 범례 항목 설정
|
|
|
|
|
*/
|
2026-02-15 10:28:27 +09:00
|
|
|
const LEGEND_ITEMS: LegendItemConfig[] = [
|
2026-02-05 09:59:30 +09:00
|
|
|
{ code: SIGNAL_KIND_CODE_FISHING, label: '어선' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_PASSENGER, label: '여객선' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_CARGO, label: '화물선' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_TANKER, label: '유조선' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_GOV, label: '관공선' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_KCGV, label: '경비함정' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_BUOY, label: '어망/부이' },
|
|
|
|
|
{ code: SIGNAL_KIND_CODE_NORMAL, label: '기타' },
|
|
|
|
|
];
|
|
|
|
|
|
2026-02-15 10:28:27 +09:00
|
|
|
interface ReplayLegendItemProps {
|
|
|
|
|
code: string;
|
|
|
|
|
label: string;
|
|
|
|
|
count: number;
|
|
|
|
|
icon: string;
|
|
|
|
|
isVisible: boolean;
|
|
|
|
|
onToggle: (code: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 09:59:30 +09:00
|
|
|
/**
|
|
|
|
|
* 리플레이 범례 항목
|
|
|
|
|
*/
|
2026-02-15 10:28:27 +09:00
|
|
|
const ReplayLegendItem = memo(function ReplayLegendItem({ code, label, count, icon, isVisible, onToggle }: ReplayLegendItemProps) {
|
2026-02-05 09:59:30 +09:00
|
|
|
const isBuoy = code === SIGNAL_KIND_CODE_BUOY;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<li className={`legend-item ${!isVisible ? 'disabled' : ''}`}>
|
|
|
|
|
<div className="legend-info" onClick={() => onToggle(code)}>
|
|
|
|
|
<span className="legend-icon">
|
|
|
|
|
<img
|
|
|
|
|
src={icon}
|
|
|
|
|
alt={label}
|
|
|
|
|
style={{ transform: isBuoy ? 'rotate(0deg)' : 'rotate(45deg)' }}
|
|
|
|
|
/>
|
|
|
|
|
</span>
|
|
|
|
|
<span className="legend-label">{label}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<span className="legend-count">{count}</span>
|
|
|
|
|
</li>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 리플레이 전용 범례 컴포넌트
|
|
|
|
|
*/
|
2026-02-15 10:28:27 +09:00
|
|
|
const ReplayLegend = memo(function ReplayLegend() {
|
2026-02-05 09:59:30 +09:00
|
|
|
const { replayShipCounts, replayTotalCount, shipKindCodeFilter } =
|
|
|
|
|
useReplayStore(
|
2026-02-15 10:28:27 +09:00
|
|
|
(state: {
|
|
|
|
|
replayShipCounts: Record<string, number>;
|
|
|
|
|
replayTotalCount: number;
|
|
|
|
|
shipKindCodeFilter: Set<string>;
|
|
|
|
|
}) => ({
|
2026-02-05 09:59:30 +09:00
|
|
|
replayShipCounts: state.replayShipCounts,
|
|
|
|
|
replayTotalCount: state.replayTotalCount,
|
|
|
|
|
shipKindCodeFilter: state.shipKindCodeFilter,
|
|
|
|
|
}),
|
|
|
|
|
shallow
|
|
|
|
|
);
|
2026-02-15 10:28:27 +09:00
|
|
|
const toggleShipKindCode = useReplayStore((state: { toggleShipKindCode: (code: string) => void }) => state.toggleShipKindCode);
|
2026-02-05 09:59:30 +09:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<article className="ship-legend">
|
|
|
|
|
{/* 헤더 */}
|
|
|
|
|
<div className="legend-header">
|
|
|
|
|
<div className="legend-title">
|
|
|
|
|
<span>리플레이 현황</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 선박 종류별 목록 */}
|
|
|
|
|
<ul className="legend-list">
|
|
|
|
|
{LEGEND_ITEMS.map((item) => (
|
|
|
|
|
<ReplayLegendItem
|
|
|
|
|
key={item.code}
|
|
|
|
|
code={item.code}
|
|
|
|
|
label={item.label}
|
|
|
|
|
count={replayShipCounts[item.code] || 0}
|
|
|
|
|
icon={SHIP_KIND_ICONS[item.code]}
|
|
|
|
|
isVisible={shipKindCodeFilter.has(item.code)}
|
|
|
|
|
onToggle={toggleShipKindCode}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
{/* 푸터 - 전체 카운트 */}
|
|
|
|
|
<div className="legend-footer">
|
|
|
|
|
<span>전체</span>
|
|
|
|
|
<span className="total-count">{replayTotalCount}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</article>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default ReplayLegend;
|