@mint-ui/map 1.2.0-test.31 → 1.2.0-test.33

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 (28) hide show
  1. package/dist/components/mint-map/core/MintMapCore.js +5 -5
  2. package/dist/components/mint-map/core/advanced/index.d.ts +2 -1
  3. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/context.d.ts +5 -5
  4. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/context.js +8 -8
  5. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/performance.d.ts +1 -1
  6. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/performance.js +1 -1
  7. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/types.d.ts +9 -12
  8. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/utils.d.ts +2 -2
  9. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/utils.js +3 -3
  10. package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +97 -0
  11. package/dist/components/mint-map/core/advanced/{woongCanvas/WoongCanvasLayer.js → woongCanvasMarker/WoongCanvasMarker.js} +42 -123
  12. package/dist/components/mint-map/core/advanced/woongCanvasMarker/index.d.ts +3 -0
  13. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +113 -0
  14. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +948 -0
  15. package/dist/components/mint-map/core/advanced/woongCanvasPolygon/index.d.ts +3 -0
  16. package/dist/components/mint-map/core/advanced/{woongCanvas/shared → woongCanvasPolygon}/renderer.d.ts +1 -1
  17. package/dist/components/mint-map/google/GoogleMintMapController.js +4 -4
  18. package/dist/components/mint-map/kakao/KakaoMintMapController.js +4 -4
  19. package/dist/components/mint-map/naver/NaverMintMapController.js +4 -4
  20. package/dist/index.es.js +1101 -260
  21. package/dist/index.js +10 -8
  22. package/dist/index.umd.js +1104 -262
  23. package/package.json +1 -1
  24. package/dist/components/mint-map/core/advanced/woongCanvas/WoongCanvasLayer.d.ts +0 -162
  25. package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +0 -3
  26. /package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/index.d.ts +0 -0
  27. /package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/types.js +0 -0
  28. /package/dist/components/mint-map/core/advanced/{woongCanvas/shared → woongCanvasPolygon}/renderer.js +0 -0
package/dist/index.es.js CHANGED
@@ -992,8 +992,8 @@ var calculateTextBoxWidth = function (_a) {
992
992
  return Math.max(minWidth, textWidth + padding);
993
993
  };
994
994
 
995
- var KonvaMarkerContext = createContext(null);
996
- var KonvaMarkerProvider = function (_a) {
995
+ var WoongCanvasContext = createContext(null);
996
+ var WoongCanvasProvider = function (_a) {
997
997
  var children = _a.children;
998
998
  var controller = useMintMapController(); // Refs
999
999
 
@@ -1138,12 +1138,12 @@ var KonvaMarkerProvider = function (_a) {
1138
1138
  unregisterComponent: unregisterComponent
1139
1139
  };
1140
1140
  }, [registerComponent, unregisterComponent]);
