@mint-ui/map 1.2.0-test.65 → 1.2.0-test.67

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
@@ -170,49 +164,20 @@ var CanvasMarkerLayer = function (props) {
170
164
  name: 'base-render-shape',
171
165
  perfectDrawEnabled: false,
172
166
  sceneFunc: function (konvaContext) {
173
- var ctx = konvaContext;
174
- var hovered = hoveredItemRef.current; // 뷰포트 컬링: 화면에 보이는 항목만 필터링
167
+ var ctx = konvaContext; // 뷰포트 컬링: 화면에 보이는 항목만 필터링
175
168
 
176
169
  var visibleItems = enableViewportCullingRef.current ? dataRef.current.filter(function (item) {
177
170
  return isInViewport(item);
178
- }) : dataRef.current; // topStageZIndex가 설정되고 topOnHover가 true인 경우 hover된 항목은 Top Layer에서 처리
179
- // topOnHover 옵션: hover된 항목을 나중에 그려서 최상위에 표시
180
-
181
- if (topStageZIndex !== undefined && topOnHover && hovered) {
182
- visibleItems = visibleItems.filter(function (item) {
183
- return item.id !== hovered.id;
184
- });
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에서 스킵됨
193
-
194
-
195
- var baseSelectedIds = !topOnHover && renderEvent ? selectedIdsRef.current : new Set();
171
+ }) : dataRef.current; // Base Layer는 기본 마커만 렌더링 (hover된 항목도 포함)
172
+ // hover 스타일은 Event Layer와 Top Layer에서 덧그려짐
173
+
196
174
  renderBase({
197
175
  ctx: ctx,
198
- hoveredItem: hovered,
176
+ hoveredItem: null,
199
177
  items: visibleItems,
200
- selectedIds: baseSelectedIds,
178
+ selectedIds: new Set(),
201
179
  utils: renderUtils
202
- }); // 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
- }
180
+ });
216
181
  }
217
182
  });
218
183
  layer.add(shape);
@@ -236,67 +201,30 @@ var CanvasMarkerLayer = function (props) {
236
201
  sceneFunc: function (konvaContext) {
237
202
  var ctx = konvaContext;
238
203
  var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
239
- var hovered = hoveredItemRef.current; // topStageZIndex가 설정되고 topOnHover가 true이면 hover된 항목은 Top Layer에서 처리
240
- // event 레이어에서는 hover된 항목을 완전히 제외하고 선택된 항목만 렌더링
241
-
242
- if (topStageZIndex !== undefined && topOnHover && hovered) {
243
- // hover된 항목을 제외한 선택된 항목들
244
- var selectedItemsWithoutHovered = selectedItems.filter(function (item) {
245
- return item.id !== hovered.id;
246
- }); // 선택된 항목만 그리기 (hover된 항목은 Top Layer에서 처리)
247
-
248
- renderEvent({
249
- ctx: ctx,
250
- hoveredItem: null,
251
- selectedItem: selectedItemRef.current,
252
- 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,
265
- utils: renderUtils
266
- });
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
- });
204
+ var hovered = hoveredItemRef.current; // selectedItem이 있으면 selectedItems 배열에 포함 (renderMarkerEvent가 selectedItems만 사용하므로)
205
+
206
+ if (selectedItemRef.current) {
207
+ // selectedItem이 이미 selectedItems에 포함되어 있지 않으면 추가
208
+ var selectedItemId_1 = selectedItemRef.current.id;
209
+
210
+ if (!selectedItems.some(function (item) {
211
+ return item.id === selectedItemId_1;
212
+ })) {
213
+ selectedItems = tslib.__spreadArray(tslib.__spreadArray([], selectedItems, true), [selectedItemRef.current], false);
281
214
  }
282
- } 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;
291
- renderEvent({
292
- ctx: ctx,
293
- hoveredItem: hovered,
294
- selectedItem: selectedItemRef.current,
295
- selectedItems: finalSelectedItems,
296
- topOnHover: topOnHover,
297
- utils: renderUtils
298
- });
299
- }
215
+ } // Event Layer는 hover 효과 및 선택 상태를 덧그림
216
+ // topStageZIndex있으면 hover된 항목은 Top Stage에서도 처리되지만,
217
+ // Event Layer에서도 hover 스타일을 덧그려서 Base Layer 위에 표시됨
218
+
219
+
220
+ renderEvent({
221
+ ctx: ctx,
222
+ hasTopStage: topStageZIndex !== undefined,
223
+ hoveredItem: hovered,
224
+ selectedItem: selectedItemRef.current,
225
+ selectedItems: selectedItems,
226
+ utils: renderUtils
227
+ });
300
228
  }
