@mint-ui/map 1.2.0-test.63 → 1.2.0-test.66

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.
@@ -1,128 +1,8 @@
1
1
  /// <reference types="react" />
2
- import { MarkerOptions } from '../../../types';
3
- import { CanvasData, CustomRenderBase, CustomRenderEvent } from '../shared';
2
+ import type { CanvasMarkerLayerProps } from './types';
4
3
  export { CanvasDataType, CanvasProvider, LRUCache, SpatialHashGrid } from '../shared';
5
4
  export type { CanvasData, CanvasOption, CustomRenderBase, CustomRenderEvent, MarkerBoundingBox, RenderBaseParams, RenderEventParams, RenderUtils } from '../shared';
6
- /**
7
- * CanvasMarkerLayer Props (renderEvent가 없는 경우 - 인터랙션 불가)
8
- *
9
- * @template T 마커 데이터의 추가 속성 타입
10
- */
11
- interface CanvasMarkerLayerPropsWithoutEvent<T> extends Pick<MarkerOptions, 'zIndex' | 'anchor' | 'visible'> {
12
- /** 렌더링할 마커 데이터 배열 */
13
- data: CanvasData<T>[];
14
- /** 뷰포트 컬링 활성화 여부 (기본값: false) */
15
- enableViewportCulling?: boolean;
16
- /** 뷰포트 컬링 여유 공간 (픽셀 단위, 기본값: 100) */
17
- cullingMargin?: number;
18
- /** LRU 캐시 최대 크기 (기본값: 10000) */
19
- maxCacheSize?: number;
20
- /** Base Layer 렌더링 함수 (필수) */
21
- renderBase: CustomRenderBase<T>;
22
- /** Event Layer 렌더링 함수가 없으면 인터랙션 관련 props 사용 불가 */
23
- renderEvent?: never;
24
- /** 인터랙션 관련 props는 renderEvent가 있을 때만 사용 가능 */
25
- onClick?: never;
26
- onMouseOver?: never;
27
- onMouseOut?: never;
28
- enableMultiSelect?: never;
29
- topOnHover?: never;
30
- selectedItems?: never;
31
- selectedItem?: never;
32
- disableInteraction?: never;
33
- topStageZIndex?: never;
34
- }
35
- /**
36
- * CanvasMarkerLayer Props (renderEvent가 있는 경우 - 인터랙션 가능)
37
- * topStageZIndex가 없을 때
38
- *
39
- * @template T 마커 데이터의 추가 속성 타입
40
- */
41
- interface CanvasMarkerLayerPropsWithEventBase<T> extends Pick<MarkerOptions, 'zIndex' | 'anchor' | 'visible'> {
42
- /** 렌더링할 마커 데이터 배열 */
43
- data: CanvasData<T>[];
44
- /** 마커 클릭 시 호출되는 콜백 함수 */
45
- onClick?: (payload: CanvasData<T>, selectedIds: Set<string>) => void;
46
- /** 마커에 마우스 오버 시 호출되는 콜백 함수 */
47
- onMouseOver?: (payload: CanvasData<T>) => void;
48
- /** 마커에서 마우스 아웃 시 호출되는 콜백 함수 */
49
- onMouseOut?: (payload: CanvasData<T>) => void;
50
- /** 다중 선택 활성화 여부 (기본값: false) */
51
- enableMultiSelect?: boolean;
52
- /** hover 시 마커를 최상단으로 표시할지 여부 (기본값: false) */
53
- topOnHover?: boolean;
54
- /** 뷰포트 컬링 활성화 여부 (기본값: false) */
55
- enableViewportCulling?: boolean;
56
- /** 뷰포트 컬링 여유 공간 (픽셀 단위, 기본값: 100) */
57
- cullingMargin?: number;
58
- /** LRU 캐시 최대 크기 (기본값: 10000) */
59
- maxCacheSize?: number;
60
- /** 외부에서 제어하는 선택된 항목 배열 */
61
- selectedItems?: CanvasData<T>[];
62
- /** 외부에서 전달된 단일 선택 아이템 */
63
- selectedItem?: CanvasData<T> | null;
64
- /** 상호작용 비활성화 여부 (기본값: false) */
65
- disableInteraction?: boolean;
66
- /** Base Layer 렌더링 함수 (필수) */
67
- renderBase: CustomRenderBase<T>;
68
- /** Event Layer 렌더링 함수 (필수 - 인터랙션 사용 시) */
69
- renderEvent: CustomRenderEvent<T>;
70
- /** Top Layer의 zIndex (hover된 항목을 최상단에 렌더링하기 위한 레이어, topOnHover가 true일 때만 사용 가능) */
71
- topStageZIndex?: never;
72
- }
73
- /**
74
- * CanvasMarkerLayer Props (renderEvent가 있는 경우 - 인터랙션 가능)
75
- * topStageZIndex가 있을 때 (topOnHover가 반드시 true여야 함)
76
- *
77
- * @template T 마커 데이터의 추가 속성 타입
78
- */
79
- interface CanvasMarkerLayerPropsWithEventWithTopStage<T> extends Pick<MarkerOptions, 'zIndex' | 'anchor' | 'visible'> {
80
- /** 렌더링할 마커 데이터 배열 */
81
- data: CanvasData<T>[];
82
- /** 마커 클릭 시 호출되는 콜백 함수 */
83
- onClick?: (payload: CanvasData<T>, selectedIds: Set<string>) => void;
84
- /** 마커에 마우스 오버 시 호출되는 콜백 함수 */
85
- onMouseOver?: (payload: CanvasData<T>) => void;
86
- /** 마커에서 마우스 아웃 시 호출되는 콜백 함수 */
87
- onMouseOut?: (payload: CanvasData<T>) => void;
88
- /** 다중 선택 활성화 여부 (기본값: false) */
89
- enableMultiSelect?: boolean;
90
- /** hover 시 마커를 최상단으로 표시할지 여부 (topStageZIndex 사용 시 반드시 true) */
91
- topOnHover: true;
92
- /** 뷰포트 컬링 활성화 여부 (기본값: false) */
93
- enableViewportCulling?: boolean;
94
- /** 뷰포트 컬링 여유 공간 (픽셀 단위, 기본값: 100) */
95
- cullingMargin?: number;
96
- /** LRU 캐시 최대 크기 (기본값: 10000) */
97
- maxCacheSize?: number;
98
- /** 외부에서 제어하는 선택된 항목 배열 */
99
- selectedItems?: CanvasData<T>[];
100
- /** 외부에서 전달된 단일 선택 아이템 */
101
- selectedItem?: CanvasData<T> | null;
102
- /** 상호작용 비활성화 여부 (기본값: false) */
103
- disableInteraction?: boolean;
104
- /** Base Layer 렌더링 함수 (필수) */
105
- renderBase: CustomRenderBase<T>;
106
- /** Event Layer 렌더링 함수 (필수 - 인터랙션 사용 시) */
107
- renderEvent: CustomRenderEvent<T>;
108
- /** Top Layer의 zIndex (hover된 항목을 최상단에 렌더링하기 위한 레이어, topOnHover가 true일 때만 사용 가능) */
109
- topStageZIndex: number;
110
- }
111
- /**
112
- * CanvasMarkerLayer Props (renderEvent가 있는 경우 - 인터랙션 가능)
113
- *
114
- * @template T 마커 데이터의 추가 속성 타입
115
- */
116
- declare type CanvasMarkerLayerPropsWithEvent<T> = CanvasMarkerLayerPropsWithEventBase<T> | CanvasMarkerLayerPropsWithEventWithTopStage<T>;
117
- /**
118
- * CanvasMarkerLayer Props
119
- *
120
- * renderEvent가 없으면 인터랙션 관련 props를 사용할 수 없습니다.
121
- * renderEvent가 있으면 모든 인터랙션 관련 props를 사용할 수 있습니다.
122
- *
123
- * @template T 마커 데이터의 추가 속성 타입
124
- */
125
- export declare type CanvasMarkerLayerProps<T> = CanvasMarkerLayerPropsWithoutEvent<T> | CanvasMarkerLayerPropsWithEvent<T>;
5
+ export type { CanvasMarkerLayerProps, CanvasMarkerLayerPropsWithEvent, CanvasMarkerLayerPropsWithEventWithoutSelection, CanvasMarkerLayerPropsWithEventWithSelectedItem, CanvasMarkerLayerPropsWithEventWithSelectedItems, CanvasMarkerLayerPropsWithEventWithTopStageWithoutSelection, CanvasMarkerLayerPropsWithEventWithTopStageWithSelectedItem, CanvasMarkerLayerPropsWithEventWithTopStageWithSelectedItems, CanvasMarkerLayerPropsWithoutEvent } from './types';
126
6
  declare const CanvasMarkerLayer: <T>(props: CanvasMarkerLayerProps<T>) => JSX.Element;
