@mint-ui/map 1.1.1 → 1.2.0-test.1

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 (31) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/.vscode/settings.json +11 -0
  3. package/CLAUDE.md +100 -0
  4. package/README.md +190 -0
  5. package/dist/components/mint-map/core/MintMapController.d.ts +1 -1
  6. package/dist/components/mint-map/core/MintMapCore.js +4 -1
  7. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerClaude.d.ts +63 -0
  8. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerClaude.js +1084 -0
  9. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.d.ts +22 -0
  10. package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.js +413 -0
  11. package/dist/components/mint-map/core/advanced/canvas/index.d.ts +2 -0
  12. package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +11 -0
  13. package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +48 -0
  14. package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.d.ts +31 -0
  15. package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.js +159 -0
  16. package/dist/components/mint-map/core/advanced/woongCanvas/shared/index.d.ts +4 -0
  17. package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.d.ts +161 -0
  18. package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +121 -0
  19. package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +23 -0
  20. package/dist/components/mint-map/core/util/geohash.d.ts +2 -0
  21. package/dist/components/mint-map/core/util/geohash.js +125 -0
  22. package/dist/components/mint-map/google/GoogleMintMapController.js +1 -0
  23. package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +58 -0
  24. package/dist/components/mint-map/kakao/KakaoMintMapController.js +1 -0
  25. package/dist/components/mint-map/naver/NaverMintMapController.js +1 -0
  26. package/dist/components/mint-map/types/CommonTypes.d.ts +11 -0
  27. package/dist/index.es.js +1863 -137
  28. package/dist/index.js +4 -0
  29. package/dist/index.umd.js +1862 -134
  30. package/dist/mock.d.ts +133 -0
  31. package/package.json +17 -4
