Compare commits
1 Commits
951a1f64ac
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5680954094 |
1081
src/components/admin/MapPointsTab.tsx
Normal file
1081
src/components/admin/MapPointsTab.tsx
Normal file
File diff suppressed because it is too large
Load Diff
215
src/lib/map/ArenariumMarkers.ts
Normal file
215
src/lib/map/ArenariumMarkers.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
// src/lib/map/ArenariumMarkers.ts
|
||||
// Markers usando Mapbox nativo (sin Arenarium para mejor control)
|
||||
|
||||
import mapboxgl from 'mapbox-gl';
|
||||
import { getPOIMarkerIcon } from './POIMarkerIcons';
|
||||
|
||||
// CSS para el popup
|
||||
const popupStyles = `
|
||||
.mapboxgl-popup-close-button {
|
||||
right: 10px !important;
|
||||
top: 5px !important;
|
||||
font-size: 18px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
// Inyectar estilos una sola vez
|
||||
let stylesInjected = false;
|
||||
function injectPopupStyles() {
|
||||
if (stylesInjected) return;
|
||||
const style = document.createElement('style');
|
||||
style.textContent = popupStyles;
|
||||
document.head.appendChild(style);
|
||||
stylesInjected = true;
|
||||
}
|
||||
|
||||
export interface ArenariumPOI {
|
||||
id: string;
|
||||
name: string;
|
||||
category: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
address?: string;
|
||||
description?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export class ArenariumMarkerManager {
|
||||
private map: mapboxgl.Map;
|
||||
private markers: Map<string, mapboxgl.Marker> = new Map();
|
||||
private pois: Map<string, ArenariumPOI> = new Map();
|
||||
private activePopup: mapboxgl.Popup | null = null;
|
||||
|
||||
constructor(map: mapboxgl.Map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
injectPopupStyles();
|
||||
console.log('[Markers] Initialized with native Mapbox markers');
|
||||
}
|
||||
|
||||
// Crear elemento HTML para el pin
|
||||
private createPinElement(poi: ArenariumPOI): HTMLElement {
|
||||
const iconInfo = getPOIMarkerIcon(poi.category);
|
||||
const el = document.createElement('div');
|
||||
el.className = 'mapbox-pin';
|
||||
el.style.cssText = `
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
background: ${iconInfo.color};
|
||||
border-radius: 50%;
|
||||
border: 3px solid white;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.15s ease;
|
||||
`;
|
||||
|
||||
el.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="width: 18px; height: 18px;">
|
||||
${iconInfo.svg}
|
||||
</svg>
|
||||
`;
|
||||
|
||||
// Hover effect - solo cambiar sombra, no escalar
|
||||
el.addEventListener('mouseenter', () => {
|
||||
el.style.boxShadow = '0 4px 12px rgba(0,0,0,0.4)';
|
||||
});
|
||||
el.addEventListener('mouseleave', () => {
|
||||
el.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
|
||||
});
|
||||
|
||||
// Click handler
|
||||
el.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
console.log('[Markers] Pin clicked:', poi.id, poi.name);
|
||||
|
||||
// Mostrar popup
|
||||
this.showPopup(poi.id);
|
||||
|
||||
// Ejecutar callback si existe
|
||||
if (poi.onClick) {
|
||||
poi.onClick();
|
||||
}
|
||||
});
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
// Crear popup HTML
|
||||
private createPopupContent(poi: ArenariumPOI): string {
|
||||
const iconInfo = getPOIMarkerIcon(poi.category);
|
||||
return `
|
||||
<div style="font-family: system-ui, -apple-system, sans-serif; min-width: 180px;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
||||
<div style="
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: ${iconInfo.color}20;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${iconInfo.color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width: 16px; height: 16px;">
|
||||
${iconInfo.svg}
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-weight: 600; font-size: 14px;">${poi.name}</div>
|
||||
<div style="font-size: 11px; color: #888;">${iconInfo.label}</div>
|
||||
</div>
|
||||
</div>
|
||||
${poi.address ? `<div style="font-size: 12px; color: #666; margin-bottom: 6px;">${poi.address}</div>` : ''}
|
||||
${poi.description ? `<div style="font-size: 12px; color: #444;">${poi.description}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Agregar un POI
|
||||
addMarker(poi: ArenariumPOI): void {
|
||||
this.pois.set(poi.id, poi);
|
||||
}
|
||||
|
||||
// Remover un POI
|
||||
removeMarker(id: string): void {
|
||||
const marker = this.markers.get(id);
|
||||
if (marker) {
|
||||
marker.remove();
|
||||
this.markers.delete(id);
|
||||
}
|
||||
this.pois.delete(id);
|
||||
}
|
||||
|
||||
// Limpiar todos los markers
|
||||
clearMarkers(): void {
|
||||
this.markers.forEach(marker => marker.remove());
|
||||
this.markers.clear();
|
||||
this.pois.clear();
|
||||
this.hidePopup();
|
||||
}
|
||||
|
||||
// Actualizar markers en el mapa
|
||||
async updateMarkers(): Promise<void> {
|
||||
// Remover markers existentes que ya no están en pois
|
||||
const currentIds = new Set(this.pois.keys());
|
||||
this.markers.forEach((marker, id) => {
|
||||
if (!currentIds.has(id)) {
|
||||
marker.remove();
|
||||
this.markers.delete(id);
|
||||
}
|
||||
});
|
||||
|
||||
// Agregar o actualizar markers
|
||||
this.pois.forEach((poi, id) => {
|
||||
if (!this.markers.has(id)) {
|
||||
const el = this.createPinElement(poi);
|
||||
const marker = new mapboxgl.Marker({ element: el })
|
||||
.setLngLat([poi.lng, poi.lat])
|
||||
.addTo(this.map);
|
||||
|
||||
this.markers.set(id, marker);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[Markers] Updated ${this.markers.size} markers`);
|
||||
}
|
||||
|
||||
// Mostrar popup de un marker
|
||||
showPopup(id: string): void {
|
||||
const poi = this.pois.get(id);
|
||||
if (!poi) return;
|
||||
|
||||
// Cerrar popup anterior
|
||||
this.hidePopup();
|
||||
|
||||
// Crear nuevo popup
|
||||
this.activePopup = new mapboxgl.Popup({
|
||||
closeButton: true,
|
||||
closeOnClick: true,
|
||||
maxWidth: '300px',
|
||||
offset: [0, -20]
|
||||
})
|
||||
.setLngLat([poi.lng, poi.lat])
|
||||
.setHTML(this.createPopupContent(poi))
|
||||
.addTo(this.map);
|
||||
}
|
||||
|
||||
// Ocultar popup
|
||||
hidePopup(): void {
|
||||
if (this.activePopup) {
|
||||
this.activePopup.remove();
|
||||
this.activePopup = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Destruir manager
|
||||
destroy(): void {
|
||||
this.clearMarkers();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user