Split the trip page into tabs and move the header into the navbar
Three deep-linkable tabs (#/trip/:id/:tab): Trip (countdown, calendar, map, summary), Money (costs + expenses side by side), Checklist. Panels stay mounted; switching toggles visibility and updates the URL via history.replaceState so an open day editor and the live tickers are undisturbed. The Leaflet map revalidates its size on every trip-tab activation but only auto-fits once, when its container first gains real size - later visits keep the user's pan/zoom. The full-width trip-header card is gone: the navbar now carries a compact identity (back link, truncated name, date/days/currency) plus a single overflow menu holding members, join code, edit (now a centered modal) and delete. Dead header CSS removed; new styles live in tabs.css.
This commit is contained in:
+29
-6
@@ -17,6 +17,13 @@ export function renderMap(tctx) {
|
||||
const route = tctx.route || { stops: [], legs: [], totalKm: 0 };
|
||||
const stops = route.stops || [];
|
||||
|
||||
// Leaflet initialised while its tab panel is hidden gets a zero-size
|
||||
// container; tripDetail calls this on every activation of the trip tab so
|
||||
// the map (and its fitBounds zoom, computed against whatever size the
|
||||
// container had at creation time) catches up. No-op until the map actually
|
||||
// exists — overwritten at the end of initMap() below.
|
||||
tctx.mapInvalidate = () => {};
|
||||
|
||||
const section = el('section', { class: 'card map-section' });
|
||||
section.appendChild(
|
||||
el(
|
||||
@@ -54,7 +61,7 @@ export function renderMap(tctx) {
|
||||
|
||||
// Leaflet needs the container attached with a real size, so init on the
|
||||
// next tick after this section is mounted into the page.
|
||||
setTimeout(() => initMap(mapDiv, stops, legs, kmEls), 0);
|
||||
setTimeout(() => initMap(mapDiv, stops, legs, kmEls, tctx), 0);
|
||||
|
||||
return section;
|
||||
}
|
||||
@@ -75,7 +82,7 @@ function visibleLegs(route, stops) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function initMap(mapDiv, stops, legs, kmEls) {
|
||||
function initMap(mapDiv, stops, legs, kmEls, tctx) {
|
||||
const L = window.L;
|
||||
if (!L) {
|
||||
mapDiv.appendChild(el('p', { class: 'muted' }, 'Map library failed to load.'));
|
||||
@@ -126,13 +133,29 @@ function initMap(mapDiv, stops, legs, kmEls) {
|
||||
return { leg, a, b, line, label, color };
|
||||
});
|
||||
|
||||
if (points.length === 1) {
|
||||
map.setView(points[0], 10);
|
||||
} else {
|
||||
map.fitBounds(L.latLngBounds(points).pad(0.2));
|
||||
function fit() {
|
||||
if (points.length === 1) map.setView(points[0], 10);
|
||||
else map.fitBounds(L.latLngBounds(points).pad(0.2));
|
||||
}
|
||||
fit();
|
||||
map.invalidateSize();
|
||||
|
||||
// A container sized 0 at creation time (hidden tab panel) gives fitBounds a
|
||||
// bogus zoom that invalidateSize() alone won't correct, so the first
|
||||
// activation after the container actually gets a real size must re-fit.
|
||||
// But re-fitting on EVERY activation would silently discard any pan/zoom
|
||||
// the user did on a previous visit to the tab — so fit() only runs once,
|
||||
// the first time the container transitions from zero-width to sized.
|
||||
let fitted = mapDiv.offsetWidth > 0;
|
||||
tctx.mapInvalidate = () => {
|
||||
if (!mapDiv.isConnected) return;
|
||||
map.invalidateSize();
|
||||
if (!fitted && mapDiv.offsetWidth > 0) {
|
||||
fit();
|
||||
fitted = true;
|
||||
}
|
||||
};
|
||||
|
||||
fetchRoadGeometry(L, map, mapDiv, legLayers, kmEls);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user