diff --git a/web/sections/LayoutPanel.tsx b/web/sections/LayoutPanel.tsx index 157feae..8968de3 100644 --- a/web/sections/LayoutPanel.tsx +++ b/web/sections/LayoutPanel.tsx @@ -1,7 +1,4 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { GripVertical, Plus, Trash2 } from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; import { useClient } from 'hooks/useClient'; import { readableError } from '../errors'; import { useFlash } from '../useFlash'; @@ -17,6 +14,15 @@ import { useSaveTarget } from '../SaveTarget'; * nav that has to be edited by a developer every time the client adds a page. Layout is what is left * once you take the pages out: the part of every page that is not the page. * + * ── The menu and the footer are NOT here, and that is the design ── + * + * They were, briefly: rows of links, footer columns, a bottom line. They are gone because the header + * and the footer are DESIGNED — they belong to whoever writes the site's code, not to whoever edits its + * content. A panel offering to rearrange them offers a power that should not exist, and every use of it + * would be a decision made in the wrong place. + * + * What is left is the part that IS a content decision: the colours and the typefaces. + * * ── The preview is the point ── * * It draws the menu across the top, a body, and the footer at the bottom, in the chosen colours and @@ -28,13 +34,7 @@ import { useSaveTarget } from '../SaveTarget'; * changed. What it is honest about is the thing being decided — order, wording, colour, typeface. */ -type LayoutLink = { label: string; href: string }; -type FooterColumn = { title: string; links: LayoutLink[] }; - type Layout = { - menu_items: LayoutLink[]; - footer_columns: FooterColumn[]; - footer_note: string | null; background: string | null; foreground: string | null; accent: string | null; @@ -44,8 +44,6 @@ type Layout = { font_display: string | null; /** What the picker may offer — sent by the API so this file holds no third copy of the list. */ fonts: string[]; - /** The pages that exist, for the menu's href suggestions. */ - pages: { path: string; label: string }[]; }; /** The four roles styles.css declares. A template asks for a role; this decides which face plays it. */ @@ -107,15 +105,12 @@ export const LayoutPanel = () => { const patch = (values: Partial) => setDraft((was) => (was ? { ...was, ...values } : was)); /* - Compared field by field rather than whole, because the payload carries two things this does not - own: `fonts` and `pages` are the API telling the client what exists, not values anybody edits. - Including them would be harmless today and wrong the first time a page is added — the panel would - show unsaved changes for a page somebody else created. + Compared field by field rather than whole, because the payload carries something this does not own: + `fonts` is the API saying which faces the build subsets, not a value anybody edits. Including it + would be harmless today and wrong the first time a face is added — the panel would show unsaved + changes for a decision made in the build script. */ const WRITABLE = [ - 'menu_items', - 'footer_columns', - 'footer_note', 'background', 'foreground', 'accent', @@ -149,16 +144,6 @@ export const LayoutPanel = () => { if (!draft) return
{error ?? 'Loading…'}
; - const menu = draft.menu_items; - const setMenu = (items: LayoutLink[]) => patch({ menu_items: items }); - - const columns = draft.footer_columns; - const setColumns = (next: FooterColumn[]) => patch({ footer_columns: next }); - - /** Falls back to what the page itself falls back to, so the sketch is not a different design. */ - const bg = draft.background ?? '#ffffff'; - const fg = draft.foreground ?? '#000000'; - return (
{saved &&

Saved

} @@ -174,142 +159,6 @@ export const LayoutPanel = () => { */} - {/* ── Menu ─────────────────────────────────────────────────────────────────────────────── */} -
-
-

Menu

- {!menu.length && ( - Showing the site’s own pages - )} -
- - {menu.map((item, index) => ( - setMenu(menu.map((was, i) => (i === index ? next : was)))} - onRemove={() => setMenu(menu.filter((_, i) => i !== index))} - onMove={(by) => setMenu(moved(menu, index, by))} - first={index === 0} - last={index === menu.length - 1} - /> - ))} - -
- - - {/* - Start from the site's own pages rather than from nothing. - - The menu falls back to them until somebody sets one, so the first edit is almost always - "the pages, with one change". Typing all eight back in to make that change is the kind of - small cruelty that makes people leave a panel alone. - */} - {!menu.length && draft.pages.length > 0 && ( - - )} -
-
- - {/* ── Footer ───────────────────────────────────────────────────────────────────────────── */} -
-

Footer

- - {columns.map((column, index) => ( -
-
- - setColumns(columns.map((was, i) => (i === index ? { ...was, title: event.currentTarget.value } : was))) - } - /> - -
- - {column.links.map((link, i) => ( - - setColumns( - columns.map((was, c) => - c === index ? { ...was, links: was.links.map((l, li) => (li === i ? next : l)) } : was, - ), - ) - } - onRemove={() => - setColumns( - columns.map((was, c) => (c === index ? { ...was, links: was.links.filter((_, li) => li !== i) } : was)), - ) - } - onMove={(by) => - setColumns(columns.map((was, c) => (c === index ? { ...was, links: moved(was.links, i, by) } : was))) - } - first={i === 0} - last={i === column.links.length - 1} - /> - ))} - - -
- ))} - - - -
- - patch({ footer_note: event.currentTarget.value || null })} - /> -
-
- {/* ── Colours ──────────────────────────────────────────────────────────────────────────── */}

Colours

@@ -376,100 +225,6 @@ export const LayoutPanel = () => { ); }; -/** Reordering, which is the one thing a menu needs more than editing. */ -const moved = (items: T[], from: number, by: number): T[] => { - const to = from + by; - if (to < 0 || to >= items.length) return items; - const next = [...items]; - const [item] = next.splice(from, 1); - next.splice(to, 0, item!); - return next; -}; - -/** - * One link: what it says, and where it goes. - * - * The href is a text field with the site's own pages offered beside it rather than a dropdown of - * them. A menu item can point at an anchor, an external address, or a page that does not exist yet — - * and while a site is being built the last of those is the common case, so a control that could only - * express "one of the pages that already exist" would be in the way most of the time. - */ -const LinkRow = ({ - item, - pages, - onChange, - onRemove, - onMove, - first, - last, -}: { - item: LayoutLink; - pages: { path: string; label: string }[]; - onChange: (item: LayoutLink) => void; - onRemove: () => void; - onMove: (by: number) => void; - first: boolean; - last: boolean; -}) => { - const known = pages.some((page) => page.path === item.href); - - return ( -
-
- - -
- - - - onChange({ ...item, label: event.currentTarget.value })} - /> - -
- onChange({ ...item, href: event.currentTarget.value })} - /> - {/* Said rather than enforced. An unknown href is usually a page not built yet, not a mistake, - and refusing it would make this panel unusable during exactly the phase it is for. */} - {item.href && !known && !item.href.startsWith('http') && !item.href.startsWith('#') && ( - - no page there yet - - )} -
- - - {pages.map((page) => ( - - ))} - - - -
- ); -}; - /** * The site's own `/layout` page, through the PLATFORM's dev proxy. *