Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9900cfefcc | ||
|
|
952c5512bd | ||
|
|
671cfc4325 | ||
|
|
e34b0d39c6 |
@@ -2268,6 +2268,23 @@ a {
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
/* Secondary count on the Covoiturage accordion: how many carpooling areas
|
||||
were found. The accordion sits on --color-secondary, so this is a white
|
||||
pill with the header's own green inside — legible on that background, and
|
||||
clearly a different kind of count from the orange trip badge next to it. */
|
||||
.accordion-badge-areas {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-secondary);
|
||||
gap: 0.25rem;
|
||||
padding: 0 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.accordion-badge-areas svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
.accordion-arrow {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@@ -2429,31 +2446,54 @@ a {
|
||||
}
|
||||
|
||||
/* Map Markers */
|
||||
/* MapLibre writes `transform: translate(-50%,-50%) translate(Xpx,Ypx)` on
|
||||
.map-marker to position it, and rewrites it on every map frame. Any
|
||||
transform or transition declared here would fight that — a hover scale
|
||||
would also scale the pixel offset, and a transition would make the marker
|
||||
lag behind the map. So .map-marker only reserves space; everything visual
|
||||
lives on .map-marker-inner, which MapLibre never touches. */
|
||||
.map-marker {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.map-marker-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--color-white);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.map-marker:hover {
|
||||
.map-marker-inner:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.map-marker-departure {
|
||||
.map-marker-departure .map-marker-inner {
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.map-marker-arrival {
|
||||
.map-marker-arrival .map-marker-inner {
|
||||
background-color: var(--color-highlight);
|
||||
}
|
||||
|
||||
/* Carpooling areas: smaller than trip endpoints, they are context not
|
||||
destinations. */
|
||||
.map-marker-area {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.map-marker-area .map-marker-inner {
|
||||
background-color: var(--color-highlight);
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
/* MapLibre Popup Styling */
|
||||
.maplibregl-popup-content {
|
||||
padding: 12px 16px;
|
||||
@@ -2989,3 +3029,76 @@ a {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Aires de covoiturage (BNLC), sous les trajets de l'onglet Covoiturage */
|
||||
.carpool-areas {
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.carpool-areas-title {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.carpool-areas-group {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.carpool-areas-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.carpool-areas-subtitle {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #6b7280;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.carpool-area-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.carpool-area-item:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.carpool-area-name {
|
||||
font-size: 14px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.carpool-area-address {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.carpool-area-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.carpool-area-tag {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
padding: 0.125rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
background-color: #f3f4f6;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.carpool-area-tag:first-child {
|
||||
background-color: #fdf1e3;
|
||||
color: var(--color-highlight);
|
||||
}
|
||||
|
||||
@@ -121,10 +121,20 @@
|
||||
</div>
|
||||
|
||||
<!-- Covoiturage -->
|
||||
<div class="result-accordion" x-data="{ open: false }">
|
||||
<div class="result-accordion"
|
||||
x-data="{ open: false, get areaCount() { return (results.carpool_areas?.departure?.number || 0) + (results.carpool_areas?.arrival?.number || 0); } }"
|
||||
x-init="$watch('open', value => value ? showCarpoolAreaMarkers() : hideCarpoolAreaMarkers())">
|
||||
<button type="button" class="accordion-header" @click="open = !open" :class="{ 'active': open }">
|
||||
<span class="accordion-title">Covoiturage</span>
|
||||
<span class="accordion-badge" x-text="results.carpools?.number || 0"></span>
|
||||
<span class="accordion-badge accordion-badge-areas" x-show="areaCount" x-cloak
|
||||
:title="areaCount + ' aire' + (areaCount > 1 ? 's' : '') + ' de covoiturage à proximité'">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M15 10.5a3 3 0 1 1 -6 0a3 3 0 0 1 6 0z"/>
|
||||
<path d="M19.5 10.5c0 7.142 -7.5 11.25 -7.5 11.25s-7.5 -4.108 -7.5 -11.25a7.5 7.5 0 1 1 15 0z"/>
|
||||
</svg>
|
||||
<span x-text="areaCount"></span>
|
||||
</span>
|
||||
{{ if $iconArrow }}
|
||||
<img src="{{ $iconArrow.RelPermalink }}" alt="" class="accordion-arrow" :class="{ 'open': open }" />
|
||||
{{ end }}
|
||||
@@ -156,6 +166,32 @@
|
||||
<template x-if="!results.carpools?.number">
|
||||
<p class="no-result-text">Aucun covoiturage disponible pour ce trajet.</p>
|
||||
</template>
|
||||
|
||||
<!-- Aires de covoiturage (BNLC) autour du départ et de l'arrivée -->
|
||||
<div class="carpool-areas" x-data="carpoolAreasBlock()" x-show="groups.length" x-cloak>
|
||||
<div class="carpool-areas-title">Aires de covoiturage</div>
|
||||
<template x-for="group in groups" :key="group.end">
|
||||
<div class="carpool-areas-group">
|
||||
<div class="carpool-areas-subtitle" x-text="group.label"></div>
|
||||
<template x-for="(area, index) in group.areas" :key="group.end + '-' + index">
|
||||
<div class="carpool-area-item" @click="focusArea(area)">
|
||||
<span class="carpool-area-name" x-text="area.name"></span>
|
||||
<span class="carpool-area-address" x-show="area.address || area.city">
|
||||
<span x-text="area.address" x-show="area.address"></span>
|
||||
<span x-show="area.address && area.city">·</span>
|
||||
<span x-text="area.city" x-show="area.city"></span>
|
||||
</span>
|
||||
<span class="carpool-area-tags">
|
||||
<span class="carpool-area-tag" x-show="area.type" x-text="area.type"></span>
|
||||
<span class="carpool-area-tag" x-show="area.places" x-text="area.places + ' place(s)'"></span>
|
||||
<span class="carpool-area-tag" x-show="area.placesPmr" x-text="area.placesPmr + ' PMR'"></span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{{ with .Params.contactCTA.carpool }}
|
||||
<div class="accordion-cta">
|
||||
<p>{{ . }}</p>
|
||||
@@ -668,6 +704,127 @@
|
||||
}
|
||||
|
||||
|
||||
// Aires de covoiturage (BNLC), servies par le service geography via
|
||||
// parcoursmob. Les propriétés BNLC sont éparses : tout est gardé.
|
||||
function carpoolAreasFromData() {
|
||||
const areas = ((window.__PARCOURSMOB_DATA__ || {}).results || {}).carpool_areas || {};
|
||||
const toArea = (feature, end) => {
|
||||
const props = (feature && feature.properties) || {};
|
||||
return {
|
||||
end: end,
|
||||
name: props.nom || 'Aire de covoiturage',
|
||||
address: props.adresse || null,
|
||||
city: props.commune || null,
|
||||
type: props.type || null,
|
||||
places: typeof props.places === 'number' ? props.places : null,
|
||||
placesPmr: typeof props.places_pmr === 'number' ? props.places_pmr : null,
|
||||
coordinates: (feature && feature.geometry) ? feature.geometry.coordinates : null
|
||||
};
|
||||
};
|
||||
return []
|
||||
.concat(((areas.departure || {}).results || []).map(f => toArea(f, 'departure')))
|
||||
.concat(((areas.arrival || {}).results || []).map(f => toArea(f, 'arrival')))
|
||||
.filter(area => area.coordinates);
|
||||
}
|
||||
|
||||
function carpoolAreasBlock() {
|
||||
const areas = carpoolAreasFromData();
|
||||
return {
|
||||
groups: [
|
||||
{end: 'departure', label: 'Près du départ', areas: areas.filter(a => a.end === 'departure')},
|
||||
{end: 'arrival', label: "Près de l'arrivée", areas: areas.filter(a => a.end === 'arrival')}
|
||||
].filter(group => group.areas.length),
|
||||
|
||||
focusArea(area) {
|
||||
if (window.parcoursmobMap && area.coordinates) {
|
||||
window.parcoursmobMap.flyTo({center: area.coordinates, zoom: 14});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Marker glyph per BNLC type — same set as the back-office compact view.
|
||||
function carpoolAreaIcon(type) {
|
||||
const glyph = (body, width) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="white" stroke-width="${width || 2}" stroke-linecap="round" stroke-linejoin="round">${body}</svg>`;
|
||||
|
||||
switch (type) {
|
||||
case 'Aire de covoiturage':
|
||||
return glyph('<circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/><path d="M5 17h-2v-6l2 -5h9l4 5h1a2 2 0 0 1 2 2v4h-2m-4 0h-6"/>');
|
||||
case 'Parking':
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="13" height="13"><text x="12" y="18.5" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="20" font-weight="700" fill="white">P</text></svg>`;
|
||||
case 'Parking relais':
|
||||
return glyph('<circle cx="6" cy="17" r="2"/><circle cx="18" cy="17" r="2"/><path d="M4 17h-2v-11a1 1 0 0 1 1 -1h14a5 7 0 0 1 5 7v5h-2m-4 0h-8"/><path d="M2 10h15"/>');
|
||||
case "Sortie d'autoroute":
|
||||
return glyph('<path d="M4 21v-8a4 4 0 0 1 4 -4h9"/><path d="M14 6l3 3l-3 3"/>');
|
||||
case 'Supermarché':
|
||||
return glyph('<circle cx="9" cy="19" r="1.5"/><circle cx="17" cy="19" r="1.5"/><path d="M3 4h2l2.4 10.5a2 2 0 0 0 2 1.5h7.6a2 2 0 0 0 2 -1.6l1 -5.4h-13"/>');
|
||||
case 'Auto-stop':
|
||||
return glyph('<circle cx="13" cy="4" r="1"/><path d="M7 21l3 -4"/><path d="M16 21l-2 -4l-3 -3l1 -6"/><path d="M6 12l2 -3l4 -1l3 3l3 1"/>');
|
||||
case 'Délaissé routier':
|
||||
return glyph('<path d="M12 3v4"/><path d="M12 10v4"/><path d="M12 17v4"/><path d="M5 3v18"/><path d="M19 3v18"/>');
|
||||
default:
|
||||
return glyph('<path d="M15 10.5a3 3 0 1 1 -6 0a3 3 0 0 1 6 0z"/><path d="M19.5 10.5c0 7.142 -7.5 11.25 -7.5 11.25s-7.5 -4.108 -7.5 -11.25a7.5 7.5 0 1 1 15 0z"/>', 1.8);
|
||||
}
|
||||
}
|
||||
|
||||
// The areas only show while the Covoiturage accordion is open. The flag
|
||||
// outlives the map so the markers come back when the map is recreated on
|
||||
// a desktop/mobile switch.
|
||||
let carpoolAreasVisible = false;
|
||||
let carpoolAreaMarkers = [];
|
||||
|
||||
function clearCarpoolAreaMarkers() {
|
||||
carpoolAreaMarkers.forEach(marker => marker.remove());
|
||||
carpoolAreaMarkers = [];
|
||||
}
|
||||
|
||||
function renderCarpoolAreaMarkers() {
|
||||
const map = window.parcoursmobMap;
|
||||
clearCarpoolAreaMarkers();
|
||||
if (!map || !carpoolAreasVisible) return;
|
||||
|
||||
const areas = carpoolAreasFromData();
|
||||
if (!areas.length) return;
|
||||
|
||||
const data = window.__PARCOURSMOB_DATA__ || {};
|
||||
const bounds = new maplibregl.LngLatBounds();
|
||||
|
||||
areas.forEach((area) => {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'map-marker map-marker-area';
|
||||
el.innerHTML = `<div class="map-marker-inner">${carpoolAreaIcon(area.type)}</div>`;
|
||||
|
||||
const lines = [`<strong>${area.name}</strong>`];
|
||||
if (area.city) lines.push(area.city);
|
||||
if (area.address) lines.push(area.address);
|
||||
if (area.type) lines.push(area.type);
|
||||
|
||||
const marker = new maplibregl.Marker({ element: el })
|
||||
.setLngLat(area.coordinates)
|
||||
.setPopup(new maplibregl.Popup({ offset: 20 }).setHTML(lines.join('<br>')))
|
||||
.addTo(map);
|
||||
|
||||
carpoolAreaMarkers.push(marker);
|
||||
bounds.extend(area.coordinates);
|
||||
});
|
||||
|
||||
// Keep the trip framed while bringing the areas into view, rather than
|
||||
// recentring on the areas alone.
|
||||
if (data.departure) bounds.extend(data.departure.geometry.coordinates);
|
||||
if (data.destination) bounds.extend(data.destination.geometry.coordinates);
|
||||
map.fitBounds(bounds, { padding: 60, maxZoom: 14 });
|
||||
}
|
||||
|
||||
function showCarpoolAreaMarkers() {
|
||||
carpoolAreasVisible = true;
|
||||
renderCarpoolAreaMarkers();
|
||||
}
|
||||
|
||||
function hideCarpoolAreaMarkers() {
|
||||
carpoolAreasVisible = false;
|
||||
clearCarpoolAreaMarkers();
|
||||
}
|
||||
|
||||
function initMap() {
|
||||
const data = window.__PARCOURSMOB_DATA__ || {};
|
||||
const departure = data.departure;
|
||||
@@ -706,9 +863,11 @@
|
||||
const departureEl = document.createElement('div');
|
||||
departureEl.className = 'map-marker map-marker-departure';
|
||||
departureEl.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18" height="18">
|
||||
<path fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<div class="map-marker-inner">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18" height="18">
|
||||
<path fill-rule="evenodd" d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
new maplibregl.Marker({ element: departureEl })
|
||||
.setLngLat(depCoords)
|
||||
@@ -719,15 +878,21 @@
|
||||
const arrivalEl = document.createElement('div');
|
||||
arrivalEl.className = 'map-marker map-marker-arrival';
|
||||
arrivalEl.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18" height="18">
|
||||
<path fill-rule="evenodd" d="M3 2.25a.75.75 0 01.75.75v.54l1.838-.46a9.75 9.75 0 016.725.738l.108.054a8.25 8.25 0 005.58.652l3.109-.732a.75.75 0 01.917.81 47.784 47.784 0 00.005 10.337.75.75 0 01-.574.812l-3.114.733a9.75 9.75 0 01-6.594-.77l-.108-.054a8.25 8.25 0 00-5.69-.625l-2.202.55V21a.75.75 0 01-1.5 0V3A.75.75 0 013 2.25z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<div class="map-marker-inner">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18" height="18">
|
||||
<path fill-rule="evenodd" d="M3 2.25a.75.75 0 01.75.75v.54l1.838-.46a9.75 9.75 0 016.725.738l.108.054a8.25 8.25 0 005.58.652l3.109-.732a.75.75 0 01.917.81 47.784 47.784 0 00.005 10.337.75.75 0 01-.574.812l-3.114.733a9.75 9.75 0 01-6.594-.77l-.108-.054a8.25 8.25 0 00-5.69-.625l-2.202.55V21a.75.75 0 01-1.5 0V3A.75.75 0 013 2.25z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
||||
new maplibregl.Marker({ element: arrivalEl })
|
||||
.setLngLat(destCoords)
|
||||
.setPopup(new maplibregl.Popup().setHTML(`<strong>Arrivée</strong><br>${destination.properties.label}`))
|
||||
.addTo(map);
|
||||
|
||||
// window.parcoursmobMap is assigned by the caller, which has already
|
||||
// returned by the time `load` fires.
|
||||
map.on('load', () => renderCarpoolAreaMarkers());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user