1141
- return React.createElement(KonvaMarkerContext.Provider, {
1141
+ return React.createElement(WoongCanvasContext.Provider, {
1142
1142
  value: contextValue
1143
1143
  }, children);
1144
1144
  };
1145
- var useKonvaMarkerContext = function () {
1146
- var context = useContext(KonvaMarkerContext);
1145
+ var useWoongCanvasContext = function () {
1146
+ var context = useContext(WoongCanvasContext);
1147
1147
  return context;
1148
1148
  };
1149
1149
 
@@ -1225,7 +1225,7 @@ function () {
1225
1225
  * - 장점: 읽기 성능 대폭 향상 (10,000번 get → 이전보다 2배 빠름)
1226
1226
  * - 단점: 접근 빈도가 아닌 삽입 순서 기반 eviction (FIFO)
1227
1227
  *
1228
- * WoongKonvaMarker 사용 사례에 최적:
1228
+ * WoongCanvasMarker 사용 사례에 최적:
1229
1229
  * - 좌표 변환 결과는 zoom/pan 시 어차피 전체 초기화
1230
1230
  * - 접근 빈도 추적보다 빠른 조회가 더 중요
1231
1231
  */
@@ -1563,7 +1563,7 @@ function MintMapCore(_a) {
1563
1563
  }, [center]);
1564
1564
  return React.createElement("div", {
1565
1565
  className: cn$3('mint-map-root')
1566
- }, mapInitialized && React.createElement(KonvaMarkerProvider, null, children), React.createElement("div", {
1566
+ }, mapInitialized && React.createElement(WoongCanvasProvider, null, children), React.createElement("div", {
1567
1567
  className: cn$3('mint-map-container'),
1568
1568
  style: {
1569
1569
  visibility: visible ? 'inherit' : 'hidden'
@@ -5421,6 +5421,1011 @@ function LoadingImage(_a) {
5421
5421
  }))));
5422
5422
  }
5423
5423
 
5424
+ // 메인 컴포넌트
5425
+ // ============================================================================
5426
+
5427
+ var WoongCanvasMarker = function (props) {
5428
+ var data = props.data,
5429
+ onClick = props.onClick,
5430
+ onMouseOver = props.onMouseOver,
5431
+ onMouseOut = props.onMouseOut,
5432
+ _a = props.enableMultiSelect,
5433
+ enableMultiSelect = _a === void 0 ? false : _a,
5434
+ _b = props.topOnHover,
5435
+ topOnHover = _b === void 0 ? false : _b,
5436
+ _c = props.enableViewportCulling,
5437
+ enableViewportCulling = _c === void 0 ? true : _c,
5438
+ _d = props.cullingMargin,
5439
+ cullingMargin = _d === void 0 ? DEFAULT_CULLING_MARGIN : _d,
5440
+ _e = props.maxCacheSize,
5441
+ maxCacheSize = _e === void 0 ? DEFAULT_MAX_CACHE_SIZE : _e,
5442
+ externalSelectedItems = props.selectedItems,
5443
+ externalSelectedItem = props.selectedItem,
5444
+ _f = props.disableInteraction,
5445
+ disableInteraction = _f === void 0 ? false : _f,
5446
+ renderBase = props.renderBase,
5447
+ renderAnimation = props.renderAnimation,
5448
+ renderEvent = props.renderEvent,
5449
+ options = __rest(props, ["data", "onClick", "onMouseOver", "onMouseOut", "enableMultiSelect", "topOnHover", "enableViewportCulling", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction", "renderBase", "renderAnimation", "renderEvent"]); // --------------------------------------------------------------------------
5450
+ // Hooks & Context
5451
+ // --------------------------------------------------------------------------
5452
+
5453
+
5454
+ var controller = useMintMapController();
5455
+ var context = useWoongCanvasContext();
5456
+ var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; // --------------------------------------------------------------------------
5457
+ // DOM Refs
5458
+ // --------------------------------------------------------------------------
5459
+
5460
+ var divRef = useRef(document.createElement('div'));
5461
+ var divElement = divRef.current;
5462
+ var containerRef = useRef(null);
5463
+ var markerRef = useRef(); // --------------------------------------------------------------------------
5464
+ // Konva Refs
5465
+ // --------------------------------------------------------------------------
5466
+
5467
+ var stageRef = useRef(null);
5468
+ var baseLayerRef = useRef(null);
5469
+ var animationLayerRef = useRef(null);
5470
+ var eventLayerRef = useRef(null); // --------------------------------------------------------------------------
5471
+ // Data Refs - 선택 및 Hover 상태 관리
5472
+ // --------------------------------------------------------------------------
5473
+
5474
+ /** data prop을 ref로 추적 (stale closure 방지, useEffect에서 동기화) */
5475
+
5476
+ var dataRef = useRef(data); // --------------------------------------------------------------------------
5477
+ // State Refs - 선택 및 Hover 상태 관리
5478
+ // --------------------------------------------------------------------------
5479
+
5480
+ /** 상호작용 비활성화 상태 (Ref로 관리하여 클로저 문제 해결) */
5481
+
5482
+ var disableInteractionRef = useRef(disableInteraction);
5483
+ /** 현재 Hover 중인 항목 */
5484
+
5485
+ var hoveredItemRef = useRef(null);
5486
+ /** 외부에서 전달된 선택 항목 (Ref로 관리하여 클로저 문제 해결) */
5487
+
5488
+ var selectedItemRef = useRef(externalSelectedItem);
5489
+ /**
5490
+ * 선택된 항목의 ID Set
5491
+ *
5492
+ * 용도:
5493
+ * 1. onClick 콜백에 전달 - onClick(data, selectedIdsRef.current)
5494
+ * 2. 선택 여부 빠른 체크 - selectedIdsRef.current.has(id)
5495
+ * 3. 메모리 효율 - ID만 저장 (작음)
5496
+ *
5497
+ * selectedItemsMapRef와 차이:
5498
+ * - selectedIdsRef: ID만 저장 { "id1", "id2" }
5499
+ * - selectedItemsMapRef: 전체 객체 저장 { id1: {...}, id2: {...} }
5500
+ *
5501
+ * 둘 다 필요: ID만 필요한 곳은 이것, 전체 데이터 필요한 곳은 Map
5502
+ */
5503
+
5504
+ var selectedIdsRef = useRef(new Set());
5505
+ /**
5506
+ * 선택된 항목의 실제 데이터 Map (핵심 성능 최적화!)
5507
+ *
5508
+ * 목적: doRenderEvent에서 filter() 순회 제거
5509
+ * - 이전: markersRef.current.filter() → O(전체 마커 수)
5510
+ * - 현재: Map.values() → O(선택된 항목 수)
5511
+ *
5512
+ * 성능 개선: 10,000개 중 1개 선택 시
5513
+ * - 이전: 10,000번 체크
5514
+ * - 현재: 1번 접근 (10,000배 빠름!)
5515
+ */
5516
+
5517
+ var selectedItemsMapRef = useRef(new Map()); // --------------------------------------------------------------------------
5518
+ // Drag Refs
5519
+ // --------------------------------------------------------------------------
5520
+
5521
+ var draggingRef = useRef(false);
5522
+ var prevCenterOffsetRef = useRef(null);
5523
+ var accumTranslateRef = useRef({
5524
+ x: 0,
5525
+ y: 0
5526
+ }); // --------------------------------------------------------------------------
5527
+ // Performance Refs (캐싱 & 최적화)
5528
+ // --------------------------------------------------------------------------
5529
+
5530
+ /** 좌표 변환 결과 LRU 캐시 */
5531
+
5532
+ var offsetCacheRef = useRef(new LRUCache(maxCacheSize));
5533
+ /** 공간 인덱스 (빠른 Hit Test) */
5534
+
5535
+ var spatialIndexRef = useRef(new SpatialHashGrid(SPATIAL_GRID_CELL_SIZE));
5536
+ /** 바운딩 박스 캐시 (Viewport Culling 최적화) */
5537
+
5538
+ var boundingBoxCacheRef = useRef(new Map());
5539
+ /** 뷰포트 경계 캐시 (Viewport Culling) */
5540
+
5541
+ var viewportRef = useRef(null); // --------------------------------------------------------------------------
5542
+ // 유틸리티 함수: 뷰포트 관리
5543
+ // --------------------------------------------------------------------------
5544
+
5545
+ /**
5546
+ * 현재 뷰포트 영역 계산
5547
+ */
5548
+
5549
+ var updateViewport = function () {
5550
+ if (!stageRef.current) return;
5551
+ var stage = stageRef.current;
5552
+ viewportRef.current = {
5553
+ minX: -cullingMargin,
5554
+ maxX: stage.width() + cullingMargin,
5555
+ minY: -cullingMargin,
5556
+ maxY: stage.height() + cullingMargin
5557
+ };
5558
+ };
5559
+ /**
5560
+ * 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
5561
+ */
5562
+
5563
+
5564
+ var isInViewport = function (item) {
5565
+ if (!enableViewportCulling || !viewportRef.current) return true;
5566
+ var viewport = viewportRef.current; // 캐시된 바운딩 박스 확인
5567
+
5568
+ var bbox = boundingBoxCacheRef.current.get(item.id);
5569
+
5570
+ if (!bbox) {
5571
+ // 바운딩 박스 계산 (공통 함수 사용)
5572
+ var computed = computeBoundingBox(item);
5573
+ if (!computed) return false;
5574
+ bbox = computed;
5575
+ boundingBoxCacheRef.current.set(item.id, bbox);
5576
+ } // 바운딩 박스와 viewport 교차 체크
5577
+
5578
+
5579
+ return !(bbox.maxX < viewport.minX || bbox.minX > viewport.maxX || bbox.maxY < viewport.minY || bbox.minY > viewport.maxY);
5580
+ }; // --------------------------------------------------------------------------
5581
+ // 유틸리티 함수: 좌표 변환 캐싱
5582
+ // --------------------------------------------------------------------------
5583
+
5584
+ /**
5585
+ * 마커 좌표 변환 결과를 캐시하고 반환
5586
+ *
5587
+ * @param markerData 마커 데이터
5588
+ * @returns 변환된 좌표 또는 null
5589
+ */
5590
+
5591
+
5592
+ var getOrComputeMarkerOffset = function (markerData) {
5593
+ var cached = offsetCacheRef.current.get(markerData.id);
5594
+ if (cached && !Array.isArray(cached)) return cached;
5595
+ var result = computeMarkerOffset(markerData, controller);
5596
+
5597
+ if (result) {
5598
+ offsetCacheRef.current.set(markerData.id, result);
5599
+ }
5600
+
5601
+ return result;
5602
+ }; // --------------------------------------------------------------------------
5603
+ // 유틸리티 함수: 바운딩 박스 계산
5604
+ // --------------------------------------------------------------------------
5605
+
5606
+ /**
5607
+ * 마커의 바운딩 박스 계산
5608
+ *
5609
+ * 🎯 마커의 경우:
5610
+ * - boxHeight: 본체만 (Hit Test 영역)
5611
+ * - tailHeight: 꼬리 높이 (Viewport Culling용, 화면에 보이는 전체 영역)
5612
+ *
5613
+ * @param item 마커 데이터
5614
+ * @returns 바운딩 박스 또는 null
5615
+ */
5616
+
5617
+
5618
+ var computeBoundingBox = function (item) {
5619
+ // 마커: 중심점 기준 박스 크기 계산 (꼬리 포함)
5620
+ var offset = getOrComputeMarkerOffset(item);
5621
+ if (!offset) return null;
5622
+ var boxWidth = item.boxWidth || 50;
5623
+ var boxHeight = item.boxHeight || 28;
5624
+ var tailHeight = item.tailHeight || 0; // 🎯 tailHeight 사용 (Viewport Culling용)
5625
+
5626
+ return {
5627
+ minX: offset.x - boxWidth / 2,
5628
+ minY: offset.y - boxHeight - tailHeight,
5629
+ maxX: offset.x + boxWidth / 2,
5630
+ maxY: offset.y
5631
+ };
5632
+ }; // --------------------------------------------------------------------------
5633
+ // 유틸리티 함수: 공간 인덱싱
5634
+ // --------------------------------------------------------------------------
5635
+
5636
+ /**
5637
+ * 공간 인덱스 빌드 (빠른 Hit Test를 위한 자료구조)
5638
+ */
5639
+
5640
+
5641
+ var buildSpatialIndex = function () {
5642
+ var spatial = spatialIndexRef.current;
5643
+ spatial.clear();
5644
+ var currentData = dataRef.current;
5645
+
5646
+ for (var _i = 0, currentData_1 = currentData; _i < currentData_1.length; _i++) {
5647
+ var item = currentData_1[_i]; // 바운딩 박스 계산 (공통 함수 사용)
5648
+
5649
+ var bbox = computeBoundingBox(item);
5650
+
5651
+ if (bbox) {
5652
+ spatial.insert(item, bbox.minX, bbox.minY, bbox.maxX, bbox.maxY);
5653
+ }
5654
+ }
5655
+ }; // --------------------------------------------------------------------------
5656
+ // 렌더링 함수 결정 (dataType에 따라)
5657
+ // --------------------------------------------------------------------------
5658
+
5659
+ /**
5660
+ * 외부 렌더링 함수에 전달할 유틸리티 객체
5661
+ */
5662
+
5663
+
5664
+ var renderUtils = {
5665
+ getOrComputePolygonOffsets: function () {
5666
+ return null;
5667
+ },
5668
+ getOrComputeMarkerOffset: getOrComputeMarkerOffset
5669
+ };
5670
+ /** Base Layer에서 사용할 빈 Set (재사용) */
5671
+
5672
+ useRef(new Set());
5673
+ /**
5674
+ * Base 레이어 렌더링 (뷰포트 컬링 적용, 선택된 마커 제외)
5675
+ *
5676
+ * 🔥 최적화:
5677
+ * 1. Shape 재사용으로 객체 생성/파괴 오버헤드 제거
5678
+ * 2. sceneFunc 한 번만 설정 (함수 재생성 제거)
5679
+ * 3. 클로저로 최신 데이터 참조
5680
+ *
5681
+ * 🎯 topOnHover 지원:
5682
+ * - renderEvent가 없을 때 Base Layer에서 hover 처리 (fallback)
5683
+ * - renderEvent가 있으면 Event Layer에서 처리 (성능 최적화)
5684
+ */
5685
+
5686
+ var doRenderBase = function () {
5687
+ var layer = baseLayerRef.current;
5688
+ if (!layer) return; // 🔥 Shape 재사용: 이미 존재하면 재사용, 없으면 생성
5689
+
5690
+ var shape = layer.findOne('.base-render-shape');
5691
+
5692
+ if (!shape) {
5693
+ // 최초 생성 (한 번만 실행됨)
5694
+ // sceneFunc도 여기서 한 번만 설정 (클로저로 최신 데이터 참조)
5695
+ shape = new Konva.Shape({
5696
+ name: 'base-render-shape',
5697
+ sceneFunc: function (context, shape) {
5698
+ var ctx = context;
5699
+ var hovered = hoveredItemRef.current; // 클로저로 최신 ref 값 참조
5700
+
5701
+ var visibleItems = enableViewportCulling ? dataRef.current.filter(function (item) {
5702
+ return isInViewport(item);
5703
+ }) : dataRef.current; // topOnHover가 true이고 renderEvent가 없으면 Base Layer에서 hover 처리
5704
+
5705
+ if (topOnHover && !renderEvent && hovered) {
5706
+ // hover된 항목 제외하고 렌더링
5707
+ visibleItems = visibleItems.filter(function (item) {
5708
+ return item.id !== hovered.id;
5709
+ });
5710
+ } // 일반 항목 렌더링
5711
+
5712
+
5713
+ renderBase({
5714
+ ctx: ctx,
5715
+ items: visibleItems,
5716
+ selectedIds: selectedIdsRef.current,
5717
+ hoveredItem: hovered,
5718
+ utils: renderUtils
5719
+ }); // hover된 항목을 최상단에 렌더링 (renderEvent가 없을 때만)
5720
+
5721
+ if (topOnHover && !renderEvent && hovered) {
5722
+ var isHoveredInViewport = enableViewportCulling ? isInViewport(hovered) : true;
5723
+
5724
+ if (isHoveredInViewport) {
5725
+ renderBase({
5726
+ ctx: ctx,
5727
+ items: [hovered],
5728
+ selectedIds: selectedIdsRef.current,
5729
+ hoveredItem: hovered,
5730
+ utils: renderUtils
5731
+ });
5732
+ }
5733
+ }
5734
+ },
5735
+ perfectDrawEnabled: false,
5736
+ listening: false,
5737
+ hitStrokeWidth: 0
5738
+ });
5739
+ layer.add(shape);
5740
+ } // sceneFunc는 이미 설정되어 있으므로 다시 그리기만
5741
+
5742
+
5743
+ layer.batchDraw();
5744
+ };
5745
+ /**
5746
+ * Animation 레이어 렌더링 (선택된 마커 애니메이션)
5747
+ *
5748
+ * 🔥 최적화: sceneFunc 내부에서 최신 items 참조
5749
+ * - 선택 변경 시에만 재생성
5750
+ * - 지도 이동 시에는 기존 Animation 계속 실행
5751
+ */
5752
+
5753
+
5754
+ var doRenderAnimation = function () {
5755
+ if (!renderAnimation) return;
5756
+ var layer = animationLayerRef.current;
5757
+ if (!layer) return;
5758
+ renderAnimation({
5759
+ layer: layer,
5760
+ selectedIds: selectedIdsRef.current,
5761
+ items: dataRef.current,
5762
+ utils: renderUtils
5763
+ });
5764
+ };
5765
+ /**
5766
+ * Event 레이어 렌더링 (hover + 선택 상태 표시)
5767
+ *
5768
+ * 🔥 최적화:
5769
+ * 1. Shape 재사용으로 객체 생성/파괴 오버헤드 제거
5770
+ * 2. sceneFunc 한 번만 설정 (함수 재생성 제거)
5771
+ * 3. 클로저로 최신 데이터 참조
5772
+ */
5773
+
5774
+
5775
+ var doRenderEvent = function () {
5776
+ var layer = eventLayerRef.current;
5777
+ if (!layer) return;
5778
+ if (!renderEvent) return; // 🔥 Shape 재사용: 이미 존재하면 재사용, 없으면 생성
5779
+
5780
+ var shape = layer.findOne('.event-render-shape');
5781
+
5782
+ if (!shape) {
5783
+ // 최초 생성 (한 번만 실행됨)
5784
+ // sceneFunc도 여기서 한 번만 설정 (클로저로 최신 데이터 참조)
5785
+ shape = new Konva.Shape({
5786
+ name: 'event-render-shape',
5787
+ sceneFunc: function (context, shape) {
5788
+ var ctx = context; // 클로저로 최신 ref 값 참조
5789
+
5790
+ var selectedItems = Array.from(selectedItemsMapRef.current.values());
5791
+ var hovered = hoveredItemRef.current; // topOnHover가 true이면 hover된 항목을 최상단에 렌더링
5792
+
5793
+ if (topOnHover && hovered) {
5794
+ // 1. 먼저 일반 항목들 렌더링 (hover된 항목 제외)
5795
+ renderEvent({
5796
+ ctx: ctx,
5797
+ hoveredItem: null,
5798
+ utils: renderUtils,
5799
+ selectedItems: selectedItems.filter(function (item) {
5800
+ return item.id !== hovered.id;
5801
+ }),
5802
+ selectedItem: selectedItemRef.current
5803
+ }); // 2. hover된 항목을 최상단에 렌더링
5804
+
5805
+ var isHoveredInViewport = enableViewportCulling ? isInViewport(hovered) : true;
5806
+
5807
+ if (isHoveredInViewport) {
5808
+ // hover된 항목이 선택되어 있다면 hoverSelectedItems에 포함시켜서
5809
+ // renderEvent에서 hover 스타일만 적용되도록 함
5810
+ var hoveredIsSelected = selectedItems.some(function (item) {
5811
+ return item.id === hovered.id;
5812
+ });
5813
+ var hoverSelectedItems = hoveredIsSelected ? [hovered] : [];
5814
+ renderEvent({
5815
+ ctx: ctx,
5816
+ hoveredItem: hovered,
5817
+ utils: renderUtils,
5818
+ selectedItems: hoverSelectedItems,
5819
+ selectedItem: selectedItemRef.current
5820
+ });
5821
+ }
5822
+ } else {
5823
+ // topOnHover가 false이거나 hover된 항목이 없으면 일반 렌더링
5824
+ renderEvent({
5825
+ ctx: ctx,
5826
+ hoveredItem: hovered,
5827
+ utils: renderUtils,
5828
+ selectedItems: selectedItems,
5829
+ selectedItem: selectedItemRef.current
5830
+ });
5831
+ }
5832
+ },
5833
+ perfectDrawEnabled: false,
5834
+ listening: false,
5835
+ hitStrokeWidth: 0
5836
+ });
5837
+ layer.add(shape);
5838
+ } // sceneFunc는 이미 설정되어 있으므로 다시 그리기만
5839
+
5840
+
5841
+ layer.batchDraw();
5842
+ };
5843
+ /**
5844
+ * 전체 즉시 렌더링 (IDLE 시 호출)
5845
+ */
5846
+
5847
+
5848
+ var renderAllImmediate = function () {
5849
+ updateViewport();
5850
+ buildSpatialIndex();
5851
+ doRenderBase();
5852
+ doRenderAnimation();
5853
+ doRenderEvent();
5854
+ }; // --------------------------------------------------------------------------
5855
+ // 이벤트 핸들러: 지도 이벤트
5856
+ // --------------------------------------------------------------------------
5857
+
5858
+ /**
5859
+ * 지도 이동/줌 완료 시 처리
5860
+ */
5861
+
5862
+
5863
+ var handleIdle = function () {
5864
+ prevCenterOffsetRef.current = null;
5865
+ accumTranslateRef.current = {
5866
+ x: 0,
5867
+ y: 0
5868
+ }; // 2. 캐시 정리 (지도 이동/줌으로 좌표 변환 결과가 바뀜)
5869
+
5870
+ offsetCacheRef.current.clear();
5871
+ boundingBoxCacheRef.current.clear(); // 3. 마커 위치 업데이트
5872
+
5873
+ var bounds = controller.getCurrBounds();
5874
+
5875
+ var markerOptions = __assign({
5876
+ position: bounds.nw
5877
+ }, options);
5878
+
5879
+ markerRef.current && controller.updateMarker(markerRef.current, markerOptions); // 4. transform 제거 전에 새 데이터로 즉시 렌더링 (겹침 방지)
5880
+
5881
+ if (containerRef.current) {
5882
+ containerRef.current.style.transform = '';
5883
+ containerRef.current.style.visibility = '';
5884
+ } // 5. 새 위치에서 렌더링
5885
+
5886
+
5887
+ renderAllImmediate();
5888
+ };
5889
+ /**
5890
+ * 줌 시작 시 처리 (일시적으로 숨김)
5891
+ */
5892
+
5893
+
5894
+ var handleZoomStart = function () {
5895
+ if (containerRef.current) {
5896
+ containerRef.current.style.visibility = 'hidden';
5897
+ }
5898
+ };
5899
+ /**
5900
+ * 줌 종료 시 처리 (다시 표시)
5901
+ */
5902
+
5903
+
5904
+ var handleZoomEnd = function () {
5905
+ if (containerRef.current) {
5906
+ containerRef.current.style.visibility = '';
5907
+ }
5908
+ };
5909
+ /**
5910
+ * 지도 중심 변경 시 처리 (transform으로 이동 추적)
5911
+ */
5912
+
5913
+
5914
+ var handleCenterChanged = function () {
5915
+ var center = controller.getCurrBounds().getCenter();
5916
+ var curr = controller.positionToOffset(center);
5917
+ var prev = prevCenterOffsetRef.current;
5918
+
5919
+ if (!prev) {
5920
+ prevCenterOffsetRef.current = {
5921
+ x: curr.x,
5922
+ y: curr.y
5923
+ };
5924
+ return;
5925
+ }
5926
+
5927
+ var dx = prev.x - curr.x;
5928
+ var dy = prev.y - curr.y;
5929
+ accumTranslateRef.current = {
5930
+ x: accumTranslateRef.current.x + dx,
5931
+ y: accumTranslateRef.current.y + dy
5932
+ };
5933
+ prevCenterOffsetRef.current = {
5934
+ x: curr.x,
5935
+ y: curr.y
5936
+ };
5937
+
5938
+ if (containerRef.current) {
5939
+ containerRef.current.style.transform = "translate(".concat(accumTranslateRef.current.x, "px, ").concat(accumTranslateRef.current.y, "px)");
5940
+ }
5941
+ }; // --------------------------------------------------------------------------
5942
+ // Hit Test & 상태 관리
5943
+ // --------------------------------------------------------------------------
5944
+
5945
+ /**
5946
+ * 특정 좌표의 마커 데이터 찾기 (Spatial Index 사용)
5947
+ *
5948
+ * topOnHover가 true일 때:
5949
+ * - 현재 hover된 항목을 최우선으로 체크
5950
+ * - 시각적으로 최상단에 있는 항목이 hit test에서도 우선됨
5951
+ *
5952
+ * @param offset 검사할 좌표
5953
+ * @returns 찾은 마커 데이터 또는 null
5954
+ */
5955
+
5956
+
5957
+ var findData = function (offset) {
5958
+ // topOnHover가 true이고 현재 hover된 항목이 있으면, 그것을 먼저 체크
5959
+ if (topOnHover && hoveredItemRef.current) {
5960
+ var hovered = hoveredItemRef.current;
5961
+
5962
+ if (isPointInMarkerData(offset, hovered, getOrComputeMarkerOffset)) {
5963
+ return hovered; // 여전히 hover된 항목 위에 있음
5964
+ }
5965
+ } // Spatial Index로 후보 항목만 빠르게 추출 (30,000개 → ~10개)
5966
+
5967
+
5968
+ var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); // 마커 체크
5969
+
5970
+ for (var i = candidates.length - 1; i >= 0; i--) {
5971
+ var item = candidates[i];
5972
+
5973
+ if (isPointInMarkerData(offset, item, getOrComputeMarkerOffset)) {
5974
+ return item;
5975
+ }
5976
+ }
5977
+
5978
+ return null;
5979
+ };
5980
+ /**
5981
+ * Hover 상태 설정 및 레이어 렌더링
5982
+ *
5983
+ * @param data hover된 마커/폴리곤 데이터 또는 null
5984
+ *
5985
+ * 최적화: RAF 제거하여 즉시 렌더링 (16ms 지연 제거)
5986
+ *
5987
+ * 🎯 topOnHover 지원:
5988
+ * - renderEvent가 있으면: Event Layer에서만 처리 (성능 최적화)
5989
+ * - renderEvent가 없고 topOnHover=true면: Base Layer에서 처리
5990
+ */
5991
+
5992
+
5993
+ var setHovered = function (data) {
5994
+ hoveredItemRef.current = data;
5995
+
5996
+ if (draggingRef.current) {
5997
+ controller.setMapCursor('grabbing');
5998
+ } else {
5999
+ controller.setMapCursor(data ? 'pointer' : 'grab');
6000
+ } // 즉시 렌더링 (RAF 없이)
6001
+
6002
+
6003
+ if (renderEvent) {
6004
+ // renderEvent가 있으면 Event Layer에서만 처리 (성능 최적화)
6005
+ doRenderEvent();
6006
+ } else if (topOnHover) {
6007
+ // renderEvent가 없고 topOnHover가 true면 Base Layer에서 처리
6008
+ doRenderBase();
6009
+ }
6010
+ };
6011
+ /**
6012
+ * 클릭 처리 (단일/다중 선택)
6013
+ *
6014
+ * @param data 클릭된 마커/폴리곤 데이터
6015
+ *
6016
+ * 🔥 최적화: 단일 Shape 렌더링으로 Base Layer 재렌더링 속도 향상
6017
+ * - sceneFunc에서 selectedIds를 체크하여 선택된 마커만 스킵
6018
+ * - 객체 생성 오버헤드 제거로 1000개 이상도 부드럽게 처리
6019
+ */
6020
+
6021
+
6022
+ var handleLocalClick = function (data) {
6023
+ // 1. 선택 상태 업데이트
6024
+ if (enableMultiSelect) {
6025
+ // 다중 선택: Set과 Map 동시 업데이트
6026
+ var newSelected = new Set(selectedIdsRef.current);
6027
+
6028
+ if (newSelected.has(data.id)) {
6029
+ newSelected.delete(data.id);
6030
+ selectedItemsMapRef.current.delete(data.id);
6031
+ } else {
6032
+ newSelected.add(data.id);
6033
+ selectedItemsMapRef.current.set(data.id, data);
6034
+ }
6035
+
6036
+ selectedIdsRef.current = newSelected;
6037
+ } else {
6038
+ // 단일 선택: 토글
6039
+ var newSelected = new Set();
6040
+
6041
+ if (!selectedIdsRef.current.has(data.id)) {
6042
+ newSelected.add(data.id);
6043
+ selectedItemsMapRef.current.clear();
6044
+ selectedItemsMapRef.current.set(data.id, data);
6045
+ } else {
6046
+ selectedItemsMapRef.current.clear();
6047
+ }
6048
+
6049
+ selectedIdsRef.current = newSelected;
6050
+ }
6051
+
6052
+ if (!!renderAnimation) {
6053
+ // 2. Base Layer 재렌더링 (단일 Shape로 최적화되어 빠름)
6054
+ doRenderBase(); // 3. Animation Layer 렌더링 (선택된 마커 애니메이션)
6055
+
6056
+ doRenderAnimation();
6057
+ } // 4. Event Layer 렌더링 (hover 처리)
6058
+
6059
+
6060
+ doRenderEvent();
6061
+ }; // --------------------------------------------------------------------------
6062
+ // 이벤트 핸들러: UI 이벤트
6063
+ // --------------------------------------------------------------------------
6064
+
6065
+ /**
6066
+ * 클릭 이벤트 처리
6067
+ */
6068
+
6069
+
6070
+ var handleClick = function (event) {
6071
+ var _a;
6072
+
6073
+ if (disableInteractionRef.current) return; // 🚫 상호작용 비활성화 시 즉시 반환
6074
+
6075
+ if (context || !((_a = event === null || event === void 0 ? void 0 : event.param) === null || _a === void 0 ? void 0 : _a.position)) return;
6076
+
6077
+ try {
6078
+ var clickedOffset = controller.positionToOffset(event.param.position);
6079
+ var data_1 = findData(clickedOffset);
6080
+
6081
+ if (data_1) {
6082
+ handleLocalClick(data_1);
6083
+
6084
+ if (onClick) {
6085
+ onClick(data_1, selectedIdsRef.current);
6086
+ }
6087
+ }
6088
+ } catch (error) {
6089
+ console.error('[WoongCanvasMarker] handleClick error:', error);
6090
+ }
6091
+ };
6092
+ /**
6093
+ * 마우스 이동 이벤트 처리 (hover 감지)
6094
+ */
6095
+
6096
+
6097
+ var handleMouseMove = function (event) {
6098
+ var _a;
6099
+
6100
+ if (disableInteractionRef.current) return; // 🚫 상호작용 비활성화 시 즉시 반환
6101
+
6102
+ if (context || !((_a = event === null || event === void 0 ? void 0 : event.param) === null || _a === void 0 ? void 0 : _a.position)) return;
6103
+
6104
+ try {
6105
+ var mouseOffset = controller.positionToOffset(event.param.position);
6106
+ var hoveredItem = findData(mouseOffset);
6107
+ var prevHovered = hoveredItemRef.current;
6108
+
6109
+ if (prevHovered !== hoveredItem) {
6110
+ setHovered(hoveredItem);
6111
+ if (prevHovered && onMouseOut) onMouseOut(prevHovered);
6112
+ if (hoveredItem && onMouseOver) onMouseOver(hoveredItem);
6113
+ }
6114
+ } catch (error) {
6115
+ console.error('[WoongCanvasMarker] handleMouseMove error:', error);
6116
+ }
6117
+ };
6118
+ /**
6119
+ * 드래그 시작 처리 (커서를 grabbing으로 변경)
6120
+ */
6121
+
6122
+
6123
+ var handleDragStart = function () {
6124
+ draggingRef.current = true;
6125
+ controller.setMapCursor('grabbing');
6126
+ };
6127
+ /**
6128
+ * 드래그 종료 처리 (커서를 기본으로 복원)
6129
+ */
6130
+
6131
+
6132
+ var handleDragEnd = function () {
6133
+ draggingRef.current = false;
6134
+ controller.setMapCursor('grab');
6135
+ };
6136
+ /**
6137
+ * 마우스가 canvas를 벗어날 때 hover cleanup
6138
+ */
6139
+
6140
+
6141
+ var handleMouseLeave = function () {
6142
+ if (disableInteractionRef.current) return; // 🚫 상호작용 비활성화 시 즉시 반환
6143
+
6144
+ var prevHovered = hoveredItemRef.current;
6145
+
6146
+ if (prevHovered) {
6147
+ hoveredItemRef.current = null;
6148
+ controller.setMapCursor('grab');
6149
+ doRenderEvent();
6150
+
6151
+ if (onMouseOut) {
6152
+ onMouseOut(prevHovered);
6153
+ }
6154
+ }
6155
+ }; // --------------------------------------------------------------------------
6156
+ // Lifecycle: DOM 초기화
6157
+ // --------------------------------------------------------------------------
6158
+
6159
+
6160
+ useEffect(function () {
6161
+ divElement.style.width = 'fit-content';
6162
+ return function () {
6163
+ if (markerRef.current) {
6164
+ controller.clearDrawable(markerRef.current);
6165
+ markerRef.current = undefined;
6166
+ }
6167
+ };
6168
+ }, []); // --------------------------------------------------------------------------
6169
+ // Lifecycle: 마커 생성/업데이트
6170
+ // --------------------------------------------------------------------------
6171
+
6172
+ useEffect(function () {
6173
+ if (options) {
6174
+ var bounds = controller.getCurrBounds();
6175
+
6176
+ var markerOptions = __assign({
6177
+ position: bounds.nw
6178
+ }, options);
6179
+
6180
+ if (markerRef.current) {
6181
+ controller.updateMarker(markerRef.current, markerOptions);
6182
+ } else {
6183
+ markerRef.current = new Marker(markerOptions);
6184
+ markerRef.current.element = divElement;
6185
+ controller.createMarker(markerRef.current);
6186
+
6187
+ if (divElement.parentElement) {
6188
+ divElement.parentElement.style.pointerEvents = 'none';
6189
+ }
6190
+
6191
+ if (options.zIndex !== undefined) {
6192
+ controller.setMarkerZIndex(markerRef.current, options.zIndex);
6193
+ }
6194
+ }
6195
+ }
6196
+ }, [options]); // --------------------------------------------------------------------------
6197
+ // Lifecycle: Konva 초기화 및 이벤트 리스너 등록
6198
+ // --------------------------------------------------------------------------
6199
+
6200
+ useEffect(function () {
6201
+ var mapDiv = controller.mapDivElement;
6202
+ var stage = new Konva.Stage({
6203
+ container: containerRef.current,
6204
+ width: mapDiv.offsetWidth,
6205
+ height: mapDiv.offsetHeight
6206
+ });
6207
+ stageRef.current = stage; // 레이어 최적화 설정
6208
+
6209
+ var baseLayer = new Konva.Layer({
6210
+ listening: false // 이벤트 리스닝 비활성화로 성능 향상
6211
+
6212
+ });
6213
+ var animationLayer = new Konva.Layer({
6214
+ listening: false
6215
+ });
6216
+ var eventLayer = new Konva.Layer({
6217
+ listening: false
6218
+ });
6219
+ baseLayerRef.current = baseLayer;
6220
+ animationLayerRef.current = animationLayer;
6221
+ eventLayerRef.current = eventLayer;
6222
+ stage.add(baseLayer);
6223
+
6224
+ if (renderAnimation) {
6225
+ stage.add(animationLayer);
6226
+ }
6227
+
6228
+ stage.add(eventLayer); // 초기 뷰포트 설정
6229
+
6230
+ updateViewport(); // ResizeObserver (맵 크기 변경 감지)
6231
+
6232
+ var resizeRafId = null;
6233
+ var resizeObserver = new ResizeObserver(function () {
6234
+ // RAF로 다음 프레임에 한 번만 실행 (debounce 효과)
6235
+ if (resizeRafId !== null) {
6236
+ cancelAnimationFrame(resizeRafId);
6237
+ }
6238
+
6239
+ resizeRafId = requestAnimationFrame(function () {
6240
+ stage.width(mapDiv.offsetWidth);
6241
+ stage.height(mapDiv.offsetHeight);
6242
+ offsetCacheRef.current.clear();
6243
+ boundingBoxCacheRef.current.clear();
6244
+ updateViewport();
6245
+ renderAllImmediate();
6246
+ resizeRafId = null;
6247
+ });
6248
+ });
6249
+ resizeObserver.observe(mapDiv);
6250
+ controller.addEventListener('IDLE', handleIdle);
6251
+ controller.addEventListener('ZOOMSTART', handleZoomStart);
6252
+ controller.addEventListener('ZOOM_CHANGED', handleZoomEnd);
6253
+ controller.addEventListener('CENTER_CHANGED', handleCenterChanged);
6254
+ controller.addEventListener('CLICK', handleClick);
6255
+ controller.addEventListener('MOUSEMOVE', handleMouseMove);
6256
+ controller.addEventListener('DRAGSTART', handleDragStart);
6257
+ controller.addEventListener('DRAGEND', handleDragEnd); // 맵 컨테이너에 mouseleave 이벤트 추가
6258
+
6259
+ mapDiv.addEventListener('mouseleave', handleMouseLeave);
6260
+ renderAllImmediate(); // Context 사용 시 컴포넌트 등록 (다중 인스턴스 관리)
6261
+
6262
+ var componentInstance = null;
6263
+
6264
+ if (context) {
6265
+ componentInstance = {
6266
+ zIndex: currentZIndex,
6267
+ hitTest: function (offset) {
6268
+ return findData(offset) !== null;
6269
+ },
6270
+ onClick: onClick,
6271
+ onMouseOver: onMouseOver,
6272
+ onMouseOut: onMouseOut,
6273
+ findData: findData,
6274
+ setHovered: setHovered,
6275
+ handleLocalClick: handleLocalClick,
6276
+ getSelectedIds: function () {
6277
+ return selectedIdsRef.current;
6278
+ },
6279
+ isInteractionDisabled: function () {
6280
+ return disableInteractionRef.current;
6281
+ } // 🚫 상호작용 비활성화 여부 반환
6282
+
6283
+ };
6284
+ context.registerComponent(componentInstance);
6285
+ } // Cleanup 함수
6286
+
6287
+
6288
+ return function () {
6289
+ // RAF 정리
6290
+ if (resizeRafId !== null) {
6291
+ cancelAnimationFrame(resizeRafId);
6292
+ } // 옵저버 정리
6293
+
6294
+
6295
+ resizeObserver.disconnect(); // 이벤트 리스너 정리
6296
+
6297
+ controller.removeEventListener('IDLE', handleIdle);
6298
+ controller.removeEventListener('ZOOMSTART', handleZoomStart);
6299
+ controller.removeEventListener('ZOOM_CHANGED', handleZoomEnd);
6300
+ controller.removeEventListener('CENTER_CHANGED', handleCenterChanged);
6301
+ controller.removeEventListener('CLICK', handleClick);
6302
+ controller.removeEventListener('MOUSEMOVE', handleMouseMove);
6303
+ controller.removeEventListener('DRAGSTART', handleDragStart);
6304
+ controller.removeEventListener('DRAGEND', handleDragEnd);
6305
+ mapDiv.removeEventListener('mouseleave', handleMouseLeave); // Context 정리
6306
+
6307
+ if (context && componentInstance) {
6308
+ context.unregisterComponent(componentInstance);
6309
+ } // Konva 리소스 정리
6310
+
6311
+
6312
+ baseLayer.destroyChildren();
6313
+ animationLayer.destroyChildren();
6314
+ eventLayer.destroyChildren();
6315
+ stage.destroy(); // 캐시 정리
6316
+
6317
+ offsetCacheRef.current.clear();
6318
+ boundingBoxCacheRef.current.clear();
6319
+ spatialIndexRef.current.clear();
6320
+ };
6321
+ }, []); // 초기화는 한 번만
6322
+ // --------------------------------------------------------------------------
6323
+ // Lifecycle: disableInteraction 동기화
6324
+ // --------------------------------------------------------------------------
6325
+
6326
+ useEffect(function () {
6327
+ disableInteractionRef.current = disableInteraction;
6328
+ }, [disableInteraction]); // --------------------------------------------------------------------------
6329
+ // Lifecycle: 외부 selectedItems 동기화
6330
+ // --------------------------------------------------------------------------
6331
+
6332
+ useEffect(function () {
6333
+ if (!stageRef.current) return; // externalSelectedItems가 undefined면 외부 제어 안 함
6334
+
6335
+ if (externalSelectedItems === undefined) return; // 외부에서 전달된 selectedItems로 동기화
6336
+
6337
+ var newSelectedIds = new Set();
6338
+ var newSelectedItemsMap = new Map();
6339
+ externalSelectedItems.forEach(function (item) {
6340
+ newSelectedIds.add(item.id);
6341
+ newSelectedItemsMap.set(item.id, item);
6342
+ });
6343
+ selectedIdsRef.current = newSelectedIds;
6344
+ selectedItemsMapRef.current = newSelectedItemsMap; // 렌더링
6345
+
6346
+ doRenderBase();
6347
+ doRenderAnimation();
6348
+ doRenderEvent();
6349
+ }, [externalSelectedItems]); // 배열 자체를 dependency로 사용
6350
+ // --------------------------------------------------------------------------
6351
+ // Lifecycle: 외부 selectedItem 변경 시 Event Layer 리렌더링
6352
+ // --------------------------------------------------------------------------
6353
+
6354
+ useEffect(function () {
6355
+ if (!stageRef.current) return; // Ref 동기화
6356
+
6357
+ selectedItemRef.current = externalSelectedItem; // selectedItem이 변경되면 Event Layer만 다시 그림
6358
+
6359
+ doRenderEvent();
6360
+ }, [externalSelectedItem]); // --------------------------------------------------------------------------
6361
+ // Lifecycle: 데이터 변경 시 렌더링
6362
+ // --------------------------------------------------------------------------
6363
+
6364
+ useEffect(function () {
6365
+ if (!stageRef.current) return; // dataRef 동기화
6366
+
6367
+ dataRef.current = data; // 데이터 변경 시 즉시 transform 제거 및 캐시 정리 (겹침 방지)
6368
+
6369
+ if (containerRef.current) {
6370
+ containerRef.current.style.transform = '';
6371
+ }
6372
+
6373
+ prevCenterOffsetRef.current = null;
6374
+ accumTranslateRef.current = {
6375
+ x: 0,
6376
+ y: 0
6377
+ }; // 캐시 정리 (새 데이터이므로 기존 캐시는 무효)
6378
+
6379
+ offsetCacheRef.current.clear();
6380
+ boundingBoxCacheRef.current.clear();
6381
+ /**
6382
+ * 선택 상태 동기화 (최적화 버전)
6383
+ *
6384
+ * data가 변경되면 selectedItemsMapRef도 업데이트 필요
6385
+ * (참조가 바뀌므로 기존 Map의 데이터는 stale 상태)
6386
+ *
6387
+ * 🔥 중요: 화면 밖 데이터도 선택 상태 유지!
6388
+ * - 현재 data에 있으면 최신 데이터로 업데이트
6389
+ * - 없으면 기존 selectedItemsMapRef의 데이터 유지
6390
+ *
6391
+ * 최적화: data를 Map으로 먼저 변환하여 find() 순회 제거
6392
+ * - O(전체 데이터 수 + 선택된 개수) - 매우 효율적
6393
+ */
6394
+
6395
+ var dataMap = new Map(data.map(function (m) {
6396
+ return [m.id, m];
6397
+ }));
6398
+ var newSelectedItemsMap = new Map();
6399
+ selectedIdsRef.current.forEach(function (id) {
6400
+ // 현재 data에 있으면 최신 데이터 사용
6401
+ var currentItem = dataMap.get(id);
6402
+
6403
+ if (currentItem) {
6404
+ newSelectedItemsMap.set(id, currentItem);
6405
+ } else {
6406
+ // 화면 밖이면 기존 데이터 유지
6407
+ var prevItem = selectedItemsMapRef.current.get(id);
6408
+
6409
+ if (prevItem) {
6410
+ newSelectedItemsMap.set(id, prevItem);
6411
+ }
6412
+ }
6413
+ }); // selectedIdsRef는 그대로 유지 (화면 밖 항목도 선택 상태 유지)
6414
+
6415
+ selectedItemsMapRef.current = newSelectedItemsMap; // 즉시 렌더링
6416
+
6417
+ renderAllImmediate();
6418
+ }, [data]);
6419
+ return createPortal(React.createElement("div", {
6420
+ ref: containerRef,
6421
+ style: {
6422
+ position: 'absolute',
6423
+ width: '100%',
6424
+ height: '100%'
6425
+ }
6426
+ }), divElement);
6427
+ };
6428
+
5424
6429
  /**
5425
6430
  * 폴리곤 렌더링 유틸리티
5426
6431
  *
@@ -5651,33 +6656,42 @@ var renderPolygonEvent = function (baseFillColor, baseStrokeColor, baseLineWidth
5651
6656
  // 메인 컴포넌트
5652
6657
  // ============================================================================
5653
6658
 
5654
- var WoongCanvasLayer = function (props) {
6659
+ var WoongCanvasPolygon = function (props) {
5655
6660
  var data = props.data,
5656
- dataType = props.dataType,
5657
6661
  onClick = props.onClick,
5658
6662
  onMouseOver = props.onMouseOver,
5659
6663
  onMouseOut = props.onMouseOut,
5660
6664
  _a = props.enableMultiSelect,
5661
6665
  enableMultiSelect = _a === void 0 ? false : _a,
5662
- _b = props.topOnHover,
5663
- topOnHover = _b === void 0 ? false : _b,
5664
- _c = props.enableViewportCulling,
5665
- enableViewportCulling = _c === void 0 ? true : _c,
5666
- _d = props.cullingMargin,
5667
- cullingMargin = _d === void 0 ? DEFAULT_CULLING_MARGIN : _d,
5668
- _e = props.maxCacheSize,
5669
- maxCacheSize = _e === void 0 ? DEFAULT_MAX_CACHE_SIZE : _e,
6666
+ _b = props.enableViewportCulling,
6667
+ enableViewportCulling = _b === void 0 ? true : _b,
6668
+ _c = props.cullingMargin,
6669
+ cullingMargin = _c === void 0 ? DEFAULT_CULLING_MARGIN : _c,
6670
+ _d = props.maxCacheSize,
6671
+ maxCacheSize = _d === void 0 ? DEFAULT_MAX_CACHE_SIZE : _d,
5670
6672
  externalSelectedItems = props.selectedItems,
5671
6673
  externalSelectedItem = props.selectedItem,
5672
- _f = props.disableInteraction,
5673
- disableInteraction = _f === void 0 ? false : _f,
5674
- options = __rest(props, ["data", "dataType", "onClick", "onMouseOver", "onMouseOut", "enableMultiSelect", "topOnHover", "enableViewportCulling", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction"]); // --------------------------------------------------------------------------
6674
+ _e = props.disableInteraction,
6675
+ disableInteraction = _e === void 0 ? false : _e,
6676
+ baseFillColor = props.baseFillColor,
6677
+ baseStrokeColor = props.baseStrokeColor,
6678
+ baseLineWidth = props.baseLineWidth,
6679
+ selectedFillColor = props.selectedFillColor,
6680
+ selectedStrokeColor = props.selectedStrokeColor,
6681
+ selectedLineWidth = props.selectedLineWidth,
6682
+ activeFillColor = props.activeFillColor,
6683
+ activeStrokeColor = props.activeStrokeColor,
6684
+ activeLineWidth = props.activeLineWidth,
6685
+ hoveredFillColor = props.hoveredFillColor,
6686
+ hoveredStrokeColor = props.hoveredStrokeColor,
6687
+ hoveredLineWidth = props.hoveredLineWidth,
6688
+ options = __rest(props, ["data", "onClick", "onMouseOver", "onMouseOut", "enableMultiSelect", "enableViewportCulling", "cullingMargin", "maxCacheSize", "selectedItems", "selectedItem", "disableInteraction", "baseFillColor", "baseStrokeColor", "baseLineWidth", "selectedFillColor", "selectedStrokeColor", "selectedLineWidth", "activeFillColor", "activeStrokeColor", "activeLineWidth", "hoveredFillColor", "hoveredStrokeColor", "hoveredLineWidth"]); // --------------------------------------------------------------------------
5675
6689
  // Hooks & Context
5676
6690
  // --------------------------------------------------------------------------
5677
6691
 
5678
6692
 
5679
6693
  var controller = useMintMapController();
5680
- var context = useKonvaMarkerContext();
6694
+ var context = useWoongCanvasContext();
5681
6695
  var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; // --------------------------------------------------------------------------
5682
6696
  // DOM Refs
5683
6697
  // --------------------------------------------------------------------------
@@ -5691,7 +6705,6 @@ var WoongCanvasLayer = function (props) {
5691
6705
 
5692
6706
  var stageRef = useRef(null);
5693
6707
  var baseLayerRef = useRef(null);
5694
- var animationLayerRef = useRef(null);
5695
6708
  var eventLayerRef = useRef(null); // --------------------------------------------------------------------------
5696
6709
  // Data Refs - 선택 및 Hover 상태 관리
5697
6710
  // --------------------------------------------------------------------------
@@ -5822,91 +6835,52 @@ var WoongCanvasLayer = function (props) {
5822
6835
  offsetCacheRef.current.set(polygonData.id, result);
5823
6836
  }
5824
6837
 
5825
- return result;
5826
- };
5827
- /**
5828
- * 마커 좌표 변환 결과를 캐시하고 반환
5829
- *
5830
- * @param markerData 마커 데이터
5831
- * @returns 변환된 좌표 또는 null
5832
- */
5833
-
5834
-
5835
- var getOrComputeMarkerOffset = function (markerData) {
5836
- var cached = offsetCacheRef.current.get(markerData.id);
5837
- if (cached && !Array.isArray(cached)) return cached;
5838
- var result = computeMarkerOffset(markerData, controller);
5839
-
5840
- if (result) {
5841
- offsetCacheRef.current.set(markerData.id, result);
5842
- }
5843
-
5844
6838
  return result;
5845
6839
  }; // --------------------------------------------------------------------------
5846
6840
  // 유틸리티 함수: 바운딩 박스 계산
5847
6841
  // --------------------------------------------------------------------------
5848
6842
 
5849
6843
  /**
5850
- * 아이템의 바운딩 박스 계산 (폴리곤/마커 공통)
5851
- *
5852
- * 🎯 마커의 경우:
5853
- * - boxHeight: 본체만 (Hit Test 영역)
5854
- * - tailHeight: 꼬리 높이 (Viewport Culling용, 화면에 보이는 전체 영역)
6844
+ * 폴리곤의 바운딩 박스 계산
5855
6845
  *
5856
- * @param item 마커 또는 폴리곤 데이터
6846
+ * @param item 폴리곤 데이터
5857
6847
  * @returns 바운딩 박스 또는 null
5858
6848
  */
5859
6849
 
5860
6850
 
5861
6851
  var computeBoundingBox = function (item) {
5862
- if (dataType === CanvasDataType.POLYGON) {
5863
- // 폴리곤: 모든 좌표의 최소/최대값 계산
5864
- var offsets = getOrComputePolygonOffsets(item);
5865
- if (!offsets) return null;
5866
- var minX = Infinity,
5867
- minY = Infinity,
5868
- maxX = -Infinity,
5869
- maxY = -Infinity;
5870
-
5871
- for (var _i = 0, offsets_1 = offsets; _i < offsets_1.length; _i++) {
5872
- var multiPolygon = offsets_1[_i];
5873
-
5874
- for (var _a = 0, multiPolygon_1 = multiPolygon; _a < multiPolygon_1.length; _a++) {
5875
- var polygonGroup = multiPolygon_1[_a];
5876
-
5877
- for (var _b = 0, polygonGroup_1 = polygonGroup; _b < polygonGroup_1.length; _b++) {
5878
- var _c = polygonGroup_1[_b],
5879
- x = _c[0],
5880
- y = _c[1];
5881
- if (x < minX) minX = x;
5882
- if (y < minY) minY = y;
5883
- if (x > maxX) maxX = x;
5884
- if (y > maxY) maxY = y;
5885
- }
6852
+ // 폴리곤: 모든 좌표의 최소/최대값 계산
6853
+ var offsets = getOrComputePolygonOffsets(item);
6854
+ if (!offsets) return null;
6855
+ var minX = Infinity,
6856
+ minY = Infinity,
6857
+ maxX = -Infinity,
6858
+ maxY = -Infinity;
6859
+
6860
+ for (var _i = 0, offsets_1 = offsets; _i < offsets_1.length; _i++) {
6861
+ var multiPolygon = offsets_1[_i];
6862
+
6863
+ for (var _a = 0, multiPolygon_1 = multiPolygon; _a < multiPolygon_1.length; _a++) {
6864
+ var polygonGroup = multiPolygon_1[_a];
6865
+
6866
+ for (var _b = 0, polygonGroup_1 = polygonGroup; _b < polygonGroup_1.length; _b++) {
6867
+ var _c = polygonGroup_1[_b],
6868
+ x = _c[0],
6869
+ y = _c[1];
6870
+ if (x < minX) minX = x;
6871
+ if (y < minY) minY = y;
6872
+ if (x > maxX) maxX = x;
6873
+ if (y > maxY) maxY = y;
5886
6874
  }
5887
6875
  }
5888
-
5889
- return {
5890
- minX: minX,
5891
- minY: minY,
5892
- maxX: maxX,
5893
- maxY: maxY
5894
- };
5895
- } else {
5896
- // 마커: 중심점 기준 박스 크기 계산 (꼬리 포함)
5897
- var offset = getOrComputeMarkerOffset(item);
5898
- if (!offset) return null;
5899
- var boxWidth = item.boxWidth || 50;
5900
- var boxHeight = item.boxHeight || 28;
5901
- var tailHeight = item.tailHeight || 0; // 🎯 tailHeight 사용 (Viewport Culling용)
5902
-
5903
- return {
5904
- minX: offset.x - boxWidth / 2,
5905
- minY: offset.y - boxHeight - tailHeight,
5906
- maxX: offset.x + boxWidth / 2,
5907
- maxY: offset.y
5908
- };
5909
6876
  }
6877
+
6878
+ return {
6879
+ minX: minX,
6880
+ minY: minY,
6881
+ maxX: maxX,
6882
+ maxY: maxY
6883
+ };
5910
6884
  }; // --------------------------------------------------------------------------
5911
6885
  // 유틸리티 함수: 공간 인덱싱
5912
6886
  // --------------------------------------------------------------------------
@@ -5941,23 +6915,20 @@ var WoongCanvasLayer = function (props) {
5941
6915
 
5942
6916
  var renderUtils = {
5943
6917
  getOrComputePolygonOffsets: getOrComputePolygonOffsets,
5944
- getOrComputeMarkerOffset: getOrComputeMarkerOffset
5945
- };
5946
- /** Base Layer에서 사용할 빈 Set (재사용) */
6918
+ getOrComputeMarkerOffset: function () {
6919
+ return null;
6920
+ } // 폴리곤에서는 사용하지 않음
5947
6921
 
5948
- useRef(new Set());
6922
+ };
5949
6923
  /**
5950
- * 실제 사용할 렌더링 함수 결정
5951
- * - MARKER: 외부에서 전달받은 renderBase 사용 (필수)
5952
- * - POLYGON: renderer.ts의 팩토리 함수로 생성 (props 기반)
6924
+ * 렌더링 함수 생성 (props 기반)
5953
6925
  */
5954
6926
 
5955
- var renderBase = dataType === CanvasDataType.MARKER ? props.renderBase : renderPolygonBase(props.baseFillColor, props.baseStrokeColor, props.baseLineWidth);
5956
- var renderAnimation = dataType === CanvasDataType.MARKER ? props.renderAnimation : undefined;
5957
- var renderEvent = dataType === CanvasDataType.MARKER ? props.renderEvent : function () {
5958
- var polygonProps = props;
5959
- return renderPolygonEvent(polygonProps.baseFillColor, polygonProps.baseStrokeColor, polygonProps.baseLineWidth, polygonProps.selectedFillColor, polygonProps.selectedStrokeColor, polygonProps.selectedLineWidth, polygonProps.activeFillColor, polygonProps.activeStrokeColor, polygonProps.activeLineWidth, polygonProps.hoveredFillColor, polygonProps.hoveredStrokeColor, polygonProps.hoveredLineWidth);
5960
- }();
6927
+ var renderBase = renderPolygonBase(baseFillColor, baseStrokeColor, baseLineWidth);
6928
+ var renderEvent = renderPolygonEvent(baseFillColor, baseStrokeColor, baseLineWidth, selectedFillColor, selectedStrokeColor, selectedLineWidth, activeFillColor, activeStrokeColor, activeLineWidth, hoveredFillColor, hoveredStrokeColor, hoveredLineWidth);
6929
+ /** Base Layer에서 사용할 Set (재사용) */
6930
+
6931
+ useRef(new Set());
5961
6932
  /**
5962
6933
  * Base 레이어 렌더링 (뷰포트 컬링 적용, 선택된 마커 제외)
5963
6934
  *
@@ -5988,15 +6959,7 @@ var WoongCanvasLayer = function (props) {
5988
6959
 
5989
6960
  var visibleItems = enableViewportCulling ? dataRef.current.filter(function (item) {
5990
6961
  return isInViewport(item);
5991
- }) : dataRef.current; // topOnHover가 true이고 renderEvent가 없으면 Base Layer에서 hover 처리
5992
-
5993
- if (topOnHover && !renderEvent && hovered) {
5994
- // hover된 항목 제외하고 렌더링
5995
- visibleItems = visibleItems.filter(function (item) {
5996
- return item.id !== hovered.id;
5997
- });
5998
- } // 일반 항목 렌더링
5999
-
6962
+ }) : dataRef.current; // 일반 항목 렌더링
6000
6963
 
6001
6964
  renderBase({
6002
6965
  ctx: ctx,
@@ -6004,21 +6967,7 @@ var WoongCanvasLayer = function (props) {
6004
6967
  selectedIds: selectedIdsRef.current,
6005
6968
  hoveredItem: hovered,
6006
6969
  utils: renderUtils
6007
- }); // hover된 항목을 최상단에 렌더링 (renderEvent가 없을 때만)
6008
-
6009
- if (topOnHover && !renderEvent && hovered) {
6010
- var isHoveredInViewport = enableViewportCulling ? isInViewport(hovered) : true;
6011
-
6012
- if (isHoveredInViewport) {
6013
- renderBase({
6014
- ctx: ctx,
6015
- items: [hovered],
6016
- selectedIds: selectedIdsRef.current,
6017
- hoveredItem: hovered,
6018
- utils: renderUtils
6019
- });
6020
- }
6021
- }
6970
+ });
6022
6971
  },
6023
6972
  perfectDrawEnabled: false,
6024
6973
  listening: false,
@@ -6030,26 +6979,6 @@ var WoongCanvasLayer = function (props) {
6030
6979
 
6031
6980
  layer.batchDraw();
6032
6981
  };
6033
- /**
6034
- * Animation 레이어 렌더링 (선택된 마커 애니메이션)
6035
- *
6036
- * 🔥 최적화: sceneFunc 내부에서 최신 items 참조
6037
- * - 선택 변경 시에만 재생성
6038
- * - 지도 이동 시에는 기존 Animation 계속 실행
6039
- */
6040
-
6041
-
6042
- var doRenderAnimation = function () {
6043
- if (!renderAnimation) return;
6044
- var layer = animationLayerRef.current;
6045
- if (!layer) return;
6046
- renderAnimation({
6047
- layer: layer,
6048
- selectedIds: selectedIdsRef.current,
6049
- items: dataRef.current,
6050
- utils: renderUtils
6051
- });
6052
- };
6053
6982
  /**
6054
6983
  * Event 레이어 렌더링 (hover + 선택 상태 표시)
6055
6984
  *
@@ -6062,8 +6991,7 @@ var WoongCanvasLayer = function (props) {
6062
6991
 
6063
6992
  var doRenderEvent = function () {
6064
6993
  var layer = eventLayerRef.current;
6065
- if (!layer) return;
6066
- if (!renderEvent) return; // 🔥 Shape 재사용: 이미 존재하면 재사용, 없으면 생성
6994
+ if (!layer) return; // 🔥 Shape 재사용: 이미 존재하면 재사용, 없으면 생성
6067
6995
 
6068
6996
  var shape = layer.findOne('.event-render-shape');
6069
6997
 
@@ -6076,47 +7004,15 @@ var WoongCanvasLayer = function (props) {
6076
7004
  var ctx = context; // 클로저로 최신 ref 값 참조
6077
7005
 
6078
7006
  var selectedItems = Array.from(selectedItemsMapRef.current.values());
6079
- var hovered = hoveredItemRef.current; // topOnHover가 true이면 hover된 항목을 최상단에 렌더링
6080
-
6081
- if (topOnHover && hovered) {
6082
- // 1. 먼저 일반 항목들 렌더링 (hover된 항목 제외)
6083
- renderEvent({
6084
- ctx: ctx,
6085
- hoveredItem: null,
6086
- utils: renderUtils,
6087
- selectedItems: selectedItems.filter(function (item) {
6088
- return item.id !== hovered.id;
6089
- }),
6090
- selectedItem: selectedItemRef.current
6091
- }); // 2. hover된 항목을 최상단에 렌더링
6092
-
6093
- var isHoveredInViewport = enableViewportCulling ? isInViewport(hovered) : true;
7007
+ var hovered = hoveredItemRef.current; // 일반 렌더링
6094
7008
 
6095
- if (isHoveredInViewport) {
6096
- // hover된 항목이 선택되어 있다면 hoverSelectedItems에 포함시켜서
6097
- // renderEvent에서 hover 스타일만 적용되도록 함
6098
- var hoveredIsSelected = selectedItems.some(function (item) {
6099
- return item.id === hovered.id;
6100
- });
6101
- var hoverSelectedItems = hoveredIsSelected ? [hovered] : [];
6102
- renderEvent({
6103
- ctx: ctx,
6104
- hoveredItem: hovered,
6105
- utils: renderUtils,
6106
- selectedItems: hoverSelectedItems,
6107
- selectedItem: selectedItemRef.current
6108
- });
6109
- }
6110
- } else {
6111
- // topOnHover가 false이거나 hover된 항목이 없으면 일반 렌더링
6112
- renderEvent({
6113
- ctx: ctx,
6114
- hoveredItem: hovered,
6115
- utils: renderUtils,
6116
- selectedItems: selectedItems,
6117
- selectedItem: selectedItemRef.current
6118
- });
6119
- }
7009
+ renderEvent({
7010
+ ctx: ctx,
7011
+ hoveredItem: hovered,
7012
+ utils: renderUtils,
7013
+ selectedItems: selectedItems,
7014
+ selectedItem: selectedItemRef.current
7015
+ });
6120
7016
  },
6121
7017
  perfectDrawEnabled: false,
6122
7018
  listening: false,
@@ -6137,7 +7033,6 @@ var WoongCanvasLayer = function (props) {
6137
7033
  updateViewport();
6138
7034
  buildSpatialIndex();
6139
7035
  doRenderBase();
6140
- doRenderAnimation();
6141
7036
  doRenderEvent();
6142
7037
  }; // --------------------------------------------------------------------------
6143
7038
  // 이벤트 핸들러: 지도 이벤트
@@ -6231,54 +7126,22 @@ var WoongCanvasLayer = function (props) {
6231
7126
  // --------------------------------------------------------------------------
6232
7127
 
6233
7128
  /**
6234
- * 특정 좌표의 마커/폴리곤 데이터 찾기 (Spatial Index 사용)
6235
- *
6236
- * topOnHover가 true일 때:
6237
- * - 현재 hover된 항목을 최우선으로 체크
6238
- * - 시각적으로 최상단에 있는 항목이 hit test에서도 우선됨
7129
+ * 특정 좌표의 폴리곤 데이터 찾기 (Spatial Index 사용)
6239
7130
  *
6240
7131
  * @param offset 검사할 좌표
6241
- * @returns 찾은 마커/폴리곤 데이터 또는 null
7132
+ * @returns 찾은 폴리곤 데이터 또는 null
6242
7133
  */
6243
7134
 
6244
7135
 
6245
7136
  var findData = function (offset) {
6246
- // topOnHover가 true이고 현재 hover된 항목이 있으면, 그것을 먼저 체크
6247
- if (topOnHover && hoveredItemRef.current) {
6248
- var hovered = hoveredItemRef.current; // 폴리곤인 경우
6249
-
6250
- if (dataType === CanvasDataType.POLYGON) {
6251
- if (isPointInPolygonData(offset, hovered, getOrComputePolygonOffsets)) {
6252
- return hovered; // 여전히 hover된 항목 위에 있음
6253
- }
6254
- } // 마커인 경우
6255
- else {
6256
- if (isPointInMarkerData(offset, hovered, getOrComputeMarkerOffset)) {
6257
- return hovered; // 여전히 hover된 항목 위에 있음
6258
- }
6259
- }
6260
- } // Spatial Index로 후보 항목만 빠르게 추출 (30,000개 → ~10개)
6261
-
7137
+ // Spatial Index로 후보 항목만 빠르게 추출 (30,000개 ~10개)
7138
+ var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); // 폴리곤 체크
6262
7139
 
6263
- var candidates = spatialIndexRef.current.queryPoint(offset.x, offset.y); // 데이터 타입에 따라 적절한 히트 테스트 수행
7140
+ for (var i = candidates.length - 1; i >= 0; i--) {
7141
+ var item = candidates[i];
6264
7142
 
6265
- if (dataType === CanvasDataType.MARKER) {
6266
- // 마커 체크
6267
- for (var i = candidates.length - 1; i >= 0; i--) {
6268
- var item = candidates[i];
6269
-
6270
- if (isPointInMarkerData(offset, item, getOrComputeMarkerOffset)) {
6271
- return item;
6272
- }
6273
- }
6274
- } else {
6275
- // 폴리곤 체크
6276
- for (var i = candidates.length - 1; i >= 0; i--) {
6277
- var item = candidates[i];
6278
-
6279
- if (isPointInPolygonData(offset, item, getOrComputePolygonOffsets)) {
6280
- return item;
6281
- }
7143
+ if (isPointInPolygonData(offset, item, getOrComputePolygonOffsets)) {
7144
+ return item;
6282
7145
  }
6283
7146
  }
6284
7147
 
@@ -6307,13 +7170,7 @@ var WoongCanvasLayer = function (props) {
6307
7170
  } // 즉시 렌더링 (RAF 없이)
6308
7171
 
6309
7172
 
6310
- if (renderEvent) {
6311
- // renderEvent가 있으면 Event Layer에서만 처리 (성능 최적화)
6312
- doRenderEvent();
6313
- } else if (topOnHover) {
6314
- // renderEvent가 없고 topOnHover가 true면 Base Layer에서 처리
6315
- doRenderBase();
6316
- }
7173
+ doRenderEvent();
6317
7174
  };
6318
7175
  /**
6319
7176
  * 클릭 처리 (단일/다중 선택)
@@ -6354,15 +7211,10 @@ var WoongCanvasLayer = function (props) {
6354
7211
  }
6355
7212
 
6356
7213
  selectedIdsRef.current = newSelected;
6357
- }
6358
-
6359
- if (!!renderAnimation) {
6360
- // 2. Base Layer 재렌더링 (단일 Shape로 최적화되어 빠름)
6361
- doRenderBase(); // 3. Animation Layer 렌더링 (선택된 마커 애니메이션)
7214
+ } // 2. Base Layer 재렌더링 (단일 Shape로 최적화되어 빠름)
6362
7215
 
6363
- doRenderAnimation();
6364
- } // 4. Event Layer 렌더링 (hover 처리)
6365
7216
 
7217
+ doRenderBase(); // 3. Event Layer 렌더링 (hover 처리)
6366
7218
 
6367
7219
  doRenderEvent();
6368
7220
  }; // --------------------------------------------------------------------------
@@ -6393,7 +7245,7 @@ var WoongCanvasLayer = function (props) {
6393
7245
  }
6394
7246
  }
6395
7247
  } catch (error) {
6396
- console.error('[WoongKonvaMarker] handleClick error:', error);
7248
+ console.error('[WoongCanvasPolygon] handleClick error:', error);
6397
7249
  }
6398
7250
  };
6399
7251
  /**
@@ -6419,7 +7271,7 @@ var WoongCanvasLayer = function (props) {
6419
7271
  if (hoveredItem && onMouseOver) onMouseOver(hoveredItem);
6420
7272
  }
6421
7273
  } catch (error) {
6422
- console.error('[WoongKonvaMarker] handleMouseMove error:', error);
7274
+ console.error('[WoongCanvasPolygon] handleMouseMove error:', error);
6423
7275
  }
6424
7276
  };
6425
7277
  /**
@@ -6517,21 +7369,12 @@ var WoongCanvasLayer = function (props) {
6517
7369
  listening: false // 이벤트 리스닝 비활성화로 성능 향상
6518
7370
 
6519
7371
  });
6520
- var animationLayer = new Konva.Layer({
6521
- listening: false
6522
- });
6523
7372
  var eventLayer = new Konva.Layer({
6524
7373
  listening: false
6525
7374
  });
6526
7375
  baseLayerRef.current = baseLayer;
6527
- animationLayerRef.current = animationLayer;
6528
7376
  eventLayerRef.current = eventLayer;
6529
7377
  stage.add(baseLayer);
6530
-
6531
- if (renderAnimation) {
6532
- stage.add(animationLayer);
6533
- }
6534
-
6535
7378
  stage.add(eventLayer); // 초기 뷰포트 설정
6536
7379
 
6537
7380
  updateViewport(); // ResizeObserver (맵 크기 변경 감지)
@@ -6617,7 +7460,6 @@ var WoongCanvasLayer = function (props) {
6617
7460
 
6618
7461
 
6619
7462
  baseLayer.destroyChildren();
6620
- animationLayer.destroyChildren();
6621
7463
  eventLayer.destroyChildren();
6622
7464
  stage.destroy(); // 캐시 정리
6623
7465
 
@@ -6651,7 +7493,6 @@ var WoongCanvasLayer = function (props) {
6651
7493
  selectedItemsMapRef.current = newSelectedItemsMap; // 렌더링
6652
7494
 
6653
7495
  doRenderBase();
6654
- doRenderAnimation();
6655
7496
  doRenderEvent();
6656
7497
  }, [externalSelectedItems]); // 배열 자체를 dependency로 사용
6657
7498
  // --------------------------------------------------------------------------
@@ -9564,4 +10405,4 @@ function MintMap(_a) {
9564
10405
  }), loading));
9565
10406
  }
9566
10407
 
9567
- export { AnimationPlayer, Bounds, CanvasDataType, CanvasMarker, CanvasMarkerClaude, CanvasMarkerHanquf, CircleMarker, DEFAULT_CULLING_MARGIN, DEFAULT_MAX_CACHE_SIZE, Drawable, GeoCalulator, GoogleMintMapController, KonvaMarkerProvider, LRUCache, MapBuildingProjection, MapCanvasMarkerWrapper, MapCanvasWrapper, MapControlWrapper, MapEvent, MapLoadingWithImage, MapMarkerWrapper, MapPolygonWrapper, MapPolylineWrapper, MapUIEvent, Marker, MintMap, MintMapCanvasRenderer, MintMapController, MintMapCore, MintMapProvider, NaverMintMapController, Offset, PointLoading, Polygon, PolygonCalculator, PolygonMarker, Polyline, Position, SPATIAL_GRID_CELL_SIZE, SVGCircle, SVGPolygon, SVGRect, Spacing, SpatialHashGrid, Status, WoongCanvasLayer, calculateTextBoxWidth, computeMarkerOffset, computePolygonOffsets, getClusterInfo, getMapOfType, hexToRgba, isPointInMarkerData, isPointInPolygon, isPointInPolygonData, log, useKonvaMarkerContext, useMarkerMoving, useMintMapController, waiting };
10408
+ export { AnimationPlayer, Bounds, CanvasDataType, CanvasMarker, CanvasMarkerClaude, CanvasMarkerHanquf, CircleMarker, DEFAULT_CULLING_MARGIN, DEFAULT_MAX_CACHE_SIZE, Drawable, GeoCalulator, GoogleMintMapController, LRUCache, MapBuildingProjection, MapCanvasMarkerWrapper, MapCanvasWrapper, MapControlWrapper, MapEvent, MapLoadingWithImage, MapMarkerWrapper, MapPolygonWrapper, MapPolylineWrapper, MapUIEvent, Marker, MintMap, MintMapCanvasRenderer, MintMapController, MintMapCore, MintMapProvider, NaverMintMapController, Offset, PointLoading, Polygon, PolygonCalculator, PolygonMarker, Polyline, Position, SPATIAL_GRID_CELL_SIZE, SVGCircle, SVGPolygon, SVGRect, Spacing, SpatialHashGrid, Status, WoongCanvasMarker, WoongCanvasPolygon, WoongCanvasProvider, calculateTextBoxWidth, computeMarkerOffset, computePolygonOffsets, getClusterInfo, getMapOfType, hexToRgba, isPointInMarkerData, isPointInPolygon, isPointInPolygonData, log, useMarkerMoving, useMintMapController, useWoongCanvasContext, waiting };