301
229
  });
302
230
  layer.add(shape);
@@ -309,7 +237,7 @@ var CanvasMarkerLayer = function (props) {
309
237
  var doRenderTop = function () {
310
238
  var stage = topStageRef.current;
311
239
  var layer = topLayerRef.current;
312
- if (!stage || !layer || topStageZIndex === undefined || !topOnHover) return;
240
+ if (!stage || !layer || topStageZIndex === undefined) return;
313
241
  var hovered = hoveredItemRef.current;
314
242
  var shape = layer.findOne('.top-render-shape'); // hover된 항목이 없으면 shape 제거
315
243
 
@@ -337,6 +265,8 @@ var CanvasMarkerLayer = function (props) {
337
265
 
338
266
 
339
267
  shape.sceneFunc(function (konvaContext) {
268
+ var _a;
269
+
340
270
  var ctx = konvaContext;
341
271
  var currentHovered = hoveredItemRef.current; // hover된 항목이 없으면 아무것도 그리지 않음
342
272
 
@@ -346,10 +276,11 @@ var CanvasMarkerLayer = function (props) {
346
276
  return;
347
277
  }
348
278
 
349
- var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current);
279
+ var selectedItems = helpers.mapValuesToArray(selectedItemsMapRef.current); // selectedItem이 있으면 hover된 항목이 선택되었는지 확인
280
+
350
281
  var hoveredIsSelected = selectedItems.some(function (item) {
351
282
  return item.id === currentHovered.id;
352
- }); // Top Layer에서는 hover된 마커만 hover 스타일로 그리기
283
+ }) || ((_a = selectedItemRef.current) === null || _a === void 0 ? void 0 : _a.id) === currentHovered.id; // Top Layer에서는 hover된 마커만 hover 스타일로 그리기
353
284
  // renderEvent가 있으면: base는 그리지 않고 event만 그리기 (hover 스타일 적용)
354
285
  // renderEvent가 없으면: base를 hover 스타일로 그리기
355
286
 
@@ -358,10 +289,10 @@ var CanvasMarkerLayer = function (props) {
358
289
  var hoverSelectedItems = hoveredIsSelected ? [currentHovered] : [];
359
290
  renderEvent({
360
291
  ctx: ctx,
292
+ hasTopStage: true,
361
293
  hoveredItem: currentHovered,
362
294
  selectedItem: selectedItemRef.current,
363
295
  selectedItems: hoverSelectedItems,
364
- topOnHover: topOnHover,
365
296
  utils: renderUtils
366
297
  });
367
298
  } else {
@@ -392,13 +323,13 @@ var CanvasMarkerLayer = function (props) {
392
323
  topContainerRef.current.style.transform = containerRef.current.style.transform || '';
393
324
  }
394
325
 
395
- if (topStageZIndex !== undefined && topOnHover) {
326
+ if (topStageZIndex !== undefined) {
396
327
  doRenderTop();
397
328
  }
398
329
  }; // 지도 이벤트 핸들러 생성
399
330
 
400
331
 
401
- var _h = hooks.createMapEventHandlers({
332
+ var _f = hooks.createMapEventHandlers({
402
333
  accumTranslateRef: accumTranslateRef,
403
334
  boundingBoxCacheRef: boundingBoxCacheRef,
404
335
  containerRef: containerRef,
@@ -409,12 +340,30 @@ var CanvasMarkerLayer = function (props) {
409
340
  prevCenterOffsetRef: prevCenterOffsetRef,
410
341
  renderAllImmediate: renderAllImmediate
411
342
  }),
412
- handleIdle = _h.handleIdle,
413
- handleZoomStart = _h.handleZoomStart,
414
- handleZoomEnd = _h.handleZoomEnd,
415
- handleCenterChanged = _h.handleCenterChanged,
416
- handleDragStartShared = _h.handleDragStart,
417
- handleDragEndShared = _h.handleDragEnd;
343
+ handleIdleShared = _f.handleIdle,
344
+ handleZoomStart = _f.handleZoomStart,
345
+ handleZoomEnd = _f.handleZoomEnd,
346
+ handleCenterChangedShared = _f.handleCenterChanged,
347
+ handleDragStartShared = _f.handleDragStart,
348
+ handleDragEndShared = _f.handleDragEnd; // handleIdle 래핑: topStage transform 제거 추가
349
+
350
+
351
+ var handleIdle = function () {
352
+ handleIdleShared(); // 드래그 완료 시 topStage의 transform도 제거 (메인 stage와 동기화)
353
+
354
+ if (topStageZIndex !== undefined && topContainerRef.current) {
355
+ topContainerRef.current.style.transform = '';
356
+ }
357
+ }; // handleCenterChanged 래핑: topStage transform 동기화 추가
358
+
359
+
360
+ var handleCenterChanged = function () {
361
+ handleCenterChangedShared(); // 드래그 중 메인 stage의 transform을 topStage에도 동기화 (반대 방향 이동 방지)
362
+
363
+ if (topStageZIndex !== undefined && topContainerRef.current && containerRef.current) {
364
+ topContainerRef.current.style.transform = containerRef.current.style.transform || '';
365
+ }
366
+ };
418
367
 
419
368
  var handleDragStart = function () {
420
369
  handleDragStartShared(); // 드래그 시작 시점의 hover 상태 저장
@@ -442,8 +391,8 @@ var CanvasMarkerLayer = function (props) {
442
391
 
443
392
 
444
393
  var findData = function (offset) {
445
- // topOnHover 옵션이 켜져 있으면 hover된 항목을 최우선으로 확인
446
- if (topOnHover && hoveredItemRef.current) {
394
+ // topStageZIndex가 설정되어 있으면 hover된 항목을 최우선으로 확인
395
+ if (topStageZIndex !== undefined && hoveredItemRef.current) {
447
396
  var hovered = hoveredItemRef.current;
448
397
 
449
398
  if (utils.isPointInMarkerData(offset, hovered, getOrComputeMarkerOffset)) {
@@ -480,57 +429,40 @@ var CanvasMarkerLayer = function (props) {
480
429
  controller.setMapCursor(hoveredData ? 'pointer' : 'grab');
481
430
  }
482
431
 
483
- if (topStageZIndex !== undefined && topOnHover) {
484
- // topStageZIndex가 설정되고 topOnHover가 true인 경우 Top Layer에서 hover된 항목 렌더링
485
- // base 레이어와 event 레이어도 업데이트 (hover 항목 제외)
486
- doRenderBase();
432
+ if (topStageZIndex !== undefined) {
433
+ // topStageZIndex가 설정된 경우 Top Layer에서 hover된 항목 렌더링
434
+ // Base Layer는 모든 마커를 포함하므로 hover 상태 변경 시 재렌더링 불필요
435
+ // Event Layer는 hover 스타일과 선택된 항목을 렌더링하므로 업데이트 필요
487
436
  doRenderEvent();
488
437
  doRenderTop();
489
438
  } else if (renderEvent) {
490
- // renderEvent가 있을 때는 Event Layer 업데이트
491
- // topOnHoverfalse일 때는 Base Layer에서도 hover 스타일을 적용해야 하므로 Base Layer도 업데이트
492
- if (!topOnHover) {
493
- doRenderBase();
494
- }
495
-
439
+ // renderEvent가 있을 때는 Event Layer 업데이트
440
+ // topStageZIndex없으면 Base Layer 정적이므로 재렌더링 불필요 (성능 우선)
441
+ // hover 효과는 Event Layer에서만 처리하면 됨
496
442
  doRenderEvent();
497
- } else if (topOnHover) {
498
- doRenderBase();
499
443
  }
500
- }; // 클릭 처리: 선택 상태 업데이트
444
+ }; // 클릭 처리: 선택 상태 업데이트 (단일 선택만 지원)
501
445
 
502
446
 
503
447
  var handleLocalClick = function (clickedData) {
504
- if (enableMultiSelect) {
505
- var newSelected = new Set(selectedIdsRef.current);
448
+ var newSelected = new Set();
506
449
 
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
- }
450
+ if (!selectedIdsRef.current.has(clickedData.id)) {
451
+ newSelected.add(clickedData.id);
452
+ selectedItemsMapRef.current.clear();
453
+ selectedItemsMapRef.current.set(clickedData.id, clickedData); // externalSelectedItem이 있을 때를 대비해 selectedItemRef도 업데이트
514
454
 
515
- selectedIdsRef.current = newSelected;
455
+ selectedItemRef.current = clickedData;
516
456
  } 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;
457
+ selectedItemsMapRef.current.clear();
458
+ selectedItemRef.current = null;
528
459
  }
529
460
 
530
- doRenderBase();
461
+ selectedIdsRef.current = newSelected; // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
462
+
531
463
  doRenderEvent();
532
464
 
533
- if (topStageZIndex !== undefined && topOnHover) {
465
+ if (topStageZIndex !== undefined) {
534
466
  doRenderTop();
535
467
  }
536
468
  }; // 클릭 이벤트 핸들러
@@ -594,18 +526,15 @@ var CanvasMarkerLayer = function (props) {
594
526
  hoveredItemRef.current = null;
595
527
  controller.setMapCursor('grab');
596
528
 
597
- if (topStageZIndex !== undefined && topOnHover) {
598
- // base 레이어와 event 레이어도 업데이트 (hover 항목 다시 표시)
599
- doRenderBase();
529
+ if (topStageZIndex !== undefined) {
530
+ // Base Layer는 모든 마커를 포함하므로 hover 상태 변경 시 재렌더링 불필요
531
+ // Event Layer는 hover 스타일과 선택된 항목을 렌더링하므로 업데이트 필요
600
532
  doRenderEvent();
601
533
  doRenderTop();
602
534
  } else if (renderEvent) {
603
- // renderEvent가 있을 때는 Event Layer 업데이트
604
- // topOnHoverfalse일 때는 Base Layer에서도 hover 스타일을 제거해야 하므로 Base Layer도 업데이트
605
- if (!topOnHover) {
606
- doRenderBase();
607
- }
608
-
535
+ // renderEvent가 있을 때는 Event Layer 업데이트
536
+ // topStageZIndex없으면 Base Layer 정적이므로 재렌더링 불필요 (성능 우선)
537
+ // hover 효과 제거는 Event Layer에서만 처리하면 됨
609
538
  doRenderEvent();
610
539
  }
611
540
 
@@ -614,9 +543,9 @@ var CanvasMarkerLayer = function (props) {
614
543
 
615
544
 
616
545
  React.useEffect(function () {
617
- divElement.style.width = 'fit-content'; // Top Layer용 div도 초기화 (topStageZIndex가 설정되고 topOnHover가 true인 경우)
546
+ divElement.style.width = 'fit-content'; // Top Layer용 div도 초기화 (topStageZIndex가 설정된 경우)
618
547
 
619
- if (topStageZIndex !== undefined && topOnHover) {
548
+ if (topStageZIndex !== undefined) {
620
549
  topDivElement.style.width = 'fit-content';
621
550
  }
622
551
 
@@ -802,10 +731,10 @@ var CanvasMarkerLayer = function (props) {
802
731
 
803
732
  renderAllImmediate();
804
733
  }
805
- }, [enableViewportCulling]); // Top Layer용 별도 캔버스 DOM 생성 (topStageZIndex가 설정되고 topOnHover가 true인 경우)
734
+ }, [enableViewportCulling]); // Top Layer용 별도 캔버스 DOM 생성 (topStageZIndex가 설정된 경우)
806
735
 
807
736
  React.useEffect(function () {
808
- if (topStageZIndex === undefined || !topOnHover) return;
737
+ if (topStageZIndex === undefined) return;
809
738
  if (!topContainerRef.current) return;
810
739
  var mapDiv = controller.mapDivElement; // Top Layer용 div 요소 설정
811
740
 
@@ -874,13 +803,14 @@ var CanvasMarkerLayer = function (props) {
874
803
 
875
804
 
876
805
  var handleTopIdle = function () {
877
- // topMarker의 position을 메인 marker와 동일하게 업데이트
806
+ // 드래그 완료 후 topMarker의 position을 메인 marker와 동일하게 업데이트
878
807
  updateTopMarkerPosition(); // topStage 크기 업데이트
879
808
 
880
809
  if (topStageRef.current) {
881
810
  topStageRef.current.width(mapDiv.offsetWidth);
882
811
  topStageRef.current.height(mapDiv.offsetHeight);
883
- } // 메인 stage의 transform topStage에도 동기화
812
+ } // 드래그 완료 transform handleIdle에서 제거되므로 여기서는 동기화만
813
+ // (handleIdle이 먼저 실행되어 transform을 제거한 후, 여기서 position 업데이트)
884
814
 
885
815
 
886
816
  if (topContainerRef.current && containerRef.current) {
@@ -909,13 +839,18 @@ var CanvasMarkerLayer = function (props) {
909
839
  };
910
840
 
911
841
  var handleTopCenterChanged = function () {
912
- // topMarker의 position 메인 marker와 동일하게 업데이트
913
- updateTopMarkerPosition(); // topStage 크기 업데이트
842
+ // 드래그 중에는 transform만 동기화 (position 업데이트는 드래그 완료 후에만)
843
+ // 드래그 중에 position을 업데이트하면 transform과 충돌하여 반대 방향으로 이동하는 버그 발생
844
+ if (!draggingRef.current) {
845
+ // 드래그가 아닐 때만 topMarker의 position을 업데이트
846
+ updateTopMarkerPosition();
847
+ } // topStage 크기 업데이트
848
+
914
849
 
915
850
  if (topStageRef.current) {
916
851
  topStageRef.current.width(mapDiv.offsetWidth);
917
852
  topStageRef.current.height(mapDiv.offsetHeight);
918
- } // 메인 stage의 transform을 topStage에도 동기화
853
+ } // 메인 stage의 transform을 topStage에도 동기화 (드래그 중 필수)
919
854
 
920
855
 
921
856
  if (topContainerRef.current && containerRef.current) {
@@ -957,25 +892,39 @@ var CanvasMarkerLayer = function (props) {
957
892
  }, [topStageZIndex, renderEvent, options]); // 외부 selectedItems 동기화
958
893
 
959
894
  React.useEffect(function () {
960
- if (!stageRef.current) return;
961
- hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
962
- doRenderBase();
895
+ if (!stageRef.current) return; // externalSelectedItems가 있으면 selectedItem은 무시 (공존 불가)
896
+
897
+ if (externalSelectedItems !== undefined) {
898
+ selectedItemRef.current = undefined;
899
+ hooks.syncExternalSelectedItems(externalSelectedItems, selectedIdsRef, selectedItemsMapRef);
900
+ } // 선택 상태 변경은 Event Layer에서만 처리 (Base Layer는 정적이므로 재렌더링 불필요)
901
+
902
+
963
903
  doRenderEvent();
964
904
 
965
- if (topStageZIndex !== undefined && topOnHover) {
905
+ if (topStageZIndex !== undefined) {
966
906
  doRenderTop();
967
907
  }
968
908
  }, [externalSelectedItems]); // 외부 selectedItem 변경 시 Event Layer 리렌더링
969
909
 
970
910
  React.useEffect(function () {
971
- if (!stageRef.current) return;
972
- selectedItemRef.current = externalSelectedItem;
911
+ if (!stageRef.current) return; // externalSelectedItem이 있으면 selectedItems는 무시 (공존 불가)
912
+
913
+ if (externalSelectedItem !== undefined) {
914
+ selectedIdsRef.current.clear();
915
+ selectedItemsMapRef.current.clear();
916
+ selectedItemRef.current = externalSelectedItem;
917
+ } else if (externalSelectedItems === undefined) {
918
+ // 둘 다 없으면 selectedItemRef만 업데이트 (내부 클릭으로 선택한 항목은 유지)
919
+ selectedItemRef.current = externalSelectedItem;
920
+ }
921
+
973
922
  doRenderEvent();
974
923
 
975
- if (topStageZIndex !== undefined && topOnHover) {
924
+ if (topStageZIndex !== undefined) {
976
925
  doRenderTop();
977
926
  }
978
- }, [externalSelectedItem]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
927
+ }, [externalSelectedItem, externalSelectedItems]); // 데이터 변경 시 렌더링 (캐시 정리 및 선택 상태 동기화)
979
928
 
980
929
  React.useEffect(function () {
981
930
  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';