Files
fofos-admin/web/EditorPanel.tsx
pastilhasandClaude Opus 5 15042ccc76 a gallery is a carousel
It always rendered as one — data-carousel, a snap track, two arrows, driven by
carousel.client.js. The entity was the last thing still calling it something
else, and that collided with the site's own Gallery section: two nav rows
reading Gallery and Galleries, meaning a list of objects and a list of
photograph sets.

GalleriesPanel, GalleryPicker and GalleryItems become Carousels*, the endpoint
becomes /fofos-admin/carousels, the block kind becomes carousel and
subject-gallery becomes subject-carousel. The publish allowlist follows the
renamed tables.

What did NOT move: GALLERY_CONFIG at /fofos-admin/gallery, subjects.ts's
base '/gallery', and home_gallery_media. Those are the Gallery section and the
legacy rows, and both are correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 18:01:15 +01:00

100 lines
4.5 KiB
TypeScript

import { DeviceToggle } from './DeviceToggle';
import { PageSettings } from './sections/PageSettings';
import { useNav } from './routes';
import { pageFromParam, usePages } from './usePages';
import { useEditorSlot } from './useEditorSlot';
/**
* Where an editor opens — a column beside the preview rather than a drawer beneath it.
*
* It was rendered under the frame, which took the height from the thing being edited: choosing a
* spacing shrank the page you were choosing it for, and on a laptop the preview lost a third of itself
* to a row of two dropdowns. A panel keeps the page whole and puts the controls where a workspace puts
* controls.
*
* It holds nothing of its own except the width. The panel that owns a selection builds the editor and
* announces it — see useEditorSlot for why it carries the element rather than a description of one.
*
* ── The device toggle is here, above everything ──
*
* It was in a bar across the top of the detail panel, which put it beside Sync and Publish — three
* things that act on the SITE next to one that decides how you are looking at it. It belongs with the
* settings it scopes: a spacing is chosen per width, and the control saying which width should be the
* first thing in the column those choices are made in.
*
* Always drawn, including when nothing is selected. It is not part of the editor — it decides what the
* PREVIEW shows, so hiding it until a block is picked would mean selecting something to change the
* width and then deselecting it.
*
* It reads `?device=` rather than a context, which is what lets it live in a panel that is a sibling of
* the one holding the preview. See DeviceToggle.
*/
export const EditorPanel = () => {
const [editor] = useEditorSlot();
/*
The selected page's own settings, above everything.
Read here rather than announced like the editor below it, because they are not the editor: they
belong to the page whatever is selected inside it, and a block being picked must not replace them.
Above the device toggle, because the toggle scopes what is under it to one width and none of these
three have widths.
*/
const { section, item, part } = useNav();
const { pages } = usePages();
/*
Only when the PAGE row itself is selected.
A band or a block selected means the editor below is about that, and the page's own name and
address sitting above it read as belonging to whatever is open. They are reached by pressing the
page again, which is what the nav already implies.
*/
const page = section === 'pages' && !part ? pageFromParam(pages, item) : null;
return (
<div className="flex size-full flex-col overflow-hidden">
{page && (
<div className="shrink-0 border-b border-border px-3 py-3">
<PageSettings page={page} />
</div>
)}
{/*
Only where there is a page to look at.
The toggle sets the width the PREVIEW renders at, and Carousels, Media and Administration have
no preview — the control would be three buttons that do nothing visible. Named here rather
than read from `usePreviewable`, which is a context inside the detail panel and this is its
sibling.
Outside the scroll area, so a long editor cannot carry it off the screen.
*/}
{(section === 'layout' || section === 'pages') && (
<div className="shrink-0 border-b border-border px-3 py-2">
<DeviceToggle />
</div>
)}
{editor ? (
/*
`@container`, once, for every section inside it.
The sections lay out in two columns and have to fall back to one when the panel is dragged
narrow — and the window's width says nothing about that, because the width here is a divider
somebody moves. Declared on the scroller rather than per section, so a new section is
responsive by being in the column rather than by remembering to say so.
*/
<div className="@container min-h-0 flex-1 overflow-auto p-3">{editor}</div>
) : (
<div className="flex min-h-0 flex-1 items-center justify-center p-6 text-center">
{/* Names what the panel is FOR rather than saying it is empty. "Nothing selected" describes
this component's state; this describes the reader's next move. */}
<p className="text-xs text-muted-foreground">
Pick a block in the page to edit it, or press Spacing on a band.
</p>
</div>
)}
</div>
);
};