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;
|
kind: string;
|
||||||
/** The heading the page draws above the block, or null. Every kind may have one. */
|
/** The heading the page draws above the block, or null. Every kind may have one. */
|
||||||
title?: string | null;
|
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
|
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
|
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;
|
pageId: number;
|
||||||
sectionId: 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. */
|
/** The padding scale, sent by the API so this file holds no copy of it. */
|
||||||
steps: number[];
|
steps: number[];
|
||||||
onSaved: () => void;
|
onSaved: () => void;
|
||||||
@@ -39,13 +44,20 @@ export const BlockSettings = ({
|
|||||||
const [device] = useDevice();
|
const [device] = useDevice();
|
||||||
const breakpoint = ({ desktop: 'lg', tablet: 'md', mobile: 'base' } as const)[device];
|
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 [busy, setBusy] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [saved, flashSaved] = useFlash();
|
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.
|
// 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. */
|
/** What an axis falls back to at this width: the nearest smaller one that has a value. */
|
||||||
const inherited = (axis: 'x' | 'y'): number | null => {
|
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".
|
// Empty is sent as an empty string, which the API reads as "take the heading off".
|
||||||
await clientRef.current.patch(
|
await clientRef.current.patch(
|
||||||
`/fofos-admin/pages/${pageId}/sections/${sectionId}/components/${block.id}`,
|
`/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();
|
flashSaved();
|
||||||
onSaved();
|
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);
|
const pending = JSON.stringify(draft);
|
||||||
|
|
||||||
useAutosave(pending !== stored ? pending : null, busy, save);
|
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.
|
The block's own spacing, at the width the toggle above is showing.
|
||||||
|
|
||||||
@@ -139,20 +178,7 @@ export const BlockSettings = ({
|
|||||||
<option value="">
|
<option value="">
|
||||||
{breakpoint === 'base' ? 'None' : from === null ? 'Inherit (none)' : `Inherit (${from})`}
|
{breakpoint === 'base' ? 'None' : from === null ? 'Inherit (none)' : `Inherit (${from})`}
|
||||||
</option>
|
</option>
|
||||||
{/*
|
{steps.map((step) => (
|
||||||
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) => (
|
|
||||||
<option key={step} value={String(step)}>
|
<option key={step} value={String(step)}>
|
||||||
{step}
|
{step}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
Reference in New Issue
Block a user