@@ -0,0 +1,1084 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var tslib = require('tslib');
6
+ var React = require('react');
7
+ var MintMapProvider = require('../../provider/MintMapProvider.js');
8
+ var MapDrawables = require('../../../types/MapDrawables.js');
9
+ var MapTypes = require('../../../types/MapTypes.js');
10
+ require('../../../types/MapEventTypes.js');
11
+ var reactDom = require('react-dom');
12
+ var canvasUtil = require('./draw/canvas-util.js');
13
+
14
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
15
+
16
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
17
+
18
+ var defaultPolygonRenderer = function (_a) {
19
+ var context = _a.context,
20
+ offsets = _a.offsets,
21
+ innerOffsets = _a.innerOffsets,
22
+ polygon = _a.polygon,
23
+ isHovered = _a.isHovered,
24
+ defaultBackground = _a.defaultBackground,
25
+ defaultStrokeColor = _a.defaultStrokeColor,
26
+ defaultStrokeWidth = _a.defaultStrokeWidth,
27
+ hoverBackground = _a.hoverBackground,
28
+ hoverStrokeColor = _a.hoverStrokeColor;
29
+ if (offsets.length < 3) return; // 외부 폴리곤 그리기
30
+
31
+ context.beginPath();
32
+ context.moveTo(offsets[0].x, offsets[0].y);
33
+
34
+ for (var i = 1; i < offsets.length; i++) {
35
+ context.lineTo(offsets[i].x, offsets[i].y);
36
+ }
37
+
38
+ context.closePath(); // 내부 폴리곤 (구멍) 그리기
39
+
40
+ if (innerOffsets) {
41
+ innerOffsets.forEach(function (innerOffset) {
42
+ if (innerOffset.length < 3) return;
43
+ context.moveTo(innerOffset[0].x, innerOffset[0].y);
44
+
45
+ for (var i = 1; i < innerOffset.length; i++) {
46
+ context.lineTo(innerOffset[i].x, innerOffset[i].y);
47
+ }
48
+
49
+ context.closePath();
50
+ });
51
+ } // 스타일 설정
52
+
53
+
54
+ var fillColor = isHovered ? hoverBackground : polygon.background || defaultBackground;
55
+ var lineColor = isHovered ? hoverStrokeColor : polygon.strokeColor || defaultStrokeColor;
56
+ var lineWidth = polygon.strokeWidth || defaultStrokeWidth;
57
+ context.fillStyle = fillColor;
58
+ context.strokeStyle = lineColor;
59
+ context.lineWidth = lineWidth;
60
+ context.fill('evenodd');
61
+ context.stroke();
62
+ };
63
+
64
+ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerClaude(_a) {
65
+ var _b = _a.polygons,
66
+ polygons = _b === void 0 ? [] : _b,
67
+ _c = _a.markers,
68
+ markers = _c === void 0 ? [] : _c,
69
+ _d = _a.background,
70
+ background = _d === void 0 ? 'rgba(0, 100, 255, 0.3)' : _d,
71
+ _e = _a.strokeColor,
72
+ strokeColor = _e === void 0 ? 'blue' : _e,
73
+ _f = _a.strokeWidth,
74
+ strokeWidth = _f === void 0 ? 1 : _f,
75
+ onPolygonClick = _a.onPolygonClick,
76
+ onPolygonHover = _a.onPolygonHover,
77
+ onPolygonLeave = _a.onPolygonLeave;
78
+ _a.onMarkerClick;
79
+ var onMarkerHover = _a.onMarkerHover,
80
+ onMarkerLeave = _a.onMarkerLeave,
81
+ onRenderComplete = _a.onRenderComplete,
82
+ markerRenderer = _a.markerRenderer,
83
+ options = tslib.__rest(_a, ["polygons", "markers", "background", "strokeColor", "strokeWidth", "onPolygonClick", "onPolygonHover", "onPolygonLeave", "onMarkerClick", "onMarkerHover", "onMarkerLeave", "onRenderComplete", "markerRenderer"]); // controller
84
+
85
+
86
+ var controller = MintMapProvider.useMintMapController(); // element (CanvasMarker 스타일)
87
+
88
+ var divRef = React.useRef(document.createElement('div'));
89
+ var divElement = divRef.current; // canvas container ref
90
+
91
+ var containerRef = React.useRef(null); // base canvas (모든 폴리곤 - IDLE 시에만 업데이트)
92
+
93
+ var baseCanvasRef = React.useRef(null);
94
+ var baseContextRef = React.useRef(); // hover canvas (호버된 폴리곤만 - 마우스 이동 시 업데이트)
95
+
96
+ var hoverCanvasRef = React.useRef(null);
97
+ var hoverContextRef = React.useRef(); // base marker canvas (기본 상태 마커 - IDLE 시 업데이트)
98
+
99
+ var baseMarkerCanvasRef = React.useRef(null);
100
+ var baseMarkerContextRef = React.useRef(); // interactive marker canvas (호버/클릭 마커 - 마우스 이동 시 업데이트)
101
+
102
+ var interactiveMarkerCanvasRef = React.useRef(null);
103
+ var interactiveMarkerContextRef = React.useRef(); // marker
104
+
105
+ var markerRef = React.useRef(); // interaction states
106
+
107
+ var hoveredPolygonRef = React.useRef(null);
108
+ var clickedPolygonRef = React.useRef(null);
109
+ var hoveredMarkerRef = React.useRef(null);
110
+ var clickedMarkerRef = React.useRef(null); // 🚀 WoongCanvasMarker 방식: 단순한 data ref (geohash 제거)
111
+
112
+ var polygonsRef = React.useRef([]);
113
+ var markersRef = React.useRef([]); // 마커 경계 정보 저장 (hit-test용)
114
+
115
+ var markerBoundsRef = React.useRef(new Map()); // 🚀 드래그 추적용 (WoongCanvasMarker 방식)
116
+
117
+ var draggingRef = React.useRef(false);
118
+ var prevCenterOffsetRef = React.useRef(null);
119
+ var accumTranslateRef = React.useRef({
120
+ x: 0,
121
+ y: 0
122
+ }); // 🚀 Offset 캐싱 (ID 문자열 기반으로 변경하여 메모리 누수 방지)
123
+
124
+ var MAX_CACHE_SIZE = 50000; // 최대 캐시 크기 제한 (3만개 + 여유분)
125
+
126
+ var polygonOffsetCacheRef = React.useRef(new Map());
127
+ var polygonInnerOffsetCacheRef = React.useRef(new Map()); // innerOffsets 캐시 추가
128
+
129
+ var markerOffsetCacheRef = React.useRef(new Map()); // create object (CanvasMarker 스타일)
130
+
131
+ React.useEffect(function () {
132
+ divElement.style.width = 'fit-content';
133
+ divElement.style.pointerEvents = 'none'; // CanvasMarkerHanquf 방식
134
+
135
+ return function () {
136
+ if (markerRef.current) {
137
+ controller.clearDrawable(markerRef.current);
138
+ markerRef.current = undefined;
139
+ }
140
+ };
141
+ }, []); // create / update object (CanvasMarker 스타일)
142
+
143
+ React.useEffect(function () {
144
+ if (options) {
145
+ var bounds = controller.getCurrBounds();
146
+
147
+ var markerOptions = tslib.__assign({
148
+ position: bounds.nw
149
+ }, options);
150
+
151
+ if (markerRef.current) {
152
+ controller.updateMarker(markerRef.current, markerOptions);
153
+ } else {
154
+ markerRef.current = new MapDrawables.Marker(markerOptions);
155
+ markerRef.current.element = divElement;
156
+ controller.createMarker(markerRef.current); // CanvasMarkerHanquf 방식: pointer events 비활성화
157
+
158
+ if (divElement.parentElement) {
159
+ divElement.style.pointerEvents = 'none';
160
+ divElement.parentElement.style.pointerEvents = 'none';
161
+ } // z-index 처리
162
+
163
+
164
+ if (options.zIndex !== undefined) {
165
+ controller.setMarkerZIndex(markerRef.current, options.zIndex);
166
+ }
167
+ }
168
+ }
169
+ }, [options]); // 베이스 폴리곤 렌더링 (모든 폴리곤, 호버 효과 없음)
170
+
171
+ var renderBasePolygons = function () {
172
+ if (!baseContextRef.current || !polygonsRef.current || polygonsRef.current.length === 0) return;
173
+ canvasUtil.clearRect(baseCanvasRef.current, baseContextRef.current);
174
+ var cacheMissCount = 0; // 캐시 미스 카운트
175
+ // 🚀 WoongCanvasMarker 방식: 전체 데이터 순회 (필터링 제거)
176
+
177
+ polygonsRef.current.forEach(function (polygon) {
178
+ if (polygon.visible === false) return; // 🚀 캐시 확인 (ID 기반으로 변경)
179
+
180
+ var offsets = polygonOffsetCacheRef.current.get(polygon.id);
181
+
182
+ if (!offsets) {
183
+ cacheMissCount++;
184
+ offsets = polygon.positions.map(function (pos) {
185
+ return controller.positionToOffset(pos);
186
+ }); // LRU: 캐시 크기 제한
187
+
188
+ if (polygonOffsetCacheRef.current.size >= MAX_CACHE_SIZE) {
189
+ var firstKey = polygonOffsetCacheRef.current.keys().next().value;
190
+
191
+ if (firstKey) {
192
+ polygonOffsetCacheRef.current.delete(firstKey);
193
+ }
194
+ }
195
+
196
+ polygonOffsetCacheRef.current.set(polygon.id, offsets);
197
+ }
198
+
199
+ var innerOffsets;
200
+
201
+ if (polygon.innerPositions) {
202
+ // innerOffsets도 캐시 사용
203
+ innerOffsets = polygonInnerOffsetCacheRef.current.get(polygon.id);
204
+
205
+ if (!innerOffsets) {
206
+ innerOffsets = polygon.innerPositions.map(function (innerPositions) {
207
+ return innerPositions.map(function (pos) {
208
+ return controller.positionToOffset(pos);
209
+ });
210
+ });
211
+ polygonInnerOffsetCacheRef.current.set(polygon.id, innerOffsets);
212
+ }
213
+ } // 렌더러 호출 (항상 기본 스타일)
214
+
215
+
216
+ defaultPolygonRenderer({
217
+ context: baseContextRef.current,
218
+ offsets: offsets,
219
+ innerOffsets: innerOffsets,
220
+ polygon: polygon,
221
+ isHovered: false,
222
+ defaultBackground: background,
223
+ defaultStrokeColor: strokeColor,
224
+ defaultStrokeWidth: strokeWidth,
225
+ hoverBackground: background,
226
+ hoverStrokeColor: strokeColor
227
+ });
228
+ }); // 요약 로그만 출력 (캐시 미스가 있을 때만)
229
+
230
+ if (cacheMissCount > 0) {
231
+ console.log("\uD83C\uDFA8 [renderBasePolygons] \uCE90\uC2DC \uBBF8\uC2A4: ".concat(cacheMissCount, "\uAC1C, \uD604\uC7AC \uCE90\uC2DC \uD06C\uAE30: ").concat(polygonOffsetCacheRef.current.size));
232
+ } // 렌더링 완료 콜백 호출
233
+
234
+
235
+ if (onRenderComplete) {
236
+ onRenderComplete();
237
+ }
238
+ }; // 베이스 마커 렌더링 (기본 상태 마커만, IDLE 시 업데이트)
239
+
240
+
241
+ var renderBaseMarkers = function () {
242
+ if (!baseMarkerContextRef.current || !markerRenderer) return;
243
+ if (!markersRef.current || markersRef.current.length === 0) return;
244
+ canvasUtil.clearRect(baseMarkerCanvasRef.current, baseMarkerContextRef.current); // 마커 경계 정보 초기화
245
+
246
+ markerBoundsRef.current.clear(); // 🚀 WoongCanvasMarker 방식: 전체 데이터 순회 (필터링 제거)
247
+
248
+ markersRef.current.forEach(function (marker) {
249
+ var _a, _b;
250
+
251
+ if (marker.visible === false) return; // 호버/클릭 상태인 마커는 스킵 (인터랙션 레이어에서 렌더링)
252
+
253
+ if (marker.id === ((_a = hoveredMarkerRef.current) === null || _a === void 0 ? void 0 : _a.id)) return;
254
+ if (marker.id === ((_b = clickedMarkerRef.current) === null || _b === void 0 ? void 0 : _b.id)) return; // 🚀 캐시 확인 (ID 기반으로 변경)
255
+
256
+ var offset = markerOffsetCacheRef.current.get(marker.id);
257
+
258
+ if (!offset) {
259
+ offset = controller.positionToOffset(new MapTypes.Position(marker.lat, marker.lng));
260
+ markerOffsetCacheRef.current.set(marker.id, offset);
261
+ } // 커스텀 마커 렌더러 호출 (기본 상태)
262
+
263
+
264
+ var markerBounds = markerRenderer({
265
+ context: baseMarkerContextRef.current,
266
+ x: offset.x,
267
+ y: offset.y,
268
+ marker: marker,
269
+ isHovered: false,
270
+ isClicked: false
271
+ }); // 마커 경계 정보 저장 (hit-test용)
272
+
273
+ markerBoundsRef.current.set(marker.id, {
274
+ marker: marker,
275
+ bounds: markerBounds
276
+ });
277
+ });
278
+ }; // 인터랙션 마커 렌더링 (호버/클릭 마커만, 마우스 이동 시 업데이트)
279
+
280
+
281
+ var renderInteractionMarkers = function () {
282
+ if (!interactiveMarkerContextRef.current || !markerRenderer) return;
283
+ canvasUtil.clearRect(interactiveMarkerCanvasRef.current, interactiveMarkerContextRef.current); // 클릭된 마커 먼저 그리기 (하단 레이어)
284
+
285
+ if (clickedMarkerRef.current) {
286
+ var marker = clickedMarkerRef.current;
287
+
288
+ if (marker.visible !== false) {
289
+ // 🚀 캐시 확인 (ID 기반으로 변경)
290
+ var offset = markerOffsetCacheRef.current.get(marker.id);
291
+
292
+ if (!offset) {
293
+ offset = controller.positionToOffset(new MapTypes.Position(marker.lat, marker.lng));
294
+ markerOffsetCacheRef.current.set(marker.id, offset);
295
+ }
296
+
297
+ var markerBounds = markerRenderer({
298
+ context: interactiveMarkerContextRef.current,
299
+ x: offset.x,
300
+ y: offset.y,
301
+ marker: marker,
302
+ isHovered: false,
303
+ isClicked: true
304
+ }); // 마커 경계 정보 업데이트
305
+
306
+ markerBoundsRef.current.set(marker.id, {
307
+ marker: marker,
308
+ bounds: markerBounds
309
+ });
310
+ }
311
+ } // 호버된 마커 나중에 그리기 (최상단 레이어)
312
+
313
+
314
+ if (hoveredMarkerRef.current) {
315
+ var marker = hoveredMarkerRef.current;
316
+
317
+ if (marker.visible !== false) {
318
+ // 🚀 캐시 확인 (ID 기반으로 변경)
319
+ var offset = markerOffsetCacheRef.current.get(marker.id);
320
+
321
+ if (!offset) {
322
+ offset = controller.positionToOffset(new MapTypes.Position(marker.lat, marker.lng));
323
+ markerOffsetCacheRef.current.set(marker.id, offset);
324
+ }
325
+
326
+ var markerBounds = markerRenderer({
327
+ context: interactiveMarkerContextRef.current,
328
+ x: offset.x,
329
+ y: offset.y,
330
+ marker: marker,
331
+ isHovered: true,
332
+ isClicked: false
333
+ }); // 마커 경계 정보 업데이트
334
+
335
+ markerBoundsRef.current.set(marker.id, {
336
+ marker: marker,
337
+ bounds: markerBounds
338
+ });
339
+ }
340
+ }
341
+ }; // 인터랙션 폴리곤 렌더링 (호버 + 클릭)
342
+
343
+
344
+ var renderInteractionPolygons = function () {
345
+ if (!hoverContextRef.current) return; // 인터랙션 캔버스 클리어
346
+
347
+ canvasUtil.clearRect(hoverCanvasRef.current, hoverContextRef.current); // 1. 클릭된 폴리곤 먼저 그리기 (하단 레이어)
348
+
349
+ if (clickedPolygonRef.current) {
350
+ var polygon = clickedPolygonRef.current;
351
+
352
+ if (polygon.visible !== false) {
353
+ // 🚀 캐시 확인 (ID 기반으로 변경)
354
+ var offsets = polygonOffsetCacheRef.current.get(polygon.id);
355
+
356
+ if (!offsets) {
357
+ offsets = polygon.positions.map(function (pos) {
358
+ return controller.positionToOffset(pos);
359
+ });
360
+ polygonOffsetCacheRef.current.set(polygon.id, offsets);
361
+ }
362
+
363
+ var innerOffsets = void 0;
364
+
365
+ if (polygon.innerPositions) {
366
+ // innerOffsets도 캐시 사용
367
+ innerOffsets = polygonInnerOffsetCacheRef.current.get(polygon.id);
368
+
369
+ if (!innerOffsets) {
370
+ innerOffsets = polygon.innerPositions.map(function (innerPositions) {
371
+ return innerPositions.map(function (pos) {
372
+ return controller.positionToOffset(pos);
373
+ });
374
+ });
375
+ polygonInnerOffsetCacheRef.current.set(polygon.id, innerOffsets);
376
+ }
377
+ } // 클릭 스타일 (노란색)
378
+
379
+
380
+ defaultPolygonRenderer({
381
+ context: hoverContextRef.current,
382
+ offsets: offsets,
383
+ innerOffsets: innerOffsets,
384
+ polygon: polygon,
385
+ isHovered: true,
386
+ defaultBackground: background,
387
+ defaultStrokeColor: strokeColor,
388
+ defaultStrokeWidth: strokeWidth,
389
+ hoverBackground: 'rgba(255, 200, 0, 0.6)',
390
+ hoverStrokeColor: 'orange' // 주황색 테두리
391
+
392
+ });
393
+ }
394
+ } // 2. 호버된 폴리곤 나중에 그리기 (최상단 레이어)
395
+
396
+
397
+ if (hoveredPolygonRef.current) {
398
+ var polygon = hoveredPolygonRef.current;
399
+
400
+ if (polygon.visible !== false) {
401
+ // 🚀 캐시 확인 (ID 기반으로 변경)
402
+ var offsets = polygonOffsetCacheRef.current.get(polygon.id);
403
+
404
+ if (!offsets) {
405
+ offsets = polygon.positions.map(function (pos) {
406
+ return controller.positionToOffset(pos);
407
+ });
408
+ polygonOffsetCacheRef.current.set(polygon.id, offsets);
409
+ }
410
+
411
+ var innerOffsets = void 0;
412
+
413
+ if (polygon.innerPositions) {
414
+ // innerOffsets도 캐시 사용
415
+ innerOffsets = polygonInnerOffsetCacheRef.current.get(polygon.id);
416
+
417
+ if (!innerOffsets) {
418
+ innerOffsets = polygon.innerPositions.map(function (innerPositions) {
419
+ return innerPositions.map(function (pos) {
420
+ return controller.positionToOffset(pos);
421
+ });
422
+ });
423
+ polygonInnerOffsetCacheRef.current.set(polygon.id, innerOffsets);
424
+ }
425
+ } // 호버 스타일 (빨간색)
426
+
427
+
428
+ defaultPolygonRenderer({
429
+ context: hoverContextRef.current,
430
+ offsets: offsets,
431
+ innerOffsets: innerOffsets,
432
+ polygon: polygon,
433
+ isHovered: true,
434
+ defaultBackground: background,
435
+ defaultStrokeColor: strokeColor,
436
+ defaultStrokeWidth: strokeWidth,
437
+ hoverBackground: 'rgba(255, 0, 0, 0.5)',
438
+ hoverStrokeColor: 'red' // 빨간색 테두리
439
+
440
+ });
441
+ }
442
+ }
443
+ }; // point-in-polygon 테스트 (ray casting algorithm)
444
+
445
+
446
+ var pointInPolygon = function (x, y, polygon) {
447
+ if (polygon.length < 3) return false;
448
+ var inside = false;
449
+
450
+ for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
451
+ var xi = polygon[i].x,
452
+ yi = polygon[i].y;
453
+ var xj = polygon[j].x,
454
+ yj = polygon[j].y;
455
+
456
+ if (yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
457
+ inside = !inside;
458
+ }
459
+ }
460
+
461
+ return inside;
462
+ }; // 히트 테스트 함수 (마커 우선)
463
+
464
+
465
+ var findPolygonAt = function (x, y) {
466
+ var _a, _b;
467
+
468
+ if (!polygonsRef.current || polygonsRef.current.length === 0) return null; // 1. 마커 먼저 체크 (우선순위: hover 중 → clicked 중 → 배열 역순)
469
+
470
+ if (markersRef.current) {
471
+ // 🎯 최우선: 현재 hover 중인 마커가 있으면 그것부터 체크
472
+ if (hoveredMarkerRef.current && hoveredMarkerRef.current.visible !== false) {
473
+ var markerData = markerBoundsRef.current.get(hoveredMarkerRef.current.id);
474
+
475
+ if (markerData) {
476
+ var bounds = markerData.bounds;
477
+
478
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
479
+ return null; // 마커 히트 시 null 반환 (폴리곤이 아니므로)
480
+ }
481
+ }
482
+ } // 🎯 두 번째: hover 중인 마커에 히트 안 되고, clicked 마커가 있으면 체크
483
+
484
+
485
+ if (clickedMarkerRef.current && clickedMarkerRef.current.visible !== false) {
486
+ var markerData = markerBoundsRef.current.get(clickedMarkerRef.current.id);
487
+
488
+ if (markerData) {
489
+ var bounds = markerData.bounds;
490
+
491
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
492
+ return null; // 마커 히트 시 null 반환 (폴리곤이 아니므로)
493
+ }
494
+ }
495
+ } // 🎯 세 번째: 나머지 마커들을 역순으로 체크 (위에 그려진 것부터)
496
+
497
+
498
+ for (var i = markersRef.current.length - 1; i >= 0; i--) {
499
+ var marker = markersRef.current[i];
500
+ if (marker.visible === false) continue; // 이미 체크한 hover/clicked 마커는 스킵
501
+
502
+ if (marker.id === ((_a = hoveredMarkerRef.current) === null || _a === void 0 ? void 0 : _a.id)) continue;
503
+ if (marker.id === ((_b = clickedMarkerRef.current) === null || _b === void 0 ? void 0 : _b.id)) continue;
504
+ var markerData = markerBoundsRef.current.get(marker.id);
505
+ if (!markerData) continue;
506
+ var bounds = markerData.bounds; // 사각형 경계 체크
507
+
508
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
509
+ // 마커 히트 시 null 반환 (폴리곤이 아니므로)
510
+ return null;
511
+ }
512
+ }
513
+ } // 2. 마커에 히트 안 되면 폴리곤 체크 - 🚀 전체 폴리곤 순회
514
+ // 역순으로 체크 (위에 그려진 것부터)
515
+
516
+
517
+ for (var i = polygonsRef.current.length - 1; i >= 0; i--) {
518
+ var polygon = polygonsRef.current[i];
519
+ if (polygon.visible === false) continue; // 🚀 캐시 확인 (ID 기반으로 변경)
520
+
521
+ var offsets = polygonOffsetCacheRef.current.get(polygon.id);
522
+
523
+ if (!offsets) {
524
+ offsets = polygon.positions.map(function (pos) {
525
+ return controller.positionToOffset(pos);
526
+ });
527
+ polygonOffsetCacheRef.current.set(polygon.id, offsets);
528
+ } // 외부 폴리곤 체크
529
+
530
+
531
+ if (pointInPolygon(x, y, offsets)) {
532
+ // 도넛 폴리곤인 경우 내부 구멍도 체크
533
+ if (polygon.innerPositions && polygon.innerPositions.length > 0) {
534
+ var innerOffsets = polygonInnerOffsetCacheRef.current.get(polygon.id);
535
+
536
+ if (!innerOffsets) {
537
+ innerOffsets = polygon.innerPositions.map(function (innerPositions) {
538
+ return innerPositions.map(function (pos) {
539
+ return controller.positionToOffset(pos);
540
+ });
541
+ });
542
+ polygonInnerOffsetCacheRef.current.set(polygon.id, innerOffsets);
543
+ } // 내부 구멍 중 하나라도 포함되면 이 폴리곤은 스킵하고 다음 폴리곤 체크
544
+
545
+
546
+ var isInHole = false;
547
+
548
+ for (var _i = 0, innerOffsets_1 = innerOffsets; _i < innerOffsets_1.length; _i++) {
549
+ var innerOffset = innerOffsets_1[_i];
550
+
551
+ if (pointInPolygon(x, y, innerOffset)) {
552
+ isInHole = true;
553
+ break;
554
+ }
555
+ } // 구멍 안에 있으면 이 폴리곤은 스킵하고 다음 폴리곤 계속 검색
556
+
557
+
558
+ if (isInHole) {
559
+ continue;
560
+ }
561
+ } // 외부 폴리곤 안에 있고, 구멍 안에는 없으면 히트
562
+
563
+
564
+ return polygon;
565
+ }
566
+ }
567
+
568
+ return null;
569
+ }; // IDLE 이벤트 핸들러 (WoongCanvasMarker 최적화 적용)
570
+
571
+
572
+ var handleIdle = function () {
573
+ var beforeCacheSize = polygonOffsetCacheRef.current.size;
574
+ canvasUtil.clearRect(baseCanvasRef.current, baseContextRef.current); // 표시 복구 (드래그/줌 중 숨김 처리 복원)
575
+
576
+ containerRef.current && (containerRef.current.style.visibility = '');
577
+ draggingRef.current = false;
578
+ prevCenterOffsetRef.current = null;
579
+ accumTranslateRef.current = {
580
+ x: 0,
581
+ y: 0
582
+ };
583
+
584
+ if (containerRef.current) {
585
+ containerRef.current.style.transform = '';
586
+ } // 🚀 캐시 무효화 (지도 이동/줌으로 좌표가 바뀌므로 전체 clear 필요)
587
+
588
+
589
+ polygonOffsetCacheRef.current.clear();
590
+ polygonInnerOffsetCacheRef.current.clear();
591
+ markerOffsetCacheRef.current.clear();
592
+ markerBoundsRef.current.clear(); // 캐시 clear 로그
593
+
594
+ if (beforeCacheSize > 0) {
595
+ console.log("\uD83E\uDDF9 [IDLE] \uCE90\uC2DC \uCD08\uAE30\uD654: ".concat(beforeCacheSize, "\uAC1C \uC81C\uAC70 (\uC88C\uD45C \uAC31\uC2E0 \uD544\uC694)"));
596
+ } // 마커 이동
597
+
598
+
599
+ var bounds = controller.getCurrBounds();
600
+
601
+ var markerOptions = tslib.__assign({
602
+ position: bounds.nw
603
+ }, options);
604
+
605
+ markerRef.current && controller.updateMarker(markerRef.current, markerOptions); // 🚀 폴리곤 렌더링 (독립)
606
+
607
+ if (polygonsRef.current.length > 0) {
608
+ renderBasePolygons();
609
+ renderInteractionPolygons();
610
+ } // 🚀 마커 렌더링 (독립)
611
+
612
+
613
+ if (markersRef.current.length > 0 && markerRenderer) {
614
+ renderBaseMarkers();
615
+ renderInteractionMarkers();
616
+ }
617
+ };
618
+
619
+ var handleZoomStart = function () {
620
+ containerRef.current && (containerRef.current.style.visibility = 'hidden');
621
+ };
622
+
623
+ var handleZoomEnd = function () {
624
+ containerRef.current && (containerRef.current.style.visibility = '');
625
+ }; // 지도 이벤트를 화면 좌표로 변환 (CanvasMarkerHanquf 방식)
626
+
627
+
628
+ var parseEventToOffset = function (event) {
629
+ var mapDiv = controller.mapDivElement;
630
+ var rect = mapDiv.getBoundingClientRect();
631
+ return {
632
+ x: event.clientX - rect.left,
633
+ y: event.clientY - rect.top
634
+ };
635
+ }; // 🚀 지도 클릭 이벤트 핸들러 (WoongCanvasMarker 방식 - controller.addEventListener)
636
+
637
+
638
+ var handleMapClick = function (event) {
639
+ var _a, _b, _c;
640
+
641
+ var clickedOffset = controller.positionToOffset(event.param.position); // 1. 마커 먼저 체크 (우선순위: hover 중 → clicked 중 → 배열 역순)
642
+
643
+ var hitMarker = null;
644
+
645
+ if (markersRef.current) {
646
+ // 🎯 최우선: 현재 hover 중인 마커가 있으면 그것부터 체크
647
+ if (hoveredMarkerRef.current && hoveredMarkerRef.current.visible !== false) {
648
+ var markerData = markerBoundsRef.current.get(hoveredMarkerRef.current.id);
649
+
650
+ if (markerData) {
651
+ var bounds = markerData.bounds;
652
+
653
+ if (clickedOffset.x >= bounds.x && clickedOffset.x <= bounds.x + bounds.width && clickedOffset.y >= bounds.y && clickedOffset.y <= bounds.y + bounds.height) {
654
+ hitMarker = hoveredMarkerRef.current;
655
+ }
656
+ }
657
+ } // 🎯 두 번째: hover 중인 마커에 히트 안 되고, clicked 마커가 있으면 체크
658
+
659
+
660
+ if (!hitMarker && clickedMarkerRef.current && clickedMarkerRef.current.visible !== false) {
661
+ var markerData = markerBoundsRef.current.get(clickedMarkerRef.current.id);
662
+
663
+ if (markerData) {
664
+ var bounds = markerData.bounds;
665
+
666
+ if (clickedOffset.x >= bounds.x && clickedOffset.x <= bounds.x + bounds.width && clickedOffset.y >= bounds.y && clickedOffset.y <= bounds.y + bounds.height) {
667
+ hitMarker = clickedMarkerRef.current;
668
+ }
669
+ }
670
+ } // 🎯 세 번째: 나머지 마커들을 역순으로 체크 (위에 그려진 것부터)
671
+
672
+
673
+ if (!hitMarker) {
674
+ for (var i = markersRef.current.length - 1; i >= 0; i--) {
675
+ var marker = markersRef.current[i];
676
+ if (marker.visible === false) continue; // 이미 체크한 hover/clicked 마커는 스킵
677
+
678
+ if (marker.id === ((_a = hoveredMarkerRef.current) === null || _a === void 0 ? void 0 : _a.id)) continue;
679
+ if (marker.id === ((_b = clickedMarkerRef.current) === null || _b === void 0 ? void 0 : _b.id)) continue;
680
+ var markerData = markerBoundsRef.current.get(marker.id);
681
+ if (!markerData) continue;
682
+ var bounds = markerData.bounds;
683
+
684
+ if (clickedOffset.x >= bounds.x && clickedOffset.x <= bounds.x + bounds.width && clickedOffset.y >= bounds.y && clickedOffset.y <= bounds.y + bounds.height) {
685
+ hitMarker = marker;
686
+ break;
687
+ }
688
+ }
689
+ }
690
+ } // 2. 마커에 히트 안 되면 폴리곤 체크
691
+
692
+
693
+ var hitPolygon = findPolygonAt(clickedOffset.x, clickedOffset.y); // 마커 클릭 해제
694
+
695
+ if (clickedMarkerRef.current) {
696
+ clickedMarkerRef.current = null;
697
+ renderInteractionMarkers();
698
+ }
699
+
700
+ if (hitPolygon) {
701
+ // 같은 폴리곤 다시 클릭하면 선택 해제 (토글)
702
+ if (((_c = clickedPolygonRef.current) === null || _c === void 0 ? void 0 : _c.id) === hitPolygon.id) {
703
+ console.log('🖱️ Polygon deselected:', hitPolygon.id);
704
+ clickedPolygonRef.current = null; // 선택 해제 시에도 콜백 호출 (null 전달)
705
+
706
+ if (onPolygonClick) {
707
+ onPolygonClick(null);
708
+ }
709
+ } else {
710
+ console.log('🖱️ Polygon selected:', hitPolygon.id);
711
+ clickedPolygonRef.current = hitPolygon; // 선택 시 콜백 호출
712
+
713
+ if (onPolygonClick) {
714
+ onPolygonClick(hitPolygon);
715
+ }
716
+ } // 인터랙션 레이어 재렌더링
717
+
718
+
719
+ renderInteractionPolygons();
720
+ }
721
+ }; // 마우스 이벤트 RAF 스로틀링용
722
+
723
+
724
+ var rafIdRef = React.useRef(); // 마우스 이벤트 핸들러 (requestAnimationFrame으로 최적화)
725
+
726
+ var handleMouseMove = function (e) {
727
+ // 🚀 드래그 중이면 호버 처리 스킵 (WoongCanvasMarker 방식)
728
+ if (draggingRef.current) return; // 이미 RAF가 대기 중이면 스킵
729
+
730
+ if (rafIdRef.current) return;
731
+ rafIdRef.current = requestAnimationFrame(function () {
732
+ var _a, _b, _c;
733
+
734
+ rafIdRef.current = undefined;
735
+
736
+ var _d = parseEventToOffset(e),
737
+ x = _d.x,
738
+ y = _d.y; // 1. 마커 먼저 체크 (우선순위: hover 중 → clicked 중 → 배열 역순)
739
+
740
+
741
+ var hitMarker = null;
742
+
743
+ if (markersRef.current) {
744
+ // 🎯 최우선: 현재 hover 중인 마커가 있으면 그것부터 체크
745
+ if (hoveredMarkerRef.current && hoveredMarkerRef.current.visible !== false) {
746
+ var markerData = markerBoundsRef.current.get(hoveredMarkerRef.current.id);
747
+
748
+ if (markerData) {
749
+ var bounds = markerData.bounds;
750
+
751
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
752
+ hitMarker = hoveredMarkerRef.current;
753
+ }
754
+ }
755
+ } // 🎯 두 번째: hover 중인 마커에 히트 안 되고, clicked 마커가 있으면 체크
756
+
757
+
758
+ if (!hitMarker && clickedMarkerRef.current && clickedMarkerRef.current.visible !== false) {
759
+ var markerData = markerBoundsRef.current.get(clickedMarkerRef.current.id);
760
+
761
+ if (markerData) {
762
+ var bounds = markerData.bounds;
763
+
764
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
765
+ hitMarker = clickedMarkerRef.current;
766
+ }
767
+ }
768
+ } // 🎯 세 번째: 나머지 마커들을 역순으로 체크 (위에 그려진 것부터)
769
+
770
+
771
+ if (!hitMarker) {
772
+ for (var i = markersRef.current.length - 1; i >= 0; i--) {
773
+ var marker = markersRef.current[i];
774
+ if (marker.visible === false) continue; // 이미 체크한 hover/clicked 마커는 스킵
775
+
776
+ if (marker.id === ((_a = hoveredMarkerRef.current) === null || _a === void 0 ? void 0 : _a.id)) continue;
777
+ if (marker.id === ((_b = clickedMarkerRef.current) === null || _b === void 0 ? void 0 : _b.id)) continue;
778
+ var markerData = markerBoundsRef.current.get(marker.id);
779
+ if (!markerData) continue;
780
+ var bounds = markerData.bounds;
781
+
782
+ if (x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height) {
783
+ hitMarker = marker;
784
+ break;
785
+ }
786
+ }
787
+ }
788
+ } // 마커가 호버되었을 때
789
+
790
+
791
+ if (hitMarker) {
792
+ // 폴리곤 호버 해제
793
+ if (hoveredPolygonRef.current) {
794
+ hoveredPolygonRef.current = null;
795
+
796
+ if (onPolygonLeave) {
797
+ onPolygonLeave();
798
+ }
799
+ } // 마커 호버 상태 업데이트
800
+
801
+
802
+ if (hitMarker.id !== ((_c = hoveredMarkerRef.current) === null || _c === void 0 ? void 0 : _c.id)) {
803
+ hoveredMarkerRef.current = hitMarker;
804
+ console.log('🎯 Marker hovered:', hitMarker.id);
805
+
806
+ if (onMarkerHover) {
807
+ onMarkerHover(hitMarker);
808
+ }
809
+
810
+ renderInteractionMarkers();
811
+ renderInteractionPolygons();
812
+ }
813
+
814
+ return;
815
+ } // 2. 마커에 히트 안 되면 폴리곤 체크
816
+
817
+
818
+ var hitPolygon = findPolygonAt(x, y); // 마커 호버 해제
819
+
820
+ if (hoveredMarkerRef.current) {
821
+ if (onMarkerLeave) {
822
+ onMarkerLeave();
823
+ }
824
+
825
+ hoveredMarkerRef.current = null;
826
+ renderInteractionMarkers();
827
+ }
828
+
829
+ if (hitPolygon !== hoveredPolygonRef.current) {
830
+ // leave 이벤트
831
+ if (hoveredPolygonRef.current && onPolygonLeave) {
832
+ onPolygonLeave();
833
+ } // hover 이벤트
834
+
835
+
836
+ hoveredPolygonRef.current = hitPolygon;
837
+
838
+ if (hitPolygon && onPolygonHover) {
839
+ onPolygonHover(hitPolygon);
840
+ } // 인터랙션 레이어 재렌더링 (호버 + 클릭)
841
+
842
+
843
+ renderInteractionPolygons();
844
+ }
845
+ });
846
+ };
847
+
848
+ var handleCenterChanged = function () {
849
+ // 드래그 중에는 리렌더 대신 transform 으로만 추종
850
+ var center = controller.getCurrBounds().getCenter();
851
+ var curr = controller.positionToOffset(center);
852
+ var prev = prevCenterOffsetRef.current;
853
+
854
+ if (!prev) {
855
+ prevCenterOffsetRef.current = {
856
+ x: curr.x,
857
+ y: curr.y
858
+ };
859
+ draggingRef.current = true;
860
+ return;
861
+ }
862
+
863
+ var dx = prev.x - curr.x;
864
+ var dy = prev.y - curr.y;
865
+ accumTranslateRef.current = {
866
+ x: accumTranslateRef.current.x + dx,
867
+ y: accumTranslateRef.current.y + dy
868
+ };
869
+ prevCenterOffsetRef.current = {
870
+ x: curr.x,
871
+ y: curr.y
872
+ };
873
+ draggingRef.current = true;
874
+
875
+ if (containerRef.current) {
876
+ containerRef.current.style.transform = "translate(".concat(accumTranslateRef.current.x, "px, ").concat(accumTranslateRef.current.y, "px)");
877
+ }
878
+ };
879
+
880
+ var handleMouseLeave = function () {
881
+ // RAF 취소
882
+ if (rafIdRef.current) {
883
+ cancelAnimationFrame(rafIdRef.current);
884
+ rafIdRef.current = undefined;
885
+ }
886
+
887
+ if (hoveredPolygonRef.current && onPolygonLeave) {
888
+ onPolygonLeave();
889
+ }
890
+
891
+ hoveredPolygonRef.current = null;
892
+ hoveredMarkerRef.current = null;
893
+ renderInteractionMarkers();
894
+ renderInteractionPolygons();
895
+ }; // 🚀 initialize - 폴리곤 캔버스 초기화 (독립)
896
+
897
+
898
+ React.useEffect(function () {
899
+ if (baseCanvasRef.current && hoverCanvasRef.current && containerRef.current) {
900
+ // 2d 컨텍스트 설정 (폴리곤 캔버스만)
901
+ baseContextRef.current = baseCanvasRef.current.getContext('2d');
902
+ hoverContextRef.current = hoverCanvasRef.current.getContext('2d'); // 스케일링 (폴리곤 캔버스만)
903
+
904
+ if (baseContextRef.current) {
905
+ canvasUtil.scaleCanvas(controller, baseCanvasRef.current, baseContextRef.current);
906
+ }
907
+
908
+ if (hoverContextRef.current) {
909
+ canvasUtil.scaleCanvas(controller, hoverCanvasRef.current, hoverContextRef.current);
910
+ } // 폴리곤 데이터가 있으면 렌더링
911
+
912
+
913
+ if (polygonsRef.current.length > 0) {
914
+ renderBasePolygons();
915
+ renderInteractionPolygons();
916
+ }
917
+ }
918
+ }, []); // 🚀 initialize - 마커 캔버스 초기화 (독립)
919
+
920
+ React.useEffect(function () {
921
+ if (baseMarkerCanvasRef.current && interactiveMarkerCanvasRef.current) {
922
+ // 2d 컨텍스트 설정 (마커 캔버스만)
923
+ baseMarkerContextRef.current = baseMarkerCanvasRef.current.getContext('2d');
924
+ interactiveMarkerContextRef.current = interactiveMarkerCanvasRef.current.getContext('2d'); // 스케일링 (마커 캔버스만)
925
+
926
+ if (baseMarkerContextRef.current) {
927
+ canvasUtil.scaleCanvas(controller, baseMarkerCanvasRef.current, baseMarkerContextRef.current);
928
+ }
929
+
930
+ if (interactiveMarkerContextRef.current) {
931
+ canvasUtil.scaleCanvas(controller, interactiveMarkerCanvasRef.current, interactiveMarkerContextRef.current);
932
+ } // 마커 데이터가 있으면 렌더링
933
+
934
+
935
+ if (markersRef.current.length > 0 && markerRenderer) {
936
+ renderBaseMarkers();
937
+ renderInteractionMarkers();
938
+ }
939
+ }
940
+ }, [markerRenderer]); // 🚀 공통 초기화 - 이벤트 등록 및 리사이즈
941
+
942
+ React.useEffect(function () {
943
+ var resizeObserver;
944
+
945
+ if (containerRef.current) {
946
+ // 리사이즈 처리
947
+ resizeObserver = new ResizeObserver(function () {
948
+ // 폴리곤 캔버스 리사이즈
949
+ if (baseCanvasRef.current && baseContextRef.current) {
950
+ canvasUtil.clearRect(baseCanvasRef.current, baseContextRef.current);
951
+ canvasUtil.scaleCanvas(controller, baseCanvasRef.current, baseContextRef.current);
952
+ }
953
+
954
+ if (hoverCanvasRef.current && hoverContextRef.current) {
955
+ canvasUtil.clearRect(hoverCanvasRef.current, hoverContextRef.current);
956
+ canvasUtil.scaleCanvas(controller, hoverCanvasRef.current, hoverContextRef.current);
957
+ } // 마커 캔버스 리사이즈
958
+
959
+
960
+ if (baseMarkerCanvasRef.current && baseMarkerContextRef.current) {
961
+ canvasUtil.clearRect(baseMarkerCanvasRef.current, baseMarkerContextRef.current);
962
+ canvasUtil.scaleCanvas(controller, baseMarkerCanvasRef.current, baseMarkerContextRef.current);
963
+ }
964
+
965
+ if (interactiveMarkerCanvasRef.current && interactiveMarkerContextRef.current) {
966
+ canvasUtil.clearRect(interactiveMarkerCanvasRef.current, interactiveMarkerContextRef.current);
967
+ canvasUtil.scaleCanvas(controller, interactiveMarkerCanvasRef.current, interactiveMarkerContextRef.current);
968
+ } // 폴리곤 렌더링
969
+
970
+
971
+ if (polygonsRef.current.length > 0) {
972
+ renderBasePolygons();
973
+ renderInteractionPolygons();
974
+ } // 마커 렌더링
975
+
976
+
977
+ if (markersRef.current.length > 0 && markerRenderer) {
978
+ renderBaseMarkers();
979
+ renderInteractionMarkers();
980
+ }
981
+ });
982
+ resizeObserver.observe(controller.mapDivElement); // 🚀 이벤트 등록 (한 번만)
983
+
984
+ controller.addEventListener('IDLE', handleIdle);
985
+ controller.addEventListener('ZOOMSTART', handleZoomStart);
986
+ controller.addEventListener('ZOOM_CHANGED', handleZoomEnd);
987
+ controller.addEventListener('CLICK', handleMapClick);
988
+ controller.addEventListener('CENTER_CHANGED', handleCenterChanged); // 지도 div에 마우스 이벤트 등록
989
+
990
+ controller.mapDivElement.addEventListener('mousemove', handleMouseMove);
991
+ controller.mapDivElement.addEventListener('mouseleave', handleMouseLeave);
992
+ }
993
+
994
+ return function () {
995
+ // RAF 정리
996
+ if (rafIdRef.current) {
997
+ cancelAnimationFrame(rafIdRef.current);
998
+ }
999
+
1000
+ resizeObserver && resizeObserver.disconnect();
1001
+ controller.removeEventListener('IDLE', handleIdle);
1002
+ controller.removeEventListener('ZOOMSTART', handleZoomStart);
1003
+ controller.removeEventListener('ZOOM_CHANGED', handleZoomEnd);
1004
+ controller.removeEventListener('CLICK', handleMapClick);
1005
+ controller.removeEventListener('CENTER_CHANGED', handleCenterChanged);
1006
+ controller.mapDivElement.removeEventListener('mousemove', handleMouseMove);
1007
+ controller.mapDivElement.removeEventListener('mouseleave', handleMouseLeave);
1008
+ };
1009
+ }, []); // 🚀 polygons 데이터 업데이트 (독립)
1010
+
1011
+ React.useEffect(function () {
1012
+ console.log('📦 [useEffect] polygons 변경:', polygons === null || polygons === void 0 ? void 0 : polygons.length, '개'); // refs 업데이트
1013
+
1014
+ polygonsRef.current = polygons || []; // 🚀 폴리곤만 렌더링 (마커와 독립)
1015
+
1016
+ if (baseContextRef.current) {
1017
+ renderBasePolygons();
1018
+ renderInteractionPolygons();
1019
+ }
1020
+ }, [polygons]); // 🚀 markers 데이터 업데이트 (독립)
1021
+
1022
+ React.useEffect(function () {
1023
+ console.log('📦 [useEffect] markers 변경:', markers === null || markers === void 0 ? void 0 : markers.length, '개'); // refs 업데이트
1024
+
1025
+ markersRef.current = markers || []; // 🚀 마커만 렌더링 (폴리곤과 독립)
1026
+
1027
+ if (baseMarkerContextRef.current && markerRenderer) {
1028
+ renderBaseMarkers();
1029
+ renderInteractionMarkers();
1030
+ }
1031
+ }, [markers, markerRenderer]); // CanvasMarker 스타일의 createPortal return
1032
+
1033
+ return reactDom.createPortal(React__default["default"].createElement("div", {
1034
+ ref: containerRef,
1035
+ style: {
1036
+ position: 'absolute',
1037
+ width: '100%',
1038
+ height: '100%',
1039
+ pointerEvents: 'none'
1040
+ }
1041
+ }, React__default["default"].createElement("canvas", {
1042
+ ref: baseCanvasRef,
1043
+ style: {
1044
+ position: 'absolute',
1045
+ top: 0,
1046
+ left: 0,
1047
+ pointerEvents: 'revert-layer',
1048
+ width: '100%',
1049
+ height: '100%'
1050
+ }
1051
+ }), React__default["default"].createElement("canvas", {
1052
+ ref: hoverCanvasRef,
1053
+ style: {
1054
+ position: 'absolute',
1055
+ top: 0,
1056
+ left: 0,
1057
+ pointerEvents: 'none',
1058
+ width: '100%',
1059
+ height: '100%'
1060
+ }
1061
+ }), React__default["default"].createElement("canvas", {
1062
+ ref: baseMarkerCanvasRef,
1063
+ style: {
1064
+ position: 'absolute',
1065
+ top: 0,
1066
+ left: 0,
1067
+ pointerEvents: 'none',
1068
+ width: '100%',
1069
+ height: '100%'
1070
+ }
1071
+ }), React__default["default"].createElement("canvas", {
1072
+ ref: interactiveMarkerCanvasRef,
1073
+ style: {
1074
+ position: 'absolute',
1075
+ top: 0,
1076
+ left: 0,
1077
+ pointerEvents: 'none',
1078
+ width: '100%',
1079
+ height: '100%'
1080
+ }
1081
+ })), divElement);
1082
+ });
1083
+
1084
+ exports.CanvasMarkerClaude = CanvasMarkerClaude;