@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.
Files changed (60) hide show
  1. package/.eslintrc.js +11 -4
  2. package/.vscode/settings.json +32 -9
  3. package/dist/components/mint-map/core/MintMapController.d.ts +1 -0
  4. package/dist/components/mint-map/core/MintMapCore.js +5 -6
  5. package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.d.ts +12 -0
  6. package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.js +962 -0
  7. package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/index.d.ts +4 -0
  8. package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/types.d.ts +280 -0
  9. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.d.ts +17 -0
  10. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.js +624 -0
  11. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/index.d.ts +4 -0
  12. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.d.ts +303 -0
  13. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.js +1091 -0
  14. package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/types.d.ts +284 -0
  15. package/dist/components/mint-map/core/advanced/canvas/CanvasMarker.d.ts +7 -0
  16. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerClaude.js +7 -7
  17. package/dist/components/mint-map/core/advanced/canvas/index.d.ts +0 -1
  18. package/dist/components/mint-map/core/advanced/index.d.ts +4 -2
  19. package/dist/components/mint-map/core/advanced/shared/context.d.ts +44 -0
  20. package/dist/components/mint-map/core/advanced/shared/context.js +230 -0
  21. package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
  22. package/dist/components/mint-map/core/advanced/shared/helpers.js +41 -0
  23. package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
  24. package/dist/components/mint-map/core/advanced/shared/hooks.js +196 -0
  25. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/index.d.ts +5 -2
  26. package/dist/components/mint-map/core/advanced/shared/performance.d.ts +82 -0
  27. package/dist/components/mint-map/core/advanced/shared/performance.js +288 -0
  28. package/dist/components/mint-map/core/advanced/shared/types.d.ts +150 -0
  29. package/dist/components/mint-map/core/advanced/shared/types.js +31 -0
  30. package/dist/components/mint-map/core/advanced/shared/utils.d.ts +173 -0
  31. package/dist/components/mint-map/core/advanced/shared/utils.js +382 -0
  32. package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
  33. package/dist/components/mint-map/core/advanced/shared/viewport.js +52 -0
  34. package/dist/components/mint-map/core/wrapper/MapMarkerWrapper.js +22 -1
  35. package/dist/components/mint-map/google/GoogleMintMapController.d.ts +1 -0
  36. package/dist/components/mint-map/google/GoogleMintMapController.js +13 -8
  37. package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +1 -0
  38. package/dist/components/mint-map/kakao/KakaoMintMapController.js +13 -8
  39. package/dist/components/mint-map/naver/NaverMintMapController.d.ts +3 -0
  40. package/dist/components/mint-map/naver/NaverMintMapController.js +46 -11
  41. package/dist/index.es.js +5605 -4056
  42. package/dist/index.js +47 -27
  43. package/dist/index.umd.js +5621 -4059
  44. package/package.json +1 -1
  45. package/CLAUDE.md +0 -100
  46. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.d.ts +0 -22
  47. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.js +0 -413
  48. package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +0 -11
  49. package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +0 -53
  50. package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.js +0 -1123
  51. package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +0 -3
  52. package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.d.ts +0 -31
  53. package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.js +0 -164
  54. package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.d.ts +0 -161
  55. package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.js +0 -343
  56. package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +0 -131
  57. package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.js +0 -14
  58. package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +0 -31
  59. package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.js +0 -164
  60. 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;