|
|
@@ -14,6 +14,7 @@ export interface MarkerOptions {
|
|
|
type?: 'place' | 'studio' | 'visitor'
|
|
|
title?: string
|
|
|
onClick?: () => void
|
|
|
+ ghost?: boolean
|
|
|
}
|
|
|
|
|
|
const TILE_URL = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
|
|
|
@@ -23,13 +24,19 @@ const MARKER_COLORS: Record<string, string> = {
|
|
|
visitor: '#22c55e',
|
|
|
}
|
|
|
|
|
|
-function createIcon(type: string, size: number): L.DivIcon {
|
|
|
- const color = MARKER_COLORS[type] || '#3b82f6'
|
|
|
+function createIcon(type: string, size: number, ghost?: boolean): L.DivIcon {
|
|
|
+ const color = ghost
|
|
|
+ ? 'radial-gradient(circle, rgba(255, 215, 0, 0.9) 0%, rgba(255, 215, 0, 0.3) 50%, rgba(255, 215, 0, 0) 70%)'
|
|
|
+ : MARKER_COLORS[type] || '#3b82f6'
|
|
|
+ const style = ghost
|
|
|
+ ? `width:${size * 2.5}px;height:${size * 2.5}px;background:${color};border-radius:50%;box-shadow:0 0 20px rgba(255, 215, 0, 0.4);pointer-events:none`
|
|
|
+ : `width:${size}px;height:${size}px;background:${color};border-radius:50%;border:${size > 20 ? '3px' : '2px'} solid white;box-shadow:0 2px 8px rgba(0,0,0,0.3)`
|
|
|
+
|
|
|
return L.divIcon({
|
|
|
className: '',
|
|
|
- iconSize: [size, size],
|
|
|
- iconAnchor: [size / 2, size / 2],
|
|
|
- html: `<div style="width:${size}px;height:${size}px;background:${color};border-radius:50%;border:${size > 20 ? '3px' : '2px'} solid white;box-shadow:0 2px 8px rgba(0,0,0,0.3);transition:transform 0.2s"></div>`,
|
|
|
+ iconSize: ghost ? [size * 2.5, size * 2.5] : [size, size],
|
|
|
+ iconAnchor: ghost ? [size * 2.5 / 2, size * 2.5 / 2] : [size / 2, size / 2],
|
|
|
+ html: `<div style="${style}"></div>`,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -42,6 +49,7 @@ export interface MapProvider {
|
|
|
setCenter(lat: number, lng: number): void
|
|
|
fitBounds(bounds: [[number, number], [number, number]]): void
|
|
|
onMove(cb: (center: [number, number], zoom: number, bounds: MapBounds) => void): void
|
|
|
+ getBounds(): MapBounds | null
|
|
|
}
|
|
|
|
|
|
export function createLeafletMapProvider(): MapProvider {
|
|
|
@@ -94,15 +102,16 @@ export function createLeafletMapProvider(): MapProvider {
|
|
|
|
|
|
addMarker(id, lat, lng, options) {
|
|
|
if (!map) return
|
|
|
+ const ghost = options?.ghost
|
|
|
const type = options?.type || 'place'
|
|
|
const size = type === 'visitor' ? 12 : 32
|
|
|
- const icon = createIcon(type, size)
|
|
|
- const marker = L.marker([lat, lng], { icon }).addTo(map)
|
|
|
+ const icon = createIcon(type, size, ghost)
|
|
|
+ const marker = L.marker([lat, lng], { icon, interactive: !ghost }).addTo(map)
|
|
|
|
|
|
- if (options?.title) {
|
|
|
+ if (options?.title && !ghost) {
|
|
|
marker.bindTooltip(options.title, { direction: 'top' })
|
|
|
}
|
|
|
- if (options?.onClick) {
|
|
|
+ if (options?.onClick && !ghost) {
|
|
|
marker.on('click', () => options.onClick?.())
|
|
|
}
|
|
|
|
|
|
@@ -131,5 +140,16 @@ export function createLeafletMapProvider(): MapProvider {
|
|
|
onMove(cb) {
|
|
|
moveHandler = cb
|
|
|
},
|
|
|
+
|
|
|
+ getBounds() {
|
|
|
+ if (!map) return null
|
|
|
+ const b = map.getBounds()
|
|
|
+ return {
|
|
|
+ swLat: b.getSouthWest().lat,
|
|
|
+ swLng: b.getSouthWest().lng,
|
|
|
+ neLat: b.getNorthEast().lat,
|
|
|
+ neLng: b.getNorthEast().lng,
|
|
|
+ }
|
|
|
+ },
|
|
|
}
|
|
|
}
|