127
7
  /**
128
8
  * CanvasMarkerLayer - Konva 기반 고성능 마커 렌더링 컴포넌트
@@ -40,32 +40,26 @@ var CanvasMarkerLayer = function (props) {
40
40
 
41
41
  var _d = hasRenderEvent && 'renderEvent' in props ? props : {
42
42
  disableInteraction: false,
43
- enableMultiSelect: false,
44
43
  onClick: undefined,
45
44
  onMouseOut: undefined,
46
45
  onMouseOver: undefined,
47
46
  renderEvent: undefined,
48
47
  selectedItem: undefined,
49
48
  selectedItems: undefined,
50
- topOnHover: false,
51
49
  topStageZIndex: undefined
52
50
  },
53
51
  _e = _d.disableInteraction,
54
52
  disableInteraction = _e === void 0 ? false : _e,
55
- _f = _d.enableMultiSelect,
56
- enableMultiSelect = _f === void 0 ? false : _f,
57
53
  onClick = _d.onClick,
58
54
  onMouseOut = _d.onMouseOut,
59
55
  onMouseOver = _d.onMouseOver,
60
56
  renderEvent = _d.renderEvent,
61
57
  externalSelectedItem = _d.selectedItem,
62
58
  externalSelectedItems = _d.selectedItems,
63
- rawTopStageZIndex = _d.topStageZIndex,
64
- _g = _d.topOnHover,
65
- topOnHover = _g === void 0 ? false : _g; // topOnHover가 false이거나 없으면 topStageZIndex를 사용하지 못하도록 제한
59
+ rawTopStageZIndex = _d.topStageZIndex; // topStageZIndex가 있으면 hover 최상단 표시 활성화, 없으면 비활성화 (성능 우선)
66
60
 
67
61
 
68
- var topStageZIndex = topOnHover && rawTopStageZIndex !== undefined ? rawTopStageZIndex : undefined;
62
+ var topStageZIndex = rawTopStageZIndex;
69
63
  var controller = MintMapProvider.useMintMapController();
70
64
  var context$1 = context.useCanvasContext();
71
65
  var currentZIndex = options.zIndex !== undefined ? options.zIndex : 0; // DOM Refs
@@ -175,44 +169,24 @@ var CanvasMarkerLayer = function (props) {
175
169
 
176
170
  var visibleItems = enableViewportCullingRef.current ? dataRef.current.filter(function (item) {
177
171
  return isInViewport(item);
178
- }) : dataRef.current; // topStageZIndex가 설정되고 topOnHover가 true인 경우 hover된 항목은 Top Layer에서 처리
179
- // topOnHover 옵션: hover된 항목을 나중에 그려서 최상위에 표시
172
+ }) : dataRef.current; // topStageZIndex가 설정된 경우 hover된 항목은 Top Layer에서 처리
180
173
 
181
- if (topStageZIndex !== undefined && topOnHover && hovered) {
174
+ if (topStageZIndex !== undefined && hovered) {
182
175
  visibleItems = visibleItems.filter(function (item) {
183
176
  return item.id !== hovered.id;
184
177
  });
185
- } else if (topOnHover && !renderEvent && hovered) {
186
- visibleItems = visibleItems.filter(function (item) {
187
- return item.id !== hovered.id;
188
- });
189
- } // 일반 항목들 먼저 렌더링
190
- // topOnHover가 false이고 renderEvent가 있으면 선택된 항목도 Base Layer에서 렌더링
191
- // (Event Layer에서 선택된 항목을 렌더링하지 않으므로)
192
- // topOnHover가 true이거나 renderEvent가 없으면 선택된 항목은 Base Layer에서 스킵됨
178
+ } // Base Layer는 기본 마커만 렌더링 (성능 최적화)
179
+ // hover/selected는 Event Layer에서 덧그리므로 Base Layer에서는 제외
193
180
 
194
181
 
195
- var baseSelectedIds = !topOnHover && renderEvent ? selectedIdsRef.current : new Set();
196
182
  renderBase({
197
183
  ctx: ctx,
198
- hoveredItem: hovered,
184
+ hoveredItem: null,
199
185
  items: visibleItems,
200
- selectedIds: baseSelectedIds,
186
+ selectedIds: new Set(),
201
187
  utils: renderUtils
202
188
  }); // topStageZIndex가 설정된 경우 hover된 항목은 Top Layer에서 처리하므로 여기서는 스킵
203
- // hover된 항목을 마지막에 렌더링하여 최상위에 표시
204
-
205
- if (topStageZIndex === undefined && topOnHover && !renderEvent && hovered) {
206
- if (!enableViewportCullingRef.current || isInViewport(hovered)) {
207
- renderBase({
208
- ctx: ctx,
209
- hoveredItem: hovered,
210
- items: [hovered],
211
- selectedIds: selectedIdsRef.current,
212
- utils: renderUtils
213
- });
214
- }
215
- }
189
+ // topStageZIndex가 없으면 성능 우선으로 Base Layer 재렌더링 안 함 (hover된 항목이 뒤편 마커 위로 올라갈 수 있음)
216
190
  }
217
191
  });
218
192
  layer.add(shape);
@@ -236,64 +210,45 @@ var CanvasMarkerLayer = function (props) {
236
210
  sceneFunc: function (konvaContext) {
237
211
  var ctx = konvaContext;
238
212
  var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
239
- var hovered = hoveredItemRef.current; // topStageZIndex가 설정되고 topOnHover가 true이면 hover된 항목은 Top Layer에서 처리
213
+ var hovered = hoveredItemRef.current; // selectedItem이 있으면 selectedItems 배열에 포함 (renderMarkerEvent가 selectedItems만 사용하므로)
214
+
215
+ if (selectedItemRef.current) {
216
+ // selectedItem이 이미 selectedItems에 포함되어 있지 않으면 추가
217
+ var selectedItemId_1 = selectedItemRef.current.id;
218
+
219
+ if (!selectedItems.some(function (item) {
220
+ return item.id === selectedItemId_1;
221
+ })) {
222
+ selectedItems = tslib.__spreadArray(tslib.__spreadArray([], selectedItems, true), [selectedItemRef.current], false);
223
+ }
224
+ } // topStageZIndex가 설정된 경우 hover된 항목은 Top Layer에서 처리
240
225
  // event 레이어에서는 hover된 항목을 완전히 제외하고 선택된 항목만 렌더링
241
226
 
242
- if (topStageZIndex !== undefined && topOnHover && hovered) {
243
- // hover된 항목을 제외한 선택된 항목들
244
- var selectedItemsWithoutHovered = selectedItems.filter(function (item) {
227
+
228
+ if (topStageZIndex !== undefined && hovered) {
229
+ // hover된 항목을 제외한 선택된 항목들 (성능 최적화: 필요할 때만 필터링)
230
+ var selectedItemsWithoutHovered = selectedItems.length > 0 ? selectedItems.filter(function (item) {
245
231
  return item.id !== hovered.id;
246
- }); // 선택된 항목만 그리기 (hover된 항목은 Top Layer에서 처리)
232
+ }) : selectedItems; // 선택된 항목만 그리기 (hover된 항목은 Top Layer에서 처리)
247
233
 
248
234
  renderEvent({
249
235
  ctx: ctx,
250
236
  hoveredItem: null,
251
237
  selectedItem: selectedItemRef.current,
252
238
  selectedItems: selectedItemsWithoutHovered,
253
- topOnHover: topOnHover,
254
- utils: renderUtils
255
- });
256
- } else if (topOnHover && hovered) {
257
- renderEvent({
258
- ctx: ctx,
259
- hoveredItem: null,
260
- selectedItem: selectedItemRef.current,
261
- selectedItems: selectedItems.filter(function (item) {
262
- return item.id !== hovered.id;
263
- }),
264
- topOnHover: topOnHover,
239
+ topOnHover: true,
265
240
  utils: renderUtils
266
241
  });
267
-
268
- if (!enableViewportCullingRef.current || isInViewport(hovered)) {
269
- var hoveredIsSelected = selectedItems.some(function (item) {
270
- return item.id === hovered.id;
271
- });
272
- var hoverSelectedItems = hoveredIsSelected ? [hovered] : [];
273
- renderEvent({
274
- ctx: ctx,
275
- hoveredItem: hovered,
276
- selectedItem: selectedItemRef.current,
277
- selectedItems: hoverSelectedItems,
278
- topOnHover: topOnHover,
279
- utils: renderUtils
280
- });
281
- }
282
242
  } else {
283
- // topOnHoverfalse일 때는 hover된 항목을 나중에 그리지 않음
284
- // 하지만 hover 스타일은 적용해야 하므로, hover된 항목이 selectedItems 포함되어 있지 않으면 추가
285
- // renderEvent 함수에 topOnHover 정보를 전달하여,
286
- // renderEvent 함수 내부에서 topOnHover가 false일 때는 hover된 항목을 마지막에 그리지 않도록 함
287
- var hoveredIsSelected = hovered ? selectedItems.some(function (item) {
288
- return item.id === hovered.id;
289
- }) : false;
290
- var finalSelectedItems = hovered && !hoveredIsSelected && !topOnHover ? tslib.__spreadArray(tslib.__spreadArray([], selectedItems, true), [hovered], false) : selectedItems;
243
+ // topStageZIndex없으면 hover된 항목을 selectedItems에 추가하지 않음 (성능 우선)
244
+ // renderEvent 함수가 hoveredItem을 별도로 처리하므로, selectedItems 그대로 전달
245
+ // 이렇게 하면 hover 변경 시 선택된 항목들을 다시 그리지 않고 hover된 항목만 업데이트됨
291
246
  renderEvent({
292
247
  ctx: ctx,
293
248
  hoveredItem: hovered,
294
249
  selectedItem: selectedItemRef.current,
295
- selectedItems: finalSelectedItems,
296
- topOnHover: topOnHover,
250
+ selectedItems: selectedItems,
251
+ topOnHover: false,
297
252
  utils: renderUtils
298
253
  });
299
254
  }
@@ -309,7 +264,7 @@ var CanvasMarkerLayer = function (props) {
309
264
  var doRenderTop = function () {
310
265
  var stage = topStageRef.current;
311
266
  var layer = topLayerRef.current;
312
- if (!stage || !layer || topStageZIndex === undefined || !topOnHover) return;
267
+ if (!stage || !layer || topStageZIndex === undefined) return;
313
268
  var hovered = hoveredItemRef.current;
314
269
  var shape = layer.findOne('.top-render-shape'); // hover된 항목이 없으면 shape 제거
315
270
 
@@ -337,6 +292,8 @@ var CanvasMarkerLayer = function (props) {
337
292
 
338
293
 
339
294
  shape.sceneFunc(function (konvaContext) {
295
+ var _a;
296
+
340
297
  var ctx = konvaContext;
341
298
  var currentHovered = hoveredItemRef.current; // hover된 항목이 없으면 아무것도 그리지 않음
342
299
 
@@ -346,10 +303,11 @@ var CanvasMarkerLayer = function (props) {
346
303
  return;
347
304
  }
348
305
 
349
- var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
306
+ var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current); // selectedItem이 있으면 hover된 항목이 선택되었는지 확인
307
+
350
308
  var hoveredIsSelected = selectedItems.some(function (item) {
351
309
  return item.id === currentHovered.id;
352
- }); // Top Layer에서는 hover된 마커만 hover 스타일로 그리기
310
+ }) || ((_a = selectedItemRef.current) === null || _a === void 0 ? void 0 : _a.id) === currentHovered.id; // Top Layer에서는 hover된 마커만 hover 스타일로 그리기
353
311
  // renderEvent가 있으면: base는 그리지 않고 event만 그리기 (hover 스타일 적용)
354
312
  // renderEvent가 없으면: base를 hover 스타일로 그리기
355
313
 
@@ -361,7 +319,7 @@ var CanvasMarkerLayer = function (props) {
361
319
  hoveredItem: currentHovered,
362
320
  selectedItem: selectedItemRef.current,
363
321
  selectedItems: hoverSelectedItems,
364
- topOnHover: topOnHover,
322
+ topOnHover: true,
365
323
  utils: renderUtils
366
324
  });
367
325
  } else {
@@ -392,13 +350,13 @@ var CanvasMarkerLayer = function (props) {
392
350
  topContainerRef.current.style.transform = containerRef.current.style.transform || '';
393
351
  }
394
352
 
395
- if (topStageZIndex !== undefined && topOnHover) {
353
+ if (topStageZIndex !== undefined) {
396
354
  doRenderTop();
397
355
  }
398
356
  }; // 지도 이벤트 핸들러 생성
399
357
 
400
358
 
401
- var _h = hooks.createMapEventHandlers({
359
+ var _f = hooks.createMapEventHandlers({
402
360
  accumTranslateRef: accumTranslateRef,
403
361
  boundingBoxCacheRef: boundingBoxCacheRef,
404
362
  containerRef: containerRef,
@@ -409,12 +367,30 @@ var CanvasMarkerLayer = function (props) {
409
367
  prevCenterOffsetRef: prevCenterOffsetRef,
410
368
  renderAllImmediate: renderAllImmediate
411
369
  }),
412
- handleIdle = _h.handleIdle,
413
- handleZoomStart = _h.handleZoomStart,
414
- handleZoomEnd = _h.handleZoomEnd,
415
- handleCenterChanged = _h.handleCenterChanged,
416
- handleDragStartShared = _h.handleDragStart,
417
- handleDragEndShared = _h.handleDragEnd;
370
+ handleIdleShared = _f.handleIdle,
371
+ handleZoomStart = _f.handleZoomStart,
372
+ handleZoomEnd = _f.handleZoomEnd,
373
+ handleCenterChangedShared = _f.handleCenterChanged,
374
+ handleDragStartShared = _f.handleDragStart,
375
+ handleDragEndShared = _f.handleDragEnd; // handleIdle 래핑: topStage transform 제거 추가
376
+
377
+
378
+ var handleIdle = function () {
379
+ handleIdleShared(); // 드래그 완료 시 topStage의 transform도 제거 (메인 stage와 동기화)
380
+
381
+ if (topStageZIndex !== undefined && topContainerRef.current) {
382
+ topContainerRef.current.style.transform = '';
383
+ }
384
+ }; // handleCenterChanged 래핑: topStage transform 동기화 추가
385
+
386
+
387
+ var handleCenterChanged = function () {
388
+ handleCenterChangedShared(); // 드래그 중 메인 stage의 transform을 topStage에도 동기화 (반대 방향 이동 방지)
389
+
390
+ if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
391
+ topContainerRef.current.style.transform = containerRef.current.style.transform || '';
392
+ }
393
+ };
418
394
 
419
395
  var handleDragStart = function () {
420
396
  handleDragStartShared(); // 드래그 시작 시점의 hover 상태 저장
@@ -442,8 +418,8 @@ var CanvasMarkerLayer = function (props) {
442
418
 
443
419
 
444
420
  var findData = function (offset) {
445
- // topOnHover 옵션이 켜져 있으면 hover된 항목을 최우선으로 확인
446
- if (topOnHover && hoveredItemRef.current) {
421
+ // topStageZIndex가 설정되어 있으면 hover된 항목을 최우선으로 확인
422
+ if (topStageZIndex !== undefined && hoveredItemRef.current) {
447
423
  var hovered = hoveredItemRef.current;
448
424
 
449
425
  if (utils.isPointInMarkerData(offset, hovered, getOrComputeMarkerOffset)) {
@@ -480,57 +456,40 @@ var CanvasMarkerLayer = function (props) {
480
456
  controller.setMapCursor(hoveredData ? 'pointer' : 'grab');
481
457
  }
482
458
 
483
- if (topStageZIndex !== undefined && topOnHover) {
484
- // topStageZIndex가 설정되고 topOnHover가 true인 경우 Top Layer에서 hover된 항목 렌더링
485
- // base 레이어와 event 레이어도 업데이트 (hover 항목 제외)
486
- doRenderBase();
459
+ if (topStageZIndex !== undefined) {
460
+ // topStageZIndex가 설정된 경우 Top Layer에서 hover된 항목 렌더링
461
+ // Base Layer는 hover 상태 변경 시 재렌더링 불필요 (sceneFunc 내에서 hover 항목 제외 처리)
462
+ // Event Layer는 선택된 항목만 렌더링하므로 업데이트 필요
487
463
  doRenderEvent();
488
464
  doRenderTop();
489
465
  } else if (renderEvent) {
490
- // renderEvent가 있을 때는 Event Layer 업데이트
491
- // topOnHoverfalse일 때는 Base Layer에서도 hover 스타일을 적용해야 하므로 Base Layer도 업데이트
492
- if (!topOnHover) {
493
- doRenderBase();
494
- }
495
-
466
+ // renderEvent가 있을 때는 Event Layer 업데이트
467
+ // topStageZIndex없으면 Base Layer 정적이므로 재렌더링 불필요 (성능 우선)
468
+ // hover 효과는 Event Layer에서만 처리하면 됨
496
469
  doRenderEvent();
497
- } else if (topOnHover) {
498
- doRenderBase();
499
470
  }
500
- }; // 클릭 처리: 선택 상태 업데이트
471
+ }; // 클릭 처리: 선택 상태 업데이트 (단일 선택만 지원)
501
472
 
502
473
 
503
474
  var handleLocalClick = function (clickedData) {
504
- if (enableMultiSelect) {
505
- var newSelected = new Set(selectedIdsRef.current);
475
+ var newSelected = new Set();
506
476
 
507
- if (newSelected.has(clickedData.id)) {
508
- newSelected.delete(clickedData.id);
509
- selectedItemsMapRef.current.delete(clickedData.id);
510
- } else {
511
- newSelected.add(clickedData.id);
512
- selectedItemsMapRef.current.set(clickedData.id, clickedData);
513
- }
477
+ if (!selectedIdsRef.current.has(clickedData.id)) {
478
+ newSelected.add(clickedData.id);
479
+ selectedItemsMapRef.current.clear();
480
+ selectedItemsMapRef.current.set(clickedData.id, clickedData); // externalSelectedItem이 있을 때를 대비해 selectedItemRef도 업데이트
514
481
 
515
- selectedIdsRef.current = newSelected;
482
+ selectedItemRef.current = clickedData;
516
483
  } else {
517
- var newSelected = new Set();
518
-
519
- if (!selectedIdsRef.current.has(clickedData.id)) {
520
- newSelected.add(clickedData.id);
521
- selectedItemsMapRef.current.clear();
522
- selectedItemsMapRef.current.set(clickedData.id, clickedData);
523
- } else {
524
- selectedItemsMapRef.current.clear();
525
- }
526
-
527
- selectedIdsRef.current = newSelected;
484
+ selectedItemsMapRef.current.clear();
485
+ selectedItemRef.current = null;
528
486
  }
529
487
 
530
- doRenderBase();
488
+ selectedIdsRef.current = newSelected; // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
489
+
531
490
  doRenderEvent();
532
491
 
533
- if (topStageZIndex !== undefined && topOnHover) {
492
+ if (topStageZIndex !== undefined) {
534
493
  doRenderTop();
535
494
  }
536
495
  }; // 클릭 이벤트 핸들러
@@ -594,18 +553,15 @@ var CanvasMarkerLayer = function (props) {
594
553
  hoveredItemRef.current = null;
595
554
  controller.setMapCursor('grab');
596
555
 
597
- if (topStageZIndex !== undefined && topOnHover) {
598
- // base 레이어와 event 레이어도 업데이트 (hover 항목 다시 표시)
599
- doRenderBase();
556
+ if (topStageZIndex !== undefined) {
557
+ // Base Layer는 hover 상태 변경 시 재렌더링 불필요 (sceneFunc 내에서 hover 항목 제외 처리)
558
+ // Event Layer는 선택된 항목만 렌더링하므로 업데이트 필요
600
559
  doRenderEvent();
601
560
  doRenderTop();
602
561
  } else if (renderEvent) {
603
- // renderEvent가 있을 때는 Event Layer 업데이트
604
- // topOnHoverfalse일 때는 Base Layer에서도 hover 스타일을 제거해야 하므로 Base Layer도 업데이트
605
- if (!topOnHover) {
606
- doRenderBase();
607
- }
608
-
562
+ // renderEvent가 있을 때는 Event Layer 업데이트
563
+ // topStageZIndex없으면 Base Layer 정적이므로 재렌더링 불필요 (성능 우선)
564
+ // hover 효과 제거는 Event Layer에서만 처리하면 됨
609
565
  doRenderEvent();
610
566
  }
611
567
 
@@ -614,9 +570,9 @@ var CanvasMarkerLayer = function (props) {
614
570
 
615
571
 
616
572
  React.useEffect(function () {
617
- divElement.style.width = 'fit-content'; // Top Layer용 div도 초기화 (topStageZIndex가 설정되고 topOnHover가 true인 경우)
573
+ divElement.style.width = 'fit-content'; // Top Layer용 div도 초기화 (topStageZIndex가 설정된 경우)
618
574
 
619
- if (topStageZIndex !== undefined && topOnHover) {
575
+ if (topStageZIndex !== undefined) {
620
576
  topDivElement.style.width = 'fit-content';
621
577
  }
622
578
 
@@ -802,10 +758,10 @@ var CanvasMarkerLayer = function (props) {
802
758
 
803
759
  renderAllImmediate();
804
760
  }
805
- }, [enableViewportCulling]); // Top Layer용 별도 캔버스 DOM 생성 (topStageZIndex가 설정되고 topOnHover가 true인 경우)
761
+ }, [enableViewportCulling]); // Top Layer용 별도 캔버스 DOM 생성 (topStageZIndex가 설정된 경우)
806
762
 
807
763
  React.useEffect(function () {
808
- if (topStageZIndex === undefined || !topOnHover) return;
764
+ if (topStageZIndex === undefined) return;
809
765
  if (!topContainerRef.current) return;
810
766
  var mapDiv = controller.mapDivElement; // Top Layer용 div 요소 설정
811
767
 
@@ -874,13 +830,14 @@ var CanvasMarkerLayer = function (props) {
874
830
 
875
831
 
876
832
  var handleTopIdle = function () {
877
- // topMarker의 position을 메인 marker와 동일하게 업데이트
833
+ // 드래그 완료 후 topMarker의 position을 메인 marker와 동일하게 업데이트
878
834
  updateTopMarkerPosition(); // topStage 크기 업데이트
879
835
 
880
836
  if (topStageRef.current) {
881
837
  topStageRef.current.width(mapDiv.offsetWidth);
882
838
  topStageRef.current.height(mapDiv.offsetHeight);
883
- } // 메인 stage의 transform topStage에도 동기화
839
+ } // 드래그 완료 transform handleIdle에서 제거되므로 여기서는 동기화만
840
+ // (handleIdle이 먼저 실행되어 transform을 제거한 후, 여기서 position 업데이트)
884
841
 
885
842
 
886
843
  if (topContainerRef.current && containerRef.current) {
@@ -909,13 +866,18 @@ var CanvasMarkerLayer = function (props) {
909
866
  };
910
867
 
911
868
  var handleTopCenterChanged = function () {
912
- // topMarker의 position 메인 marker와 동일하게 업데이트
913
- updateTopMarkerPosition(); // topStage 크기 업데이트
869
+ // 드래그 중에는 transform만 동기화 (position 업데이트는 드래그 완료 후에만)
870
+ // 드래그 중에 position을 업데이트하면 transform과 충돌하여 반대 방향으로 이동하는 버그 발생
871
+ if (!draggingRef.current) {
872
+ // 드래그가 아닐 때만 topMarker의 position을 업데이트
873
+ updateTopMarkerPosition();
874
+ } // topStage 크기 업데이트
875
+
914
876
 
915
877
  if (topStageRef.current) {
916
878
  topStageRef.current.width(mapDiv.offsetWidth);
917
879
  topStageRef.current.height(mapDiv.offsetHeight);
918
- } // 메인 stage의 transform을 topStage에도 동기화
880
+ } // 메인 stage의 transform을 topStage에도 동기화 (드래그 중 필수)
919
881
 
920
882
 
921
883
  if (topContainerRef.current && containerRef.current) {
@@ -957,25 +919,39 @@ var CanvasMarkerLayer = function (props) {
957
919
  }, [topStageZIndex, renderEvent, options]); // 외부 selectedItems 동기화
958
920
 
959
921
  React.useEffect(function () {
960
- if (!stageRef.current) return;
961
- hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
962
- doRenderBase();
922
+ if (!stageRef.current) return; // externalSelectedItems가 있으면 selectedItem은 무시 (공존 불가)
923
+
924
+ if (externalSelectedItems !== undefined) {
925
+ selectedItemRef.current = undefined;
926
+ hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
927
+ } // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
928
+
929
+
963
930
  doRenderEvent();
964
931
 
965
- if (topStageZIndex !== undefined && topOnHover) {
932
+ if (topStageZIndex !== undefined) {
966
933
  doRenderTop();
967
934
  }
968
935
  }, [externalSelectedItems]); // 외부 selectedItem 변경 시 Event Layer 리렌더링
969
936
 
970
937
  React.useEffect(function () {
971
- if (!stageRef.current) return;
972
- selectedItemRef.current = externalSelectedItem;
938
+ if (!stageRef.current) return; // externalSelectedItem이 있으면 selectedItems는 무시 (공존 불가)
939
+
940
+ if (externalSelectedItem !== undefined) {
941
+ selectedIdsRef.current.clear();
942
+ selectedItemsMapRef.current.clear();
943
+ selectedItemRef.current = externalSelectedItem;
944
+ } else if (externalSelectedItems === undefined) {
945
+ // 둘 다 없으면 selectedItemRef만 업데이트 (내부 클릭으로 선택한 항목은 유지)
946
+ selectedItemRef.current = externalSelectedItem;
947
+ }
948
+
973
949
  doRenderEvent();
974
950
 
975
- if (topStageZIndex !== undefined && topOnHover) {
951
+ if (topStageZIndex !== undefined) {
976
952
  doRenderTop();
977
953
  }
978
- }, [externalSelectedItem]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
954
+ }, [externalSelectedItem, externalSelectedItems]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
979
955
 
980
956
  React.useEffect(function () {
981
957
  if (!stageRef.current) return;
@@ -1,3 +1,4 @@
1
1
  export * from '../shared';
2
+ export * from './types';
2
3
  export * from './CanvasMarkerLayer';
3
4
  export { default as CanvasMarkerLayer } from './CanvasMarkerLayer';