@mint-ui/map 1.2.0-test.34 → 1.2.0-test.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/mint-map/core/advanced/shared/context.d.ts +19 -12
- package/dist/components/mint-map/core/advanced/shared/context.js +54 -75
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.js +40 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.js +189 -0
- package/dist/components/mint-map/core/advanced/shared/index.d.ts +3 -0
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +12 -110
- package/dist/components/mint-map/core/advanced/shared/performance.js +56 -151
- package/dist/components/mint-map/core/advanced/shared/types.d.ts +18 -153
- package/dist/components/mint-map/core/advanced/shared/types.js +0 -1
- package/dist/components/mint-map/core/advanced/shared/utils.d.ts +36 -27
- package/dist/components/mint-map/core/advanced/shared/utils.js +58 -52
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.js +51 -0
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +22 -74
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.js +156 -617
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +26 -76
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +152 -551
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.d.ts +67 -8
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.js +81 -20
- package/dist/index.es.js +917 -1575
- package/dist/index.js +11 -0
- package/dist/index.umd.js +923 -1573
- package/package.json +1 -1
|
@@ -14,6 +14,9 @@ require('../shared/types.js');
|
|
|
14
14
|
var utils = require('../shared/utils.js');
|
|
15
15
|
var context = require('../shared/context.js');
|
|
16
16
|
var performance = require('../shared/performance.js');
|
|
17
|
+
var viewport = require('../shared/viewport.js');
|
|
18
|
+
var hooks = require('../shared/hooks.js');
|
|
19
|
+
var helpers = require('../shared/helpers.js');
|
|
17
20
|
var renderer = require('./renderer.js');
|
|
18
21
|
|
|
19
22
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
@@ -21,24 +24,19 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
21
24
|
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
22
25
|
var Konva__default = /*#__PURE__*/_interopDefaultLegacy(Konva);
|
|
23
26
|
|
|
24
|
-
// 메인 컴포넌트
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
27
|
var WoongCanvasPolygon = function (props) {
|
|
28
28
|
var data = props.data,
|
|
29
29
|
onClick = props.onClick,
|
|
30
30
|
_a = props.enableMultiSelect,
|
|
31
31
|
enableMultiSelect = _a === void 0 ? false : _a,
|
|
32
|
-
_b = props.
|
|
33
|
-
|
|
34
|
-
_c = props.
|
|
35
|
-
|
|
36
|
-
_d = props.maxCacheSize,
|
|
37
|
-
maxCacheSize = _d === void 0 ? performance.DEFAULT_MAX_CACHE_SIZE : _d,
|
|
32
|
+
_b = props.cullingMargin,
|
|
33
|
+
cullingMargin = _b === void 0 ? performance.DEFAULT_CULLING_MARGIN : _b,
|
|
34
|
+
_c = props.maxCacheSize,
|
|
35
|
+
maxCacheSize = _c === void 0 ? performance.DEFAULT_MAX_CACHE_SIZE : _c,
|
|
38
36
|
externalSelectedItems = props.selectedItems,
|
|
39
37
|
externalSelectedItem = props.selectedItem,
|
|
40
|
-
|
|
41
|
-
disableInteraction =
|
|
38
|
+
_d = props.disableInteraction,
|
|
39
|
+
disableInteraction = _d === void 0 ? false : _d,
|
|
42
40
|
baseFillColor = props.baseFillColor,
|
|
43
41
|
baseStrokeColor = props.baseStrokeColor,
|
|
44
42
|
baseLineWidth = props.baseLineWidth,
|
|
@@ -51,173 +49,67 @@ var WoongCanvasPolygon = function (props) {
|
|
|
51
49
|
hoveredFillColor = props.hoveredFillColor,
|
|
52
50
|
hoveredStrokeColor = props.hoveredStrokeColor,
|
|
53
51
|
hoveredLineWidth = props.hoveredLineWidth,
|
|
54
|
-
options = tslib.__rest(props, ["data", "onClick", "enableMultiSelect", "
|
|
52
|
+
options = tslib.__rest(props, ["data", "onClick", "enableMultiSelect", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction", "baseFillColor", "baseStrokeColor", "baseLineWidth", "selectedFillColor", "selectedStrokeColor", "selectedLineWidth", "activeFillColor", "activeStrokeColor", "activeLineWidth", "hoveredFillColor", "hoveredStrokeColor", "hoveredLineWidth"]); // --------------------------------------------------------------------------
|
|
55
53
|
// Hooks & Context
|
|
56
54
|
// --------------------------------------------------------------------------
|
|
57
55
|
|
|
58
56
|
|
|
59
57
|
var controller = MintMapProvider.useMintMapController();
|
|
60
58
|
var context$1 = context.useWoongCanvasContext();
|
|
61
|
-
var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; //
|
|
62
|
-
// DOM Refs
|
|
63
|
-
// --------------------------------------------------------------------------
|
|
59
|
+
var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; // DOM Refs
|
|
64
60
|
|
|
65
61
|
var divRef = React.useRef(document.createElement('div'));
|
|
66
62
|
var divElement = divRef.current;
|
|
67
63
|
var containerRef = React.useRef(null);
|
|
68
|
-
var markerRef = React.useRef(); //
|
|
69
|
-
// Konva Refs
|
|
70
|
-
// --------------------------------------------------------------------------
|
|
64
|
+
var markerRef = React.useRef(); // Konva Refs
|
|
71
65
|
|
|
72
66
|
var stageRef = React.useRef(null);
|
|
73
67
|
var baseLayerRef = React.useRef(null);
|
|
74
|
-
var eventLayerRef = React.useRef(null); //
|
|
75
|
-
// Data Refs - 선택 및 Hover 상태 관리
|
|
76
|
-
// --------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
/** data prop을 ref로 추적 (stale closure 방지, useEffect에서 동기화) */
|
|
79
|
-
|
|
80
|
-
var dataRef = React.useRef(data); // --------------------------------------------------------------------------
|
|
81
|
-
// State Refs - 선택 및 Hover 상태 관리
|
|
82
|
-
// --------------------------------------------------------------------------
|
|
83
|
-
|
|
84
|
-
/** 상호작용 비활성화 상태 (Ref로 관리하여 클로저 문제 해결) */
|
|
68
|
+
var eventLayerRef = React.useRef(null); // 상태 관리 Refs (React 리렌더링 최소화)
|
|
85
69
|
|
|
70
|
+
var dataRef = React.useRef(data);
|
|
86
71
|
var disableInteractionRef = React.useRef(disableInteraction);
|
|
87
|
-
/** 현재 Hover 중인 항목 */
|
|
88
|
-
|
|
89
72
|
var hoveredItemRef = React.useRef(null);
|
|
90
|
-
/** 외부에서 전달된 선택 항목 (Ref로 관리하여 클로저 문제 해결) */
|
|
91
|
-
|
|
92
73
|
var selectedItemRef = React.useRef(externalSelectedItem);
|
|
93
|
-
/**
|
|
94
|
-
* 선택된 항목의 ID Set
|
|
95
|
-
*
|
|
96
|
-
* 용도:
|
|
97
|
-
* 1. onClick 콜백에 전달 - onClick(data, selectedIdsRef.current)
|
|
98
|
-
* 2. 선택 여부 빠른 체크 - selectedIdsRef.current.has(id)
|
|
99
|
-
* 3. 메모리 효율 - ID만 저장 (작음)
|
|
100
|
-
*
|
|
101
|
-
* selectedItemsMapRef와 차이:
|
|
102
|
-
* - selectedIdsRef: ID만 저장 { "id1", "id2" }
|
|
103
|
-
* - selectedItemsMapRef: 전체 객체 저장 { id1: {...}, id2: {...} }
|
|
104
|
-
*
|
|
105
|
-
* 둘 다 필요: ID만 필요한 곳은 이것, 전체 데이터 필요한 곳은 Map
|
|
106
|
-
*/
|
|
107
|
-
|
|
108
74
|
var selectedIdsRef = React.useRef(new Set());
|
|
109
|
-
|
|
110
|
-
* 선택된 항목의 실제 데이터 Map (핵심 성능 최적화!)
|
|
111
|
-
*
|
|
112
|
-
* 목적: doRenderEvent에서 filter() 순회 제거
|
|
113
|
-
* - 이전: markersRef.current.filter() → O(전체 마커 수)
|
|
114
|
-
* - 현재: Map.values() → O(선택된 항목 수)
|
|
115
|
-
*
|
|
116
|
-
* 성능 개선: 10,000개 중 1개 선택 시
|
|
117
|
-
* - 이전: 10,000번 체크
|
|
118
|
-
* - 현재: 1번 접근 (10,000배 빠름!)
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
|
-
var selectedItemsMapRef = React.useRef(new Map()); // --------------------------------------------------------------------------
|
|
122
|
-
// Drag Refs
|
|
123
|
-
// --------------------------------------------------------------------------
|
|
75
|
+
var selectedItemsMapRef = React.useRef(new Map()); // 드래그 상태 Refs
|
|
124
76
|
|
|
125
77
|
var draggingRef = React.useRef(false);
|
|
126
78
|
var prevCenterOffsetRef = React.useRef(null);
|
|
127
79
|
var accumTranslateRef = React.useRef({
|
|
128
80
|
x: 0,
|
|
129
81
|
y: 0
|
|
130
|
-
}); //
|
|
131
|
-
// Performance Refs (캐싱 & 최적화)
|
|
132
|
-
// --------------------------------------------------------------------------
|
|
133
|
-
|
|
134
|
-
/** 좌표 변환 결과 LRU 캐시 */
|
|
82
|
+
}); // 성능 최적화 Refs
|
|
135
83
|
|
|
136
84
|
var offsetCacheRef = React.useRef(new performance.LRUCache(maxCacheSize));
|
|
137
|
-
/** 공간 인덱스 (빠른 Hit Test) */
|
|
138
|
-
|
|
139
85
|
var spatialIndexRef = React.useRef(new performance.SpatialHashGrid(performance.SPATIAL_GRID_CELL_SIZE));
|
|
140
|
-
/** 바운딩 박스 캐시 (Viewport Culling 최적화) */
|
|
141
|
-
|
|
142
86
|
var boundingBoxCacheRef = React.useRef(new Map());
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
var viewportRef = React.useRef(null); // --------------------------------------------------------------------------
|
|
146
|
-
// 유틸리티 함수: 뷰포트 관리
|
|
147
|
-
// --------------------------------------------------------------------------
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* 현재 뷰포트 영역 계산
|
|
151
|
-
*/
|
|
87
|
+
var viewportRef = React.useRef(null); // 뷰포트 영역 계산 (Viewport Culling용)
|
|
152
88
|
|
|
153
89
|
var updateViewport = function () {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
viewportRef.current = {
|
|
157
|
-
minX: -cullingMargin,
|
|
158
|
-
maxX: stage.width() + cullingMargin,
|
|
159
|
-
minY: -cullingMargin,
|
|
160
|
-
maxY: stage.height() + cullingMargin
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
|
|
165
|
-
*/
|
|
90
|
+
viewport.updateViewport(stageRef.current, cullingMargin, viewportRef);
|
|
91
|
+
}; // 뷰포트 내부 여부 확인 (바운딩 박스 캐싱)
|
|
166
92
|
|
|
167
93
|
|
|
168
94
|
var isInViewport = function (item) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
var bbox = boundingBoxCacheRef.current.get(item.id);
|
|
173
|
-
|
|
174
|
-
if (!bbox) {
|
|
175
|
-
// 바운딩 박스 계산 (공통 함수 사용)
|
|
176
|
-
var computed = computeBoundingBox(item);
|
|
177
|
-
if (!computed) return false;
|
|
178
|
-
bbox = computed;
|
|
179
|
-
boundingBoxCacheRef.current.set(item.id, bbox);
|
|
180
|
-
} // 바운딩 박스와 viewport 교차 체크
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return !(bbox.maxX < viewport.minX || bbox.minX > viewport.maxX || bbox.maxY < viewport.minY || bbox.minY > viewport.maxY);
|
|
184
|
-
}; // --------------------------------------------------------------------------
|
|
185
|
-
// 유틸리티 함수: 좌표 변환 캐싱
|
|
186
|
-
// --------------------------------------------------------------------------
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* 폴리곤 좌표 변환 결과를 캐시하고 반환
|
|
190
|
-
* @param polygonData 폴리곤 데이터
|
|
191
|
-
* @returns 변환된 좌표 배열 또는 null
|
|
192
|
-
*/
|
|
95
|
+
return viewport.isInViewport(item, viewportRef, boundingBoxCacheRef, computeBoundingBox);
|
|
96
|
+
}; // 폴리곤 좌표 변환 (위경도 → 화면 좌표, LRU 캐시 사용)
|
|
193
97
|
|
|
194
98
|
|
|
195
99
|
var getOrComputePolygonOffsets = function (polygonData) {
|
|
196
100
|
var cached = offsetCacheRef.current.get(polygonData.id);
|
|
197
101
|
if (cached && Array.isArray(cached)) return cached;
|
|
198
102
|
var result = utils.computePolygonOffsets(polygonData, controller);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
offsetCacheRef.current.set(polygonData.id, result);
|
|
202
|
-
}
|
|
203
|
-
|
|
103
|
+
if (!result) return null;
|
|
104
|
+
offsetCacheRef.current.set(polygonData.id, result);
|
|
204
105
|
return result;
|
|
205
|
-
}; //
|
|
206
|
-
// 유틸리티 함수: 바운딩 박스 계산
|
|
207
|
-
// --------------------------------------------------------------------------
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* 폴리곤의 바운딩 박스 계산
|
|
211
|
-
*
|
|
212
|
-
* @param item 폴리곤 데이터
|
|
213
|
-
* @returns 바운딩 박스 또는 null
|
|
214
|
-
*/
|
|
106
|
+
}; // 폴리곤 바운딩 박스 계산 (Viewport Culling 및 Hit Test용)
|
|
215
107
|
|
|
216
108
|
|
|
217
109
|
var computeBoundingBox = function (item) {
|
|
218
|
-
// 폴리곤: 모든 좌표의 최소/최대값 계산
|
|
219
110
|
var offsets = getOrComputePolygonOffsets(item);
|
|
220
|
-
if (!offsets) return null;
|
|
111
|
+
if (!offsets) return null; // 모든 좌표를 순회하며 최소/최대값 찾기
|
|
112
|
+
|
|
221
113
|
var minX = Infinity,
|
|
222
114
|
minY = Infinity,
|
|
223
115
|
maxX = -Infinity,
|
|
@@ -247,86 +139,39 @@ var WoongCanvasPolygon = function (props) {
|
|
|
247
139
|
maxX: maxX,
|
|
248
140
|
maxY: maxY
|
|
249
141
|
};
|
|
250
|
-
}; //
|
|
251
|
-
// 유틸리티 함수: 공간 인덱싱
|
|
252
|
-
// --------------------------------------------------------------------------
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* 공간 인덱스 빌드 (빠른 Hit Test를 위한 자료구조)
|
|
256
|
-
*/
|
|
142
|
+
}; // 공간 인덱스 빌드 (빠른 Hit Test용)
|
|
257
143
|
|
|
258
144
|
|
|
259
145
|
var buildSpatialIndex = function () {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
var currentData = dataRef.current;
|
|
263
|
-
|
|
264
|
-
for (var _i = 0, currentData_1 = currentData; _i < currentData_1.length; _i++) {
|
|
265
|
-
var item = currentData_1[_i]; // 바운딩 박스 계산 (공통 함수 사용)
|
|
266
|
-
|
|
267
|
-
var bbox = computeBoundingBox(item);
|
|
268
|
-
|
|
269
|
-
if (bbox) {
|
|
270
|
-
spatial.insert(item, bbox.minX, bbox.minY, bbox.maxX, bbox.maxY);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}; // --------------------------------------------------------------------------
|
|
274
|
-
// 렌더링 함수 결정 (dataType에 따라)
|
|
275
|
-
// --------------------------------------------------------------------------
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* 외부 렌더링 함수에 전달할 유틸리티 객체
|
|
279
|
-
*/
|
|
146
|
+
hooks.buildSpatialIndex(dataRef.current, spatialIndexRef.current, computeBoundingBox);
|
|
147
|
+
}; // 렌더링 유틸리티 객체
|
|
280
148
|
|
|
281
149
|
|
|
282
150
|
var renderUtils = {
|
|
283
151
|
getOrComputePolygonOffsets: getOrComputePolygonOffsets,
|
|
284
152
|
getOrComputeMarkerOffset: function () {
|
|
285
153
|
return null;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
};
|
|
289
|
-
/**
|
|
290
|
-
* 렌더링 함수 생성 (props 기반)
|
|
291
|
-
*/
|
|
154
|
+
}
|
|
155
|
+
}; // 렌더링 함수 생성
|
|
292
156
|
|
|
293
157
|
var renderBase = renderer.renderPolygonBase(baseFillColor, baseStrokeColor, baseLineWidth);
|
|
294
|
-
var renderEvent = renderer.renderPolygonEvent(baseFillColor, baseStrokeColor, baseLineWidth, selectedFillColor, selectedStrokeColor, selectedLineWidth, activeFillColor, activeStrokeColor, activeLineWidth, hoveredFillColor, hoveredStrokeColor, hoveredLineWidth);
|
|
295
|
-
/** Base Layer에서 사용할 빈 Set (재사용) */
|
|
296
|
-
|
|
297
|
-
React.useRef(new Set());
|
|
298
|
-
/**
|
|
299
|
-
* Base 레이어 렌더링 (뷰포트 컬링 적용, 선택된 마커 제외)
|
|
300
|
-
*
|
|
301
|
-
* 🔥 최적화:
|
|
302
|
-
* 1. Shape 재사용으로 객체 생성/파괴 오버헤드 제거
|
|
303
|
-
* 2. sceneFunc 한 번만 설정 (함수 재생성 제거)
|
|
304
|
-
* 3. 클로저로 최신 데이터 참조
|
|
305
|
-
*
|
|
306
|
-
* 🎯 topOnHover 지원:
|
|
307
|
-
* - renderEvent가 없을 때 Base Layer에서 hover 처리 (fallback)
|
|
308
|
-
* - renderEvent가 있으면 Event Layer에서 처리 (성능 최적화)
|
|
309
|
-
*/
|
|
158
|
+
var renderEvent = renderer.renderPolygonEvent(baseFillColor, baseStrokeColor, baseLineWidth, selectedFillColor, selectedStrokeColor, selectedLineWidth, activeFillColor, activeStrokeColor, activeLineWidth, hoveredFillColor, hoveredStrokeColor, hoveredLineWidth); // Base Layer 렌더링 (뷰포트 컬링 적용)
|
|
310
159
|
|
|
311
160
|
var doRenderBase = function () {
|
|
312
161
|
var layer = baseLayerRef.current;
|
|
313
|
-
if (!layer) return;
|
|
314
|
-
|
|
162
|
+
if (!layer) return;
|
|
315
163
|
var shape = layer.findOne('.base-render-shape');
|
|
316
164
|
|
|
317
165
|
if (!shape) {
|
|
318
|
-
// 최초 생성 (한 번만 실행됨)
|
|
319
|
-
// sceneFunc도 여기서 한 번만 설정 (클로저로 최신 데이터 참조)
|
|
320
166
|
shape = new Konva__default["default"].Shape({
|
|
321
167
|
name: 'base-render-shape',
|
|
322
168
|
sceneFunc: function (context, shape) {
|
|
323
169
|
var ctx = context;
|
|
324
|
-
var hovered = hoveredItemRef.current; //
|
|
170
|
+
var hovered = hoveredItemRef.current; // 뷰포트 컬링: 화면에 보이는 항목만 필터링
|
|
325
171
|
|
|
326
|
-
var visibleItems =
|
|
172
|
+
var visibleItems = dataRef.current.filter(function (item) {
|
|
327
173
|
return isInViewport(item);
|
|
328
|
-
})
|
|
329
|
-
|
|
174
|
+
});
|
|
330
175
|
renderBase({
|
|
331
176
|
ctx: ctx,
|
|
332
177
|
items: visibleItems,
|
|
@@ -340,38 +185,24 @@ var WoongCanvasPolygon = function (props) {
|
|
|
340
185
|
hitStrokeWidth: 0
|
|
341
186
|
});
|
|
342
187
|
layer.add(shape);
|
|
343
|
-
}
|
|
344
|
-
|
|
188
|
+
}
|
|
345
189
|
|
|
346
190
|
layer.batchDraw();
|
|
347
|
-
};
|
|
348
|
-
/**
|
|
349
|
-
* Event 레이어 렌더링 (hover + 선택 상태 표시)
|
|
350
|
-
*
|
|
351
|
-
* 🔥 최적화:
|
|
352
|
-
* 1. Shape 재사용으로 객체 생성/파괴 오버헤드 제거
|
|
353
|
-
* 2. sceneFunc 한 번만 설정 (함수 재생성 제거)
|
|
354
|
-
* 3. 클로저로 최신 데이터 참조
|
|
355
|
-
*/
|
|
191
|
+
}; // Event Layer 렌더링 (hover 효과 및 선택 상태 표시)
|
|
356
192
|
|
|
357
193
|
|
|
358
194
|
var doRenderEvent = function () {
|
|
359
195
|
var layer = eventLayerRef.current;
|
|
360
|
-
if (!layer) return;
|
|
361
|
-
|
|
196
|
+
if (!layer) return;
|
|
362
197
|
var shape = layer.findOne('.event-render-shape');
|
|
363
198
|
|
|
364
199
|
if (!shape) {
|
|
365
|
-
// 최초 생성 (한 번만 실행됨)
|
|
366
|
-
// sceneFunc도 여기서 한 번만 설정 (클로저로 최신 데이터 참조)
|
|
367
200
|
shape = new Konva__default["default"].Shape({
|
|
368
201
|
name: 'event-render-shape',
|
|
369
202
|
sceneFunc: function (context, shape) {
|
|
370
|
-
var ctx = context;
|
|
371
|
-
|
|
372
|
-
var
|
|
373
|
-
var hovered = hoveredItemRef.current; // 일반 렌더링
|
|
374
|
-
|
|
203
|
+
var ctx = context;
|
|
204
|
+
var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
|
|
205
|
+
var hovered = hoveredItemRef.current;
|
|
375
206
|
renderEvent({
|
|
376
207
|
ctx: ctx,
|
|
377
208
|
hoveredItem: hovered,
|
|
@@ -385,14 +216,10 @@ var WoongCanvasPolygon = function (props) {
|
|
|
385
216
|
hitStrokeWidth: 0
|
|
386
217
|
});
|
|
387
218
|
layer.add(shape);
|
|
388
|
-
}
|
|
389
|
-
|
|
219
|
+
}
|
|
390
220
|
|
|
391
221
|
layer.batchDraw();
|
|
392
|
-
};
|
|
393
|
-
/**
|
|
394
|
-
* 전체 즉시 렌더링 (IDLE 시 호출)
|
|
395
|
-
*/
|
|
222
|
+
}; // 전체 즉시 렌더링
|
|
396
223
|
|
|
397
224
|
|
|
398
225
|
var renderAllImmediate = function () {
|
|
@@ -400,111 +227,46 @@ var WoongCanvasPolygon = function (props) {
|
|
|
400
227
|
buildSpatialIndex();
|
|
401
228
|
doRenderBase();
|
|
402
229
|
doRenderEvent();
|
|
403
|
-
}; //
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
var markerOptions = tslib.__assign({
|
|
425
|
-
position: bounds.nw
|
|
426
|
-
}, options);
|
|
427
|
-
|
|
428
|
-
markerRef.current && controller.updateMarker(markerRef.current, markerOptions); // 4. transform 제거 전에 새 데이터로 즉시 렌더링 (겹침 방지)
|
|
429
|
-
|
|
430
|
-
if (containerRef.current) {
|
|
431
|
-
containerRef.current.style.transform = '';
|
|
432
|
-
containerRef.current.style.visibility = '';
|
|
433
|
-
} // 5. 새 위치에서 렌더링
|
|
230
|
+
}; // 지도 이벤트 핸들러 생성
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
var _e = hooks.createMapEventHandlers({
|
|
234
|
+
controller: controller,
|
|
235
|
+
containerRef: containerRef,
|
|
236
|
+
markerRef: markerRef,
|
|
237
|
+
options: options,
|
|
238
|
+
prevCenterOffsetRef: prevCenterOffsetRef,
|
|
239
|
+
accumTranslateRef: accumTranslateRef,
|
|
240
|
+
offsetCacheRef: offsetCacheRef,
|
|
241
|
+
boundingBoxCacheRef: boundingBoxCacheRef,
|
|
242
|
+
renderAllImmediate: renderAllImmediate
|
|
243
|
+
}),
|
|
244
|
+
handleIdle = _e.handleIdle,
|
|
245
|
+
handleZoomStart = _e.handleZoomStart,
|
|
246
|
+
handleZoomEnd = _e.handleZoomEnd,
|
|
247
|
+
handleCenterChanged = _e.handleCenterChanged,
|
|
248
|
+
handleDragStartShared = _e.handleDragStart,
|
|
249
|
+
handleDragEndShared = _e.handleDragEnd;
|
|
434
250
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
* 줌 시작 시 처리 (일시적으로 숨김)
|
|
440
|
-
*/
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
var handleZoomStart = function () {
|
|
444
|
-
if (containerRef.current) {
|
|
445
|
-
containerRef.current.style.visibility = 'hidden';
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
/**
|
|
449
|
-
* 줌 종료 시 처리 (다시 표시)
|
|
450
|
-
*/
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
var handleZoomEnd = function () {
|
|
454
|
-
if (containerRef.current) {
|
|
455
|
-
containerRef.current.style.visibility = '';
|
|
456
|
-
}
|
|
251
|
+
var handleDragStart = function () {
|
|
252
|
+
handleDragStartShared();
|
|
253
|
+
draggingRef.current = true;
|
|
254
|
+
controller.setMapCursor('grabbing');
|
|
457
255
|
};
|
|
458
|
-
/**
|
|
459
|
-
* 지도 중심 변경 시 처리 (transform으로 이동 추적)
|
|
460
|
-
*/
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
var handleCenterChanged = function () {
|
|
464
|
-
var center = controller.getCurrBounds().getCenter();
|
|
465
|
-
var curr = controller.positionToOffset(center);
|
|
466
|
-
var prev = prevCenterOffsetRef.current;
|
|
467
|
-
|
|
468
|
-
if (!prev) {
|
|
469
|
-
prevCenterOffsetRef.current = {
|
|
470
|
-
x: curr.x,
|
|
471
|
-
y: curr.y
|
|
472
|
-
};
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
var dx = prev.x - curr.x;
|
|
477
|
-
var dy = prev.y - curr.y;
|
|
478
|
-
accumTranslateRef.current = {
|
|
479
|
-
x: accumTranslateRef.current.x + dx,
|
|
480
|
-
y: accumTranslateRef.current.y + dy
|
|
481
|
-
};
|
|
482
|
-
prevCenterOffsetRef.current = {
|
|
483
|
-
x: curr.x,
|
|
484
|
-
y: curr.y
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
if (containerRef.current) {
|
|
488
|
-
containerRef.current.style.transform = "translate(".concat(accumTranslateRef.current.x, "px, ").concat(accumTranslateRef.current.y, "px)");
|
|
489
|
-
}
|
|
490
|
-
}; // --------------------------------------------------------------------------
|
|
491
|
-
// Hit Test & 상태 관리
|
|
492
|
-
// --------------------------------------------------------------------------
|
|
493
256
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
*/
|
|
257
|
+
var handleDragEnd = function () {
|
|
258
|
+
handleDragEndShared();
|
|
259
|
+
draggingRef.current = false;
|
|
260
|
+
controller.setMapCursor('grab');
|
|
261
|
+
}; // Hit Test: 특정 좌표의 폴리곤 찾기
|
|
500
262
|
|
|
501
263
|
|
|
502
264
|
var findData = function (offset) {
|
|
503
|
-
//
|
|
504
|
-
var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); //
|
|
265
|
+
// 공간 인덱스에서 후보 항목 조회 (O(1) 수준의 빠른 조회)
|
|
266
|
+
var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); // 역순 순회: 나중에 추가된 항목(최상위)이 먼저 선택되도록
|
|
505
267
|
|
|
506
268
|
for (var i = candidates.length - 1; i >= 0; i--) {
|
|
507
|
-
var item = candidates[i];
|
|
269
|
+
var item = candidates[i]; // 정확한 Hit Test: Ray Casting 알고리즘으로 폴리곤 내부 여부 확인
|
|
508
270
|
|
|
509
271
|
if (utils.isPointInPolygonData(offset, item, getOrComputePolygonOffsets)) {
|
|
510
272
|
return item;
|
|
@@ -512,18 +274,7 @@ var WoongCanvasPolygon = function (props) {
|
|
|
512
274
|
}
|
|
513
275
|
|
|
514
276
|
return null;
|
|
515
|
-
};
|
|
516
|
-
/**
|
|
517
|
-
* Hover 상태 설정 및 레이어 렌더링
|
|
518
|
-
*
|
|
519
|
-
* @param data hover된 마커/폴리곤 데이터 또는 null
|
|
520
|
-
*
|
|
521
|
-
* 최적화: RAF 제거하여 즉시 렌더링 (16ms 지연 제거)
|
|
522
|
-
*
|
|
523
|
-
* 🎯 topOnHover 지원:
|
|
524
|
-
* - renderEvent가 있으면: Event Layer에서만 처리 (성능 최적화)
|
|
525
|
-
* - renderEvent가 없고 topOnHover=true면: Base Layer에서 처리
|
|
526
|
-
*/
|
|
277
|
+
}; // Hover 상태 설정 및 렌더링
|
|
527
278
|
|
|
528
279
|
|
|
529
280
|
var setHovered = function (data) {
|
|
@@ -533,26 +284,14 @@ var WoongCanvasPolygon = function (props) {
|
|
|
533
284
|
controller.setMapCursor('grabbing');
|
|
534
285
|
} else {
|
|
535
286
|
controller.setMapCursor(data ? 'pointer' : 'grab');
|
|
536
|
-
}
|
|
537
|
-
|
|
287
|
+
}
|
|
538
288
|
|
|
539
289
|
doRenderEvent();
|
|
540
|
-
};
|
|
541
|
-
/**
|
|
542
|
-
* 클릭 처리 (단일/다중 선택)
|
|
543
|
-
*
|
|
544
|
-
* @param data 클릭된 마커/폴리곤 데이터
|
|
545
|
-
*
|
|
546
|
-
* 🔥 최적화: 단일 Shape 렌더링으로 Base Layer 재렌더링 속도 향상
|
|
547
|
-
* - sceneFunc에서 selectedIds를 체크하여 선택된 마커만 스킵
|
|
548
|
-
* - 객체 생성 오버헤드 제거로 1000개 이상도 부드럽게 처리
|
|
549
|
-
*/
|
|
290
|
+
}; // 클릭 처리: 선택 상태 업데이트
|
|
550
291
|
|
|
551
292
|
|
|
552
293
|
var handleLocalClick = function (data) {
|
|
553
|
-
// 1. 선택 상태 업데이트
|
|
554
294
|
if (enableMultiSelect) {
|
|
555
|
-
// 다중 선택: Set과 Map 동시 업데이트
|
|
556
295
|
var newSelected = new Set(selectedIdsRef.current);
|
|
557
296
|
|
|
558
297
|
if (newSelected.has(data.id)) {
|
|
@@ -565,7 +304,6 @@ var WoongCanvasPolygon = function (props) {
|
|
|
565
304
|
|
|
566
305
|
selectedIdsRef.current = newSelected;
|
|
567
306
|
} else {
|
|
568
|
-
// 단일 선택: 토글
|
|
569
307
|
var newSelected = new Set();
|
|
570
308
|
|
|
571
309
|
if (!selectedIdsRef.current.has(data.id)) {
|
|
@@ -577,144 +315,79 @@ var WoongCanvasPolygon = function (props) {
|
|
|
577
315
|
}
|
|
578
316
|
|
|
579
317
|
selectedIdsRef.current = newSelected;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
doRenderBase(); // 3. Event Layer 렌더링 (hover 처리)
|
|
318
|
+
}
|
|
584
319
|
|
|
320
|
+
doRenderBase();
|
|
585
321
|
doRenderEvent();
|
|
586
|
-
}; //
|
|
587
|
-
// 이벤트 핸들러: UI 이벤트
|
|
588
|
-
// --------------------------------------------------------------------------
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* 클릭 이벤트 처리
|
|
592
|
-
*/
|
|
322
|
+
}; // 클릭 이벤트 핸들러
|
|
593
323
|
|
|
594
324
|
|
|
595
325
|
var handleClick = function (event) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
if (
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
var data_1 = findData(clickedOffset);
|
|
605
|
-
|
|
606
|
-
if (data_1) {
|
|
607
|
-
handleLocalClick(data_1);
|
|
608
|
-
|
|
609
|
-
if (onClick) {
|
|
610
|
-
onClick(data_1, selectedIdsRef.current);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
} catch (error) {
|
|
614
|
-
console.error('[WoongCanvasPolygon] handleClick error:', error);
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
/**
|
|
618
|
-
* 마우스 이동 이벤트 처리 (hover 감지)
|
|
619
|
-
*/
|
|
326
|
+
if (disableInteractionRef.current) return;
|
|
327
|
+
var clickedOffset = helpers.validateEvent(event, context$1, controller);
|
|
328
|
+
if (!clickedOffset) return;
|
|
329
|
+
var data = findData(clickedOffset);
|
|
330
|
+
if (!data) return;
|
|
331
|
+
handleLocalClick(data);
|
|
332
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(data, selectedIdsRef.current);
|
|
333
|
+
}; // 마우스 이동 이벤트 핸들러 (hover 감지)
|
|
620
334
|
|
|
621
335
|
|
|
622
336
|
var handleMouseMove = function (event) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
var hoveredItem = findData(mouseOffset);
|
|
632
|
-
var prevHovered = hoveredItemRef.current;
|
|
633
|
-
|
|
634
|
-
if (prevHovered !== hoveredItem) {
|
|
635
|
-
setHovered(hoveredItem);
|
|
636
|
-
}
|
|
637
|
-
} catch (error) {
|
|
638
|
-
console.error('[WoongCanvasPolygon] handleMouseMove error:', error);
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
/**
|
|
642
|
-
* 드래그 시작 처리 (커서를 grabbing으로 변경)
|
|
643
|
-
*/
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
var handleDragStart = function () {
|
|
647
|
-
draggingRef.current = true;
|
|
648
|
-
controller.setMapCursor('grabbing');
|
|
649
|
-
};
|
|
650
|
-
/**
|
|
651
|
-
* 드래그 종료 처리 (커서를 기본으로 복원)
|
|
652
|
-
*/
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
var handleDragEnd = function () {
|
|
656
|
-
draggingRef.current = false;
|
|
657
|
-
controller.setMapCursor('grab');
|
|
658
|
-
};
|
|
659
|
-
/**
|
|
660
|
-
* 마우스가 canvas를 벗어날 때 hover cleanup
|
|
661
|
-
*/
|
|
337
|
+
if (disableInteractionRef.current) return;
|
|
338
|
+
var mouseOffset = helpers.validateEvent(event, context$1, controller);
|
|
339
|
+
if (!mouseOffset) return;
|
|
340
|
+
var hoveredItem = findData(mouseOffset);
|
|
341
|
+
var prevHovered = hoveredItemRef.current;
|
|
342
|
+
if (prevHovered === hoveredItem) return;
|
|
343
|
+
setHovered(hoveredItem);
|
|
344
|
+
}; // 마우스가 맵 영역을 벗어날 때 hover 상태 초기화
|
|
662
345
|
|
|
663
346
|
|
|
664
347
|
var handleMouseLeave = function () {
|
|
665
|
-
if (disableInteractionRef.current) return;
|
|
666
|
-
|
|
348
|
+
if (disableInteractionRef.current) return;
|
|
667
349
|
var prevHovered = hoveredItemRef.current;
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}
|
|
674
|
-
}; // --------------------------------------------------------------------------
|
|
675
|
-
// Lifecycle: DOM 초기화
|
|
676
|
-
// --------------------------------------------------------------------------
|
|
350
|
+
if (!prevHovered) return;
|
|
351
|
+
hoveredItemRef.current = null;
|
|
352
|
+
controller.setMapCursor('grab');
|
|
353
|
+
doRenderEvent();
|
|
354
|
+
}; // DOM 초기화
|
|
677
355
|
|
|
678
356
|
|
|
679
357
|
React.useEffect(function () {
|
|
680
358
|
divElement.style.width = 'fit-content';
|
|
681
359
|
return function () {
|
|
682
|
-
if (markerRef.current)
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
360
|
+
if (!markerRef.current) return;
|
|
361
|
+
controller.clearDrawable(markerRef.current);
|
|
362
|
+
markerRef.current = undefined;
|
|
686
363
|
};
|
|
687
|
-
}, []); //
|
|
688
|
-
// Lifecycle: 마커 생성/업데이트
|
|
689
|
-
// --------------------------------------------------------------------------
|
|
364
|
+
}, []); // 마커 생성/업데이트
|
|
690
365
|
|
|
691
366
|
React.useEffect(function () {
|
|
692
|
-
if (options)
|
|
693
|
-
|
|
367
|
+
if (!options) return;
|
|
368
|
+
var bounds = controller.getCurrBounds();
|
|
694
369
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
370
|
+
var markerOptions = tslib.__assign({
|
|
371
|
+
position: bounds.nw
|
|
372
|
+
}, options);
|
|
698
373
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
markerRef.current.element = divElement;
|
|
704
|
-
controller.createMarker(markerRef.current);
|
|
374
|
+
if (markerRef.current) {
|
|
375
|
+
controller.updateMarker(markerRef.current, markerOptions);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
705
378
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
379
|
+
markerRef.current = new MapDrawables.Marker(markerOptions);
|
|
380
|
+
markerRef.current.element = divElement;
|
|
381
|
+
controller.createMarker(markerRef.current);
|
|
709
382
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
}
|
|
383
|
+
if (divElement.parentElement) {
|
|
384
|
+
divElement.parentElement.style.pointerEvents = 'none';
|
|
714
385
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
386
|
+
|
|
387
|
+
if (options.zIndex !== undefined) {
|
|
388
|
+
controller.setMarkerZIndex(markerRef.current, options.zIndex);
|
|
389
|
+
}
|
|
390
|
+
}, [options]); // Konva 초기화 및 이벤트 리스너 등록
|
|
718
391
|
|
|
719
392
|
React.useEffect(function () {
|
|
720
393
|
var mapDiv = controller.mapDivElement;
|
|
@@ -723,11 +396,9 @@ var WoongCanvasPolygon = function (props) {
|
|
|
723
396
|
width: mapDiv.offsetWidth,
|
|
724
397
|
height: mapDiv.offsetHeight
|
|
725
398
|
});
|
|
726
|
-
stageRef.current = stage;
|
|
727
|
-
|
|
399
|
+
stageRef.current = stage;
|
|
728
400
|
var baseLayer = new Konva__default["default"].Layer({
|
|
729
|
-
listening: false
|
|
730
|
-
|
|
401
|
+
listening: false
|
|
731
402
|
});
|
|
732
403
|
var eventLayer = new Konva__default["default"].Layer({
|
|
733
404
|
listening: false
|
|
@@ -735,13 +406,11 @@ var WoongCanvasPolygon = function (props) {
|
|
|
735
406
|
baseLayerRef.current = baseLayer;
|
|
736
407
|
eventLayerRef.current = eventLayer;
|
|
737
408
|
stage.add(baseLayer);
|
|
738
|
-
stage.add(eventLayer);
|
|
739
|
-
|
|
740
|
-
updateViewport(); // ResizeObserver (맵 크기 변경 감지)
|
|
409
|
+
stage.add(eventLayer);
|
|
410
|
+
updateViewport(); // ResizeObserver: 맵 크기 변경 감지 (RAF로 debounce)
|
|
741
411
|
|
|
742
412
|
var resizeRafId = null;
|
|
743
413
|
var resizeObserver = new ResizeObserver(function () {
|
|
744
|
-
// RAF로 다음 프레임에 한 번만 실행 (debounce 효과)
|
|
745
414
|
if (resizeRafId !== null) {
|
|
746
415
|
cancelAnimationFrame(resizeRafId);
|
|
747
416
|
}
|
|
@@ -764,10 +433,9 @@ var WoongCanvasPolygon = function (props) {
|
|
|
764
433
|
controller.addEventListener('CLICK', handleClick);
|
|
765
434
|
controller.addEventListener('MOUSEMOVE', handleMouseMove);
|
|
766
435
|
controller.addEventListener('DRAGSTART', handleDragStart);
|
|
767
|
-
controller.addEventListener('DRAGEND', handleDragEnd);
|
|
768
|
-
|
|
436
|
+
controller.addEventListener('DRAGEND', handleDragEnd);
|
|
769
437
|
mapDiv.addEventListener('mouseleave', handleMouseLeave);
|
|
770
|
-
renderAllImmediate(); // Context 사용 시 컴포넌트 등록
|
|
438
|
+
renderAllImmediate(); // Context 사용 시 컴포넌트 등록
|
|
771
439
|
|
|
772
440
|
var componentInstance = null;
|
|
773
441
|
|
|
@@ -786,22 +454,17 @@ var WoongCanvasPolygon = function (props) {
|
|
|
786
454
|
},
|
|
787
455
|
isInteractionDisabled: function () {
|
|
788
456
|
return disableInteractionRef.current;
|
|
789
|
-
}
|
|
790
|
-
|
|
457
|
+
}
|
|
791
458
|
};
|
|
792
459
|
context$1.registerComponent(componentInstance);
|
|
793
|
-
}
|
|
794
|
-
|
|
460
|
+
}
|
|
795
461
|
|
|
796
462
|
return function () {
|
|
797
|
-
// RAF 정리
|
|
798
463
|
if (resizeRafId !== null) {
|
|
799
464
|
cancelAnimationFrame(resizeRafId);
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
resizeObserver.disconnect(); // 이벤트 리스너 정리
|
|
465
|
+
}
|
|
804
466
|
|
|
467
|
+
resizeObserver.disconnect();
|
|
805
468
|
controller.removeEventListener('IDLE', handleIdle);
|
|
806
469
|
controller.removeEventListener('ZOOMSTART', handleZoomStart);
|
|
807
470
|
controller.removeEventListener('ZOOM_CHANGED', handleZoomEnd);
|
|
@@ -810,67 +473,41 @@ var WoongCanvasPolygon = function (props) {
|
|
|
810
473
|
controller.removeEventListener('MOUSEMOVE', handleMouseMove);
|
|
811
474
|
controller.removeEventListener('DRAGSTART', handleDragStart);
|
|
812
475
|
controller.removeEventListener('DRAGEND', handleDragEnd);
|
|
813
|
-
mapDiv.removeEventListener('mouseleave', handleMouseLeave);
|
|
476
|
+
mapDiv.removeEventListener('mouseleave', handleMouseLeave);
|
|
814
477
|
|
|
815
478
|
if (context$1 && componentInstance) {
|
|
816
479
|
context$1.unregisterComponent(componentInstance);
|
|
817
|
-
}
|
|
818
|
-
|
|
480
|
+
}
|
|
819
481
|
|
|
820
482
|
baseLayer.destroyChildren();
|
|
821
483
|
eventLayer.destroyChildren();
|
|
822
|
-
stage.destroy();
|
|
823
|
-
|
|
484
|
+
stage.destroy();
|
|
824
485
|
offsetCacheRef.current.clear();
|
|
825
486
|
boundingBoxCacheRef.current.clear();
|
|
826
487
|
spatialIndexRef.current.clear();
|
|
827
488
|
};
|
|
828
|
-
}, []); //
|
|
829
|
-
// --------------------------------------------------------------------------
|
|
830
|
-
// Lifecycle: disableInteraction 동기화
|
|
831
|
-
// --------------------------------------------------------------------------
|
|
489
|
+
}, []); // disableInteraction 동기화
|
|
832
490
|
|
|
833
491
|
React.useEffect(function () {
|
|
834
492
|
disableInteractionRef.current = disableInteraction;
|
|
835
|
-
}, [disableInteraction]); //
|
|
836
|
-
// Lifecycle: 외부 selectedItems 동기화
|
|
837
|
-
// --------------------------------------------------------------------------
|
|
493
|
+
}, [disableInteraction]); // 외부 selectedItems 동기화
|
|
838
494
|
|
|
839
495
|
React.useEffect(function () {
|
|
840
|
-
if (!stageRef.current) return;
|
|
841
|
-
|
|
842
|
-
if (externalSelectedItems === undefined) return; // 외부에서 전달된 selectedItems로 동기화
|
|
843
|
-
|
|
844
|
-
var newSelectedIds = new Set();
|
|
845
|
-
var newSelectedItemsMap = new Map();
|
|
846
|
-
externalSelectedItems.forEach(function (item) {
|
|
847
|
-
newSelectedIds.add(item.id);
|
|
848
|
-
newSelectedItemsMap.set(item.id, item);
|
|
849
|
-
});
|
|
850
|
-
selectedIdsRef.current = newSelectedIds;
|
|
851
|
-
selectedItemsMapRef.current = newSelectedItemsMap; // 렌더링
|
|
852
|
-
|
|
496
|
+
if (!stageRef.current) return;
|
|
497
|
+
hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
|
|
853
498
|
doRenderBase();
|
|
854
499
|
doRenderEvent();
|
|
855
|
-
}, [externalSelectedItems]); //
|
|
856
|
-
// --------------------------------------------------------------------------
|
|
857
|
-
// Lifecycle: 외부 selectedItem 변경 시 Event Layer 리렌더링
|
|
858
|
-
// --------------------------------------------------------------------------
|
|
500
|
+
}, [externalSelectedItems]); // 외부 selectedItem 변경 시 Event Layer 리렌더링
|
|
859
501
|
|
|
860
502
|
React.useEffect(function () {
|
|
861
|
-
if (!stageRef.current) return;
|
|
862
|
-
|
|
863
|
-
selectedItemRef.current = externalSelectedItem; // selectedItem이 변경되면 Event Layer만 다시 그림
|
|
864
|
-
|
|
503
|
+
if (!stageRef.current) return;
|
|
504
|
+
selectedItemRef.current = externalSelectedItem;
|
|
865
505
|
doRenderEvent();
|
|
866
|
-
}, [externalSelectedItem]); //
|
|
867
|
-
// Lifecycle: 데이터 변경 시 렌더링
|
|
868
|
-
// --------------------------------------------------------------------------
|
|
506
|
+
}, [externalSelectedItem]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
|
|
869
507
|
|
|
870
508
|
React.useEffect(function () {
|
|
871
|
-
if (!stageRef.current) return;
|
|
872
|
-
|
|
873
|
-
dataRef.current = data; // 데이터 변경 시 즉시 transform 제거 및 캐시 정리 (겹침 방지)
|
|
509
|
+
if (!stageRef.current) return;
|
|
510
|
+
dataRef.current = data;
|
|
874
511
|
|
|
875
512
|
if (containerRef.current) {
|
|
876
513
|
containerRef.current.style.transform = '';
|
|
@@ -880,46 +517,10 @@ var WoongCanvasPolygon = function (props) {
|
|
|
880
517
|
accumTranslateRef.current = {
|
|
881
518
|
x: 0,
|
|
882
519
|
y: 0
|
|
883
|
-
};
|
|
884
|
-
|
|
520
|
+
};
|
|
885
521
|
offsetCacheRef.current.clear();
|
|
886
522
|
boundingBoxCacheRef.current.clear();
|
|
887
|
-
|
|
888
|
-
* 선택 상태 동기화 (최적화 버전)
|
|
889
|
-
*
|
|
890
|
-
* data가 변경되면 selectedItemsMapRef도 업데이트 필요
|
|
891
|
-
* (참조가 바뀌므로 기존 Map의 데이터는 stale 상태)
|
|
892
|
-
*
|
|
893
|
-
* 🔥 중요: 화면 밖 데이터도 선택 상태 유지!
|
|
894
|
-
* - 현재 data에 있으면 최신 데이터로 업데이트
|
|
895
|
-
* - 없으면 기존 selectedItemsMapRef의 데이터 유지
|
|
896
|
-
*
|
|
897
|
-
* 최적화: data를 Map으로 먼저 변환하여 find() 순회 제거
|
|
898
|
-
* - O(전체 데이터 수 + 선택된 개수) - 매우 효율적
|
|
899
|
-
*/
|
|
900
|
-
|
|
901
|
-
var dataMap = new Map(data.map(function (m) {
|
|
902
|
-
return [m.id, m];
|
|
903
|
-
}));
|
|
904
|
-
var newSelectedItemsMap = new Map();
|
|
905
|
-
selectedIdsRef.current.forEach(function (id) {
|
|
906
|
-
// 현재 data에 있으면 최신 데이터 사용
|
|
907
|
-
var currentItem = dataMap.get(id);
|
|
908
|
-
|
|
909
|
-
if (currentItem) {
|
|
910
|
-
newSelectedItemsMap.set(id, currentItem);
|
|
911
|
-
} else {
|
|
912
|
-
// 화면 밖이면 기존 데이터 유지
|
|
913
|
-
var prevItem = selectedItemsMapRef.current.get(id);
|
|
914
|
-
|
|
915
|
-
if (prevItem) {
|
|
916
|
-
newSelectedItemsMap.set(id, prevItem);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}); // selectedIdsRef는 그대로 유지 (화면 밖 항목도 선택 상태 유지)
|
|
920
|
-
|
|
921
|
-
selectedItemsMapRef.current = newSelectedItemsMap; // 즉시 렌더링
|
|
922
|
-
|
|
523
|
+
selectedItemsMapRef.current = hooks.syncSelectedItems(data, selectedIdsRef.current, selectedItemsMapRef.current);
|
|
923
524
|
renderAllImmediate();
|
|
924
525
|
}, [data]);
|
|
925
526
|
return reactDom.createPortal(React__default["default"].createElement("div", {
|