@mint-ui/map 1.2.0-test.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +11 -4
- package/.vscode/settings.json +32 -9
- package/dist/components/mint-map/core/MintMapController.d.ts +1 -0
- package/dist/components/mint-map/core/MintMapCore.js +5 -6
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.d.ts +12 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/CanvasMarkerLayer.js +962 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/index.d.ts +4 -0
- package/dist/components/mint-map/core/advanced/CanvasMarkerLayer/types.d.ts +280 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.d.ts +17 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/CanvasPolygonLayer.js +624 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/index.d.ts +4 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.d.ts +303 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/renderer.js +1091 -0
- package/dist/components/mint-map/core/advanced/CanvasPolygonLayer/types.d.ts +284 -0
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarker.d.ts +7 -0
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerClaude.js +7 -7
- package/dist/components/mint-map/core/advanced/canvas/index.d.ts +0 -1
- package/dist/components/mint-map/core/advanced/index.d.ts +4 -2
- package/dist/components/mint-map/core/advanced/shared/context.d.ts +44 -0
- package/dist/components/mint-map/core/advanced/shared/context.js +230 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.js +41 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.js +196 -0
- package/dist/components/mint-map/core/advanced/{woongCanvas/shared → shared}/index.d.ts +5 -2
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +82 -0
- package/dist/components/mint-map/core/advanced/shared/performance.js +288 -0
- package/dist/components/mint-map/core/advanced/shared/types.d.ts +150 -0
- package/dist/components/mint-map/core/advanced/shared/types.js +31 -0
- package/dist/components/mint-map/core/advanced/shared/utils.d.ts +173 -0
- package/dist/components/mint-map/core/advanced/shared/utils.js +382 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.js +52 -0
- package/dist/components/mint-map/core/wrapper/MapMarkerWrapper.js +22 -1
- package/dist/components/mint-map/google/GoogleMintMapController.d.ts +1 -0
- package/dist/components/mint-map/google/GoogleMintMapController.js +13 -8
- package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +1 -0
- package/dist/components/mint-map/kakao/KakaoMintMapController.js +13 -8
- package/dist/components/mint-map/naver/NaverMintMapController.d.ts +3 -0
- package/dist/components/mint-map/naver/NaverMintMapController.js +46 -11
- package/dist/index.es.js +5605 -4056
- package/dist/index.js +47 -27
- package/dist/index.umd.js +5621 -4059
- package/package.json +1 -1
- package/CLAUDE.md +0 -100
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.d.ts +0 -22
- package/dist/components/mint-map/core/advanced/canvas/CanvasMarkerHanquf.js +0 -413
- package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +0 -11
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +0 -53
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.js +0 -1123
- package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +0 -3
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.d.ts +0 -31
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.js +0 -164
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.d.ts +0 -161
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.js +0 -343
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +0 -131
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.js +0 -14
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +0 -31
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.js +0 -164
- package/dist/components/mint-map/core/util/geohash.js +0 -125
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { MarkerOptions, Offset } from '../../../types';
|
|
3
|
+
import type { CanvasData } from '../shared';
|
|
4
|
+
/**
|
|
5
|
+
* 폴리곤 스타일 정의
|
|
6
|
+
*/
|
|
7
|
+
export interface PolygonStyle {
|
|
8
|
+
/** 채우기 색상 */
|
|
9
|
+
fillColor: string;
|
|
10
|
+
/** 테두리 색상 */
|
|
11
|
+
strokeColor: string;
|
|
12
|
+
/** 테두리 두께 */
|
|
13
|
+
lineWidth: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 폴리곤 스타일 컨텍스트 (선택/호버/활성 상태 정보)
|
|
17
|
+
*/
|
|
18
|
+
export interface PolygonStyleContext {
|
|
19
|
+
/** 선택 여부 */
|
|
20
|
+
isSelected: boolean;
|
|
21
|
+
/** 호버 여부 */
|
|
22
|
+
isHovered: boolean;
|
|
23
|
+
/** 활성 여부 (마지막 선택된 항목) */
|
|
24
|
+
isActive: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 폴리곤 스타일 객체
|
|
28
|
+
*
|
|
29
|
+
* 상태별 스타일을 정의합니다. 개별 props 방식과 동일한 알고리즘으로 적용됩니다.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* renderStyle={{
|
|
34
|
+
* base: { fillColor: 'gray', strokeColor: 'black', lineWidth: 2 },
|
|
35
|
+
* selected: { fillColor: 'green', strokeColor: 'darkgreen', lineWidth: 4 },
|
|
36
|
+
* active: { fillColor: 'yellow', strokeColor: 'orange', lineWidth: 5 },
|
|
37
|
+
* hovered: { fillColor: 'blue', strokeColor: 'darkblue', lineWidth: 3 }
|
|
38
|
+
* }}
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export interface PolygonStyleObject {
|
|
42
|
+
/** 기본 폴리곤 스타일 (필수) */
|
|
43
|
+
base: PolygonStyle;
|
|
44
|
+
/** 선택된 폴리곤 스타일 */
|
|
45
|
+
selected?: PolygonStyle;
|
|
46
|
+
/** 활성 폴리곤 스타일 (마지막 선택된 항목) */
|
|
47
|
+
active?: PolygonStyle;
|
|
48
|
+
/** 호버된 폴리곤 스타일 */
|
|
49
|
+
hovered?: PolygonStyle;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 폴리곤 스타일 커스터마이징 함수 타입
|
|
53
|
+
*
|
|
54
|
+
* item 데이터와 상태 정보를 기반으로 자유롭게 스타일을 커스터마이징할 수 있습니다.
|
|
55
|
+
* 스타일을 반환하지 않거나 null/undefined를 반환하면 해당 폴리곤을 그리지 않습니다.
|
|
56
|
+
*
|
|
57
|
+
* @param item 폴리곤 데이터
|
|
58
|
+
* @param context 선택/호버/활성 상태 정보
|
|
59
|
+
* @returns 커스터마이징된 폴리곤 스타일 (반환하지 않거나 null/undefined면 그리지 않음)
|
|
60
|
+
*/
|
|
61
|
+
export declare type PolygonStyleCustomizer<T> = (item: CanvasData<T>, context: PolygonStyleContext) => PolygonStyle | null | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* 폴리곤 스타일 커스터마이징 객체 타입
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // 예시 1: deps 있음 (외부 상태를 참조하는 경우)
|
|
68
|
+
* <CanvasPolygonLayer
|
|
69
|
+
* customStyle={{
|
|
70
|
+
* callback: (item, context) => {
|
|
71
|
+
* // 상태 정보를 기반으로 스타일 결정
|
|
72
|
+
* if (context.isActive) {
|
|
73
|
+
* return { fillColor: 'yellow', strokeColor: 'orange', lineWidth: 5 };
|
|
74
|
+
* }
|
|
75
|
+
* if (context.isHovered) {
|
|
76
|
+
* return { fillColor: 'blue', strokeColor: 'darkblue', lineWidth: 3 };
|
|
77
|
+
* }
|
|
78
|
+
* if (context.isSelected) {
|
|
79
|
+
* return { fillColor: 'green', strokeColor: 'darkgreen', lineWidth: 4 };
|
|
80
|
+
* }
|
|
81
|
+
* return null;
|
|
82
|
+
* },
|
|
83
|
+
* deps: [selectedPolygon] // 외부 상태 변경 시 자동 재렌더링
|
|
84
|
+
* }}
|
|
85
|
+
* />
|
|
86
|
+
*
|
|
87
|
+
* // 예시 2: deps 없음 (외부 상태를 참조하지 않는 경우)
|
|
88
|
+
* <CanvasPolygonLayer
|
|
89
|
+
* customStyle={{
|
|
90
|
+
* callback: (item, context) => {
|
|
91
|
+
* if (item.area > 1000) {
|
|
92
|
+
* return { fillColor: 'red', strokeColor: 'darkred', lineWidth: 2 };
|
|
93
|
+
* }
|
|
94
|
+
* return null;
|
|
95
|
+
* }
|
|
96
|
+
* }}
|
|
97
|
+
* />
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export interface PolygonStyleCustomizerWithDeps<T> {
|
|
101
|
+
/** 스타일 커스터마이징 콜백 함수 (필수) */
|
|
102
|
+
callback: PolygonStyleCustomizer<T>;
|
|
103
|
+
/** 외부 상태 의존성 배열 (선택, 변경 시 Canvas 재렌더링) */
|
|
104
|
+
deps?: React.DependencyList;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 공통 Props (모든 방식 공통)
|
|
108
|
+
*/
|
|
109
|
+
export interface CanvasPolygonLayerBaseProps<T> extends Pick<MarkerOptions, 'zIndex' | 'anchor' | 'visible'> {
|
|
110
|
+
/** 렌더링할 폴리곤 데이터 배열 */
|
|
111
|
+
data: CanvasData<T>[];
|
|
112
|
+
/** 폴리곤 클릭 시 호출되는 콜백 함수 */
|
|
113
|
+
onClick?: (payload: CanvasData<T>, selectedIds: Set<string>) => void;
|
|
114
|
+
/** 다중 선택 활성화 여부 (기본값: false) */
|
|
115
|
+
enableMultiSelect?: boolean;
|
|
116
|
+
/** 뷰포트 컬링 여유 공간 (픽셀 단위, 기본값: 100) */
|
|
117
|
+
cullingMargin?: number;
|
|
118
|
+
/** Queue 캐시 최대 크기 (기본값: 30000) */
|
|
119
|
+
maxCacheSize?: number;
|
|
120
|
+
/** 외부에서 제어하는 선택된 항목 배열 */
|
|
121
|
+
selectedItems?: CanvasData<T>[];
|
|
122
|
+
/** 외부에서 전달된 단일 선택 아이템 */
|
|
123
|
+
selectedItem?: CanvasData<T> | null;
|
|
124
|
+
/** 상호작용 비활성화 여부 (기본값: false) */
|
|
125
|
+
disableInteraction?: boolean;
|
|
126
|
+
/** 최적화 데이터 콜백 (디버깅/모니터링용) */
|
|
127
|
+
onOptimizationDataUpdate?: (data: {
|
|
128
|
+
cacheSize: number;
|
|
129
|
+
cacheEntries: Array<[string, number[][][][] | Offset]>;
|
|
130
|
+
spatialGridCells: Map<string, CanvasData<T>[]>;
|
|
131
|
+
}) => void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 개별 Props 방식 (기존 방식)
|
|
135
|
+
*
|
|
136
|
+
* baseFillColor, baseStrokeColor, baseLineWidth가 있으면 자동으로 개별 props 방식으로 인식됩니다.
|
|
137
|
+
*/
|
|
138
|
+
export interface CanvasPolygonLayerPropsWithIndividualStyles<T> extends CanvasPolygonLayerBaseProps<T> {
|
|
139
|
+
/** 기본 폴리곤 채우기 색상 (필수) */
|
|
140
|
+
baseFillColor: string;
|
|
141
|
+
/** 기본 폴리곤 테두리 색상 (필수) */
|
|
142
|
+
baseStrokeColor: string;
|
|
143
|
+
/** 기본 폴리곤 테두리 두께 (필수) */
|
|
144
|
+
baseLineWidth: number;
|
|
145
|
+
/** 선택된 폴리곤 채우기 색상 */
|
|
146
|
+
selectedFillColor?: string;
|
|
147
|
+
/** 선택된 폴리곤 테두리 색상 */
|
|
148
|
+
selectedStrokeColor?: string;
|
|
149
|
+
/** 선택된 폴리곤 테두리 두께 */
|
|
150
|
+
selectedLineWidth?: number;
|
|
151
|
+
/** 마지막 선택된 폴리곤(Active) 채우기 색상 */
|
|
152
|
+
activeFillColor?: string;
|
|
153
|
+
/** 마지막 선택된 폴리곤(Active) 테두리 색상 */
|
|
154
|
+
activeStrokeColor?: string;
|
|
155
|
+
/** 마지막 선택된 폴리곤(Active) 테두리 두께 */
|
|
156
|
+
activeLineWidth?: number;
|
|
157
|
+
/** Hover 시 폴리곤 채우기 색상 */
|
|
158
|
+
hoveredFillColor?: string;
|
|
159
|
+
/** Hover 시 폴리곤 테두리 색상 */
|
|
160
|
+
hoveredStrokeColor?: string;
|
|
161
|
+
/** Hover 시 폴리곤 테두리 두께 */
|
|
162
|
+
hoveredLineWidth?: number;
|
|
163
|
+
/** 다른 방식 props는 사용 불가 */
|
|
164
|
+
renderStyle?: never;
|
|
165
|
+
/**
|
|
166
|
+
* 폴리곤 스타일 커스터마이징 객체 (선택)
|
|
167
|
+
*
|
|
168
|
+
* customStyle이 제공되면 우선적으로 사용되며, null/undefined를 반환하면 개별 props로 정의된 기본 스타일을 사용합니다.
|
|
169
|
+
* item 데이터와 상태 정보를 기반으로 자유롭게 스타일을 커스터마이징할 수 있습니다.
|
|
170
|
+
*/
|
|
171
|
+
customStyle?: PolygonStyleCustomizerWithDeps<T>;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 객체 Props 방식
|
|
175
|
+
*
|
|
176
|
+
* renderStyle 객체가 있으면 자동으로 객체 방식으로 인식됩니다.
|
|
177
|
+
* 상태별 스타일을 정의하면 개별 props 방식과 동일한 알고리즘으로 적용됩니다.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* renderStyle={{
|
|
182
|
+
* base: { fillColor: 'gray', strokeColor: 'black', lineWidth: 2 },
|
|
183
|
+
* selected: { fillColor: 'green', strokeColor: 'darkgreen', lineWidth: 4 },
|
|
184
|
+
* active: { fillColor: 'yellow', strokeColor: 'orange', lineWidth: 5 },
|
|
185
|
+
* hovered: { fillColor: 'blue', strokeColor: 'darkblue', lineWidth: 3 }
|
|
186
|
+
* }}
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export interface CanvasPolygonLayerPropsWithObjectStyle<T> extends CanvasPolygonLayerBaseProps<T> {
|
|
190
|
+
/**
|
|
191
|
+
* 폴리곤 스타일 객체
|
|
192
|
+
*
|
|
193
|
+
* 상태별 스타일을 정의합니다. 개별 props 방식과 동일한 알고리즘으로 적용됩니다.
|
|
194
|
+
*/
|
|
195
|
+
renderStyle: PolygonStyleObject;
|
|
196
|
+
/** 다른 방식 props는 사용 불가 */
|
|
197
|
+
activeFillColor?: never;
|
|
198
|
+
activeLineWidth?: never;
|
|
199
|
+
activeStrokeColor?: never;
|
|
200
|
+
baseFillColor?: never;
|
|
201
|
+
baseLineWidth?: never;
|
|
202
|
+
baseStrokeColor?: never;
|
|
203
|
+
hoveredFillColor?: never;
|
|
204
|
+
hoveredLineWidth?: never;
|
|
205
|
+
hoveredStrokeColor?: never;
|
|
206
|
+
selectedFillColor?: never;
|
|
207
|
+
selectedLineWidth?: never;
|
|
208
|
+
selectedStrokeColor?: never;
|
|
209
|
+
/**
|
|
210
|
+
* 폴리곤 스타일 커스터마이징 객체 (선택)
|
|
211
|
+
*
|
|
212
|
+
* customStyle이 제공되면 우선적으로 사용되며, null/undefined를 반환하면 renderStyle로 정의된 기본 스타일을 사용합니다.
|
|
213
|
+
* item 데이터와 상태 정보를 기반으로 자유롭게 스타일을 커스터마이징할 수 있습니다.
|
|
214
|
+
*/
|
|
215
|
+
customStyle?: PolygonStyleCustomizerWithDeps<T>;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 함수 Props 방식 (커스터마이징 방식)
|
|
219
|
+
*
|
|
220
|
+
* customStyle 함수가 있으면 자동으로 함수 방식으로 인식됩니다.
|
|
221
|
+
* item 데이터와 상태 정보를 기반으로 자유롭게 스타일을 커스터마이징할 수 있습니다.
|
|
222
|
+
* 스타일을 반환하지 않거나 null/undefined를 반환하면 해당 폴리곤을 그리지 않습니다.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* customStyle={(item, context) => {
|
|
227
|
+
* // item의 속성에 따라 동적으로 스타일 결정
|
|
228
|
+
* if (item.someProperty > 100) {
|
|
229
|
+
* return { fillColor: 'red', strokeColor: 'darkred', lineWidth: 3 };
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* // 상태 정보를 기반으로 스타일 결정
|
|
233
|
+
* if (context.isActive) {
|
|
234
|
+
* return { fillColor: 'yellow', strokeColor: 'orange', lineWidth: 5 };
|
|
235
|
+
* }
|
|
236
|
+
* if (context.isHovered) {
|
|
237
|
+
* return { fillColor: 'blue', strokeColor: 'darkblue', lineWidth: 3 };
|
|
238
|
+
* }
|
|
239
|
+
* if (context.isSelected) {
|
|
240
|
+
* return { fillColor: 'green', strokeColor: 'darkgreen', lineWidth: 4 };
|
|
241
|
+
* }
|
|
242
|
+
*
|
|
243
|
+
* // 아무것도 반환하지 않으면 그리지 않음 (return null 또는 return undefined도 가능)
|
|
244
|
+
* }}
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export interface CanvasPolygonLayerPropsWithCustomStyle<T> extends CanvasPolygonLayerBaseProps<T> {
|
|
248
|
+
/**
|
|
249
|
+
* 폴리곤 스타일 커스터마이징 객체
|
|
250
|
+
*
|
|
251
|
+
* item 데이터와 상태 정보를 기반으로 자유롭게 스타일을 커스터마이징할 수 있습니다.
|
|
252
|
+
* 스타일을 반환하지 않거나 null/undefined를 반환하면 해당 폴리곤을 그리지 않습니다.
|
|
253
|
+
*/
|
|
254
|
+
customStyle: PolygonStyleCustomizerWithDeps<T>;
|
|
255
|
+
/** 다른 방식 props는 사용 불가 */
|
|
256
|
+
activeFillColor?: never;
|
|
257
|
+
activeLineWidth?: never;
|
|
258
|
+
activeStrokeColor?: never;
|
|
259
|
+
baseFillColor?: never;
|
|
260
|
+
baseLineWidth?: never;
|
|
261
|
+
baseStrokeColor?: never;
|
|
262
|
+
hoveredFillColor?: never;
|
|
263
|
+
hoveredLineWidth?: never;
|
|
264
|
+
hoveredStrokeColor?: never;
|
|
265
|
+
renderStyle?: never;
|
|
266
|
+
selectedFillColor?: never;
|
|
267
|
+
selectedLineWidth?: never;
|
|
268
|
+
selectedStrokeColor?: never;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* CanvasPolygonLayer Props (Discriminated Union)
|
|
272
|
+
*
|
|
273
|
+
* 세 가지 스타일 지정 방식을 지원:
|
|
274
|
+
* 1. 개별 props 방식: baseFillColor, baseStrokeColor, baseLineWidth가 있으면 자동으로 개별 props 방식
|
|
275
|
+
* 2. 객체 방식: renderStyle 객체가 있으면 자동으로 객체 방식
|
|
276
|
+
* 3. 함수 방식: customStyle 함수가 있으면 자동으로 함수 방식
|
|
277
|
+
*
|
|
278
|
+
* customStyle은 개별 props 방식 또는 renderStyle 객체 방식과 함께 사용할 수 있습니다.
|
|
279
|
+
* customStyle이 제공되면 우선적으로 사용되며, null/undefined를 반환하면 기본 스타일을 사용합니다.
|
|
280
|
+
* styleMode prop은 필요 없으며, 각 prop의 존재 여부로 자동 판단됩니다.
|
|
281
|
+
*
|
|
282
|
+
* @template T 폴리곤 데이터의 추가 속성 타입
|
|
283
|
+
*/
|
|
284
|
+
export declare type CanvasPolygonLayerProps<T> = CanvasPolygonLayerPropsWithIndividualStyles<T> | CanvasPolygonLayerPropsWithObjectStyle<T> | CanvasPolygonLayerPropsWithCustomStyle<T>;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { MarkerOptions, Offset, Position } from "../../../types";
|
|
3
|
+
/**
|
|
4
|
+
* 2026-01-06 장한별
|
|
5
|
+
* 고주영팀장님이 만든 캔버스 마커
|
|
6
|
+
* 클릭, hover 등의 이벤트가 구현되어있지 않는 버전
|
|
7
|
+
* RA 지도 제품에서 CanvasMarker 컴포넌트를 사용하고 있음
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
3
10
|
export interface CanvasMarkerRendererParams<T> {
|
|
4
11
|
context: CanvasRenderingContext2D;
|
|
5
12
|
offset: Offset[];
|
|
@@ -107,12 +107,12 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
107
107
|
var hoveredPolygonRef = React.useRef(null);
|
|
108
108
|
var clickedPolygonRef = React.useRef(null);
|
|
109
109
|
var hoveredMarkerRef = React.useRef(null);
|
|
110
|
-
var clickedMarkerRef = React.useRef(null); // 🚀
|
|
110
|
+
var clickedMarkerRef = React.useRef(null); // 🚀 CanvasMarker 방식: 단순한 data ref (geohash 제거)
|
|
111
111
|
|
|
112
112
|
var polygonsRef = React.useRef([]);
|
|
113
113
|
var markersRef = React.useRef([]); // 마커 경계 정보 저장 (hit-test용)
|
|
114
114
|
|
|
115
|
-
var markerBoundsRef = React.useRef(new Map()); // 🚀 드래그 추적용 (
|
|
115
|
+
var markerBoundsRef = React.useRef(new Map()); // 🚀 드래그 추적용 (CanvasMarker 방식)
|
|
116
116
|
|
|
117
117
|
var draggingRef = React.useRef(false);
|
|
118
118
|
var prevCenterOffsetRef = React.useRef(null);
|
|
@@ -172,7 +172,7 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
172
172
|
if (!baseContextRef.current || !polygonsRef.current || polygonsRef.current.length === 0) return;
|
|
173
173
|
canvasUtil.clearRect(baseCanvasRef.current, baseContextRef.current);
|
|
174
174
|
var cacheMissCount = 0; // 캐시 미스 카운트
|
|
175
|
-
// 🚀
|
|
175
|
+
// 🚀 CanvasMarker 방식: 전체 데이터 순회 (필터링 제거)
|
|
176
176
|
|
|
177
177
|
polygonsRef.current.forEach(function (polygon) {
|
|
178
178
|
if (polygon.visible === false) return; // 🚀 캐시 확인 (ID 기반으로 변경)
|
|
@@ -243,7 +243,7 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
243
243
|
if (!markersRef.current || markersRef.current.length === 0) return;
|
|
244
244
|
canvasUtil.clearRect(baseMarkerCanvasRef.current, baseMarkerContextRef.current); // 마커 경계 정보 초기화
|
|
245
245
|
|
|
246
|
-
markerBoundsRef.current.clear(); // 🚀
|
|
246
|
+
markerBoundsRef.current.clear(); // 🚀 CanvasMarker 방식: 전체 데이터 순회 (필터링 제거)
|
|
247
247
|
|
|
248
248
|
markersRef.current.forEach(function (marker) {
|
|
249
249
|
var _a, _b;
|
|
@@ -566,7 +566,7 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
566
566
|
}
|
|
567
567
|
|
|
568
568
|
return null;
|
|
569
|
-
}; // IDLE 이벤트 핸들러 (
|
|
569
|
+
}; // IDLE 이벤트 핸들러 (CanvasMarker 최적화 적용)
|
|
570
570
|
|
|
571
571
|
|
|
572
572
|
var handleIdle = function () {
|
|
@@ -632,7 +632,7 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
632
632
|
x: event.clientX - rect.left,
|
|
633
633
|
y: event.clientY - rect.top
|
|
634
634
|
};
|
|
635
|
-
}; // 🚀 지도 클릭 이벤트 핸들러 (
|
|
635
|
+
}; // 🚀 지도 클릭 이벤트 핸들러 (CanvasMarker 방식 - controller.addEventListener)
|
|
636
636
|
|
|
637
637
|
|
|
638
638
|
var handleMapClick = function (event) {
|
|
@@ -724,7 +724,7 @@ var CanvasMarkerClaude = React__default["default"].memo(function CanvasMarkerCla
|
|
|
724
724
|
var rafIdRef = React.useRef(); // 마우스 이벤트 핸들러 (requestAnimationFrame으로 최적화)
|
|
725
725
|
|
|
726
726
|
var handleMouseMove = function (e) {
|
|
727
|
-
// 🚀 드래그 중이면 호버 처리 스킵 (
|
|
727
|
+
// 🚀 드래그 중이면 호버 처리 스킵 (CanvasMarker 방식)
|
|
728
728
|
if (draggingRef.current) return; // 이미 RAF가 대기 중이면 스킵
|
|
729
729
|
|
|
730
730
|
if (rafIdRef.current) return;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export * from './shapes';
|
|
2
1
|
export * from './canvas';
|
|
2
|
+
export * from './CanvasMarkerLayer';
|
|
3
|
+
export * from './CanvasPolygonLayer';
|
|
3
4
|
export * from './MapBuildingProjection';
|
|
4
5
|
export * from './MapLoadingComponents';
|
|
5
|
-
export * from './
|
|
6
|
+
export * from './shapes';
|
|
7
|
+
export * from './shared';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { QueueCache } from './performance';
|
|
3
|
+
import { CanvasData } from './types';
|
|
4
|
+
import { Offset } from '../../../types';
|
|
5
|
+
/**
|
|
6
|
+
* Canvas 컴포넌트 인스턴스 인터페이스
|
|
7
|
+
*
|
|
8
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
9
|
+
*/
|
|
10
|
+
export interface ComponentInstance<T> {
|
|
11
|
+
zIndex: number;
|
|
12
|
+
hitTest: (offset: Offset) => boolean;
|
|
13
|
+
onClick?: (payload: CanvasData<T>, selectedIds: Set<string>) => void;
|
|
14
|
+
onMouseOver?: (payload: CanvasData<T>) => void;
|
|
15
|
+
onMouseOut?: (payload: CanvasData<T>) => void;
|
|
16
|
+
findData: (offset: Offset) => CanvasData<T> | null;
|
|
17
|
+
setHovered: (data: CanvasData<T> | null) => void;
|
|
18
|
+
handleLocalClick: (data: CanvasData<T>) => void;
|
|
19
|
+
getSelectedIds: () => Set<string>;
|
|
20
|
+
isInteractionDisabled: () => boolean;
|
|
21
|
+
}
|
|
22
|
+
interface CanvasContextValue {
|
|
23
|
+
registerComponent: <T>(instance: ComponentInstance<T>) => void;
|
|
24
|
+
unregisterComponent: <T>(instance: ComponentInstance<T>) => void;
|
|
25
|
+
sharedMarkerCache: QueueCache<string, Offset>;
|
|
26
|
+
sharedPolygonCache: QueueCache<string, number[][][][]>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* CanvasProvider 컴포넌트
|
|
30
|
+
*
|
|
31
|
+
* 다중 Canvas 인스턴스를 관리하고 zIndex 기반 이벤트 우선순위를 처리합니다.
|
|
32
|
+
*/
|
|
33
|
+
export declare const CanvasProvider: React.FC<{
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
markerMaxCacheSize?: number;
|
|
36
|
+
polygonMaxCacheSize?: number;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Canvas Context Hook
|
|
40
|
+
*
|
|
41
|
+
* @returns CanvasContextValue 또는 null (Provider 없으면)
|
|
42
|
+
*/
|
|
43
|
+
export declare const useCanvasContext: () => CanvasContextValue | null;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var performance = require('./performance.js');
|
|
7
|
+
var MintMapProvider = require('../../provider/MintMapProvider.js');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
|
+
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
12
|
+
|
|
13
|
+
var CanvasContext = React.createContext(null);
|
|
14
|
+
/**
|
|
15
|
+
* CanvasProvider 컴포넌트
|
|
16
|
+
*
|
|
17
|
+
* 다중 Canvas 인스턴스를 관리하고 zIndex 기반 이벤트 우선순위를 처리합니다.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/* eslint-disable no-restricted-syntax */
|
|
21
|
+
|
|
22
|
+
/* eslint-disable no-continue */
|
|
23
|
+
|
|
24
|
+
var CanvasProvider = function (_a) {
|
|
25
|
+
var children = _a.children,
|
|
26
|
+
_b = _a.markerMaxCacheSize,
|
|
27
|
+
markerMaxCacheSize = _b === void 0 ? 5000 : _b,
|
|
28
|
+
_c = _a.polygonMaxCacheSize,
|
|
29
|
+
polygonMaxCacheSize = _c === void 0 ? 30000 : _c;
|
|
30
|
+
var controller = MintMapProvider.useMintMapController(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
|
|
32
|
+
var componentsRef = React.useRef([]); // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
|
|
34
|
+
var currentHoveredRef = React.useRef(null); // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
|
|
36
|
+
var currentHoveredDataRef = React.useRef(null);
|
|
37
|
+
var draggingRef = React.useRef(false); // 공유 캐시 (모든 레이어가 공유)
|
|
38
|
+
|
|
39
|
+
var sharedMarkerCacheRef = React.useRef(new performance.QueueCache(markerMaxCacheSize));
|
|
40
|
+
var sharedPolygonCacheRef = React.useRef(new performance.QueueCache(polygonMaxCacheSize)); // 컴포넌트 등록 (zIndex 내림차순 정렬)
|
|
41
|
+
|
|
42
|
+
var registerComponent = React.useCallback(function (instance) {
|
|
43
|
+
componentsRef.current.push(instance);
|
|
44
|
+
componentsRef.current.sort(function (a, b) {
|
|
45
|
+
return b.zIndex - a.zIndex;
|
|
46
|
+
});
|
|
47
|
+
}, []); // 컴포넌트 등록 해제
|
|
48
|
+
|
|
49
|
+
var unregisterComponent = React.useCallback(function (instance) {
|
|
50
|
+
if (currentHoveredRef.current === instance) {
|
|
51
|
+
currentHoveredRef.current = null;
|
|
52
|
+
currentHoveredDataRef.current = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
componentsRef.current = componentsRef.current.filter(function (c) {
|
|
56
|
+
return c !== instance;
|
|
57
|
+
});
|
|
58
|
+
}, []); // 전역 클릭 핸들러 (zIndex 우선순위)
|
|
59
|
+
|
|
60
|
+
var handleGlobalClick = React.useCallback(function (event) {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
if (!((_a = event === null || event === void 0 ? void 0 : event.param) === null || _a === void 0 ? void 0 : _a.position)) return;
|
|
65
|
+
var clickedOffset = void 0;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
clickedOffset = controller.positionToOffset(event.param.position);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// positionToOffset 실패 시 조용히 종료 (기존 동작 보장)
|
|
71
|
+
console.warn('[CanvasProvider] positionToOffset failed:', error);
|
|
72
|
+
return;
|
|
73
|
+
} // zIndex 내림차순으로 정렬된 컴포넌트 순회 (높은 zIndex가 먼저 처리)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
for (var _i = 0, _c = componentsRef.current; _i < _c.length; _i++) {
|
|
77
|
+
var component = _c[_i];
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
if (component.isInteractionDisabled()) continue;
|
|
81
|
+
var data = component.findData(clickedOffset);
|
|
82
|
+
if (!data) continue; // 첫 번째로 찾은 항목만 처리하고 종료 (zIndex 우선순위)
|
|
83
|
+
|
|
84
|
+
component.handleLocalClick(data);
|
|
85
|
+
(_b = component.onClick) === null || _b === void 0 ? void 0 : _b.call(component, data, component.getSelectedIds());
|
|
86
|
+
return;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// 개별 컴포넌트 처리 중 에러 발생 시 다음 컴포넌트로 계속 진행
|
|
89
|
+
console.warn('[CanvasProvider] Component click handler error:', error);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// 전체 핸들러 에러 시 조용히 종료 (앱 크래시 방지)
|
|
95
|
+
console.error('[CanvasProvider] handleGlobalClick error:', error);
|
|
96
|
+
}
|
|
97
|
+
}, [controller]); // 전역 마우스 이동 핸들러 (zIndex 우선순위)
|
|
98
|
+
|
|
99
|
+
var handleGlobalMouseMove = React.useCallback(function (event) {
|
|
100
|
+
var _a;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
if (draggingRef.current || !((_a = event === null || event === void 0 ? void 0 : event.param) === null || _a === void 0 ? void 0 : _a.position)) return;
|
|
104
|
+
var mouseOffset = void 0;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
mouseOffset = controller.positionToOffset(event.param.position);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// positionToOffset 실패 시 조용히 종료 (기존 동작 보장)
|
|
110
|
+
console.warn('[CanvasProvider] positionToOffset failed:', error);
|
|
111
|
+
return;
|
|
112
|
+
} // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
var newHoveredComponent = null; // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
116
|
+
|
|
117
|
+
var newHoveredData = null; // zIndex 내림차순으로 정렬된 컴포넌트 순회 (높은 zIndex가 먼저 처리)
|
|
118
|
+
|
|
119
|
+
for (var _i = 0, _b = componentsRef.current; _i < _b.length; _i++) {
|
|
120
|
+
var component = _b[_i];
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
if (component.isInteractionDisabled()) continue;
|
|
124
|
+
var data = component.findData(mouseOffset);
|
|
125
|
+
if (!data) continue; // 첫 번째로 찾은 항목만 hover 처리 (zIndex 우선순위)
|
|
126
|
+
|
|
127
|
+
newHoveredComponent = component;
|
|
128
|
+
newHoveredData = data;
|
|
129
|
+
break;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
// 개별 컴포넌트 처리 중 에러 발생 시 다음 컴포넌트로 계속 진행
|
|
132
|
+
console.warn('[CanvasProvider] Component mouse move handler error:', error);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
} // hover 상태가 변경되지 않았으면 종료 (불필요한 렌더링 방지)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if (currentHoveredRef.current === newHoveredComponent && currentHoveredDataRef.current === newHoveredData) {
|
|
139
|
+
return;
|
|
140
|
+
} // 기존 hover 항목에 mouseOut 이벤트 발생
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if (currentHoveredRef.current) {
|
|
144
|
+
try {
|
|
145
|
+
currentHoveredRef.current.setHovered(null);
|
|
146
|
+
|
|
147
|
+
if (currentHoveredRef.current.onMouseOut && currentHoveredDataRef.current) {
|
|
148
|
+
currentHoveredRef.current.onMouseOut(currentHoveredDataRef.current);
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
// mouseOut 이벤트 에러 시 조용히 처리 (기존 동작 보장)
|
|
152
|
+
console.warn('[CanvasProvider] mouseOut handler error:', error);
|
|
153
|
+
}
|
|
154
|
+
} // 새 hover 항목에 mouseOver 이벤트 발생
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if (newHoveredComponent && newHoveredData) {
|
|
158
|
+
try {
|
|
159
|
+
newHoveredComponent.setHovered(newHoveredData);
|
|
160
|
+
|
|
161
|
+
if (newHoveredComponent.onMouseOver) {
|
|
162
|
+
newHoveredComponent.onMouseOver(newHoveredData);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// mouseOver 이벤트 에러 시 조용히 처리 (기존 동작 보장)
|
|
166
|
+
console.warn('[CanvasProvider] mouseOver handler error:', error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
currentHoveredRef.current = newHoveredComponent;
|
|
171
|
+
currentHoveredDataRef.current = newHoveredData;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
// 전체 핸들러 에러 시 조용히 종료 (앱 크래시 방지)
|
|
174
|
+
console.error('[CanvasProvider] handleGlobalMouseMove error:', error);
|
|
175
|
+
}
|
|
176
|
+
}, [controller]);
|
|
177
|
+
var handleZoomStart = React.useCallback(function () {
|
|
178
|
+
draggingRef.current = true;
|
|
179
|
+
}, []);
|
|
180
|
+
var handleIdle = React.useCallback(function () {
|
|
181
|
+
draggingRef.current = false;
|
|
182
|
+
}, []);
|
|
183
|
+
React.useEffect(function () {
|
|
184
|
+
try {
|
|
185
|
+
controller.addEventListener('CLICK', handleGlobalClick);
|
|
186
|
+
controller.addEventListener('MOUSEMOVE', handleGlobalMouseMove);
|
|
187
|
+
controller.addEventListener('ZOOMSTART', handleZoomStart);
|
|
188
|
+
controller.addEventListener('IDLE', handleIdle);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// 이벤트 리스너 등록 실패 시 경고만 출력 (기존 동작 보장)
|
|
191
|
+
console.warn('[CanvasProvider] Failed to add event listeners:', error);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return function () {
|
|
195
|
+
try {
|
|
196
|
+
controller.removeEventListener('CLICK', handleGlobalClick);
|
|
197
|
+
controller.removeEventListener('MOUSEMOVE', handleGlobalMouseMove);
|
|
198
|
+
controller.removeEventListener('ZOOMSTART', handleZoomStart);
|
|
199
|
+
controller.removeEventListener('IDLE', handleIdle);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// cleanup 중 에러 발생 시 조용히 처리 (controller가 이미 destroy된 경우 등)
|
|
202
|
+
// 에러를 무시해도 메모리 누수는 발생하지 않음 (이벤트 리스너는 자동으로 정리됨)
|
|
203
|
+
console.warn('[CanvasProvider] Failed to remove event listeners:', error);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}, [controller, handleGlobalClick, handleGlobalMouseMove, handleZoomStart, handleIdle]);
|
|
207
|
+
var contextValue = React.useMemo(function () {
|
|
208
|
+
return {
|
|
209
|
+
registerComponent: registerComponent,
|
|
210
|
+
sharedMarkerCache: sharedMarkerCacheRef.current,
|
|
211
|
+
sharedPolygonCache: sharedPolygonCacheRef.current,
|
|
212
|
+
unregisterComponent: unregisterComponent
|
|
213
|
+
};
|
|
214
|
+
}, [registerComponent, unregisterComponent]);
|
|
215
|
+
return React__default["default"].createElement(CanvasContext.Provider, {
|
|
216
|
+
value: contextValue
|
|
217
|
+
}, children);
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Canvas Context Hook
|
|
221
|
+
*
|
|
222
|
+
* @returns CanvasContextValue 또는 null (Provider 없으면)
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
var useCanvasContext = function () {
|
|
226
|
+
return React.useContext(CanvasContext);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
exports.CanvasProvider = CanvasProvider;
|
|
230
|
+
exports.useCanvasContext = useCanvasContext;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Offset } from '../../../types';
|
|
2
|
+
import { EventParam, MapUIEventParam } from '../../../types';
|
|
3
|
+
import type { MintMapController } from '../../MintMapController';
|
|
4
|
+
/**
|
|
5
|
+
* 이벤트 유효성 검증 및 좌표 변환
|
|
6
|
+
*
|
|
7
|
+
* @param event 이벤트 파라미터
|
|
8
|
+
* @param context CanvasContext 인스턴스
|
|
9
|
+
* @param controller MintMapController 인스턴스
|
|
10
|
+
* @returns 유효한 화면 좌표 또는 null
|
|
11
|
+
*/
|
|
12
|
+
export declare const validateEvent: (event: EventParam<MapUIEventParam> | null | undefined, context: any, controller: MintMapController) => Offset | null;
|
|
13
|
+
/**
|
|
14
|
+
* Map의 values를 배열로 변환
|
|
15
|
+
*
|
|
16
|
+
* @template T Map 값의 타입
|
|
17
|
+
* @param map 변환할 Map
|
|
18
|
+
* @returns Map의 값 배열
|
|
19
|
+
*/
|
|
20
|
+
export declare const mapValuesToArray: <T>(map: Map<string, T>) => T[];
|