refactor(theme): consolidate map init into MapUtils.initEntryMap

Extract the duplicated MapLibre init orchestration (map construction,
marker/popup loop, bounds fit, GPX journey, fullscreen toggle) into one
config-driven MapUtils.initEntryMap(opts) in maplibre-utils.js, bundled
into map.js. Convert trip.html.twig and both home.html.twig branches to
call it; home active gains the flash-highlight + a fullscreen button to
match trip, and home highlights' marker click now navigates to the
article. Adds a markLatest opt (false for highlights) and exposes the
map as window.tripMap/window.homeMap (used by existing Playwright specs).
feed-map.html.twig and map.html.twig left on their inline init (deferred).

Plan: docs/working/plans/2026-06-27-map-init-consolidation.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BftDn9vu9SonFAY4vxu4uk
This commit is contained in:
2026-06-28 00:11:34 +02:00
co-authored by Claude Opus 4.8
parent 75da83d6e1
commit 29535e39b8
4 changed files with 186 additions and 190 deletions
+20 -80
View File
@@ -61,7 +61,14 @@
<div class="home-layout">
<div class="home-map-col">
<div class="home-map" id="home-map"></div>
<div class="home-map" id="home-map">
<button class="feed-map-fullscreen-btn" id="home-map-fullscreen" aria-label="Expand map">
<svg class="feed-map-fs-open" aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="currentColor">
<path d="M0 0v4h1.5V1.5H4V0z M14 0H10v1.5h2.5V4H14z M0 14v-4h1.5v2.5H4V14z M14 14H10v-1.5h2.5V10H14z"/>
</svg>
<span class="feed-map-fs-close" aria-hidden="true">✕</span>
</button>
</div>
</div>
{% if all_items|length == 0 %}
@@ -91,49 +98,14 @@ var USE_GPX = {{ trip and trip.header.use_gpx is not null ? (trip.header.u
var AUTOCONNECT = "{{ trip ? (trip.header.autoconnect ?? 'on') : 'on' }}";
document.addEventListener('DOMContentLoaded', function() {
var homeMap = new maplibregl.Map({
container: 'home-map',
style: MapUtils.MAP_STYLE,
center: [20, 20],
zoom: 2
});
homeMap.on('load', function () {
var bounds = new maplibregl.LngLatBounds();
var coords = [];
HOME_ENTRIES.forEach(function (entry, i) {
var isLatest = (i === HOME_ENTRIES.length - 1);
var lngLat = [parseFloat(entry.lng), parseFloat(entry.lat)];
coords.push(lngLat);
bounds.extend(lngLat);
var el = MapUtils.createDotMarker(isLatest);
el.dataset.url = entry.url;
var popup = new maplibregl.Popup({ offset: 12, closeButton: false, closeOnClick: false, className: 'map-tip-popup' })
.setLngLat(lngLat)
.setHTML('<span class="map-tip">' + entry.title + '</span>');
el.addEventListener('mouseenter', function () { popup.addTo(homeMap); });
el.addEventListener('mouseleave', function () { popup.remove(); });
el.addEventListener('click', function () {
var card = document.getElementById('entry-' + entry.slug);
if (!card) return;
window.location.hash = 'entry-' + entry.slug;
});
new maplibregl.Marker({ element: el }).setLngLat(lngLat).addTo(homeMap);
window.homeMap = MapUtils.initEntryMap({
container: 'home-map',
entries: HOME_ENTRIES,
cardPrefix: 'entry-',
fullscreen: { btnId: 'home-map-fullscreen', colSelector: '.home-map-col' },
gpx: { urls: HOME_GPX_URLS, use: USE_GPX, autoconnect: AUTOCONNECT, sourcePrefix: 'home-gpx', journeyId: 'home-journey' },
fit: { padding: 60, maxZoom: 11, singleZoom: 10 }
});
if (HOME_ENTRIES.length === 1) {
homeMap.jumpTo({ center: coords[0], zoom: 10 });
} else {
homeMap.fitBounds(bounds, { padding: 60, maxZoom: 11 });
}
setTimeout(function () { homeMap.resize(); }, 100);
MapUtils.renderGpxJourney(homeMap, USE_GPX ? HOME_GPX_URLS : [], HOME_ENTRIES, 'home-gpx', 'home-journey', { connectMode: AUTOCONNECT });
});
}); // DOMContentLoaded
</script>
{% endif %}
@@ -247,44 +219,12 @@ homeMap.on('load', function () {
var HIGHLIGHTS_ENTRIES = {{ highlights_map_entries|json_encode|raw }};
document.addEventListener('DOMContentLoaded', function() {
var homeMap = new maplibregl.Map({
container: 'home-map',
style: MapUtils.MAP_STYLE,
center: [20, 20],
zoom: 2
});
homeMap.on('load', function () {
if (HIGHLIGHTS_ENTRIES.length === 0) return;
var bounds = new maplibregl.LngLatBounds();
HIGHLIGHTS_ENTRIES.forEach(function (entry) {
var lngLat = [parseFloat(entry.lng), parseFloat(entry.lat)];
bounds.extend(lngLat);
var el = MapUtils.createDotMarker(false);
var popup = new maplibregl.Popup({ offset: 12, closeButton: false, closeOnClick: false, className: 'map-tip-popup' })
.setLngLat(lngLat)
.setHTML('<span class="map-tip">' + entry.title + '</span>');
el.addEventListener('mouseenter', function () { popup.addTo(homeMap); });
el.addEventListener('mouseleave', function () { popup.remove(); });
el.addEventListener('click', function () {
var card = document.getElementById('highlight-' + entry.slug);
if (card) card.scrollIntoView({ behavior: 'smooth', block: 'center' });
});
new maplibregl.Marker({ element: el }).setLngLat(lngLat).addTo(homeMap);
window.homeMap = MapUtils.initEntryMap({
container: 'home-map',
entries: HIGHLIGHTS_ENTRIES,
markLatest: false,
fit: { padding: 60, maxZoom: 8, singleZoom: 8 }
});
if (HIGHLIGHTS_ENTRIES.length === 1) {
homeMap.jumpTo({ center: [parseFloat(HIGHLIGHTS_ENTRIES[0].lng), parseFloat(HIGHLIGHTS_ENTRIES[0].lat)], zoom: 8 });
} else {
homeMap.fitBounds(bounds, { padding: 60, maxZoom: 8 });
}
setTimeout(function () { homeMap.resize(); }, 100);
});
}); // DOMContentLoaded
</script>
{% endif %}