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:
+16
-4
@@ -18,16 +18,22 @@ function navigate(hash) {
|
||||
}
|
||||
|
||||
// Parse "#/trip/5" -> { name: 'trip', params: { id: '5' } }
|
||||
// "#/trip/5/money" -> { name: 'trip', params: { id: '5', tab: 'money' } }; an
|
||||
// unrecognized third segment is passed through as-is (tripDetail defaults it
|
||||
// to 'trip' rather than the router 404ing on it).
|
||||
function parseHash() {
|
||||
const raw = (location.hash || '').replace(/^#/, '');
|
||||
const parts = raw.split('/').filter(Boolean);
|
||||
if (parts.length === 0) return { name: 'trips', params: {} };
|
||||
if (parts[0] === 'login') return { name: 'login', params: {} };
|
||||
if (parts[0] === 'trips') return { name: 'trips', params: {} };
|
||||
if (parts[0] === 'trip' && parts[1]) return { name: 'trip', params: { id: parts[1] } };
|
||||
if (parts[0] === 'trip' && parts[1]) return { name: 'trip', params: { id: parts[1], tab: parts[2] } };
|
||||
return { name: 'trips', params: {} };
|
||||
}
|
||||
|
||||
// The empty .nav-trip-slot sits between the brand and the user area; non-trip
|
||||
// views leave it empty (it costs no space — see .nav-trip-slot:empty in
|
||||
// tabs.css). tripDetail fills it via ctx.navTripSlot once the trip loads.
|
||||
function renderNav() {
|
||||
const nav = el(
|
||||
'header',
|
||||
@@ -38,6 +44,7 @@ function renderNav() {
|
||||
el('span', { class: 'brand-mark' }, '🧭'),
|
||||
el('span', { class: 'brand-name' }, 'Trip Plan'),
|
||||
),
|
||||
el('div', { class: 'nav-trip-slot' }),
|
||||
el(
|
||||
'div',
|
||||
{ class: 'nav-right' },
|
||||
@@ -132,16 +139,21 @@ function render() {
|
||||
|
||||
const container = root();
|
||||
clear(container);
|
||||
if (state.user) container.appendChild(renderNav());
|
||||
let navTripSlot = null;
|
||||
if (state.user) {
|
||||
const nav = renderNav();
|
||||
navTripSlot = nav.querySelector('.nav-trip-slot');
|
||||
container.appendChild(nav);
|
||||
}
|
||||
|
||||
const viewEl = el('main', { class: 'view', id: 'view' });
|
||||
container.appendChild(viewEl);
|
||||
|
||||
const ctx = { state, navigate, refresh: render };
|
||||
const ctx = { state, navigate, refresh: render, navTripSlot };
|
||||
|
||||
if (view.name === 'login') renderAuth(viewEl, ctx);
|
||||
else if (view.name === 'trips') renderTrips(viewEl, ctx);
|
||||
else if (view.name === 'trip') renderTripDetail(viewEl, ctx, view.params.id);
|
||||
else if (view.name === 'trip') renderTripDetail(viewEl, ctx, view.params.id, view.params.tab);
|
||||
}
|
||||
|
||||
// Exposed so views can update the current user after login/register.
|
||||
|
||||
Reference in New Issue
Block a user