refactor: GisTree.js 주석 추가 및 최신화
This commit is contained in:
@@ -10,6 +10,24 @@ import React, { useEffect, useState, useRef, useCallback } from "react";
|
||||
import { useGisContext } from "./Gis";
|
||||
import GisApp from "./lib/gis.core";
|
||||
|
||||
/**
|
||||
* GisTree (React memoized component)
|
||||
*
|
||||
* 계층형 GIS 레이어 트리를 렌더링하고, 체크 상태에 따라 지도상의 레이어 On/Off를 제어합니다.
|
||||
* 또한 특정 순서/간격으로 레이어를 자동으로 토글하는 애니메이션 기능을 제공합니다.
|
||||
*
|
||||
* Props 요약
|
||||
* - checkedNodes, setCheckedNodes: (선택) 외부 관리 체크 상태 (현재 내부 state 사용)
|
||||
* - visible, setVisible: 사이드 패널 가시성 on/off
|
||||
* - onCheckedChange(checkedIds, checkedNodesData): 체크 변경 시 콜백
|
||||
* - onLayerIconClick(nodeNm): 각 노드의 미리보기/아이콘 클릭 시 콜백
|
||||
* - imageVisible: 레이어 옆 아이콘(썸네일) 노출 여부
|
||||
* - isPlaying, setIsPlaying: 자동 애니메이션 재생 상태 제어
|
||||
* - targetLayerTypes: 자동 애니메이션 대상이 되는 레이어 type 배열 (예: ['tb_ms_swage_mhl'])
|
||||
* - orderedLayers: 레이어 재생 순서를 지정하는 id 배열 (lyrSn)
|
||||
* - checkInterval: 자동 애니메이션 토글 간격(ms)
|
||||
* - onProgressChange(total, current): 애니메이션 진행상태 콜백
|
||||
*/
|
||||
export default React.memo(function GisTree({
|
||||
checkedNodes = [],
|
||||
setCheckedNodes = () => { },
|
||||
@@ -25,17 +43,29 @@ export default React.memo(function GisTree({
|
||||
onProgressChange = () => { },
|
||||
}) {
|
||||
const { gisCore, treeData } = useGisContext();
|
||||
|
||||
// 체크/확장/노드/탭 상태
|
||||
const [checked, setChecked] = useState([]);
|
||||
const [expanded, setExpanded] = useState([]);
|
||||
const [nodes, setNodes] = useState([]);
|
||||
const [activeTab, setActiveTab] = useState("layers");
|
||||
|
||||
// 자동 애니메이션 관련 ref (실행 인덱스, 대상 노드 목록, 일시정지, 타이머)
|
||||
const currentAnimationIndexRef = useRef(0);
|
||||
const targetNodesRef = useRef([]);
|
||||
const isPausedRef = useRef(false);
|
||||
const timeoutIdRef = useRef(null);
|
||||
|
||||
// ===== 유틸 =====
|
||||
// =====================
|
||||
// 유틸리티: 트리 탐색 관련 함수들
|
||||
// =====================
|
||||
|
||||
/**
|
||||
* 주어진 id의 모든 하위(자손) 노드 id를 평탄 배열로 반환합니다.
|
||||
* @param {number|string} id 기준 노드 id
|
||||
* @param {Array} all 전체 노드 배열
|
||||
* @returns {Array} 자손 노드 id 배열
|
||||
*/
|
||||
const getDescendants = (id, all) => {
|
||||
const children = all.filter((n) => n.parent === id);
|
||||
return children.reduce(
|
||||
@@ -44,40 +74,64 @@ export default React.memo(function GisTree({
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 주어진 id의 모든 상위(조상) 노드 id를 상단부터 배열로 반환합니다.
|
||||
* @param {number|string} id 기준 노드 id
|
||||
* @param {Array} all 전체 노드 배열
|
||||
* @returns {Array} 조상 노드 id 배열
|
||||
*/
|
||||
const getAncestors = (id, all) => {
|
||||
const node = all.find((n) => n.id === id);
|
||||
if (!node || node.parent == null) return [];
|
||||
return [node.parent, ...getAncestors(node.parent, all)];
|
||||
};
|
||||
|
||||
const getChildren = (parentId, all) =>
|
||||
all.filter((n) => n.parent === parentId);
|
||||
/**
|
||||
* 특정 parentId의 직계 자식 노드 리스트를 반환합니다.
|
||||
*/
|
||||
const getChildren = (parentId, all) => all.filter((n) => n.parent === parentId);
|
||||
|
||||
/**
|
||||
* parentId의 모든 직계 자식이 checkedIds에 포함되어 있는지 확인합니다.
|
||||
*/
|
||||
const areAllChildrenChecked = (parentId, all, checkedIds) => {
|
||||
const children = getChildren(parentId, all);
|
||||
return children.length > 0 && children.every((c) => checkedIds.includes(c.id));
|
||||
};
|
||||
|
||||
/**
|
||||
* 어떤 노드의 체크 변경 이후, 상위 부모까지 올라가며 체크 상태를 전파/정리합니다.
|
||||
* - 모든 자식이 체크되면 부모도 체크
|
||||
* - 일부라도 해제되면 부모 해제
|
||||
*/
|
||||
const updateParentCheckState = (nodeId, all, checkedIds) => {
|
||||
const parent = all.find((n) => n.id === nodeId)?.parent;
|
||||
if (parent == null) return checkedIds;
|
||||
|
||||
if (areAllChildrenChecked(parent, all, checkedIds)) {
|
||||
if (!checkedIds.includes(parent)) {
|
||||
checkedIds.push(parent);
|
||||
}
|
||||
if (!checkedIds.includes(parent)) checkedIds.push(parent);
|
||||
} else {
|
||||
checkedIds = checkedIds.filter((id) => id !== parent);
|
||||
}
|
||||
return updateParentCheckState(parent, all, checkedIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* parentId의 모든 직계 자식이 checked 배열에 포함되어 있는지 확인합니다.
|
||||
*/
|
||||
const isEveryChildChecked = (parentId, all, checked) => {
|
||||
const children = getChildren(parentId, all);
|
||||
return children.length > 0 && children.every((c) => checked.includes(c.id));
|
||||
};
|
||||
|
||||
// ===== 기본 체크 노드 =====
|
||||
// =====================
|
||||
// 기본 체크 노드 계산
|
||||
// =====================
|
||||
|
||||
/**
|
||||
* 초기 렌더 시 기본으로 체크할 노드 id 목록을 반환합니다.
|
||||
* - data.initEprssYn === 'Y' 인 노드 + 부모 전파
|
||||
*/
|
||||
const getDefaultCheckedNodes = (nodes) => {
|
||||
let checkedIds = nodes
|
||||
.filter((n) => n.data.initEprssYn === "Y")
|
||||
@@ -90,7 +144,13 @@ export default React.memo(function GisTree({
|
||||
return Array.from(new Set(checkedIds));
|
||||
};
|
||||
|
||||
// ===== 초기 로딩 =====
|
||||
// =====================
|
||||
// 초기 로딩: 트리 데이터 세팅 & 초기 체크/확장 & 지도 반영
|
||||
// =====================
|
||||
|
||||
/**
|
||||
* treeData가 들어오면 노드 변환 후, 모든 레이어를 지도에서 잠시 비활성화합니다.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (treeData && treeData.length > 0) {
|
||||
const nodeData = setNodeValues(treeData);
|
||||
@@ -99,11 +159,15 @@ export default React.memo(function GisTree({
|
||||
}
|
||||
}, [treeData]);
|
||||
|
||||
/**
|
||||
* nodes가 준비되면 기본 체크/확장 상태를 계산하고, 실제 지도 레이어 on/off 반영 및 콜백 호출.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (nodes.length > 0) {
|
||||
const defaultChecked = getDefaultCheckedNodes(nodes);
|
||||
setChecked(defaultChecked);
|
||||
|
||||
// 기본 확장 = 체크된 노드 + 그 조상들
|
||||
const defaultExpanded = Array.from(
|
||||
new Set([
|
||||
...defaultChecked,
|
||||
@@ -112,6 +176,7 @@ export default React.memo(function GisTree({
|
||||
);
|
||||
setExpanded(defaultExpanded);
|
||||
|
||||
// 지도 반영
|
||||
gisCore.setLayerOnMap(defaultChecked, true);
|
||||
|
||||
const defaultCheckedNodesData = nodes.filter((node) =>
|
||||
@@ -121,7 +186,16 @@ export default React.memo(function GisTree({
|
||||
}
|
||||
}, [nodes]);
|
||||
|
||||
// ===== 체크 이벤트 =====
|
||||
// =====================
|
||||
// 체크 이벤트: 트리에서 체크 박스 클릭 시 처리
|
||||
// =====================
|
||||
|
||||
/**
|
||||
* 단일 노드 체크/해제 처리 핸들러
|
||||
* - 체크 시: 자신 + 모든 자손 체크
|
||||
* - 해제 시: 자신 + 모든 자손 해제
|
||||
* - 이후 부모 상태 전파 및 지도 레이어 on/off 반영, 콜백 호출
|
||||
*/
|
||||
const handleCheck = (id) => {
|
||||
setChecked((prevChecked) => {
|
||||
let newChecked = [...prevChecked];
|
||||
@@ -312,6 +386,9 @@ export default React.memo(function GisTree({
|
||||
isPausedRef.current = true;
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 재생/정지/대상/간격 변경에 반응하여 애니메이션을 관리합니다.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (isPlaying)
|
||||
handleAutoCheckAnimation(true, targetLayerTypes, checkInterval);
|
||||
@@ -324,15 +401,23 @@ export default React.memo(function GisTree({
|
||||
pauseAnimation,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 언마운트 시 타이머 정리.
|
||||
*/
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutIdRef.current) {
|
||||
clearTimeout(timeoutIdRef.current);
|
||||
}
|
||||
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ===== 노드 세팅 =====
|
||||
// =====================
|
||||
// 노드/아이콘 렌더링 데이터 준비
|
||||
// =====================
|
||||
|
||||
/**
|
||||
* 서버에서 내려온 트리 데이터를 Tree 컴포넌트가 요구하는 구조로 변환합니다.
|
||||
* - id, text, parent, droppable, data, icon
|
||||
*/
|
||||
const setNodeValues = (result) => {
|
||||
return result.map((item) => {
|
||||
const src = makeLegendSrc(item);
|
||||
@@ -347,7 +432,9 @@ export default React.memo(function GisTree({
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 레이어 정보로부터 범례 이미지 경로 또는 동적 생성 이미지를 반환합니다.
|
||||
*/
|
||||
const makeLegendSrc = (layerinfo) => {
|
||||
if (!layerinfo.gobjTy) return null;
|
||||
if (layerinfo.iconNm) {
|
||||
@@ -357,6 +444,9 @@ export default React.memo(function GisTree({
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 레이어 스타일 정보를 기반으로 작은 캔버스에 간단한 범례 이미지를 그려 dataURL을 생성합니다.
|
||||
*/
|
||||
const makeLegendImage = (layerinfo) => {
|
||||
let image = document.createElement("img");
|
||||
let canvas = document.createElement("canvas");
|
||||
@@ -401,11 +491,18 @@ export default React.memo(function GisTree({
|
||||
return image;
|
||||
};
|
||||
|
||||
/**
|
||||
* 각 레이어 행의 아이콘 클릭 시 호출. 선택되지 않은 레이어면 경고, 선택된 경우 콜백 전달.
|
||||
*/
|
||||
const handleLayerImgClick = (nodeNm, nodeId) => {
|
||||
if (!checked.includes(nodeId)) alert("시설을 선택해주세요");
|
||||
else onLayerIconClick(nodeNm);
|
||||
};
|
||||
|
||||
// =====================
|
||||
// 렌더링
|
||||
// =====================
|
||||
|
||||
if (!treeData || treeData.length === 0) return null;
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user