<div x-data="googleMap()" x-init="init()" class="flex gap-4 h-full">
<div class="flex-1 relative">
<div class="flex rounded-full bg-dark-5 p-1 gap-1 relative flex md:hidden mx-auto m-4 max-w-96 bg-neutral-150 z-10" x-data="{
get isAudio() { return $store.locator.isAudio },
toggleType(isAudio) { $store.locator.toggleStoreType(isAudio) }
}">
<button type="button" @click="toggleType(false)" :class="!isAudio ? 'bg-white text-neutral-800' : 'bg-transparent text-neutral-500'" class="w-full btn btn-light ">
Opticien
</button>
<button type="button" @click="toggleType(true)" :class="isAudio ? 'bg-white text-audio-700' : 'bg-transparent text-neutral-500'" class="w-full hover:text-white btn btn-light ">
Acousticien
</button>
</div>
<div id="DEMO_MAP_ID" class="absolute inset-0"></div>
<template x-if="loading">
<div class="absolute inset-0 bg-white/80 flex items-center justify-center">
<div class="loading-spinner"></div>
</div>
</template>
</div>
</div>
<script>
function googleMap() {
let mapsLib = null;
let markerLib = null;
let mapInstance = null;
return {
// États et configurations
_initialized: false,
mapId: "DEMO_MAP_ID",
loading: true,
markers: [],
markerCluster: null,
selectedMarker: null,
markerConfig: {
size: 48,
colors: {
default: '#262626',
audio: '#C70C0F'
}
},
async init() {
if (this._initialized) return;
this._initialized = true;
try {
await this.loadLibraries();
await this.initMap();
if (this.$store.locator.loading) {
await new Promise(resolve => {
this.$watch('$store.locator.loading', (loading) => {
if (!loading) resolve();
});
});
}
this.updateMarkersFromStore();
this.$watch('$store.locator.filteredStores', () => {
console.log("this.$watch $store.locator.filteredStores")
this.updateMarkersFromStore();
}, {
deep: true
});
} catch (error) {
console.error('Erreur lors de l\'initialisation:', error);
} finally {
this.loading = false;
}
},
async loadLibraries() {
try {
const [maps, marker] = await Promise.all([
google.maps.importLibrary("maps"),
google.maps.importLibrary("marker"),
]);
mapsLib = maps;
markerLib = marker;
} catch (error) {
console.error('Erreur lors du chargement des bibliothèques:', error);
throw error;
}
},
async initMap() {
if (!mapsLib) {
throw new Error('Les bibliothèques Google Maps ne sont pas chargées');
}
try {
const urlParams = new URLSearchParams(window.location.search);
const lat = urlParams.get('lat') || 46.603354;
const lng = urlParams.get('lng') || 1.888334;
const validation = this.validateCoordinates(lat, lng);
const coordinates = validation.isValid ?
validation.coordinates : {
lat: 46.603354,
lng: 1.888334
};
mapInstance = new google.maps.Map(document.getElementById(this.mapId), {
center: coordinates,
zoom: 4,
mapId: this.mapId,
mapTypeControl: false,
fullscreenControl: false,
streetViewControl: false,
zoomControl: !this.$store.screen.isMobile
});
this.$store.locator.mapInstance = mapInstance;
mapInstance.addListener('idle', () => {
this.$store.locator.updateDistances(this.$store.locator.filteredStores);
this.updateUrlParams();
});
} catch (error) {
console.error('Erreur lors de l\'initialisation de la carte:', error);
throw error;
}
},
validateCoordinates(lat, lng) {
if (lat === null || lng === null || lat === undefined || lng === undefined) {
return {
isValid: false,
error: 'Les coordonnées ne peuvent pas être null ou undefined'
};
}
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
if (isNaN(latitude) || isNaN(longitude)) {
return {
isValid: false,
error: 'Les coordonnées doivent être des nombres valides'
};
}
if (lat === '' || lng === '') {
return {
isValid: false,
error: 'Les coordonnées ne peuvent pas être vides'
};
}
if (latitude < -90 || latitude > 90) {
return {
isValid: false,
error: 'La latitude doit être comprise entre -90 et 90'
};
}
if (longitude < -180 || longitude > 180) {
return {
isValid: false,
error: 'La longitude doit être comprise entre -180 et 180'
};
}
return {
isValid: true,
coordinates: {
lat: latitude,
lng: longitude
}
};
},
updateUrlParams() {
if (!mapInstance) return;
try {
const center = mapInstance.getCenter();
const newUrl = new URL(window.location.href);
const validation = this.validateCoordinates(
center.lat(),
center.lng()
);
if (validation.isValid) {
newUrl.searchParams.set('lat', validation.coordinates.lat.toFixed(6));
newUrl.searchParams.set('lng', validation.coordinates.lng.toFixed(6));
window.history.replaceState({}, '', newUrl.toString());
}
} catch (error) {
console.error('Erreur lors de la mise à jour des paramètres URL:', error);
}
},
createMarkerSVG(isMultiStore, isAudio) {
const leftColor = isMultiStore ? this.markerConfig.colors.default :
isAudio ? this.markerConfig.colors.audio :
this.markerConfig.colors.default;
const rightColor = isMultiStore ? this.markerConfig.colors.audio :
isAudio ? this.markerConfig.colors.audio :
this.markerConfig.colors.default;
return `
<path fill-rule="evenodd" clip-rule="evenodd"
d="M24 42C24 42 38 32.3076 38 19.8462C38 12.2308 31.7 6 24 6V42Z"
fill="${rightColor}"/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M24 6C16.3 6 10 12.2308 10 19.8462C10 32.3076 24 42 24 42V6Z"
fill="${leftColor}"/>
`;
},
createMarkerElement(store, isSelected = false) {
const container = document.createElement('div');
const size = isSelected ? 64 : 48;
// Création du SVG principal
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${this.markerConfig.size} ${this.markerConfig.size}`);
svg.setAttribute('width', size);
svg.setAttribute('height', size);
svg.innerHTML = this.createMarkerSVG(
store.locations.length > 1,
this.$store.locator.isAudio
);
// Si c'est sélectionné, ajouter le SVG overlay et l'infoWindow
if (isSelected) {
container.className = 'relative';
// Container du marker
const markerWrapper = document.createElement('div');
markerWrapper.appendChild(svg);
// SVG overlay
const overlaySvg = document.createElement('div');
overlaySvg.className = 'absolute inset-0 flex items-center justify-center pointer-events-none';
overlaySvg.innerHTML = `
<svg width="24" height="24" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 13.8392L7.04036 0.162109H11.435V8.26383L15.6054 0.162109H20V13.8392H17.13V10.7899H13.139L11.6592 13.8392H8.56503V10.7899H4.57399L3.09417 13.8392H0ZM8.56503 2.76301H8.4305L5.60538 8.68229H8.56503V2.76301ZM17.13 2.76301H16.9955L14.1704 8.68229H17.13V2.76301Z" fill="white"/>
</svg>
`;
markerWrapper.appendChild(overlaySvg);
// Conteneur pour l'infoWindow
const infoContainer = document.createElement('div');
infoContainer.className = 'absolute -translate-x-1/2 left-1/2 top-full mt-2 z-50';
infoContainer.id = 'info-container';
container.appendChild(markerWrapper);
container.appendChild(infoContainer);
} else {
container.appendChild(svg);
}
return container;
},
updateMarkersFromStore() {
if (!markerLib || !mapInstance) return;
try {
this.clearMarkers();
const validStores = this.$store.locator.filteredStores.filter(store => {
const validation = this.validateCoordinates(store.lat, store.lng);
if (!validation.isValid) {
console.warn(`Marqueur ignoré pour le store ${store.id || 'Unknown'}: ${validation.error}`);
return false;
}
return true;
});
this.markers = validStores.map(store => {
const validation = this.validateCoordinates(store.lat, store.lng);
const position = validation.coordinates;
const marker = new markerLib.AdvancedMarkerElement({
position,
content: this.createMarkerElement(store),
map: mapInstance
});
this.addMarkerEvents(marker, store);
return marker;
});
this.createMarkerCluster(this.markers);
} catch (error) {
console.error('Erreur lors de la mise à jour des marqueurs:', error);
}
},
addMarkerEvents(marker, store) {
marker.addListener('click', (e) => {
try {
console.log("Marker cliqué :", store);
// Si un nouveau marker est déjà sélectionné
if (this.selectedMarker) {
// Vérifier si le clic est sur le nouveau marker ou le marker d'origine
const isSameMarker =
this.selectedMarker.position.equals(marker.position) ||
this.selectedMarker.position.equals(marker.position);
if (isSameMarker) {
console.log("Marker déjà sélectionné détecté.");
if (this.currentInfoWindow) {
console.log("InfoWindow déjà ouverte. Pas d'action.");
return; // Ne rien faire si l'infoWindow est déjà ouverte
} else {
console.log("InfoWindow fermée. Réouverture...");
this.openInfoWindow(store); // Rouvrir l'infoWindow si elle est fermée
return;
}
}
}
// Nouveau marker cliqué : nettoyer la sélection précédente
console.log("Nouveau marker cliqué. Ancienne sélection supprimée.");
this.cleanupPreviousSelection(true);
// Création d'un nouveau marker pour la sélection
this.selectedMarker = new markerLib.AdvancedMarkerElement({
position: marker.position,
content: this.createMarkerElement(store, true),
map: mapInstance,
zIndex: 1000
});
console.log("Nouveau marker créé pour la sélection :", this.selectedMarker);
// Ajouter un gestionnaire d'événement pour le nouveau marker
this.selectedMarker.addListener('click', (e) => {
console.log("Clic sur le nouveau marker sélectionné.");
// Même logique de vérification que ci-dessus
if (this.currentInfoWindow) {
console.log("InfoWindow déjà ouverte. Pas d'action.");
return;
} else {
console.log("Réouverture de l'infoWindow pour le store :", store);
this.openInfoWindow(store);
}
});
// Met à jour le store sélectionné
this.$store.locator.selectStore(store);
// Recentre la carte sur le nouveau marker
mapInstance.panTo(marker.position);
console.log("Nouvelle sélection. Ouverture de l'infoWindow...");
this.openInfoWindow(store);
// Configurer le gestionnaire pour fermer en cliquant à l'extérieur
this.setupInfoWindowCloseHandler();
} catch (error) {
console.error('Erreur lors du clic sur un marqueur:', error);
}
});
},
openInfoWindow(store) {
console.log("Ouverture de l'infoWindow pour le store :", store);
// Créer et attacher l'infoWindow au marker sélectionné
const infoWindow = this.createInfoWindow(store);
this.currentInfoWindow = infoWindow;
const infoContainer = this.selectedMarker.content.querySelector('#info-container');
if (infoContainer) {
infoContainer.appendChild(infoWindow);
}
},
cleanupPreviousSelection(removeMarker = false) {
if (removeMarker && this.selectedMarker) {
// Supprime le marker sélectionné précédent, s'il existe
this.selectedMarker.setMap(null); // Retire le marker de la carte
this.selectedMarker = null; // Réinitialise l'état
console.log("Suppression du marker sélectionné précédent.");
}
if (this.currentInfoWindow) {
this.currentInfoWindow.remove(); // Ferme l'infoWindow
console.log("Suppression de l'infoWindow actuelle.");
this.currentInfoWindow = null; // Réinitialise l'état
}
},
setupInfoWindowCloseHandler() {
const closeHandler = (e) => {
console.log("Clic détecté en dehors. Vérification des éléments...");
// Vérifiez si le clic est sur le marker ou l'infoWindow sélectionnée
const isClickOnMarker = this.selectedMarker?.content && this.selectedMarker.content.contains(e.target);
const isClickOnInfoWindow = this.currentInfoWindow && this.currentInfoWindow.contains(e.target);
if (isClickOnMarker) {
console.log("Clic détecté sur le marker sélectionné. Pas d'action.");
}
if (isClickOnInfoWindow) {
console.log("Clic détecté sur l'infoWindow sélectionnée. Pas d'action.");
}
if (!isClickOnMarker && !isClickOnInfoWindow) {
console.log("Clic en dehors des éléments. Fermeture de la windows.");
this.cleanupPreviousSelection(false);
document.removeEventListener('click', closeHandler);
}
};
// Supprime tout gestionnaire existant
document.removeEventListener('click', closeHandler);
// Ajoute un nouveau gestionnaire global
setTimeout(() => document.addEventListener('click', closeHandler), 0);
},
clearMarkers() {
try {
if (this.currentInfoWindow) {
this.currentInfoWindow.remove();
}
if (this.selectedMarker) {
this.selectedMarker.setMap(null);
}
if (this.markerCluster) {
this.markerCluster.setMap(null);
}
this.markers.forEach(marker => marker.setMap(null));
this.markers = [];
} catch (error) {
console.error('Erreur lors du nettoyage des marqueurs:', error);
}
},
createMarkerCluster(markersToCluster) {
if (!mapInstance || !markersToCluster.length) return;
try {
this.markerCluster = new markerClusterer.MarkerClusterer({
markers: markersToCluster,
map: mapInstance,
ignoreHidden: true,
algorithm: new markerClusterer.SuperClusterAlgorithm({
radius: 250,
maxZoom: 12
}),
renderer: {
render: ({
count,
position
}) => {
const scale = count < 5 ? 30 : count < 50 ? 40 : 50;
const div = document.createElement('div');
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${scale} ${scale}`);
svg.setAttribute('width', scale);
svg.setAttribute('height', scale);
const circle = `<circle cx="${scale / 2}" cy="${scale / 2}" r="${(scale / 2) - 2}" fill="${this.$store.locator.isAudio ? "#C70C0F" : "#262626"}" stroke="white" stroke-width="2"/>`;
const text = count >= 5 ?
`<text x="${scale / 2}" y="${scale / 2}" text-anchor="middle" dominant-baseline="central" fill="white" font-size="14px" font-weight="bold">${count < 50 ? count : '50+'}</text>` :
'';
svg.innerHTML = circle + text;
div.appendChild(svg);
return new markerLib.AdvancedMarkerElement({
position,
content: div
});
}
}
});
} catch (error) {
console.error('Erreur lors de la création du cluster de marqueurs:', error);
}
},
getGoogleMapsUrl(lat, lng, address) {
const encodedAddress = encodeURIComponent(address);
return `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}&destination_place_id=${encodedAddress}`;
},
createInfoWindow(store) {
const div = document.createElement('div');
div.className = 'font-sans bg-white rounded-lg border border-neutral-300 shadow-lg p-1 min-w-40';
const fullAddress = `${store.address}, ${store.zip} ${store.city}`.trim();
const googleMapsUrl = this.getGoogleMapsUrl(store.lat, store.lng, fullAddress);
div.innerHTML = `
<div class="flex flex-col gap-2">
${store.address && store.city ? `
<div class="text-xs text-neutral-800">
${store.address}, ${store.city}
</div>
` : ''}
<div class="flex flex-col gap-2 mt-2">
<a href="#" class=" btn btn-dark-ghost btn-size-sm">
Voir la fiche magasin
</a>
<a href="${googleMapsUrl}" class=" btn btn-dark-ghost btn-size-sm">
Itinéraire
</a>
</div>
</div>
`;
return div;
}
};
}
</script>
{# components/google-map/google-map.twig #}
<div
x-data="googleMap()"
x-init="init()"
class="flex gap-4 h-full"
>
<div class="flex-1 relative">
{% render '@store-switcher' with {
className: "relative flex md:hidden mx-auto m-4 max-w-96 bg-neutral-150 z-10"
} %}
<div id="{{ mapId }}" class="absolute inset-0"></div>
<template x-if="loading">
<div class="absolute inset-0 bg-white/80 flex items-center justify-center">
<div class="loading-spinner"></div>
</div>
</template>
</div>
</div>
<script>
function googleMap() {
let mapsLib = null;
let markerLib = null;
let mapInstance = null;
return {
// États et configurations
_initialized: false,
mapId: "DEMO_MAP_ID",
loading: true,
markers: [],
markerCluster: null,
selectedMarker: null,
markerConfig: {
size: 48,
colors: {
default: '#262626',
audio: '#C70C0F'
}
},
async init() {
if (this._initialized) return;
this._initialized = true;
try {
await this.loadLibraries();
await this.initMap();
if (this.$store.locator.loading) {
await new Promise(resolve => {
this.$watch('$store.locator.loading', (loading) => {
if (!loading) resolve();
});
});
}
this.updateMarkersFromStore();
this.$watch('$store.locator.filteredStores', () => {
console.log("this.$watch $store.locator.filteredStores")
this.updateMarkersFromStore();
}, {deep: true});
} catch (error) {
console.error('Erreur lors de l\'initialisation:', error);
} finally {
this.loading = false;
}
},
async loadLibraries() {
try {
const [maps, marker] = await Promise.all([
google.maps.importLibrary("maps"),
google.maps.importLibrary("marker"),
]);
mapsLib = maps;
markerLib = marker;
} catch (error) {
console.error('Erreur lors du chargement des bibliothèques:', error);
throw error;
}
},
async initMap() {
if (!mapsLib) {
throw new Error('Les bibliothèques Google Maps ne sont pas chargées');
}
try {
const urlParams = new URLSearchParams(window.location.search);
const lat = urlParams.get('lat') || 46.603354;
const lng = urlParams.get('lng') || 1.888334;
const validation = this.validateCoordinates(lat, lng);
const coordinates = validation.isValid ?
validation.coordinates :
{lat: 46.603354, lng: 1.888334};
mapInstance = new google.maps.Map(document.getElementById(this.mapId), {
center: coordinates,
zoom: 4,
mapId: this.mapId,
mapTypeControl: false,
fullscreenControl: false,
streetViewControl: false,
zoomControl: !this.$store.screen.isMobile
});
this.$store.locator.mapInstance = mapInstance;
mapInstance.addListener('idle', () => {
this.$store.locator.updateDistances(this.$store.locator.filteredStores);
this.updateUrlParams();
});
} catch (error) {
console.error('Erreur lors de l\'initialisation de la carte:', error);
throw error;
}
},
validateCoordinates(lat, lng) {
if (lat === null || lng === null || lat === undefined || lng === undefined) {
return {
isValid: false,
error: 'Les coordonnées ne peuvent pas être null ou undefined'
};
}
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
if (isNaN(latitude) || isNaN(longitude)) {
return {
isValid: false,
error: 'Les coordonnées doivent être des nombres valides'
};
}
if (lat === '' || lng === '') {
return {
isValid: false,
error: 'Les coordonnées ne peuvent pas être vides'
};
}
if (latitude < -90 || latitude > 90) {
return {
isValid: false,
error: 'La latitude doit être comprise entre -90 et 90'
};
}
if (longitude < -180 || longitude > 180) {
return {
isValid: false,
error: 'La longitude doit être comprise entre -180 et 180'
};
}
return {
isValid: true,
coordinates: {
lat: latitude,
lng: longitude
}
};
},
updateUrlParams() {
if (!mapInstance) return;
try {
const center = mapInstance.getCenter();
const newUrl = new URL(window.location.href);
const validation = this.validateCoordinates(
center.lat(),
center.lng()
);
if (validation.isValid) {
newUrl.searchParams.set('lat', validation.coordinates.lat.toFixed(6));
newUrl.searchParams.set('lng', validation.coordinates.lng.toFixed(6));
window.history.replaceState({}, '', newUrl.toString());
}
} catch (error) {
console.error('Erreur lors de la mise à jour des paramètres URL:', error);
}
},
createMarkerSVG(isMultiStore, isAudio) {
const leftColor = isMultiStore ? this.markerConfig.colors.default :
isAudio ? this.markerConfig.colors.audio :
this.markerConfig.colors.default;
const rightColor = isMultiStore ? this.markerConfig.colors.audio :
isAudio ? this.markerConfig.colors.audio :
this.markerConfig.colors.default;
return `
<path fill-rule="evenodd" clip-rule="evenodd"
d="M24 42C24 42 38 32.3076 38 19.8462C38 12.2308 31.7 6 24 6V42Z"
fill="${rightColor}"/>
<path fill-rule="evenodd" clip-rule="evenodd"
d="M24 6C16.3 6 10 12.2308 10 19.8462C10 32.3076 24 42 24 42V6Z"
fill="${leftColor}"/>
`;
},
createMarkerElement(store, isSelected = false) {
const container = document.createElement('div');
const size = isSelected ? 64 : 48;
// Création du SVG principal
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${this.markerConfig.size} ${this.markerConfig.size}`);
svg.setAttribute('width', size);
svg.setAttribute('height', size);
svg.innerHTML = this.createMarkerSVG(
store.locations.length > 1,
this.$store.locator.isAudio
);
// Si c'est sélectionné, ajouter le SVG overlay et l'infoWindow
if (isSelected) {
container.className = 'relative';
// Container du marker
const markerWrapper = document.createElement('div');
markerWrapper.appendChild(svg);
// SVG overlay
const overlaySvg = document.createElement('div');
overlaySvg.className = 'absolute inset-0 flex items-center justify-center pointer-events-none';
overlaySvg.innerHTML = `
<svg width="24" height="24" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 13.8392L7.04036 0.162109H11.435V8.26383L15.6054 0.162109H20V13.8392H17.13V10.7899H13.139L11.6592 13.8392H8.56503V10.7899H4.57399L3.09417 13.8392H0ZM8.56503 2.76301H8.4305L5.60538 8.68229H8.56503V2.76301ZM17.13 2.76301H16.9955L14.1704 8.68229H17.13V2.76301Z" fill="white"/>
</svg>
`;
markerWrapper.appendChild(overlaySvg);
// Conteneur pour l'infoWindow
const infoContainer = document.createElement('div');
infoContainer.className = 'absolute -translate-x-1/2 left-1/2 top-full mt-2 z-50';
infoContainer.id = 'info-container';
container.appendChild(markerWrapper);
container.appendChild(infoContainer);
} else {
container.appendChild(svg);
}
return container;
},
updateMarkersFromStore() {
if (!markerLib || !mapInstance) return;
try {
this.clearMarkers();
const validStores = this.$store.locator.filteredStores.filter(store => {
const validation = this.validateCoordinates(store.lat, store.lng);
if (!validation.isValid) {
console.warn(`Marqueur ignoré pour le store ${store.id || 'Unknown'}: ${validation.error}`);
return false;
}
return true;
});
this.markers = validStores.map(store => {
const validation = this.validateCoordinates(store.lat, store.lng);
const position = validation.coordinates;
const marker = new markerLib.AdvancedMarkerElement({
position,
content: this.createMarkerElement(store),
map: mapInstance
});
this.addMarkerEvents(marker, store);
return marker;
});
this.createMarkerCluster(this.markers);
} catch (error) {
console.error('Erreur lors de la mise à jour des marqueurs:', error);
}
},
addMarkerEvents(marker, store) {
marker.addListener('click', (e) => {
try {
console.log("Marker cliqué :", store);
// Si un nouveau marker est déjà sélectionné
if (this.selectedMarker) {
// Vérifier si le clic est sur le nouveau marker ou le marker d'origine
const isSameMarker =
this.selectedMarker.position.equals(marker.position) ||
this.selectedMarker.position.equals(marker.position);
if (isSameMarker) {
console.log("Marker déjà sélectionné détecté.");
if (this.currentInfoWindow) {
console.log("InfoWindow déjà ouverte. Pas d'action.");
return; // Ne rien faire si l'infoWindow est déjà ouverte
} else {
console.log("InfoWindow fermée. Réouverture...");
this.openInfoWindow(store); // Rouvrir l'infoWindow si elle est fermée
return;
}
}
}
// Nouveau marker cliqué : nettoyer la sélection précédente
console.log("Nouveau marker cliqué. Ancienne sélection supprimée.");
this.cleanupPreviousSelection(true);
// Création d'un nouveau marker pour la sélection
this.selectedMarker = new markerLib.AdvancedMarkerElement({
position: marker.position,
content: this.createMarkerElement(store, true),
map: mapInstance,
zIndex: 1000
});
console.log("Nouveau marker créé pour la sélection :", this.selectedMarker);
// Ajouter un gestionnaire d'événement pour le nouveau marker
this.selectedMarker.addListener('click', (e) => {
console.log("Clic sur le nouveau marker sélectionné.");
// Même logique de vérification que ci-dessus
if (this.currentInfoWindow) {
console.log("InfoWindow déjà ouverte. Pas d'action.");
return;
} else {
console.log("Réouverture de l'infoWindow pour le store :", store);
this.openInfoWindow(store);
}
});
// Met à jour le store sélectionné
this.$store.locator.selectStore(store);
// Recentre la carte sur le nouveau marker
mapInstance.panTo(marker.position);
console.log("Nouvelle sélection. Ouverture de l'infoWindow...");
this.openInfoWindow(store);
// Configurer le gestionnaire pour fermer en cliquant à l'extérieur
this.setupInfoWindowCloseHandler();
} catch (error) {
console.error('Erreur lors du clic sur un marqueur:', error);
}
});
},
openInfoWindow(store) {
console.log("Ouverture de l'infoWindow pour le store :", store);
// Créer et attacher l'infoWindow au marker sélectionné
const infoWindow = this.createInfoWindow(store);
this.currentInfoWindow = infoWindow;
const infoContainer = this.selectedMarker.content.querySelector('#info-container');
if (infoContainer) {
infoContainer.appendChild(infoWindow);
}
},
cleanupPreviousSelection(removeMarker = false) {
if (removeMarker && this.selectedMarker) {
// Supprime le marker sélectionné précédent, s'il existe
this.selectedMarker.setMap(null); // Retire le marker de la carte
this.selectedMarker = null; // Réinitialise l'état
console.log("Suppression du marker sélectionné précédent.");
}
if (this.currentInfoWindow) {
this.currentInfoWindow.remove(); // Ferme l'infoWindow
console.log("Suppression de l'infoWindow actuelle.");
this.currentInfoWindow = null; // Réinitialise l'état
}
},
setupInfoWindowCloseHandler() {
const closeHandler = (e) => {
console.log("Clic détecté en dehors. Vérification des éléments...");
// Vérifiez si le clic est sur le marker ou l'infoWindow sélectionnée
const isClickOnMarker = this.selectedMarker?.content && this.selectedMarker.content.contains(e.target);
const isClickOnInfoWindow = this.currentInfoWindow && this.currentInfoWindow.contains(e.target);
if (isClickOnMarker) {
console.log("Clic détecté sur le marker sélectionné. Pas d'action.");
}
if (isClickOnInfoWindow) {
console.log("Clic détecté sur l'infoWindow sélectionnée. Pas d'action.");
}
if (!isClickOnMarker && !isClickOnInfoWindow) {
console.log("Clic en dehors des éléments. Fermeture de la windows.");
this.cleanupPreviousSelection(false);
document.removeEventListener('click', closeHandler);
}
};
// Supprime tout gestionnaire existant
document.removeEventListener('click', closeHandler);
// Ajoute un nouveau gestionnaire global
setTimeout(() => document.addEventListener('click', closeHandler), 0);
},
clearMarkers() {
try {
if (this.currentInfoWindow) {
this.currentInfoWindow.remove();
}
if (this.selectedMarker) {
this.selectedMarker.setMap(null);
}
if (this.markerCluster) {
this.markerCluster.setMap(null);
}
this.markers.forEach(marker => marker.setMap(null));
this.markers = [];
} catch (error) {
console.error('Erreur lors du nettoyage des marqueurs:', error);
}
},
createMarkerCluster(markersToCluster) {
if (!mapInstance || !markersToCluster.length) return;
try {
this.markerCluster = new markerClusterer.MarkerClusterer({
markers: markersToCluster,
map: mapInstance,
ignoreHidden: true,
algorithm: new markerClusterer.SuperClusterAlgorithm({
radius: 250,
maxZoom: 12
}),
renderer: {
render: ({count, position}) => {
const scale = count < 5 ? 30 : count < 50 ? 40 : 50;
const div = document.createElement('div');
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', `0 0 ${scale} ${scale}`);
svg.setAttribute('width', scale);
svg.setAttribute('height', scale);
const circle = `<circle cx="${scale / 2}" cy="${scale / 2}" r="${(scale / 2) - 2}" fill="${this.$store.locator.isAudio ? "#C70C0F" : "#262626"}" stroke="white" stroke-width="2"/>`;
const text = count >= 5
? `<text x="${scale / 2}" y="${scale / 2}" text-anchor="middle" dominant-baseline="central" fill="white" font-size="14px" font-weight="bold">${count < 50 ? count : '50+'}</text>`
: '';
svg.innerHTML = circle + text;
div.appendChild(svg);
return new markerLib.AdvancedMarkerElement({
position,
content: div
});
}
}
});
} catch (error) {
console.error('Erreur lors de la création du cluster de marqueurs:', error);
}
},
getGoogleMapsUrl(lat, lng, address) {
const encodedAddress = encodeURIComponent(address);
return `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}&destination_place_id=${encodedAddress}`;
},
createInfoWindow(store) {
const div = document.createElement('div');
div.className = 'font-sans bg-white rounded-lg border border-neutral-300 shadow-lg p-1 min-w-40';
const fullAddress = `${store.address}, ${store.zip} ${store.city}`.trim();
const googleMapsUrl = this.getGoogleMapsUrl(store.lat, store.lng, fullAddress);
div.innerHTML = `
<div class="flex flex-col gap-2">
${store.address && store.city ? `
<div class="text-xs text-neutral-800">
${store.address}, ${store.city}
</div>
` : ''}
<div class="flex flex-col gap-2 mt-2">
{% render "@template-button" with {
label: "Voir la fiche magasin",
size: "sm",
href: "#",
externe: true,
color: "dark-ghost",
} %}
{% render "@template-button" with {
label: "Itinéraire",
size: "sm",
href: "${googleMapsUrl}",
externe: true,
color: "dark-ghost",
} %}</script>
{
"mapId": "DEMO_MAP_ID",
"loading": false
}
No notes defined.