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