Width, instead of a negative number to match by hand
Inside the band, whole width of main, or whole page width. The negatives are gone from the padding select — padding is padding again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,8 @@ export type CanvasComponent = {
|
||||
kind: string;
|
||||
/** The heading the page draws above the block, or null. Every kind may have one. */
|
||||
title?: string | null;
|
||||
/** `main`, `page` or null — how wide it is allowed to be. See the site's schema. */
|
||||
width?: string | null;
|
||||
/*
|
||||
One optional field per kind that the panel reads back. Not `Record<string, unknown>`: every reader
|
||||
would then need a cast, and the point of naming them is that the compiler knows `free-text` holds
|
||||
|
||||
@@ -27,7 +27,12 @@ export const BlockSettings = ({
|
||||
}: {
|
||||
pageId: number;
|
||||
sectionId: number;
|
||||
block: { id: number; title?: string | null; padding?: Record<string, { x?: number; y?: number }> };
|
||||
block: {
|
||||
id: number;
|
||||
title?: string | null;
|
||||
width?: string | null;
|
||||
padding?: Record<string, { x?: number; y?: number }>;
|
||||
};
|
||||
/** The padding scale, sent by the API so this file holds no copy of it. */
|
||||
steps: number[];
|
||||
onSaved: () => void;
|
||||
@@ -39,13 +44,20 @@ export const BlockSettings = ({
|
||||
const [device] = useDevice();
|
||||
const breakpoint = ({ desktop: 'lg', tablet: 'md', mobile: 'base' } as const)[device];
|
||||
|
||||
const [draft, setDraft] = useState({ title: block.title ?? '', padding: block.padding ?? {} });
|
||||
const [draft, setDraft] = useState({
|
||||
title: block.title ?? '',
|
||||
width: block.width ?? '',
|
||||
padding: block.padding ?? {},
|
||||
});
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [saved, flashSaved] = useFlash();
|
||||
|
||||
// Keyed on the id, so re-reading the same block after a save does not throw away what is being typed.
|
||||
useEffect(() => setDraft({ title: block.title ?? '', padding: block.padding ?? {} }), [block.id]);
|
||||
useEffect(
|
||||
() => setDraft({ title: block.title ?? '', width: block.width ?? '', padding: block.padding ?? {} }),
|
||||
[block.id],
|
||||
);
|
||||
|
||||
/** What an axis falls back to at this width: the nearest smaller one that has a value. */
|
||||
const inherited = (axis: 'x' | 'y'): number | null => {
|
||||
@@ -78,7 +90,7 @@ export const BlockSettings = ({
|
||||
// Empty is sent as an empty string, which the API reads as "take the heading off".
|
||||
await clientRef.current.patch(
|
||||
`/fofos-admin/pages/${pageId}/sections/${sectionId}/components/${block.id}`,
|
||||
{ title: draft.title, padding: draft.padding },
|
||||
{ title: draft.title, width: draft.width || null, padding: draft.padding },
|
||||
);
|
||||
flashSaved();
|
||||
onSaved();
|
||||
@@ -89,7 +101,11 @@ export const BlockSettings = ({
|
||||
}
|
||||
};
|
||||
|
||||
const stored = JSON.stringify({ title: block.title ?? '', padding: block.padding ?? {} });
|
||||
const stored = JSON.stringify({
|
||||
title: block.title ?? '',
|
||||
width: block.width ?? '',
|
||||
padding: block.padding ?? {},
|
||||
});
|
||||
const pending = JSON.stringify(draft);
|
||||
|
||||
useAutosave(pending !== stored ? pending : null, busy, save);
|
||||
@@ -108,6 +124,29 @@ export const BlockSettings = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
{/*
|
||||
How wide it is allowed to be.
|
||||
|
||||
It was a NEGATIVE horizontal padding, which worked and asked the wrong thing: you had to know
|
||||
the band's gutter and type its opposite, and it broke quietly the day somebody changed the
|
||||
gutter. These two say what is wanted, and the page works out the number.
|
||||
*/}
|
||||
<div className="mt-3 flex flex-col gap-1">
|
||||
<label className="text-xs">Width</label>
|
||||
<select
|
||||
value={draft.width}
|
||||
onChange={(event) => {
|
||||
const value = event.currentTarget.value;
|
||||
setDraft((was) => ({ ...was, width: value }));
|
||||
}}
|
||||
className="h-8 rounded-md border border-input bg-transparent px-2 text-xs"
|
||||
>
|
||||
<option value="">Inside the band</option>
|
||||
<option value="main">Whole width of main</option>
|
||||
<option value="page">Whole page width</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
The block's own spacing, at the width the toggle above is showing.
|
||||
|
||||
@@ -139,20 +178,7 @@ export const BlockSettings = ({
|
||||
<option value="">
|
||||
{breakpoint === 'base' ? 'None' : from === null ? 'Inherit (none)' : `Inherit (${from})`}
|
||||
</option>
|
||||
{/*
|
||||
Horizontal offers NEGATIVES, vertical does not.
|
||||
|
||||
A negative horizontal is how a block escapes the gutter its band applied — a video
|
||||
that runs to the edge of the window inside an inset band. It becomes `-mx-*`, which
|
||||
cancels the band's `px-*` exactly, so the value to pick is the band's own number.
|
||||
|
||||
Vertically there is nothing to cancel: a band's vertical padding is the room around
|
||||
the whole band, and a block pulling itself into the one above is a bug.
|
||||
*/}
|
||||
{(axis === 'x'
|
||||
? [...steps.filter((step) => step > 0).map((step) => -step).reverse(), ...steps]
|
||||
: steps
|
||||
).map((step) => (
|
||||
{steps.map((step) => (
|
||||
<option key={step} value={String(step)}>
|
||||
{step}
|
||||
</option>
|
||||
|
||||
Reference in New Issue
Block a user