Layout's Padding tab sets the site's gutter
Not a per-band control. The header, the content column and the footer share one measure and should share one number at its edges — three sections' worth of controls would be three ways to disagree about the same decision. PaddingTab is untouched and stays. It is the per-band control and it is right for the pages, where a band genuinely does want its own spacing. This panel just is not where that decision belongs. Same interaction as everything else here: the device toggle picks the breakpoint, Inherit and 0 are separate options because they are separate instructions, and the select says which value it is inheriting rather than leaving it to be worked out. Saved with the colours and the typefaces, because it is a field of the same row — so the per-band write path this panel had briefly is gone with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,8 @@ import { useSaveTarget } from '../SaveTarget';
|
||||
import { usePreviewable, useViewMode } from '../ViewMode';
|
||||
import { paintMeasure } from '../canvas';
|
||||
import { SitePreview, useDevPort } from '../SitePreview';
|
||||
import { PaddingTab, type LayoutBand, type SectionPadding } from './PaddingTab';
|
||||
import { type LayoutBand } from './PaddingTab';
|
||||
import { GutterTab, type Gutter } from './GutterTab';
|
||||
|
||||
/**
|
||||
* The shell every page renders inside: the menu, the footer, the colours and the typefaces.
|
||||
@@ -55,6 +56,8 @@ type Layout = {
|
||||
sections: LayoutBand[];
|
||||
/** The steps a padding may take, sent by the API so this file holds no copy of the scale. */
|
||||
padding_steps: number[];
|
||||
/** The site's horizontal gutter, per breakpoint. */
|
||||
padding_x: Gutter;
|
||||
/**
|
||||
* Which face plays each role when nothing has been chosen, read from the site's own stylesheet.
|
||||
*
|
||||
@@ -122,8 +125,6 @@ export const LayoutPanel = () => {
|
||||
const devPort = useDevPort();
|
||||
/** Controlled, because the grid stack below decides visibility rather than Radix unmounting. */
|
||||
const [tab, setTab] = useState('colours');
|
||||
/** Which band the Padding tab is editing. Null until the payload has arrived. */
|
||||
const [band, setBand] = useState<number | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [saved, flashSaved] = useFlash();
|
||||
|
||||
@@ -153,19 +154,8 @@ export const LayoutPanel = () => {
|
||||
today and wrong the first time either changed: the panel would show unsaved changes for a decision
|
||||
made in a build script or a stylesheet.
|
||||
*/
|
||||
/*
|
||||
Padding is drafted here and written by the same Save, through the SECTIONS endpoint.
|
||||
|
||||
Two endpoints, one button. The alternative was a second Save for spacing, which would mean somebody
|
||||
changing a colour and a padding and having to press two things — and being told they had unsaved
|
||||
changes by only one of them.
|
||||
*/
|
||||
const setPadding = (id: number, padding: SectionPadding) =>
|
||||
setDraft((was) =>
|
||||
was ? { ...was, sections: was.sections.map((one) => (one.id === id ? { ...one, padding } : one)) } : was,
|
||||
);
|
||||
|
||||
const WRITABLE = [
|
||||
'padding_x',
|
||||
'background',
|
||||
'foreground',
|
||||
'accent',
|
||||
@@ -178,9 +168,7 @@ export const LayoutPanel = () => {
|
||||
const only = (source: Layout | null) =>
|
||||
source ? JSON.stringify(Object.fromEntries(WRITABLE.map((key) => [key, source[key]]))) : null;
|
||||
|
||||
const paddings = (source: Layout | null) => JSON.stringify(source?.sections.map((one) => one.padding) ?? null);
|
||||
|
||||
const dirty = Boolean(stored && draft) && (only(stored) !== only(draft) || paddings(stored) !== paddings(draft));
|
||||
const dirty = Boolean(stored && draft) && only(stored) !== only(draft);
|
||||
|
||||
const save = async () => {
|
||||
if (!draft) return;
|
||||
@@ -189,20 +177,6 @@ export const LayoutPanel = () => {
|
||||
try {
|
||||
const body = Object.fromEntries(WRITABLE.map((key) => [key, draft[key]]));
|
||||
|
||||
// The bands that actually changed, before the layout write, so a failure on one leaves the other
|
||||
// untouched rather than half-applied.
|
||||
if (draft.layout_page_id) {
|
||||
for (const one of draft.sections) {
|
||||
const was = stored?.sections.find((other) => other.id === one.id);
|
||||
if (was && JSON.stringify(was.padding) !== JSON.stringify(one.padding)) {
|
||||
await clientRef.current.patch(
|
||||
`/fofos-admin/pages/${draft.layout_page_id}/sections/${one.id}`,
|
||||
{ padding: one.padding },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply((await clientRef.current.patch<{ data: Layout }>('/fofos-admin/layout', body)).data);
|
||||
flashSaved();
|
||||
} catch (failure) {
|
||||
@@ -385,13 +359,18 @@ export const LayoutPanel = () => {
|
||||
className={`col-start-1 row-start-1 ${tab === 'padding' ? '' : 'invisible'}`}
|
||||
aria-hidden={tab !== 'padding'}
|
||||
>
|
||||
<PaddingTab
|
||||
bands={draft.sections}
|
||||
{/*
|
||||
The site's gutter, not a per-band padding.
|
||||
|
||||
PaddingTab is the per-band control and it stays — for the pages, where a band really
|
||||
does want its own spacing. Here the three wrappers share one measure and should share
|
||||
one number, so this is one select rather than three sections' worth.
|
||||
*/}
|
||||
<GutterTab
|
||||
gutter={draft.padding_x}
|
||||
device={device}
|
||||
steps={draft.padding_steps}
|
||||
selected={band}
|
||||
onSelect={setBand}
|
||||
onChange={setPadding}
|
||||
onChange={(padding_x) => patch({ padding_x })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user