Spacing, on a section and on a component
A Spacing handle on the section bar and in the component tools, opening an editor below the preview — the same place the text editor goes, and for the same reason: a dialog covers the thing being spaced. The device toggle picks the breakpoint. Two axes across three widths is six controls, five of them for a width you are not looking at; this way you switch to Mobile, see the phone, and change the padding you can watch changing. The site's GUTTER is the exception and shows all three at once, because it is one axis and one value and it is a progression judged as a whole. Inherit and 0 stay separate options, and the select names the value it is inheriting rather than leaving it to be worked out. Written IMMEDIATELY rather than drafted. A step is one choice from a short list and the effect is on screen before the menu closes; a Save for it would be a second thing to remember on a panel whose other verbs already act. Spacing sits before Delete in the section bar, ordered by how often each is wanted rather than how they read — the destructive one furthest from where the hand arrives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+48
-20
@@ -49,13 +49,28 @@ const ACCENT = '#3b82f6';
|
||||
* presses Edit, and it arrived with the section. Fetching it again on every Edit would be a request to
|
||||
* be told something already in hand.
|
||||
*/
|
||||
export type CanvasComponent = { id: number; column: number; kind: string; content?: { html?: string } };
|
||||
export type CanvasSection = { id: number; name: string; layout: 'one' | 'two'; components: CanvasComponent[] };
|
||||
export type CanvasComponent = {
|
||||
id: number;
|
||||
column: number;
|
||||
kind: string;
|
||||
content?: { html?: string };
|
||||
padding?: Record<string, { x?: number; y?: number }>;
|
||||
};
|
||||
export type CanvasSection = {
|
||||
id: number;
|
||||
name: string;
|
||||
layout: 'one' | 'two';
|
||||
padding?: Record<string, { x?: number; y?: number }>;
|
||||
components: CanvasComponent[];
|
||||
};
|
||||
|
||||
export type Canvas = {
|
||||
sections: CanvasSection[];
|
||||
onAddSection: () => void;
|
||||
onDeleteSection: (section: CanvasSection) => void;
|
||||
/** Somebody wants to change the space around a band or around one thing in it. */
|
||||
onSpaceSection: (section: CanvasSection) => void;
|
||||
onSpaceComponent: (component: CanvasComponent) => void;
|
||||
/** A column wants something in it. */
|
||||
onAddComponent: (section: CanvasSection, column: number) => void;
|
||||
/** Something already in a column was pressed. */
|
||||
@@ -220,25 +235,37 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
|
||||
shape.textContent = section.layout === 'two' ? 'two columns' : 'one column';
|
||||
style(shape, ['opacity:0.7']);
|
||||
|
||||
const remove = document.createElement('button');
|
||||
remove.type = 'button';
|
||||
remove.textContent = 'Delete';
|
||||
style(remove, [
|
||||
'margin-left:auto',
|
||||
'cursor:pointer',
|
||||
'background:none',
|
||||
'border:0',
|
||||
'padding:0',
|
||||
'font:inherit',
|
||||
'color:inherit',
|
||||
'opacity:0.7',
|
||||
'text-decoration:underline',
|
||||
]);
|
||||
const onRemove = () => canvas.onDeleteSection(section);
|
||||
remove.addEventListener('click', onRemove);
|
||||
undo.push(() => remove.removeEventListener('click', onRemove));
|
||||
/*
|
||||
Spacing before Delete, and both on the right.
|
||||
|
||||
bar.append(label, shape, remove);
|
||||
The order is how often they are wanted, not how they read: spacing is an ordinary adjustment and
|
||||
deleting a band is not, so the destructive one sits furthest from where the hand arrives.
|
||||
*/
|
||||
const actions: HTMLElement[] = [];
|
||||
for (const [text, run, first] of [
|
||||
['Spacing', () => canvas.onSpaceSection(section), true],
|
||||
['Delete', () => canvas.onDeleteSection(section), false],
|
||||
] as const) {
|
||||
const action = document.createElement('button');
|
||||
action.type = 'button';
|
||||
action.textContent = text;
|
||||
style(action, [
|
||||
first ? 'margin-left:auto' : '',
|
||||
'cursor:pointer',
|
||||
'background:none',
|
||||
'border:0',
|
||||
'padding:0',
|
||||
'font:inherit',
|
||||
'color:inherit',
|
||||
'opacity:0.7',
|
||||
'text-decoration:underline',
|
||||
]);
|
||||
action.addEventListener('click', run);
|
||||
undo.push(() => action.removeEventListener('click', run));
|
||||
actions.push(action);
|
||||
}
|
||||
|
||||
bar.append(label, shape, ...actions);
|
||||
element.append(bar);
|
||||
undo.push(() => bar.remove());
|
||||
|
||||
@@ -294,6 +321,7 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
|
||||
|
||||
for (const [label, run] of [
|
||||
['Edit', () => canvas.onEditComponent(component)],
|
||||
['Spacing', () => canvas.onSpaceComponent(component)],
|
||||
['Delete', () => canvas.onDeleteComponent(component)],
|
||||
] as const) {
|
||||
const action = document.createElement('button');
|
||||
|
||||
Reference in New Issue
Block a user