glcvdv-lite-api-map
Version:
A lightweight API for interactive hotel maps with Mapbox integration
139 lines • 6.27 kB
JavaScript
import mapboxgl from "mapbox-gl";
import { generateWhitelabelUrl } from "../../utils/generate-whitelabel-url";
export const addMapInteractions = (map, options) => {
const popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
map.addInteraction('places-mouseenter-interaction', {
type: 'mouseenter',
target: { layerId: 'hotels' },
handler: (e) => {
map.getCanvas().style.cursor = 'pointer';
const geometry = e.feature?.geometry;
const coordinates = geometry?.type === 'Point' ? geometry.coordinates.slice() : undefined;
const hotel = e.feature?.properties;
const rating = typeof hotel?.rating === 'number' ? Math.max(0, Math.min(10, hotel.rating)) : 0;
const filledStars = Math.floor(rating / 2); // Convert 0-10 scale to 0-5 stars
const stars = '★'.repeat(filledStars) + '☆'.repeat(5 - filledStars);
const description = `
<div style="min-width: 250px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden;">
${hotel?.image ? `<img src="${hotel.image}" alt="${hotel?.name || 'Hotel'}" style="width: 88%; height: 120px; object-fit: cover; border-radius: 8px; margin-bottom: 8px;">` : ''}
<h3 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 600; color: #1a1a1a;">${hotel?.name || 'Hotel'}</h3>
<div style="display: flex; align-items: center; margin-bottom: 8px;">
<span style="color: #ffd700; font-size: 14px; margin-right: 4px;">${stars}</span>
<span style="color: #666; font-size: 12px;">${rating}/10</span>
</div>
<p style="margin: 0; color: #333; font-size: 14px;">Rooms starting from <strong>${hotel?.rate} ${options.currency || 'EUR'}</strong></p>
</div>
`;
if (coordinates) {
popup.setLngLat([coordinates[0], coordinates[1]]).setHTML(description).addTo(map);
}
}
});
map.addInteraction('places-mouseleave-interaction', {
type: 'mouseleave',
target: { layerId: 'hotels' },
handler: () => {
map.getCanvas().style.cursor = '';
popup.remove();
}
});
map.addInteraction('places-click-interaction', {
type: 'click',
target: { layerId: 'hotels' },
handler: (e) => {
if (!e.feature)
return;
const hotel = e.feature.properties;
const url = generateWhitelabelUrl(hotel, {
language: options.language || 'EN',
currency: options.currency || 'EUR'
});
window.open(url, '_blank');
}
});
if (options.clusters) {
map.addInteraction('click-clusters', {
type: 'click',
target: { layerId: 'clusters' },
handler: (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['clusters']
});
if (features.length === 0 || !features[0]?.properties?.cluster_id)
return;
const clusterId = features[0].properties.cluster_id;
const source = map.getSource('hotels');
if (!source || typeof source.getClusterExpansionZoom !== 'function')
return;
source.getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err || !zoom)
return;
const geometry = features[0]?.geometry;
if (!geometry || geometry.type !== 'Point')
return;
const pointGeometry = geometry;
if (!pointGeometry.coordinates || !Array.isArray(pointGeometry.coordinates) || pointGeometry.coordinates.length < 2)
return;
const coordinates = pointGeometry.coordinates;
map.easeTo({
center: coordinates,
zoom: zoom
});
});
}
});
map.addInteraction('click-unclustered-point', {
type: 'click',
target: { layerId: 'unclustered-point' },
handler: (e) => {
if (!e.feature?.geometry || !e.feature?.properties)
return;
const geometry = e.feature.geometry;
if (geometry.type !== 'Point')
return;
const pointGeometry = geometry;
const coordinates = pointGeometry.coordinates.slice();
const mag = e.feature.properties.mag;
const tsunami = e.feature.properties.tsunami === 1 ? 'yes' : 'no';
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(`magnitude: ${mag}<br>Was there a tsunami?: ${tsunami}`)
.addTo(map);
}
});
map.addInteraction('clusters-mouseenter', {
type: 'mouseenter',
target: { layerId: 'clusters' },
handler: () => {
map.getCanvas().style.cursor = 'pointer';
}
});
map.addInteraction('clusters-mouseleave', {
type: 'mouseleave',
target: { layerId: 'clusters' },
handler: () => {
map.getCanvas().style.cursor = '';
}
});
// Change the cursor to a pointer when the mouse is over an individual POI.
map.addInteraction('unclustered-mouseenter', {
type: 'mouseenter',
target: { layerId: 'unclustered-point' },
handler: () => {
map.getCanvas().style.cursor = 'pointer';
}
});
// Change the cursor back to a pointer when it stops hovering over an individual POI.
map.addInteraction('unclustered-mouseleave', {
type: 'mouseleave',
target: { layerId: 'unclustered-point' },
handler: () => {
map.getCanvas().style.cursor = '';
}
});
}
};
//# sourceMappingURL=add-map-interactions.js.map