mertani-web-toolkit 0.1.63 → 0.1.65

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.
@@ -0,0 +1,424 @@
1
+ <script lang="ts">
2
+ import { onDestroy, type Snippet, tick, mount, unmount } from 'svelte';
3
+ import type { LeafletMouseEvent } from 'leaflet';
4
+ import { useMapContext, useClusterContext, setMarkerItemContext } from '../../utils/mapContext.js';
5
+ import PopupWrapper from './PopupWrapper.svelte';
6
+
7
+ interface MarkerItem {
8
+ lat: number;
9
+ lng: number;
10
+ [key: string]: unknown;
11
+ }
12
+
13
+ interface Props {
14
+ item?: MarkerItem;
15
+ lat?: number;
16
+ lng?: number;
17
+ popupHtml?: string;
18
+ popup?: Snippet;
19
+ title?: string;
20
+ iconUrl?: string;
21
+ iconSize?: [number, number];
22
+ iconAnchor?: [number, number];
23
+ onClick?: (item: MarkerItem | undefined, event: LeafletMouseEvent) => void;
24
+ zoomOnClick?: boolean;
25
+ zoomLevel?: number;
26
+ children?: Snippet;
27
+ }
28
+
29
+ let {
30
+ item,
31
+ lat,
32
+ lng,
33
+ popupHtml,
34
+ popup: popupSnippet,
35
+ title,
36
+ iconUrl,
37
+ iconSize = [32, 32],
38
+ iconAnchor,
39
+ onClick,
40
+ zoomOnClick = true,
41
+ zoomLevel = 15,
42
+ children
43
+ }: Props = $props();
44
+
45
+ const mapStore = useMapContext();
46
+ const clusterStore = useClusterContext();
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ let map: any = null;
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ let cluster: any = null;
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ let marker: any = null;
53
+ let markerElement: HTMLElement | null = $state(null);
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ let popupMountedInstance: any = null;
56
+ let unsub: (() => void) | null = null;
57
+ let clusterUnsub: (() => void) | null = null;
58
+ let popupCloseHandler: (() => void) | null = null;
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ let L: any = null;
61
+
62
+ if (item) {
63
+ setMarkerItemContext(item);
64
+ }
65
+
66
+ const markerLat = $derived(item?.lat ?? lat ?? 0);
67
+ const markerLng = $derived(item?.lng ?? lng ?? 0);
68
+
69
+ function createDefaultIcon() {
70
+ if (!L) {
71
+ throw new Error('Leaflet not initialized');
72
+ }
73
+ return L.divIcon({
74
+ html: `
75
+ <div style="
76
+ width: ${iconSize[0]}px;
77
+ height: ${iconSize[1]}px;
78
+ background-color: var(--color-bg-act-primary);
79
+ border-radius: 50% 50% 50% 0;
80
+ transform: rotate(-45deg);
81
+ border: 3px solid white;
82
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
83
+ position: relative;
84
+ ">
85
+ <div style="
86
+ position: absolute;
87
+ top: 50%;
88
+ left: 50%;
89
+ transform: translate(-50%, -50%) rotate(45deg);
90
+ width: ${Math.round(iconSize[0] * 0.4)}px;
91
+ height: ${Math.round(iconSize[1] * 0.4)}px;
92
+ background-color: white;
93
+ border-radius: 50%;
94
+ "></div>
95
+ </div>
96
+ `,
97
+ iconSize: iconSize,
98
+ iconAnchor: iconAnchor ?? [Math.round(iconSize[0] / 2), iconSize[1]],
99
+ className: 'default-marker-icon'
100
+ });
101
+ }
102
+
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ function createIconFromElement(element: HTMLElement): any {
105
+ if (!L) return null;
106
+
107
+ const cloned = element.cloneNode(true) as HTMLElement;
108
+ cloned.style.position = '';
109
+ cloned.style.left = '';
110
+ cloned.style.top = '';
111
+ cloned.style.visibility = '';
112
+ cloned.style.pointerEvents = '';
113
+ cloned.style.display = '';
114
+
115
+ return L.divIcon({
116
+ html: cloned.outerHTML,
117
+ iconSize: iconSize,
118
+ iconAnchor: iconAnchor ?? [Math.round(iconSize[0] / 2), iconSize[1]],
119
+ className: 'custom-marker-icon'
120
+ });
121
+ }
122
+
123
+ function createDefaultPopupContent(): string {
124
+ let content = '';
125
+ if (item) {
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ content += `<h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600;">${(item as any).name || title || 'Marker'}</h3>`;
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ if ((item as any).id) {
130
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
+ content += `<p style="margin: 0; font-size: 14px; color: #666;">ID: ${(item as any).id}</p>`;
132
+ }
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ if ((item as any).status) {
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ content += `<p style="margin: 4px 0 0 0; font-size: 14px; color: #666;">Status: ${(item as any).status}</p>`;
137
+ }
138
+ } else if (title) {
139
+ content += `<h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600;">${title}</h3>`;
140
+ } else {
141
+ content += `<p>Default Marker Popup</p>`;
142
+ }
143
+ return `<div style="padding: 12px;">${content}</div>`;
144
+ }
145
+
146
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
+ async function bindPopupWithMount(e: any) {
148
+ if (!popupSnippet || !marker || !L || !map) return;
149
+
150
+ const popupContent = document.createElement('div');
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ let element: any;
153
+
154
+ const onClose = () => {
155
+ if (element) {
156
+ unmount(element);
157
+ element = null;
158
+ }
159
+ if (marker) {
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ (marker as any).unbindPopup?.();
162
+ }
163
+ if (map && popupCloseHandler) {
164
+ map.off('popupclose', popupCloseHandler);
165
+ popupCloseHandler = null;
166
+ }
167
+ };
168
+
169
+ element = mount(PopupWrapper, {
170
+ target: popupContent,
171
+ props: {
172
+ snippet: popupSnippet
173
+ }
174
+ });
175
+
176
+ popupMountedInstance = element;
177
+
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ (marker as any)
180
+ .bindPopup?.(popupContent, { closeButton: true, className: 'custom-popup', maxWidth: 320 })
181
+ .openPopup(e.latlng || [markerLat, markerLng]);
182
+
183
+ popupCloseHandler = onClose;
184
+ map.on('popupclose', onClose);
185
+ }
186
+
187
+ async function ensureMarker() {
188
+ if (!map || marker) return;
189
+
190
+ if (!L) {
191
+ const leafletMod = await import('leaflet');
192
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
+ L = (leafletMod as any).default ?? leafletMod;
194
+ }
195
+
196
+ if (!L) return;
197
+
198
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
199
+ let icon: any = undefined;
200
+
201
+ if (children) {
202
+ if (markerElement) {
203
+ icon = createIconFromElement(markerElement);
204
+ } else {
205
+ await tick();
206
+ await new Promise((resolve) => setTimeout(resolve, 50));
207
+ if (markerElement) {
208
+ icon = createIconFromElement(markerElement);
209
+ }
210
+ }
211
+ }
212
+
213
+ if (!icon && iconUrl && iconUrl.length) {
214
+ icon = L.icon({
215
+ iconUrl,
216
+ iconSize,
217
+ iconAnchor: iconAnchor ?? [Math.round(iconSize[0] / 2), iconSize[1]]
218
+ });
219
+ }
220
+
221
+ if (!icon) {
222
+ icon = createDefaultIcon();
223
+ }
224
+
225
+ marker = L.marker([markerLat, markerLng], { title, icon });
226
+
227
+ if (item) {
228
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
229
+ (marker as any)._item = item;
230
+ }
231
+
232
+ if (!popupSnippet && popupHtml) {
233
+ marker.bindPopup(popupHtml, {
234
+ closeButton: true,
235
+ className: 'custom-popup',
236
+ maxWidth: 320
237
+ });
238
+ } else if (!popupSnippet && !popupHtml) {
239
+ const defaultPopup = createDefaultPopupContent();
240
+ marker.bindPopup(defaultPopup, {
241
+ closeButton: true,
242
+ className: 'custom-popup',
243
+ maxWidth: 320
244
+ });
245
+ }
246
+
247
+ if (popupSnippet) {
248
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
249
+ marker.on('click', (e: any) => {
250
+ if (zoomOnClick && map) {
251
+ map.flyTo([markerLat, markerLng], zoomLevel, {
252
+ animate: true,
253
+ duration: 0.5
254
+ });
255
+ }
256
+
257
+ if (onClick) {
258
+ onClick(item, e);
259
+ }
260
+
261
+ bindPopupWithMount(e);
262
+ });
263
+ } else {
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ marker.on('click', (e: any) => {
266
+ if (zoomOnClick && map) {
267
+ map.flyTo([markerLat, markerLng], zoomLevel, {
268
+ animate: true,
269
+ duration: 0.5
270
+ });
271
+ }
272
+
273
+ if (onClick) {
274
+ onClick(item, e);
275
+ }
276
+ });
277
+ }
278
+
279
+ if (cluster) {
280
+ cluster.addLayer(marker);
281
+ } else {
282
+ marker.addTo(map);
283
+ }
284
+ }
285
+
286
+ function sync() {
287
+ if (!marker || !L) return;
288
+ marker.setLatLng([markerLat, markerLng]);
289
+ if (typeof title === 'string') {
290
+ marker.options.title = title;
291
+ }
292
+
293
+ if (children && markerElement && L) {
294
+ const newIcon = createIconFromElement(markerElement);
295
+ if (newIcon) {
296
+ marker.setIcon(newIcon);
297
+ }
298
+ } else if (iconUrl && iconUrl.length && L) {
299
+ const newIcon = L.icon({
300
+ iconUrl,
301
+ iconSize,
302
+ iconAnchor: iconAnchor ?? [Math.round(iconSize[0] / 2), iconSize[1]]
303
+ });
304
+ marker.setIcon(newIcon);
305
+ } else if (!children && !iconUrl) {
306
+ marker.setIcon(createDefaultIcon());
307
+ }
308
+ }
309
+
310
+ $effect(() => {
311
+ if (marker) {
312
+ sync();
313
+ } else {
314
+ ensureMarker();
315
+ }
316
+ });
317
+
318
+ $effect(() => {
319
+ if (marker && children && markerElement && L) {
320
+ (async () => {
321
+ await tick();
322
+ await new Promise((resolve) => setTimeout(resolve, 50));
323
+
324
+ if (markerElement && marker && L) {
325
+ const newIcon = createIconFromElement(markerElement);
326
+ if (newIcon) {
327
+ marker.setIcon(newIcon);
328
+ }
329
+ }
330
+ })();
331
+ }
332
+ });
333
+
334
+ $effect(() => {
335
+ return () => {
336
+ if (popupMountedInstance) {
337
+ unmount(popupMountedInstance);
338
+ popupMountedInstance = null;
339
+ }
340
+ if (popupCloseHandler && map) {
341
+ map.off('popupclose', popupCloseHandler);
342
+ popupCloseHandler = null;
343
+ }
344
+ };
345
+ });
346
+
347
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
348
+ unsub = (mapStore as any).subscribe((m: any) => {
349
+ map = m;
350
+ if (!map && marker) {
351
+ if (cluster) {
352
+ cluster.removeLayer(marker);
353
+ } else {
354
+ marker.remove?.();
355
+ }
356
+ marker = null;
357
+ }
358
+ if (map) ensureMarker();
359
+ });
360
+
361
+ if (clusterStore) {
362
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
363
+ clusterUnsub = (clusterStore as any).subscribe((c: any) => {
364
+ cluster = c;
365
+ if (marker) {
366
+ if (cluster) {
367
+ marker.remove?.();
368
+ cluster.addLayer(marker);
369
+ } else if (map) {
370
+ if (marker._icon && marker._icon.parentNode) {
371
+ marker._icon.parentNode.removeChild(marker._icon);
372
+ }
373
+ marker.addTo(map);
374
+ }
375
+ } else if (map) {
376
+ ensureMarker();
377
+ }
378
+ });
379
+ }
380
+
381
+ onDestroy(() => {
382
+ try {
383
+ unsub?.();
384
+ clusterUnsub?.();
385
+ } finally {
386
+ unsub = null;
387
+ clusterUnsub = null;
388
+ if (marker) {
389
+ marker.off?.();
390
+ if (cluster) {
391
+ cluster.removeLayer(marker);
392
+ } else {
393
+ marker.remove?.();
394
+ }
395
+ marker = null;
396
+ }
397
+ }
398
+ });
399
+ </script>
400
+
401
+ {#if children}
402
+ <div
403
+ bind:this={markerElement}
404
+ style="position: absolute; left: -9999px; top: -9999px; visibility: hidden; pointer-events: none;"
405
+ >
406
+ {@render children()}
407
+ </div>
408
+ {/if}
409
+
410
+ <style>
411
+ :global(.default-marker-icon) {
412
+ background: transparent !important;
413
+ border: none !important;
414
+ }
415
+
416
+ :global(.custom-marker-icon) {
417
+ background: transparent !important;
418
+ border: none !important;
419
+ }
420
+
421
+ :global(.custom-popup .leaflet-popup-content) {
422
+ margin: 0;
423
+ }
424
+ </style>
@@ -0,0 +1,25 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { LeafletMouseEvent } from 'leaflet';
3
+ interface MarkerItem {
4
+ lat: number;
5
+ lng: number;
6
+ [key: string]: unknown;
7
+ }
8
+ interface Props {
9
+ item?: MarkerItem;
10
+ lat?: number;
11
+ lng?: number;
12
+ popupHtml?: string;
13
+ popup?: Snippet;
14
+ title?: string;
15
+ iconUrl?: string;
16
+ iconSize?: [number, number];
17
+ iconAnchor?: [number, number];
18
+ onClick?: (item: MarkerItem | undefined, event: LeafletMouseEvent) => void;
19
+ zoomOnClick?: boolean;
20
+ zoomLevel?: number;
21
+ children?: Snippet;
22
+ }
23
+ declare const MapMarker: import("svelte").Component<Props, {}, "">;
24
+ type MapMarker = ReturnType<typeof MapMarker>;
25
+ export default MapMarker;
@@ -0,0 +1,213 @@
1
+ <script lang="ts">
2
+ import { onDestroy, type Snippet } from 'svelte';
3
+ import type { Map as LMap, Marker, Icon, DivIcon, Layer } from 'leaflet';
4
+ import { useMapContext, createClusterContext } from '../../utils/mapContext.js';
5
+ import type { Writable } from 'svelte/store';
6
+
7
+ type LeafletNamespace = typeof import('leaflet');
8
+
9
+ interface MarkerClusterGroup extends Layer {
10
+ getChildCount(): number;
11
+ getAllChildMarkers(): Marker[];
12
+ addLayer(layer: Marker): this;
13
+ removeLayer(layer: Marker): this;
14
+ clearLayers(): this;
15
+ }
16
+
17
+ interface ClusterOptions {
18
+ chunkedLoading?: boolean;
19
+ chunkInterval?: number;
20
+ chunkDelay?: number;
21
+ spiderfyOnMaxZoom?: boolean;
22
+ showCoverageOnHover?: boolean;
23
+ zoomToBoundsOnClick?: boolean;
24
+ maxClusterRadius?: number;
25
+ iconCreateFunction?: (cluster: MarkerClusterGroup) => Icon | DivIcon | null;
26
+ [key: string]: unknown;
27
+ }
28
+
29
+ interface ClusterIconCreateFunction {
30
+ (cluster: MarkerClusterGroup, markers: Marker[]): Icon | DivIcon | null;
31
+ }
32
+
33
+ let {
34
+ clusterOptions = {},
35
+ iconCreateFunction,
36
+ children
37
+ }: {
38
+ clusterOptions?: ClusterOptions;
39
+ iconCreateFunction?: ClusterIconCreateFunction;
40
+ children?: Snippet;
41
+ } = $props();
42
+
43
+ const mapStore = useMapContext() as Writable<LMap | null>;
44
+ const clusterStore = createClusterContext() as Writable<MarkerClusterGroup | null>;
45
+ let map: LMap | null = null;
46
+ let cluster: MarkerClusterGroup | null = null;
47
+ let L: LeafletNamespace | null = null;
48
+ let unsub: (() => void) | null = null;
49
+
50
+ // Default icon create function if none provided
51
+ function defaultIconCreate(cluster: MarkerClusterGroup): DivIcon {
52
+ if (!L) {
53
+ throw new Error('Leaflet not initialized');
54
+ }
55
+ const count = cluster.getChildCount();
56
+ const size = count < 10 ? 'small' : count < 100 ? 'medium' : 'large';
57
+ return L.divIcon({
58
+ html: `<div class="cluster-icon cluster-${size}"><span>${count}</span></div>`,
59
+ className: 'custom-cluster-icon',
60
+ iconSize: L.point(40, 40)
61
+ });
62
+ }
63
+
64
+ function createCustomIcon(cluster: MarkerClusterGroup): Icon | DivIcon | null {
65
+ if (!iconCreateFunction || !L) {
66
+ return defaultIconCreate(cluster);
67
+ }
68
+
69
+ // Get all markers in the cluster
70
+ const markers: Marker[] = [];
71
+ cluster.getAllChildMarkers().forEach((marker: Marker) => {
72
+ markers.push(marker);
73
+ });
74
+
75
+ try {
76
+ const result = iconCreateFunction(cluster, markers);
77
+ return result || defaultIconCreate(cluster);
78
+ } catch (error) {
79
+ console.error('Error creating custom cluster icon:', error);
80
+ return defaultIconCreate(cluster);
81
+ }
82
+ }
83
+
84
+ async function initCluster() {
85
+ if (!map || cluster) return;
86
+
87
+ if (!L) {
88
+ const leafletMod = await import('leaflet');
89
+ L = (leafletMod as any).default ?? leafletMod;
90
+
91
+ await import('leaflet.markercluster');
92
+ await import('leaflet.markercluster/dist/MarkerCluster.css');
93
+ await import('leaflet.markercluster/dist/MarkerCluster.Default.css');
94
+ }
95
+
96
+ const defaultOptions: ClusterOptions = {
97
+ chunkedLoading: true,
98
+ chunkInterval: 200,
99
+ chunkDelay: 50,
100
+ spiderfyOnMaxZoom: true,
101
+ showCoverageOnHover: false,
102
+ zoomToBoundsOnClick: true,
103
+ maxClusterRadius: 80,
104
+ ...clusterOptions
105
+ };
106
+
107
+ if (iconCreateFunction) {
108
+ defaultOptions.iconCreateFunction = (cluster: MarkerClusterGroup) => {
109
+ if (L) {
110
+ (window as typeof window & { L?: LeafletNamespace }).L = L;
111
+ }
112
+ return createCustomIcon(cluster);
113
+ };
114
+ } else if (!clusterOptions.iconCreateFunction) {
115
+ defaultOptions.iconCreateFunction = defaultIconCreate;
116
+ }
117
+
118
+ const MarkerClusterGroupClass = (
119
+ L as LeafletNamespace & {
120
+ markerClusterGroup: new (options?: ClusterOptions) => MarkerClusterGroup;
121
+ }
122
+ ).markerClusterGroup;
123
+ cluster = new MarkerClusterGroupClass(defaultOptions);
124
+ map.addLayer(cluster);
125
+ clusterStore.set(cluster);
126
+ }
127
+
128
+ function destroyCluster() {
129
+ if (cluster && map) {
130
+ map.removeLayer(cluster);
131
+ cluster.clearLayers();
132
+ cluster = null;
133
+ }
134
+ clusterStore.set(null);
135
+ }
136
+
137
+ unsub = mapStore.subscribe((m: LMap | null) => {
138
+ map = m;
139
+ if (!map) {
140
+ destroyCluster();
141
+ } else {
142
+ initCluster();
143
+ }
144
+ });
145
+
146
+ onDestroy(() => {
147
+ try {
148
+ unsub?.();
149
+ } finally {
150
+ unsub = null;
151
+ destroyCluster();
152
+ }
153
+ });
154
+ </script>
155
+
156
+ {@render children?.()}
157
+
158
+ <style>
159
+ :global(.custom-cluster-icon) {
160
+ background: transparent !important;
161
+ border: none !important;
162
+ }
163
+
164
+ :global(.cluster-icon) {
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ border-radius: 50%;
169
+ font-weight: bold;
170
+ color: var(--color-text-inverse);
171
+ text-align: center;
172
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
173
+ }
174
+
175
+ :global(.cluster-small) {
176
+ width: 40px;
177
+ height: 40px;
178
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 30%) !important;
179
+ backdrop-filter: blur(2px);
180
+ font-size: 14px;
181
+
182
+ &:hover {
183
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 5%) !important;
184
+ backdrop-filter: blur(2px);
185
+ }
186
+ }
187
+
188
+ :global(.cluster-medium) {
189
+ width: 50px;
190
+ height: 50px;
191
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 30%) !important;
192
+ backdrop-filter: blur(2px);
193
+ font-size: 16px;
194
+
195
+ &:hover {
196
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 5%) !important;
197
+ backdrop-filter: blur(2px);
198
+ }
199
+ }
200
+
201
+ :global(.cluster-large) {
202
+ width: 60px;
203
+ height: 60px;
204
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 30%) !important;
205
+ backdrop-filter: blur(2px);
206
+ font-size: 18px;
207
+
208
+ &:hover {
209
+ background-color: color-mix(in srgb, var(--color-bg-act-primary), transparent 5%) !important;
210
+ backdrop-filter: blur(2px);
211
+ }
212
+ }
213
+ </style>
@@ -0,0 +1,31 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { Marker, Icon, DivIcon, Layer } from 'leaflet';
3
+ interface MarkerClusterGroup extends Layer {
4
+ getChildCount(): number;
5
+ getAllChildMarkers(): Marker[];
6
+ addLayer(layer: Marker): this;
7
+ removeLayer(layer: Marker): this;
8
+ clearLayers(): this;
9
+ }
10
+ interface ClusterOptions {
11
+ chunkedLoading?: boolean;
12
+ chunkInterval?: number;
13
+ chunkDelay?: number;
14
+ spiderfyOnMaxZoom?: boolean;
15
+ showCoverageOnHover?: boolean;
16
+ zoomToBoundsOnClick?: boolean;
17
+ maxClusterRadius?: number;
18
+ iconCreateFunction?: (cluster: MarkerClusterGroup) => Icon | DivIcon | null;
19
+ [key: string]: unknown;
20
+ }
21
+ interface ClusterIconCreateFunction {
22
+ (cluster: MarkerClusterGroup, markers: Marker[]): Icon | DivIcon | null;
23
+ }
24
+ type $$ComponentProps = {
25
+ clusterOptions?: ClusterOptions;
26
+ iconCreateFunction?: ClusterIconCreateFunction;
27
+ children?: Snippet;
28
+ };
29
+ declare const MapMarkerGroup: import("svelte").Component<$$ComponentProps, {}, "">;
30
+ type MapMarkerGroup = ReturnType<typeof MapMarkerGroup>;
31
+ export default MapMarkerGroup;