Contact form and Partners, placed in one click

Both hold nothing, so adding one opens no editor: a dialog with no field on it is a step that exists
only to be got past. The picker itself answers the opposite way for the kinds that DO have a choice
to make, which is the distinction worth keeping.

The canvas draws no Edit button for them either — an Edit that opens nothing is worse than no Edit.
It asks the panel rather than holding a list of kinds, because which kinds are editable is the same
knowledge that decides which editor opens, and that already lives there. Absent means editable, so a
caller that has never thought about it gets what it had.

The Spacing dialog's label became a map. It was two kinds and readable as a ternary, then three and
not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 07:08:30 +01:00
co-authored by Claude Opus 5
parent 48c744372b
commit a4dfb3c81a
3 changed files with 85 additions and 9 deletions
+20 -3
View File
@@ -80,6 +80,17 @@ export type Canvas = {
onAddComponent: (section: CanvasSection, column: number) => void;
/** Something already in a column was pressed. */
onEditComponent: (component: CanvasComponent) => void;
/**
* Whether this component has anything to edit — no button if not.
*
* A predicate from the panel rather than a list of kinds here, because which kinds are editable is
* the same knowledge that decides WHICH editor opens, and that already lives there. A contact form
* and a partner row are code the site owns: the block places them and there is nothing to type, so
* an Edit button would be a door onto an empty room.
*
* Absent means editable, so a caller that has never thought about it gets what it had before.
*/
canEditComponent?: (component: CanvasComponent) => boolean;
onDeleteComponent: (component: CanvasComponent) => void;
};
@@ -324,11 +335,17 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
'z-index:5',
]);
for (const [label, run] of [
['Edit', () => canvas.onEditComponent(component)],
const actions: [string, () => void][] = [
['Spacing', () => canvas.onSpaceComponent(component)],
['Delete', () => canvas.onDeleteComponent(component)],
] as const) {
];
// First, so the tools read Edit / Spacing / Delete — the order they were in when Edit was
// unconditional, rather than the order the array happens to be built in.
if (canvas.canEditComponent?.(component) ?? true) {
actions.unshift(['Edit', () => canvas.onEditComponent(component)]);
}
for (const [label, run] of actions) {
const action = document.createElement('button');
action.type = 'button';
action.textContent = label;