@mint-ui/map 1.2.0-test.8 → 1.2.0
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/.eslintrc.js +11 -4
- package/.vscode/settings.json +32 -9
- package/dist/components/mint-map/core/MintMapController.d.ts +1 -0
- package/dist/components/mint-map/core/MintMapCore.js +5 -6
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.d.ts +12 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.js +962 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/index.d.ts +4 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/types.d.ts +280 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.d.ts +17 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.js +624 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/index.d.ts +4 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.d.ts +303 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.js +1091 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/types.d.ts +284 -0
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarker.d.ts +7 -0
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerClaude.js +7 -7
- package/dist/components/mint-map/core/advanced/canvas/index.d.ts +0 -1
- package/dist/components/mint-map/core/advanced/index.d.ts +4 -2
- package/dist/components/mint-map/core/advanced/shared/context.d.ts +44 -0
- package/dist/components/mint-map/core/advanced/shared/context.js +230 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.js +41 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.js +196 -0
- package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/index.d.ts +5 -2
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +82 -0
- package/dist/components/mint-map/core/advanced/shared/performance.js +288 -0
- package/dist/components/mint-map/core/advanced/shared/types.d.ts +150 -0
- package/dist/components/mint-map/core/advanced/shared/types.js +31 -0
- package/dist/components/mint-map/core/advanced/shared/utils.d.ts +173 -0
- package/dist/components/mint-map/core/advanced/shared/utils.js +382 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.js +52 -0
- package/dist/components/mint-map/core/wrapper/MapMarkerWrapper.js +22 -1
- package/dist/components/mint-map/google/GoogleMintMapController.d.ts +1 -0
- package/dist/components/mint-map/google/GoogleMintMapController.js +13 -8
- package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +1 -0
- package/dist/components/mint-map/kakao/KakaoMintMapController.js +13 -8
- package/dist/components/mint-map/naver/NaverMintMapController.d.ts +3 -0
- package/dist/components/mint-map/naver/NaverMintMapController.js +46 -11
- package/dist/index.es.js +5605 -4056
- package/dist/index.js +47 -27
- package/dist/index.umd.js +5621 -4059
- package/package.json +1 -1
- package/CLAUDE.md +0 -100
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.d.ts +0 -22
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.js +0 -413
- package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +0 -11
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +0 -53
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.js +0 -1123
- package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +0 -3
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.d.ts +0 -31
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.js +0 -164
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.d.ts +0 -161
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.js +0 -343
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +0 -131
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.js +0 -14
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +0 -31
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.js +0 -164
- package/dist/components/mint-map/core/util/geohash.js +0 -125
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var tslib = require('tslib');
|
|
6
|
+
var Konva = require('konva');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var reactDom = require('react-dom');
|
|
9
|
+
var MapDrawables = require('../../../types/MapDrawables.js');
|
|
10
|
+
require('../../../types/MapTypes.js');
|
|
11
|
+
require('../../../types/MapEventTypes.js');
|
|
12
|
+
var MintMapProvider = require('../../provider/MintMapProvider.js');
|
|
13
|
+
var context = require('../shared/context.js');
|
|
14
|
+
var helpers = require('../shared/helpers.js');
|
|
15
|
+
var hooks = require('../shared/hooks.js');
|
|
16
|
+
var performance = require('../shared/performance.js');
|
|
17
|
+
var types = require('../shared/types.js');
|
|
18
|
+
var utils = require('../shared/utils.js');
|
|
19
|
+
var viewport = require('../shared/viewport.js');
|
|
20
|
+
|
|
21
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
22
|
+
|
|
23
|
+
var Konva__default = /*#__PURE__*/_interopDefaultLegacy(Konva);
|
|
24
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
25
|
+
|
|
26
|
+
var CanvasMarkerLayer = function (props) {
|
|
27
|
+
// 타입 가드: renderEvent가 있는지 확인
|
|
28
|
+
var hasRenderEvent = 'renderEvent' in props && props.renderEvent !== undefined; // renderEvent가 있는 경우와 없는 경우를 구분하여 props 추출
|
|
29
|
+
|
|
30
|
+
var data = props.data,
|
|
31
|
+
_a = props.cullingMargin,
|
|
32
|
+
cullingMargin = _a === void 0 ? performance.DEFAULT_CULLING_MARGIN : _a,
|
|
33
|
+
onOptimizationDataUpdate = props.onOptimizationDataUpdate,
|
|
34
|
+
renderBase = props.renderBase,
|
|
35
|
+
options = tslib.__rest(props, ["data", "cullingMargin", "onOptimizationDataUpdate", "renderBase"]); // renderEvent가 있는 경우에만 인터랙션 관련 props 추출
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
var _b = hasRenderEvent && 'renderEvent' in props ? props : {
|
|
39
|
+
disableInteraction: false,
|
|
40
|
+
onClick: undefined,
|
|
41
|
+
onMouseOut: undefined,
|
|
42
|
+
onMouseOver: undefined,
|
|
43
|
+
renderEvent: undefined,
|
|
44
|
+
selectedItem: undefined,
|
|
45
|
+
selectedItems: undefined,
|
|
46
|
+
topStageZIndex: undefined
|
|
47
|
+
},
|
|
48
|
+
_c = _b.disableInteraction,
|
|
49
|
+
disableInteraction = _c === void 0 ? false : _c,
|
|
50
|
+
onClick = _b.onClick,
|
|
51
|
+
onMouseOut = _b.onMouseOut,
|
|
52
|
+
onMouseOver = _b.onMouseOver,
|
|
53
|
+
renderEvent = _b.renderEvent,
|
|
54
|
+
externalSelectedItem = _b.selectedItem,
|
|
55
|
+
externalSelectedItems = _b.selectedItems,
|
|
56
|
+
rawTopStageZIndex = _b.topStageZIndex; // topStageZIndex가 있으면 hover 최상단 표시 활성화, 없으면 비활성화 (성능 우선)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
var topStageZIndex = rawTopStageZIndex;
|
|
60
|
+
var controller = MintMapProvider.useMintMapController();
|
|
61
|
+
var context$1 = context.useCanvasContext();
|
|
62
|
+
var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; // DOM Refs
|
|
63
|
+
|
|
64
|
+
var divRef = React.useRef(document.createElement('div'));
|
|
65
|
+
var divElement = divRef.current;
|
|
66
|
+
var containerRef = React.useRef(null);
|
|
67
|
+
var markerRef = React.useRef(); // Top Layer용 별도 DOM Refs (topStageZIndex가 설정된 경우에만 사용)
|
|
68
|
+
|
|
69
|
+
var topDivRef = React.useRef(document.createElement('div'));
|
|
70
|
+
var topDivElement = topDivRef.current;
|
|
71
|
+
var topContainerRef = React.useRef(null);
|
|
72
|
+
var topMarkerRef = React.useRef(); // Konva Refs
|
|
73
|
+
|
|
74
|
+
var stageRef = React.useRef(null);
|
|
75
|
+
var baseLayerRef = React.useRef(null);
|
|
76
|
+
var eventLayerRef = React.useRef(null);
|
|
77
|
+
var topStageRef = React.useRef(null);
|
|
78
|
+
var topLayerRef = React.useRef(null); // 상태 관리 Refs (React 리렌더링 최소화)
|
|
79
|
+
|
|
80
|
+
var dataRef = React.useRef(data);
|
|
81
|
+
var disableInteractionRef = React.useRef(disableInteraction);
|
|
82
|
+
var hoveredItemRef = React.useRef(null);
|
|
83
|
+
var selectedItemRef = React.useRef(externalSelectedItem);
|
|
84
|
+
var selectedIdsRef = React.useRef(new Set());
|
|
85
|
+
var selectedItemsMapRef = React.useRef(new Map()); // 드래그 상태 Refs
|
|
86
|
+
|
|
87
|
+
var draggingRef = React.useRef(false);
|
|
88
|
+
var prevCenterOffsetRef = React.useRef(null);
|
|
89
|
+
var accumTranslateRef = React.useRef({
|
|
90
|
+
x: 0,
|
|
91
|
+
y: 0
|
|
92
|
+
}); // 드래그 시작 시점의 hover 상태 저장 (드래그 중 hover 고정용)
|
|
93
|
+
|
|
94
|
+
var dragStartHoveredItemRef = React.useRef(null); // 공유 캐시 사용 (모든 마커 레이어가 공유)
|
|
95
|
+
|
|
96
|
+
var sharedMarkerCache = context$1 === null || context$1 === void 0 ? void 0 : context$1.sharedMarkerCache;
|
|
97
|
+
|
|
98
|
+
if (!sharedMarkerCache) {
|
|
99
|
+
throw new Error('CanvasMarkerLayer must be used within CanvasProvider');
|
|
100
|
+
} // 성능 최적화 Refs
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
var spatialIndexRef = React.useRef(new performance.SpatialHashGrid(performance.SPATIAL_GRID_CELL_SIZE));
|
|
104
|
+
var boundingBoxCacheRef = React.useRef(new Map());
|
|
105
|
+
var viewportRef = React.useRef(null); // 뷰포트 영역 계산 (Viewport Culling용)
|
|
106
|
+
|
|
107
|
+
var updateViewport = function () {
|
|
108
|
+
viewport.updateViewport(stageRef.current, cullingMargin, viewportRef);
|
|
109
|
+
}; // 마커 좌표 변환 (위경도 → 화면 좌표, 공유 캐시 사용)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
var getOrComputeMarkerOffset = function (markerData) {
|
|
113
|
+
var cached = sharedMarkerCache.get(markerData.id);
|
|
114
|
+
if (cached) return cached;
|
|
115
|
+
var result = utils.computeMarkerOffset(markerData, controller);
|
|
116
|
+
if (!result) return null;
|
|
117
|
+
sharedMarkerCache.set(markerData.id, result);
|
|
118
|
+
return result;
|
|
119
|
+
}; // 마커 바운딩 박스 계산 (Viewport Culling 및 Hit Test용, 오프셋 지원)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
var computeBoundingBox = function (item) {
|
|
123
|
+
var offset = getOrComputeMarkerOffset(item);
|
|
124
|
+
if (!offset) return null;
|
|
125
|
+
var boxWidth = item.boxWidth || 50;
|
|
126
|
+
var boxHeight = item.boxHeight || 28;
|
|
127
|
+
var tailHeight = item.tailHeight || 0;
|
|
128
|
+
var offsetX = item.offsetX || 0;
|
|
129
|
+
var offsetY = item.offsetY || 0; // 오프셋을 적용한 마커 중심점 기준으로 바운딩 박스 계산
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
maxX: offset.x + offsetX + boxWidth / 2,
|
|
133
|
+
maxY: offset.y + offsetY,
|
|
134
|
+
minX: offset.x + offsetX - boxWidth / 2,
|
|
135
|
+
minY: offset.y + offsetY - boxHeight - tailHeight
|
|
136
|
+
};
|
|
137
|
+
}; // 뷰포트 내부 여부 확인 (바운딩 박스 캐싱)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
var isInViewport = function (item) {
|
|
141
|
+
return viewport.isInViewport(item, viewportRef, boundingBoxCacheRef, computeBoundingBox);
|
|
142
|
+
}; // 공간 인덱스 빌드 (빠른 Hit Test용)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
var buildSpatialIndex = function () {
|
|
146
|
+
hooks.buildSpatialIndex(dataRef.current, spatialIndexRef.current, computeBoundingBox);
|
|
147
|
+
}; // 렌더링 유틸리티 객체
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
var renderUtils = {
|
|
151
|
+
getOrComputeMarkerOffset: getOrComputeMarkerOffset,
|
|
152
|
+
getOrComputePolygonOffsets: function () {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}; // Base Layer 렌더링 (뷰포트 컬링 적용, 선택된 마커 제외)
|
|
156
|
+
|
|
157
|
+
var doRenderBase = function () {
|
|
158
|
+
var layer = baseLayerRef.current;
|
|
159
|
+
if (!layer) return;
|
|
160
|
+
var shape = layer.findOne('.base-render-shape');
|
|
161
|
+
|
|
162
|
+
if (!shape) {
|
|
163
|
+
shape = new Konva__default["default"].Shape({
|
|
164
|
+
hitStrokeWidth: 0,
|
|
165
|
+
listening: false,
|
|
166
|
+
name: 'base-render-shape',
|
|
167
|
+
perfectDrawEnabled: false,
|
|
168
|
+
sceneFunc: function (konvaContext) {
|
|
169
|
+
var ctx = konvaContext; // 뷰포트 컬링: 화면에 보이는 항목만 필터링 (항상 활성화)
|
|
170
|
+
|
|
171
|
+
var visibleItems = dataRef.current.filter(function (item) {
|
|
172
|
+
return isInViewport(item);
|
|
173
|
+
}); // Base Layer는 기본 마커만 렌더링 (hover된 항목도 포함)
|
|
174
|
+
// hover 스타일은 Event Layer와 Top Layer에서 덧그려짐
|
|
175
|
+
|
|
176
|
+
renderBase({
|
|
177
|
+
ctx: ctx,
|
|
178
|
+
hoveredItem: null,
|
|
179
|
+
items: visibleItems,
|
|
180
|
+
selectedIds: new Set(),
|
|
181
|
+
utils: renderUtils
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
layer.add(shape);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
layer.batchDraw();
|
|
189
|
+
}; // Event Layer 렌더링 (hover 효과 및 선택 상태 표시)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
var doRenderEvent = function () {
|
|
193
|
+
var layer = eventLayerRef.current;
|
|
194
|
+
if (!layer || !renderEvent) return;
|
|
195
|
+
var shape = layer.findOne('.event-render-shape');
|
|
196
|
+
|
|
197
|
+
if (!shape) {
|
|
198
|
+
shape = new Konva__default["default"].Shape({
|
|
199
|
+
hitStrokeWidth: 0,
|
|
200
|
+
listening: false,
|
|
201
|
+
name: 'event-render-shape',
|
|
202
|
+
perfectDrawEnabled: false,
|
|
203
|
+
sceneFunc: function (konvaContext) {
|
|
204
|
+
var ctx = konvaContext;
|
|
205
|
+
var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
|
|
206
|
+
var hovered = hoveredItemRef.current; // selectedItem이 있으면 selectedItems 배열에 포함 (renderMarkerEvent가 selectedItems만 사용하므로)
|
|
207
|
+
|
|
208
|
+
if (selectedItemRef.current) {
|
|
209
|
+
// selectedItem이 이미 selectedItems에 포함되어 있지 않으면 추가
|
|
210
|
+
var selectedItemId_1 = selectedItemRef.current.id;
|
|
211
|
+
|
|
212
|
+
if (!selectedItems.some(function (item) {
|
|
213
|
+
return item.id === selectedItemId_1;
|
|
214
|
+
})) {
|
|
215
|
+
selectedItems = tslib.__spreadArray(tslib.__spreadArray([], selectedItems, true), [selectedItemRef.current], false);
|
|
216
|
+
}
|
|
217
|
+
} // Event Layer는 hover 효과 및 선택 상태를 덧그림
|
|
218
|
+
// topStageZIndex가 있으면 hover된 항목은 Top Stage에서도 처리되지만,
|
|
219
|
+
// Event Layer에서도 hover 스타일을 덧그려서 Base Layer 위에 표시됨
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
renderEvent({
|
|
223
|
+
ctx: ctx,
|
|
224
|
+
hasTopStage: topStageZIndex !== undefined,
|
|
225
|
+
hoveredItem: hovered,
|
|
226
|
+
renderSource: types.RenderSource.EVENT,
|
|
227
|
+
selectedItem: selectedItemRef.current,
|
|
228
|
+
selectedItems: selectedItems,
|
|
229
|
+
utils: renderUtils
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
layer.add(shape);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
layer.batchDraw();
|
|
237
|
+
}; // Top Layer 렌더링 (hover된 항목만, 별도 캔버스 DOM에 그리기)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
var doRenderTop = function () {
|
|
241
|
+
var stage = topStageRef.current;
|
|
242
|
+
var layer = topLayerRef.current;
|
|
243
|
+
if (!stage || !layer || topStageZIndex === undefined) return;
|
|
244
|
+
var hovered = hoveredItemRef.current;
|
|
245
|
+
var shape = layer.findOne('.top-render-shape'); // hover된 항목이 없으면 shape 제거
|
|
246
|
+
|
|
247
|
+
if (!hovered) {
|
|
248
|
+
if (shape) {
|
|
249
|
+
shape.destroy();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
layer.batchDraw();
|
|
253
|
+
return;
|
|
254
|
+
} // shape가 없으면 생성
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
if (!shape) {
|
|
258
|
+
shape = new Konva__default["default"].Shape({
|
|
259
|
+
hitStrokeWidth: 0,
|
|
260
|
+
listening: false,
|
|
261
|
+
name: 'top-render-shape',
|
|
262
|
+
perfectDrawEnabled: false,
|
|
263
|
+
sceneFunc: function () {} // 초기화만
|
|
264
|
+
|
|
265
|
+
});
|
|
266
|
+
layer.add(shape);
|
|
267
|
+
} // sceneFunc를 매번 업데이트하여 최신 상태 반영
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
shape.sceneFunc(function (konvaContext) {
|
|
271
|
+
var _a;
|
|
272
|
+
|
|
273
|
+
var ctx = konvaContext;
|
|
274
|
+
var currentHovered = hoveredItemRef.current; // hover된 항목이 없으면 아무것도 그리지 않음
|
|
275
|
+
|
|
276
|
+
if (!currentHovered) return; // 뷰포트 컬링 확인 (항상 활성화)
|
|
277
|
+
|
|
278
|
+
if (!isInViewport(currentHovered)) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current); // selectedItem이 있으면 hover된 항목이 선택되었는지 확인
|
|
283
|
+
|
|
284
|
+
var hoveredIsSelected = selectedItems.some(function (item) {
|
|
285
|
+
return item.id === currentHovered.id;
|
|
286
|
+
}) || ((_a = selectedItemRef.current) === null || _a === void 0 ? void 0 : _a.id) === currentHovered.id; // Top Layer에서는 hover된 마커만 hover 스타일로 그리기
|
|
287
|
+
// renderEvent가 있으면: base는 그리지 않고 event만 그리기 (hover 스타일 적용)
|
|
288
|
+
// renderEvent가 없으면: base를 hover 스타일로 그리기
|
|
289
|
+
|
|
290
|
+
if (renderEvent) {
|
|
291
|
+
// renderEvent가 있으면 hover 스타일로만 그리기
|
|
292
|
+
var hoverSelectedItems = hoveredIsSelected ? [currentHovered] : [];
|
|
293
|
+
renderEvent({
|
|
294
|
+
ctx: ctx,
|
|
295
|
+
hasTopStage: true,
|
|
296
|
+
hoveredItem: currentHovered,
|
|
297
|
+
renderSource: types.RenderSource.TOP,
|
|
298
|
+
selectedItem: selectedItemRef.current,
|
|
299
|
+
selectedItems: hoverSelectedItems,
|
|
300
|
+
utils: renderUtils
|
|
301
|
+
});
|
|
302
|
+
} else {
|
|
303
|
+
// renderEvent가 없으면 base를 hover 스타일로 그리기
|
|
304
|
+
renderBase({
|
|
305
|
+
ctx: ctx,
|
|
306
|
+
hoveredItem: currentHovered,
|
|
307
|
+
items: [currentHovered],
|
|
308
|
+
selectedIds: selectedIdsRef.current,
|
|
309
|
+
utils: renderUtils
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
layer.batchDraw();
|
|
314
|
+
}; // 최적화 데이터 업데이트 콜백 호출
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
var notifyOptimizationData = function () {
|
|
318
|
+
if (!onOptimizationDataUpdate) return; // 공유 캐시에서 마커 타입 항목만 필터링 (Offset 타입만)
|
|
319
|
+
|
|
320
|
+
var allCacheEntries = sharedMarkerCache.getAllEntries();
|
|
321
|
+
var cacheEntries = allCacheEntries.map(function (_a) {
|
|
322
|
+
var key = _a[0],
|
|
323
|
+
value = _a[1];
|
|
324
|
+
return [key, value];
|
|
325
|
+
});
|
|
326
|
+
var spatialGridCells = spatialIndexRef.current.getAllCells();
|
|
327
|
+
onOptimizationDataUpdate({
|
|
328
|
+
cacheEntries: cacheEntries,
|
|
329
|
+
cacheSize: sharedMarkerCache.size(),
|
|
330
|
+
spatialGridCells: spatialGridCells
|
|
331
|
+
});
|
|
332
|
+
}; // 전체 즉시 렌더링
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
var renderAllImmediate = function () {
|
|
336
|
+
updateViewport();
|
|
337
|
+
buildSpatialIndex();
|
|
338
|
+
doRenderBase();
|
|
339
|
+
doRenderEvent(); // 메인 stage의 transform을 topStage에도 동기화
|
|
340
|
+
|
|
341
|
+
if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
|
|
342
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (topStageZIndex !== undefined) {
|
|
346
|
+
doRenderTop();
|
|
347
|
+
} // 최적화 데이터 업데이트 콜백 호출
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
notifyOptimizationData();
|
|
351
|
+
}; // 지도 이벤트 핸들러 생성
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
var _d = hooks.createMapEventHandlers({
|
|
355
|
+
accumTranslateRef: accumTranslateRef,
|
|
356
|
+
boundingBoxCacheRef: boundingBoxCacheRef,
|
|
357
|
+
clearCache: function () {
|
|
358
|
+
return sharedMarkerCache.clear();
|
|
359
|
+
},
|
|
360
|
+
containerRef: containerRef,
|
|
361
|
+
controller: controller,
|
|
362
|
+
markerRef: markerRef,
|
|
363
|
+
options: options,
|
|
364
|
+
prevCenterOffsetRef: prevCenterOffsetRef,
|
|
365
|
+
renderAllImmediate: renderAllImmediate
|
|
366
|
+
}),
|
|
367
|
+
handleIdleShared = _d.handleIdle,
|
|
368
|
+
handleZoomStart = _d.handleZoomStart,
|
|
369
|
+
handleZoomEnd = _d.handleZoomEnd,
|
|
370
|
+
handleCenterChangedShared = _d.handleCenterChanged,
|
|
371
|
+
handleDragStartShared = _d.handleDragStart,
|
|
372
|
+
handleDragEndShared = _d.handleDragEnd; // handleIdle 래핑: topStage transform 제거 추가
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
var handleIdle = function () {
|
|
376
|
+
handleIdleShared(); // 드래그 완료 시 topStage의 transform도 제거 (메인 stage와 동기화)
|
|
377
|
+
|
|
378
|
+
if (topStageZIndex !== undefined && topContainerRef.current) {
|
|
379
|
+
topContainerRef.current.style.transform = '';
|
|
380
|
+
}
|
|
381
|
+
}; // handleCenterChanged 래핑: topStage transform 동기화 추가
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
var handleCenterChanged = function () {
|
|
385
|
+
handleCenterChangedShared(); // 드래그 중 메인 stage의 transform을 topStage에도 동기화 (반대 방향 이동 방지)
|
|
386
|
+
|
|
387
|
+
if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
|
|
388
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
var handleDragStart = function () {
|
|
393
|
+
handleDragStartShared(); // 드래그 시작 시점의 hover 상태 저장
|
|
394
|
+
|
|
395
|
+
dragStartHoveredItemRef.current = hoveredItemRef.current;
|
|
396
|
+
draggingRef.current = true;
|
|
397
|
+
controller.setMapCursor('grabbing'); // 메인 stage의 transform을 topStage에도 동기화
|
|
398
|
+
|
|
399
|
+
if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
|
|
400
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
var handleDragEnd = function () {
|
|
405
|
+
handleDragEndShared();
|
|
406
|
+
draggingRef.current = false; // 드래그 종료 후 hover 상태 초기화 (다음 MOUSEMOVE에서 업데이트됨)
|
|
407
|
+
|
|
408
|
+
dragStartHoveredItemRef.current = null;
|
|
409
|
+
controller.setMapCursor('grab'); // 메인 stage의 transform을 topStage에도 동기화
|
|
410
|
+
|
|
411
|
+
if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
|
|
412
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
413
|
+
}
|
|
414
|
+
}; // Hit Test: 특정 좌표의 마커 찾기
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
var findData = function (offset) {
|
|
418
|
+
// topStageZIndex가 설정되어 있으면 hover된 항목을 최우선으로 확인
|
|
419
|
+
if (topStageZIndex !== undefined && hoveredItemRef.current) {
|
|
420
|
+
var hovered = hoveredItemRef.current;
|
|
421
|
+
|
|
422
|
+
if (utils.isPointInMarkerData(offset, hovered, getOrComputeMarkerOffset)) {
|
|
423
|
+
return hovered;
|
|
424
|
+
}
|
|
425
|
+
} // 공간 인덱스에서 후보 항목 조회 (O(1) 수준의 빠른 조회)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); // 역순 순회: 나중에 추가된 항목(최상위)이 먼저 선택되도록
|
|
429
|
+
|
|
430
|
+
for (var i = candidates.length - 1; i >= 0; i--) {
|
|
431
|
+
var item = candidates[i];
|
|
432
|
+
|
|
433
|
+
if (utils.isPointInMarkerData(offset, item, getOrComputeMarkerOffset)) {
|
|
434
|
+
return item;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return null;
|
|
439
|
+
}; // Hover 상태 설정 및 렌더링
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
var setHovered = function (hoveredData) {
|
|
443
|
+
// 드래그 중에는 hover 상태 변경을 무시하고 드래그 시작 시점의 상태 유지
|
|
444
|
+
if (draggingRef.current) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
hoveredItemRef.current = hoveredData;
|
|
449
|
+
|
|
450
|
+
if (draggingRef.current) {
|
|
451
|
+
controller.setMapCursor('grabbing');
|
|
452
|
+
} else {
|
|
453
|
+
controller.setMapCursor(hoveredData ? 'pointer' : 'grab');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (topStageZIndex !== undefined) {
|
|
457
|
+
// topStageZIndex가 설정된 경우 Top Layer에서 hover된 항목 렌더링
|
|
458
|
+
// Base Layer는 모든 마커를 포함하므로 hover 상태 변경 시 재렌더링 불필요
|
|
459
|
+
// Event Layer는 hover 스타일과 선택된 항목을 렌더링하므로 업데이트 필요
|
|
460
|
+
doRenderEvent();
|
|
461
|
+
doRenderTop();
|
|
462
|
+
} else if (renderEvent) {
|
|
463
|
+
// renderEvent가 있을 때는 Event Layer만 업데이트
|
|
464
|
+
// topStageZIndex가 없으면 Base Layer는 정적이므로 재렌더링 불필요 (성능 우선)
|
|
465
|
+
// hover 효과는 Event Layer에서만 처리하면 됨
|
|
466
|
+
doRenderEvent();
|
|
467
|
+
}
|
|
468
|
+
}; // 클릭 처리: 선택 상태 업데이트 (단일 선택만 지원)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
var handleLocalClick = function (clickedData) {
|
|
472
|
+
var newSelected = new Set();
|
|
473
|
+
|
|
474
|
+
if (!selectedIdsRef.current.has(clickedData.id)) {
|
|
475
|
+
newSelected.add(clickedData.id);
|
|
476
|
+
selectedItemsMapRef.current.clear();
|
|
477
|
+
selectedItemsMapRef.current.set(clickedData.id, clickedData); // externalSelectedItem이 있을 때를 대비해 selectedItemRef도 업데이트
|
|
478
|
+
|
|
479
|
+
selectedItemRef.current = clickedData;
|
|
480
|
+
} else {
|
|
481
|
+
selectedItemsMapRef.current.clear();
|
|
482
|
+
selectedItemRef.current = null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
selectedIdsRef.current = newSelected; // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
|
|
486
|
+
|
|
487
|
+
doRenderEvent();
|
|
488
|
+
|
|
489
|
+
if (topStageZIndex !== undefined) {
|
|
490
|
+
doRenderTop();
|
|
491
|
+
}
|
|
492
|
+
}; // 클릭 이벤트 핸들러
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
var handleClick = function (event) {
|
|
496
|
+
if (controller.isMapDragged()) return;
|
|
497
|
+
if (disableInteractionRef.current) return;
|
|
498
|
+
var clickedOffset = helpers.validateEvent(event, context$1, controller);
|
|
499
|
+
if (!clickedOffset) return;
|
|
500
|
+
var clickedData = findData(clickedOffset);
|
|
501
|
+
if (!clickedData) return;
|
|
502
|
+
handleLocalClick(clickedData);
|
|
503
|
+
onClick === null || onClick === void 0 ? void 0 : onClick(clickedData, selectedIdsRef.current);
|
|
504
|
+
}; // 마커 바운딩 박스의 왼쪽 상단과 우측 하단을 위도/경도로 변환
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
var getMarkerBoundingBoxCoordinates = function (item) {
|
|
508
|
+
if (!item.position) return null;
|
|
509
|
+
return utils.calculateMarkerBoundingBox({
|
|
510
|
+
controller: controller,
|
|
511
|
+
height: item.boxHeight || 28,
|
|
512
|
+
lat: item.position.lat,
|
|
513
|
+
lng: item.position.lng,
|
|
514
|
+
offsetX: item.offsetX || 0,
|
|
515
|
+
offsetY: item.offsetY || 0,
|
|
516
|
+
tailHeight: item.tailHeight || 0,
|
|
517
|
+
width: item.boxWidth || 50
|
|
518
|
+
});
|
|
519
|
+
}; // 마우스 이동 이벤트 핸들러 (hover 감지)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
var handleMouseMove = function (event) {
|
|
523
|
+
if (disableInteractionRef.current) return; // 드래그 중에는 hover 상태를 업데이트하지 않음 (드래그 시작 시점의 hover 상태 유지)
|
|
524
|
+
|
|
525
|
+
if (draggingRef.current) return;
|
|
526
|
+
var mouseOffset = helpers.validateEvent(event, context$1, controller);
|
|
527
|
+
if (!mouseOffset) return;
|
|
528
|
+
var hoveredItem = findData(mouseOffset);
|
|
529
|
+
var prevHovered = hoveredItemRef.current;
|
|
530
|
+
if (prevHovered === hoveredItem) return;
|
|
531
|
+
setHovered(hoveredItem);
|
|
532
|
+
if (prevHovered) onMouseOut === null || onMouseOut === void 0 ? void 0 : onMouseOut(prevHovered);
|
|
533
|
+
|
|
534
|
+
if (hoveredItem) {
|
|
535
|
+
var boundingBox = getMarkerBoundingBoxCoordinates(hoveredItem); // boundingBox를 명시적으로 포함 (CanvasData<T>에 boundingBox 속성이 포함됨)
|
|
536
|
+
|
|
537
|
+
var payload = tslib.__assign(tslib.__assign({}, hoveredItem), {
|
|
538
|
+
boundingBox: boundingBox || undefined
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
onMouseOver === null || onMouseOver === void 0 ? void 0 : onMouseOver(payload);
|
|
542
|
+
}
|
|
543
|
+
}; // 마우스가 맵 영역을 벗어날 때 hover 상태 초기화
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
var handleMouseLeave = function () {
|
|
547
|
+
if (disableInteractionRef.current) return;
|
|
548
|
+
var prevHovered = hoveredItemRef.current;
|
|
549
|
+
if (!prevHovered) return;
|
|
550
|
+
hoveredItemRef.current = null;
|
|
551
|
+
controller.setMapCursor('grab');
|
|
552
|
+
|
|
553
|
+
if (topStageZIndex !== undefined) {
|
|
554
|
+
// Base Layer는 모든 마커를 포함하므로 hover 상태 변경 시 재렌더링 불필요
|
|
555
|
+
// Event Layer는 hover 스타일과 선택된 항목을 렌더링하므로 업데이트 필요
|
|
556
|
+
doRenderEvent();
|
|
557
|
+
doRenderTop();
|
|
558
|
+
} else if (renderEvent) {
|
|
559
|
+
// renderEvent가 있을 때는 Event Layer만 업데이트
|
|
560
|
+
// topStageZIndex가 없으면 Base Layer는 정적이므로 재렌더링 불필요 (성능 우선)
|
|
561
|
+
// hover 효과 제거는 Event Layer에서만 처리하면 됨
|
|
562
|
+
doRenderEvent();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
onMouseOut === null || onMouseOut === void 0 ? void 0 : onMouseOut(prevHovered);
|
|
566
|
+
}; // DOM 초기화
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
React.useEffect(function () {
|
|
570
|
+
divElement.style.width = 'fit-content'; // Top Layer용 div도 초기화 (topStageZIndex가 설정된 경우)
|
|
571
|
+
|
|
572
|
+
if (topStageZIndex !== undefined) {
|
|
573
|
+
topDivElement.style.width = 'fit-content';
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return function () {
|
|
577
|
+
if (!markerRef.current) return;
|
|
578
|
+
controller.clearDrawable(markerRef.current);
|
|
579
|
+
markerRef.current = undefined;
|
|
580
|
+
};
|
|
581
|
+
}, []); // 마커 생성/업데이트
|
|
582
|
+
|
|
583
|
+
React.useEffect(function () {
|
|
584
|
+
if (!options) return;
|
|
585
|
+
var bounds = controller.getCurrBounds();
|
|
586
|
+
|
|
587
|
+
var markerOptions = tslib.__assign({
|
|
588
|
+
position: bounds.nw
|
|
589
|
+
}, options);
|
|
590
|
+
|
|
591
|
+
if (markerRef.current) {
|
|
592
|
+
controller.updateMarker(markerRef.current, markerOptions);
|
|
593
|
+
} else {
|
|
594
|
+
markerRef.current = new MapDrawables.Marker(markerOptions);
|
|
595
|
+
markerRef.current.element = divElement;
|
|
596
|
+
controller.createMarker(markerRef.current);
|
|
597
|
+
|
|
598
|
+
if (divElement.parentElement) {
|
|
599
|
+
divElement.parentElement.style.pointerEvents = 'none';
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (options.zIndex !== undefined) {
|
|
603
|
+
controller.setMarkerZIndex(markerRef.current, options.zIndex);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}, [options]); // Konva 초기화 및 이벤트 리스너 등록
|
|
607
|
+
|
|
608
|
+
React.useEffect(function () {
|
|
609
|
+
var mapDiv = controller.mapDivElement;
|
|
610
|
+
var stage = new Konva__default["default"].Stage({
|
|
611
|
+
container: containerRef.current,
|
|
612
|
+
height: mapDiv.offsetHeight,
|
|
613
|
+
width: mapDiv.offsetWidth
|
|
614
|
+
});
|
|
615
|
+
stageRef.current = stage;
|
|
616
|
+
var baseLayer = new Konva__default["default"].Layer({
|
|
617
|
+
listening: false
|
|
618
|
+
});
|
|
619
|
+
var eventLayer = new Konva__default["default"].Layer({
|
|
620
|
+
listening: false
|
|
621
|
+
});
|
|
622
|
+
baseLayerRef.current = baseLayer;
|
|
623
|
+
eventLayerRef.current = eventLayer;
|
|
624
|
+
stage.add(baseLayer);
|
|
625
|
+
stage.add(eventLayer);
|
|
626
|
+
updateViewport(); // ResizeObserver: 맵 크기 변경 감지 (RAF로 debounce)
|
|
627
|
+
|
|
628
|
+
var resizeRafId = null;
|
|
629
|
+
var resizeObserver = new ResizeObserver(function () {
|
|
630
|
+
if (resizeRafId !== null) {
|
|
631
|
+
cancelAnimationFrame(resizeRafId);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
resizeRafId = requestAnimationFrame(function () {
|
|
635
|
+
stage.width(mapDiv.offsetWidth);
|
|
636
|
+
stage.height(mapDiv.offsetHeight);
|
|
637
|
+
sharedMarkerCache.clear();
|
|
638
|
+
boundingBoxCacheRef.current.clear();
|
|
639
|
+
updateViewport();
|
|
640
|
+
renderAllImmediate();
|
|
641
|
+
resizeRafId = null;
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
resizeObserver.observe(mapDiv);
|
|
645
|
+
controller.addEventListener('IDLE', handleIdle);
|
|
646
|
+
controller.addEventListener('ZOOMSTART', handleZoomStart);
|
|
647
|
+
controller.addEventListener('ZOOM_CHANGED', handleZoomEnd);
|
|
648
|
+
controller.addEventListener('CENTER_CHANGED', handleCenterChanged);
|
|
649
|
+
controller.addEventListener('CLICK', handleClick);
|
|
650
|
+
controller.addEventListener('MOUSEMOVE', handleMouseMove);
|
|
651
|
+
controller.addEventListener('DRAGSTART', handleDragStart);
|
|
652
|
+
controller.addEventListener('DRAGEND', handleDragEnd);
|
|
653
|
+
mapDiv.addEventListener('mouseleave', handleMouseLeave);
|
|
654
|
+
renderAllImmediate(); // Context 사용 시 컴포넌트 등록
|
|
655
|
+
|
|
656
|
+
var componentInstance = null;
|
|
657
|
+
|
|
658
|
+
if (context$1) {
|
|
659
|
+
// context를 통한 onMouseOver 호출 시에도 boundingBox를 포함하도록 래핑
|
|
660
|
+
var wrappedOnMouseOver = onMouseOver ? function (payload) {
|
|
661
|
+
var boundingBox = getMarkerBoundingBoxCoordinates(payload); // boundingBox를 명시적으로 포함 (CanvasData<T>에 boundingBox 속성이 포함됨)
|
|
662
|
+
|
|
663
|
+
var enhancedPayload = tslib.__assign(tslib.__assign({}, payload), {
|
|
664
|
+
boundingBox: boundingBox || undefined
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
onMouseOver(enhancedPayload);
|
|
668
|
+
} : undefined;
|
|
669
|
+
componentInstance = {
|
|
670
|
+
findData: findData,
|
|
671
|
+
getSelectedIds: function () {
|
|
672
|
+
return selectedIdsRef.current;
|
|
673
|
+
},
|
|
674
|
+
handleLocalClick: handleLocalClick,
|
|
675
|
+
hitTest: function (offset) {
|
|
676
|
+
return findData(offset) !== null;
|
|
677
|
+
},
|
|
678
|
+
isInteractionDisabled: function () {
|
|
679
|
+
return disableInteractionRef.current;
|
|
680
|
+
},
|
|
681
|
+
onClick: onClick,
|
|
682
|
+
onMouseOut: onMouseOut,
|
|
683
|
+
onMouseOver: wrappedOnMouseOver,
|
|
684
|
+
setHovered: setHovered,
|
|
685
|
+
zIndex: currentZIndex
|
|
686
|
+
};
|
|
687
|
+
context$1.registerComponent(componentInstance);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return function () {
|
|
691
|
+
if (resizeRafId !== null) {
|
|
692
|
+
cancelAnimationFrame(resizeRafId);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
resizeObserver.disconnect();
|
|
696
|
+
controller.removeEventListener('IDLE', handleIdle);
|
|
697
|
+
controller.removeEventListener('ZOOMSTART', handleZoomStart);
|
|
698
|
+
controller.removeEventListener('ZOOM_CHANGED', handleZoomEnd);
|
|
699
|
+
controller.removeEventListener('CENTER_CHANGED', handleCenterChanged);
|
|
700
|
+
controller.removeEventListener('CLICK', handleClick);
|
|
701
|
+
controller.removeEventListener('MOUSEMOVE', handleMouseMove);
|
|
702
|
+
controller.removeEventListener('DRAGSTART', handleDragStart);
|
|
703
|
+
controller.removeEventListener('DRAGEND', handleDragEnd);
|
|
704
|
+
mapDiv.removeEventListener('mouseleave', handleMouseLeave);
|
|
705
|
+
|
|
706
|
+
if (context$1 && componentInstance) {
|
|
707
|
+
context$1.unregisterComponent(componentInstance);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
baseLayer.destroyChildren();
|
|
711
|
+
eventLayer.destroyChildren();
|
|
712
|
+
stage.destroy(); // 공유 캐시는 cleanup 시 clear하지 않음 (다른 레이어가 사용 중일 수 있음)
|
|
713
|
+
|
|
714
|
+
boundingBoxCacheRef.current.clear();
|
|
715
|
+
spatialIndexRef.current.clear();
|
|
716
|
+
};
|
|
717
|
+
}, []); // disableInteraction 동기화
|
|
718
|
+
|
|
719
|
+
React.useEffect(function () {
|
|
720
|
+
disableInteractionRef.current = disableInteraction;
|
|
721
|
+
}, [disableInteraction]); // Top Layer용 별도 캔버스 DOM 생성 (topStageZIndex가 설정된 경우)
|
|
722
|
+
|
|
723
|
+
React.useEffect(function () {
|
|
724
|
+
if (topStageZIndex === undefined) return;
|
|
725
|
+
if (!topContainerRef.current) return;
|
|
726
|
+
var mapDiv = controller.mapDivElement; // Top Layer용 div 요소 설정
|
|
727
|
+
|
|
728
|
+
topDivElement.style.width = 'fit-content';
|
|
729
|
+
var bounds = controller.getCurrBounds();
|
|
730
|
+
|
|
731
|
+
var topMarkerOptions = tslib.__assign({
|
|
732
|
+
position: bounds.nw,
|
|
733
|
+
zIndex: topStageZIndex
|
|
734
|
+
}, options); // Top Layer용 Marker 생성 (zIndex: topStageZIndex로 DOM 마커보다 위에 위치)
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
var topMarker = new MapDrawables.Marker(topMarkerOptions);
|
|
738
|
+
topMarker.element = topDivElement;
|
|
739
|
+
controller.createMarker(topMarker);
|
|
740
|
+
topMarkerRef.current = topMarker;
|
|
741
|
+
|
|
742
|
+
if (topDivElement.parentElement) {
|
|
743
|
+
topDivElement.parentElement.style.pointerEvents = 'none';
|
|
744
|
+
} // Top Layer Marker의 zIndex 명시적으로 설정
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
controller.setMarkerZIndex(topMarker, topStageZIndex); // Top Layer용 Konva Stage 생성
|
|
748
|
+
|
|
749
|
+
var topStage = new Konva__default["default"].Stage({
|
|
750
|
+
container: topContainerRef.current,
|
|
751
|
+
height: mapDiv.offsetHeight,
|
|
752
|
+
width: mapDiv.offsetWidth
|
|
753
|
+
});
|
|
754
|
+
topStageRef.current = topStage;
|
|
755
|
+
var topLayer = new Konva__default["default"].Layer({
|
|
756
|
+
listening: false
|
|
757
|
+
});
|
|
758
|
+
topLayerRef.current = topLayer;
|
|
759
|
+
topStage.add(topLayer); // ResizeObserver: 맵 크기 변경 감지 (RAF로 debounce)
|
|
760
|
+
|
|
761
|
+
var topResizeRafId = null;
|
|
762
|
+
var topResizeObserver = new ResizeObserver(function () {
|
|
763
|
+
if (topResizeRafId !== null) {
|
|
764
|
+
cancelAnimationFrame(topResizeRafId);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
topResizeRafId = requestAnimationFrame(function () {
|
|
768
|
+
topStage.width(mapDiv.offsetWidth);
|
|
769
|
+
topStage.height(mapDiv.offsetHeight);
|
|
770
|
+
doRenderTop();
|
|
771
|
+
topResizeRafId = null;
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
topResizeObserver.observe(mapDiv); // topMarker position 업데이트 함수
|
|
775
|
+
|
|
776
|
+
var updateTopMarkerPosition = function () {
|
|
777
|
+
if (topMarkerRef.current && markerRef.current) {
|
|
778
|
+
var currentBounds = controller.getCurrBounds();
|
|
779
|
+
|
|
780
|
+
var updatedOptions = tslib.__assign(tslib.__assign({}, options), {
|
|
781
|
+
position: currentBounds.nw,
|
|
782
|
+
zIndex: topStageZIndex
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
controller.updateMarker(topMarkerRef.current, updatedOptions); // zIndex 명시적으로 설정
|
|
786
|
+
|
|
787
|
+
controller.setMarkerZIndex(topMarkerRef.current, topStageZIndex);
|
|
788
|
+
}
|
|
789
|
+
}; // 지도 이벤트 핸들러 등록 (topStage 업데이트용)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
var handleTopIdle = function () {
|
|
793
|
+
// 드래그 완료 후 topMarker의 position을 메인 marker와 동일하게 업데이트
|
|
794
|
+
updateTopMarkerPosition(); // topStage 크기 업데이트
|
|
795
|
+
|
|
796
|
+
if (topStageRef.current) {
|
|
797
|
+
topStageRef.current.width(mapDiv.offsetWidth);
|
|
798
|
+
topStageRef.current.height(mapDiv.offsetHeight);
|
|
799
|
+
} // 드래그 완료 후 transform은 handleIdle에서 제거되므로 여기서는 동기화만
|
|
800
|
+
// (handleIdle이 먼저 실행되어 transform을 제거한 후, 여기서 position 업데이트)
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
if (topContainerRef.current && containerRef.current) {
|
|
804
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
doRenderTop();
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
var handleTopZoomStart = function () {
|
|
811
|
+
// 메인 stage의 transform을 topStage에도 동기화
|
|
812
|
+
if (topContainerRef.current && containerRef.current) {
|
|
813
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
doRenderTop();
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
var handleTopZoomEnd = function () {
|
|
820
|
+
// 메인 stage의 transform을 topStage에도 동기화
|
|
821
|
+
if (topContainerRef.current && containerRef.current) {
|
|
822
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
doRenderTop();
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
var handleTopCenterChanged = function () {
|
|
829
|
+
// 드래그 중에는 transform만 동기화 (position 업데이트는 드래그 완료 후에만)
|
|
830
|
+
// 드래그 중에 position을 업데이트하면 transform과 충돌하여 반대 방향으로 이동하는 버그 발생
|
|
831
|
+
if (!draggingRef.current) {
|
|
832
|
+
// 드래그가 아닐 때만 topMarker의 position을 업데이트
|
|
833
|
+
updateTopMarkerPosition();
|
|
834
|
+
} // topStage 크기 업데이트
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
if (topStageRef.current) {
|
|
838
|
+
topStageRef.current.width(mapDiv.offsetWidth);
|
|
839
|
+
topStageRef.current.height(mapDiv.offsetHeight);
|
|
840
|
+
} // 메인 stage의 transform을 topStage에도 동기화 (드래그 중 필수)
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
if (topContainerRef.current && containerRef.current) {
|
|
844
|
+
topContainerRef.current.style.transform = containerRef.current.style.transform || '';
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
doRenderTop();
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
controller.addEventListener('IDLE', handleTopIdle);
|
|
851
|
+
controller.addEventListener('ZOOMSTART', handleTopZoomStart);
|
|
852
|
+
controller.addEventListener('ZOOM_CHANGED', handleTopZoomEnd);
|
|
853
|
+
controller.addEventListener('CENTER_CHANGED', handleTopCenterChanged);
|
|
854
|
+
doRenderTop();
|
|
855
|
+
return function () {
|
|
856
|
+
if (topResizeRafId !== null) {
|
|
857
|
+
cancelAnimationFrame(topResizeRafId);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
topResizeObserver.disconnect();
|
|
861
|
+
controller.removeEventListener('IDLE', handleTopIdle);
|
|
862
|
+
controller.removeEventListener('ZOOMSTART', handleTopZoomStart);
|
|
863
|
+
controller.removeEventListener('ZOOM_CHANGED', handleTopZoomEnd);
|
|
864
|
+
controller.removeEventListener('CENTER_CHANGED', handleTopCenterChanged);
|
|
865
|
+
|
|
866
|
+
if (topLayerRef.current) {
|
|
867
|
+
topLayerRef.current.destroyChildren();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (topStageRef.current) {
|
|
871
|
+
topStageRef.current.destroy();
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (topMarkerRef.current) {
|
|
875
|
+
controller.clearDrawable(topMarkerRef.current);
|
|
876
|
+
topMarkerRef.current = undefined;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
}, [topStageZIndex, renderEvent, options]); // 외부 selectedItems 동기화
|
|
880
|
+
|
|
881
|
+
React.useEffect(function () {
|
|
882
|
+
if (!stageRef.current) return; // externalSelectedItems가 있으면 selectedItem은 무시 (공존 불가)
|
|
883
|
+
|
|
884
|
+
if (externalSelectedItems !== undefined) {
|
|
885
|
+
selectedItemRef.current = undefined;
|
|
886
|
+
hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
|
|
887
|
+
} // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
doRenderEvent();
|
|
891
|
+
|
|
892
|
+
if (topStageZIndex !== undefined) {
|
|
893
|
+
doRenderTop();
|
|
894
|
+
}
|
|
895
|
+
}, [externalSelectedItems]); // 외부 selectedItem 변경 시 Event Layer 리렌더링
|
|
896
|
+
|
|
897
|
+
React.useEffect(function () {
|
|
898
|
+
if (!stageRef.current) return; // externalSelectedItem이 있으면 selectedItems는 무시 (공존 불가)
|
|
899
|
+
|
|
900
|
+
if (externalSelectedItem !== undefined) {
|
|
901
|
+
selectedIdsRef.current.clear();
|
|
902
|
+
selectedItemsMapRef.current.clear();
|
|
903
|
+
selectedItemRef.current = externalSelectedItem;
|
|
904
|
+
} else if (externalSelectedItems === undefined) {
|
|
905
|
+
// 둘 다 없으면 selectedItemRef만 업데이트 (내부 클릭으로 선택한 항목은 유지)
|
|
906
|
+
selectedItemRef.current = externalSelectedItem;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
doRenderEvent();
|
|
910
|
+
|
|
911
|
+
if (topStageZIndex !== undefined) {
|
|
912
|
+
doRenderTop();
|
|
913
|
+
}
|
|
914
|
+
}, [externalSelectedItem, externalSelectedItems]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
|
|
915
|
+
|
|
916
|
+
React.useEffect(function () {
|
|
917
|
+
if (!stageRef.current) return;
|
|
918
|
+
dataRef.current = data;
|
|
919
|
+
|
|
920
|
+
if (containerRef.current) {
|
|
921
|
+
containerRef.current.style.transform = '';
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
prevCenterOffsetRef.current = null;
|
|
925
|
+
accumTranslateRef.current = {
|
|
926
|
+
x: 0,
|
|
927
|
+
y: 0
|
|
928
|
+
};
|
|
929
|
+
sharedMarkerCache.clear();
|
|
930
|
+
boundingBoxCacheRef.current.clear();
|
|
931
|
+
selectedItemsMapRef.current = hooks.syncSelectedItems(data, selectedIdsRef.current, selectedItemsMapRef.current);
|
|
932
|
+
renderAllImmediate();
|
|
933
|
+
}, [data]);
|
|
934
|
+
return React__default["default"].createElement(React__default["default"].Fragment, null, reactDom.createPortal(React__default["default"].createElement("div", {
|
|
935
|
+
ref: containerRef,
|
|
936
|
+
style: {
|
|
937
|
+
height: '100%',
|
|
938
|
+
position: 'absolute',
|
|
939
|
+
width: '100%'
|
|
940
|
+
}
|
|
941
|
+
}), divElement), topStageZIndex !== undefined && reactDom.createPortal(React__default["default"].createElement("div", {
|
|
942
|
+
ref: topContainerRef,
|
|
943
|
+
style: {
|
|
944
|
+
height: '100%',
|
|
945
|
+
position: 'absolute',
|
|
946
|
+
width: '100%'
|
|
947
|
+
}
|
|
948
|
+
}), topDivElement));
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
exports.CanvasProvider = context.CanvasProvider;
|
|
952
|
+
exports.QueueCache = performance.QueueCache;
|
|
953
|
+
exports.SpatialHashGrid = performance.SpatialHashGrid;
|
|
954
|
+
Object.defineProperty(exports, 'CanvasDataType', {
|
|
955
|
+
enumerable: true,
|
|
956
|
+
get: function () { return types.CanvasDataType; }
|
|
957
|
+
});
|
|
958
|
+
Object.defineProperty(exports, 'RenderSource', {
|
|
959
|
+
enumerable: true,
|
|
960
|
+
get: function () { return types.RenderSource; }
|
|
961
|
+
});
|
|
962
|
+
exports["default"] = CanvasMarkerLayer;
|