Layout's three bands are nav rows

Top Menu, Content, Footer — the layout IS a page, and those are the only three things on it. They were
reachable by scrolling and by knowing which tab held what; as rows they take the shape the nav already
teaches with the pages under Pages, and the open one is `band` in the query string.

Fixed rather than fetched, unlike pages. A band cannot be added or removed from the admin — only the
code that draws it can — so reading them from the database would be a query that can only ever return
the same three rows in the same order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 19:19:53 +01:00
co-authored by Claude Opus 5
parent 10e5a625ff
commit 8c9e4ecb85
2 changed files with 63 additions and 1 deletions
+42 -1
View File
@@ -1,6 +1,6 @@
import { Link, useSearchParams } from 'react-router'; import { Link, useSearchParams } from 'react-router';
import { Lock } from 'lucide-react'; import { Lock } from 'lucide-react';
import { DEFAULT_SECTION, SECTIONS, isSectionKey, sectionOpen, type SectionKey } from './sections'; import { DEFAULT_SECTION, LAYOUT_BANDS, SECTIONS, isSectionKey, sectionOpen, type SectionKey } from './sections';
import { useReady } from './useReady'; import { useReady } from './useReady';
import { pageParam, usePages } from './usePages'; import { pageParam, usePages } from './usePages';
@@ -145,6 +145,47 @@ export const NavPanel = () => {
Drawn whether or not Pages is the open section. The list of pages IS the site, and hiding it Drawn whether or not Pages is the open section. The list of pages IS the site, and hiding it
until you click makes the nav a menu of menus. until you click makes the nav a menu of menus.
*/ */
/*
Layout's three bands, as rows under Layout.
The layout IS a page — a header, the slot a page goes in, and a footer — and those three are the
only things on this screen. They were reachable by scrolling and by knowing which tab held what;
as rows they are the same shape as the pages under Pages, which is the shape the nav already
teaches.
Fixed rather than fetched: unlike pages, these three are structural. A band cannot be added or
removed from the admin, only the code that draws it can, so a list read from the database would
be a query that can only ever return the same three rows.
*/
if (section.key === 'layout') {
return (
<div key={section.key} className="flex flex-col">
{row}
{LAYOUT_BANDS.map((band) => {
const childSearch = new URLSearchParams(params);
childSearch.set('section', 'layout');
childSearch.set('band', band.key);
childSearch.delete('media');
const current = active === 'layout' && params.get('band') === band.key;
return (
<Link
key={band.key}
to={{ search: `?${childSearch.toString()}` }}
replace
className={`flex items-baseline gap-2 border-b border-border py-1.5 pr-3 pl-9 text-left transition-colors ${
current ? 'bg-muted' : 'hover:bg-muted/50'
}`}
>
<span className="min-w-0 flex-1 truncate text-xs">{band.label}</span>
</Link>
);
})}
</div>
);
}
if (section.key !== 'pages' || !pages?.length) return row; if (section.key !== 'pages' || !pages?.length) return row;
return ( return (
+21
View File
@@ -70,6 +70,27 @@ export type Section = {
pinned?: boolean; pinned?: boolean;
}; };
/**
* The three bands the layout is made of, in the order they appear on the page.
*
* The layout IS a page: a header, the slot a page goes in, and a footer. These are the nav rows under
* Layout, and the `band` in the query string.
*
* FIXED, unlike the pages under Pages. A band cannot be added or removed from the admin — only the
* code that draws it can — so reading them from the database would be a query that can only ever
* return the same three rows in the same order.
*/
export const LAYOUT_BANDS = [
{ key: 'header', label: 'Top Menu' },
{ key: 'content', label: 'Content' },
{ key: 'footer', label: 'Footer' },
] as const;
export type LayoutBand = (typeof LAYOUT_BANDS)[number]['key'];
export const isLayoutBand = (value: string | null): value is LayoutBand =>
value !== null && LAYOUT_BANDS.some((band) => band.key === value);
export const SECTIONS: Section[] = [ export const SECTIONS: Section[] = [
{ {
/* /*