Store Locator
Installation
À la fin du head
Ajoutez le code suivant dans le <head> de votre fichier theme.liquid, juste avant la fermeture de la balise </head> :
<!-- MAPBOX[BEGIN] -->
<script src='https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.css' rel='stylesheet' />
<script src="{{ 'mm_map.js' | asset_url }}" defer="defer"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css" type="text/css">
<!-- MAPBOX[END] -->Section
mm-map.liquid
Créez un nouveau fichier mm-map.liquid dans le dossier sections :
{{ 'mm-map.css' | asset_url | stylesheet_tag }}
<div class="mm-section">
<input type="hidden" value="{{ shop.metaobjects.pharmacies.mapbox_settings.mapbox_token }}" id="mm-mapbox-token"/>
<div id="mm-places-json">
{{ shop.metaobjects.pharmacies.mapbox_settings.places_json | metafield_tag }}
</div>
<template id="mm-map-place-template">
<div data-place-coordinates
class="mm-map-place-card">
<p data-place-name></p>
<p data-place-address style="font-weigth: 300;"></p>
</div>
</template>
<div class="mm-map-wrapper">
<!-- MAP -->
<div id="mm-map"
style='width: 100%; height: 600px'></div>
<!-- PLACES -->
<div id="mm-map-places"></div>
</div>
</div>
<script>
</script>
<style>
@media(max-width:768px) {
}
.mm-map-place-card {
border: 1px solid black;
margin-bottom: 8px;
}
.mm-map-place-card.selected {
border-color: red !important;
}
</style>
{% schema %}
{
"name": "MM Map",
"settings": [
{
"type": "text",
"id": "title",
"label": "Titre"
}
],
"presets": [
{
"name": "MM Map",
"category": "Moon Moon"
}
]
}
{% endschema %}Assets
mm-map.js
Créez un nouveau fichier mm-map.js dans le dossier assets :
// longitude = d'est en ouest
// latitude = du nord au sud
const placeCardTemplate = document.getElementById('mm-map-place-template');
const placesContainer = document.getElementById('mm-map-places');
const placesJSONEl = document.querySelector('#mm-places-json .metafield-json');
const mapboxTokenInput = document.getElementById('mm-mapbox-token');
const PLACE_SELECTED_CLASS = 'selected';
const MARKER_DEFAULT_COLOR = '#3FB1CE';
const MARKER_SELECTED_COLOR = '#FF0000';
mapboxgl.accessToken = mapboxTokenInput.value;
const places = JSON.parse(placesJSONEl.innerText).map((placeData) => {
return {
name: placeData.name,
address: placeData.address,
coordinates: [placeData.Longitude, placeData.Latitude]
};
});
function mmInsertPlaceCard(place) {
const card = placeCardTemplate.content.cloneNode(true).firstElementChild;
const nameEl = card.querySelector('[data-place-name]');
nameEl.textContent = place.name;
const addressEl = card.querySelector('[data-place-address]');
addressEl.textContent = place.address;
card.dataset.placeCoordinates = JSON.stringify(place.coordinates);
placesContainer.appendChild(card);
}
function mmDisplayVisibleMarkers(map) {
// const { _ne: NorthEast, _sw: SouthWest } = map.getBounds();
// const maxNorth = NorthEast.lat;
// const maxEast = NorthEast.lng;
// const maxSouth = SouthWest.lat;
// const maxWest = SouthWest.lng;
// same with destructuring:
const {
_ne: { lng: maxEast, lat: maxNorth },
_sw: { lng: maxWest, lat: maxSouth }
} = map.getBounds();
const visiblePlaces = places.forEach((place) => {
// const lng = places.coordinates.lng;
// const lat = places.coordinates.lat;
const [lng, lat] = place.coordinates; // [-1.557900, 47.214370]
const visible = lng < maxEast &&
lng > maxWest &&
lat > maxSouth &&
lat < maxNorth
if (visible) {
mmInsertPlaceCard(place);
}
});
}
function mmFlyToPlaceByCoordinates(coordinates) {
const map = window.mmMap;
map.flyTo(
{ center: coordinates, zoom: 15 },
{ placeCoordinatesToSelect: coordinates }
);
}
function mmSelectPlaceByCoordinates(coordinates) {
Array.from(placesContainer.children).forEach((placeCard) => {
placeCard.classList.remove(PLACE_SELECTED_CLASS);
const [cardLng, cardLat] = JSON.parse(placeCard.dataset.placeCoordinates);
const [lng, lat] = coordinates;
if (cardLng === lng && cardLat === lat) {
placeCard.classList.add(PLACE_SELECTED_CLASS);
}
});
mmHighLightPlaceMarkerByCoordinates(coordinates);
}
function mmHighLightPlaceMarkerByCoordinates(coordinates) {
const [lng, lat] = coordinates;
const markers = document.querySelectorAll('#mm-map [data-marker-place-coordinates]');
markers.forEach((marker) => {
const [markerLng, markerLat] = JSON.parse(marker.dataset.markerPlaceCoordinates);
const markerPath = marker.querySelector('path');
if (markerLng === lng && markerLat === lat) {
markerPath.setAttribute('fill', MARKER_SELECTED_COLOR);
} else {
markerPath.setAttribute('fill', MARKER_DEFAULT_COLOR);
}
})
}
function mmInitMap() {
const map = new mapboxgl.Map({
container: 'mm-map', // container ID
style: 'mapbox://styles/mapbox/streets-v12', // style URL
center: [2.25, 46.45], // starting position [lng, lat]
zoom: 5, // starting zoom
});
window.mmMap = map;
// Add geocoded search
map.addControl(
new MapboxGeocoder({
mapboxgl: mapboxgl,
accessToken: mapboxgl.accessToken,
countries: 'fr,gf' // comma separated list
})
);
// Add geolocate control to the map.
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
// When active the map will receive updates to the device's location as it changes.
trackUserLocation: true,
// Draw an arrow next to the location dot to indicate which direction the device is heading.
showUserHeading: true
})
);
places.forEach((place) => {
const marker = new mapboxgl.Marker()
.setLngLat(place.coordinates)
.addTo(map);
const markerElement = marker.getElement();
markerElement.dataset.markerPlaceCoordinates = JSON.stringify(place.coordinates);
markerElement.addEventListener('click', () => {
mmFlyToPlaceByCoordinates(place.coordinates);
});
});
map.on('moveend', (event) => {
placesContainer.innerHTML = null;
if (map.getZoom() > 12) {
mmDisplayVisibleMarkers(map);
}
// if move was triggered by mmFlyToPlaceByCoordinates, highlight card in list
if (event.placeCoordinatesToSelect) {
mmSelectPlaceByCoordinates(event.placeCoordinatesToSelect);
}
});
placesContainer.addEventListener('click', (event) => {
const target = event.target;
const card = target.closest('.mm-map-place-card');
const coordinates = JSON.parse(card.dataset.placeCoordinates);
mmFlyToPlaceByCoordinates(coordinates);
});
}
document.addEventListener('DOMContentLoaded', mmInitMap);mm-map.css
Créez un nouveau fichier mm-map.css dans le dossier assets :
.mm-map-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 3rem;
}
/* PLACES */
.mm-map-place-card {
border: solid 1px #f5f5f5;
border-radius: 6px;
padding: 20px;
cursor: pointer;
transition: .3s;
}
.mm-map-place-card.selected {
border: solid 2px #000;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.mm-map-place-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.mm-map-place-card p[data-place-name] {
margin: 0 0 10px;
font-weight: bold;
}
.mm-map-place-card p[data-place-address] {
margin: 0;
}Configuration
Metaobjects Shopify
Ce système utilise des metaobjects Shopify pour stocker les données des pharmacies/magasins. Vous devez créer :
-
Metaobject "pharmacies" avec :
mapbox_settings(type: metaobject)mapbox_token: Votre token Mapboxplaces_json: JSON contenant la liste des lieux
-
Structure du JSON places_json :
[
{
"name": "Nom du magasin",
"address": "Adresse complète",
"Longitude": -1.557900,
"Latitude": 47.214370
}
]Fonctionnalités
- Carte interactive : Utilise Mapbox GL JS pour afficher une carte interactive
- Recherche géocodée : Barre de recherche pour trouver des adresses
- Géolocalisation : Bouton pour localiser l'utilisateur
- Marqueurs : Affiche les magasins/pharmacies sur la carte
- Liste des lieux : Affiche les lieux visibles dans la zone de la carte
- Interaction : Clic sur un marqueur ou une carte pour zoomer et sélectionner
- Responsive : Affichage adaptatif selon la taille de l'écran
Personnalisation
Changer le style de la carte
Modifiez le paramètre style dans mmInitMap() :
style: 'mapbox://styles/mapbox/streets-v12', // streets, dark, light, satellite, etc.Changer les pays de recherche
Modifiez le paramètre countries dans le MapboxGeocoder :
countries: 'fr,gf' // Ajoutez d'autres codes pays séparés par des virgulesAjuster le zoom initial
Modifiez les valeurs dans mmInitMap() :
center: [2.25, 46.45], // [longitude, latitude]
zoom: 5, // Niveau de zoom (1-20)Notes importantes
- Token Mapbox : Vous devez obtenir un token Mapbox gratuit sur mapbox.com (opens in a new tab)
- Metaobjects : Assurez-vous que les metaobjects sont correctement configurés dans Shopify
- Format des coordonnées : Les coordonnées doivent être au format [longitude, latitude]
- Performance : Les lieux ne s'affichent que si le zoom est supérieur à 12 pour optimiser les performances