the handles arrive under the cursor
Edit and Delete sat permanently at the top-right of whatever they belonged to, on top of the text — and on a paragraph that fills its column there is nowhere for them to go that is not on top of something. So they are hidden until the component is hovered, which is when they are wanted and the only time they are. The outline goes the same way: an editor should show the PAGE, with the handles arriving under the cursor. One that outlines everything permanently is showing you the editor. The tools also moved out of the component's corner into the gap above it, so even while visible they are beside the content rather than over its first line. The section's Delete gets the same treatment; its NAME stays, because that is what tells you which band you are looking at, and it already sits above the section rather than on it. A destructive verb does not need to be on screen at all times. Done with one injected stylesheet rather than listeners on every node — `:hover` is the one thing inline styles cannot express, and it is the only reason this file now has a <style> in it. `:focus-within` and `:focus` keep both reachable by keyboard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+42
-5
@@ -94,6 +94,38 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
|
|||||||
/** Everything to undo, in the order it was done. */
|
/** Everything to undo, in the order it was done. */
|
||||||
const undo: (() => void)[] = [];
|
const undo: (() => void)[] = [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
A stylesheet, for the one thing inline styles cannot express: `:hover`.
|
||||||
|
|
||||||
|
The component tools sat permanently at the top-right of whatever they belonged to, on top of the
|
||||||
|
text. On a paragraph that fills its column there is nowhere for them to go that is not on top of
|
||||||
|
something — so they are hidden until the component is hovered, which is when they are wanted and
|
||||||
|
the only time they are.
|
||||||
|
|
||||||
|
The outline goes the same way. An editor should show the page, with the handles arriving under the
|
||||||
|
cursor; an editor that outlines everything all the time is showing you the editor.
|
||||||
|
*/
|
||||||
|
const sheet = document.createElement('style');
|
||||||
|
sheet.setAttribute(MARK, 'stylesheet');
|
||||||
|
sheet.textContent = `
|
||||||
|
[data-fofos-component] { outline: 1px dotted transparent; transition: outline-color .12s; }
|
||||||
|
[data-fofos-component]:hover { outline-color: color-mix(in srgb, ${ACCENT} 55%, transparent); }
|
||||||
|
[${MARK}="component-tools"] { opacity: 0; transition: opacity .12s; }
|
||||||
|
[data-fofos-component]:hover > [${MARK}="component-tools"] { opacity: 1; }
|
||||||
|
[${MARK}="component-tools"]:focus-within { opacity: 1; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
The section's Delete, same treatment. Its NAME stays visible — that is what tells you which band
|
||||||
|
you are looking at, and it sits above the section rather than on it — but a destructive verb does
|
||||||
|
not need to be on screen at all times.
|
||||||
|
*/
|
||||||
|
[${MARK}="section-delete"] { opacity: 0; transition: opacity .12s; }
|
||||||
|
[data-section-id]:hover [${MARK}="section-delete"] { opacity: 1; }
|
||||||
|
[${MARK}="section-delete"]:focus { opacity: 1; }
|
||||||
|
`;
|
||||||
|
document.head.append(sheet);
|
||||||
|
undo.push(() => sheet.remove());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The site's empty-page placeholder, hidden while the builder draws its own controls in that space.
|
The site's empty-page placeholder, hidden while the builder draws its own controls in that space.
|
||||||
|
|
||||||
@@ -166,6 +198,7 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
|
|||||||
style(shape, ['opacity:0.7']);
|
style(shape, ['opacity:0.7']);
|
||||||
|
|
||||||
const remove = document.createElement('button');
|
const remove = document.createElement('button');
|
||||||
|
remove.setAttribute(MARK, 'section-delete');
|
||||||
remove.type = 'button';
|
remove.type = 'button';
|
||||||
remove.textContent = 'Delete';
|
remove.textContent = 'Delete';
|
||||||
style(remove, [
|
style(remove, [
|
||||||
@@ -217,27 +250,31 @@ export function paintCanvas(document: Document, canvas: Canvas): () => void {
|
|||||||
if (!node) continue;
|
if (!node) continue;
|
||||||
|
|
||||||
const wasPosition = node.style.position;
|
const wasPosition = node.style.position;
|
||||||
const wasOutline = node.style.outline;
|
|
||||||
node.style.position = 'relative';
|
node.style.position = 'relative';
|
||||||
node.style.outline = `1px dotted color-mix(in srgb, ${ACCENT} 60%, transparent)`;
|
// The outline and the tools' visibility are the stylesheet's, so they can depend on :hover.
|
||||||
|
node.setAttribute('data-fofos-component', '');
|
||||||
undo.push(() => {
|
undo.push(() => {
|
||||||
node.style.position = wasPosition;
|
node.style.position = wasPosition;
|
||||||
node.style.outline = wasOutline;
|
node.removeAttribute('data-fofos-component');
|
||||||
});
|
});
|
||||||
|
|
||||||
const tools = document.createElement('div');
|
const tools = document.createElement('div');
|
||||||
tools.setAttribute(MARK, 'component-tools');
|
tools.setAttribute(MARK, 'component-tools');
|
||||||
style(tools, [
|
style(tools, [
|
||||||
'position:absolute',
|
'position:absolute',
|
||||||
'top:0',
|
// Above its own top-right corner rather than inside it, so even while visible it sits in the
|
||||||
|
// gap between components instead of over the first line of this one.
|
||||||
|
'top:-1.25rem',
|
||||||
'right:0',
|
'right:0',
|
||||||
'display:flex',
|
'display:flex',
|
||||||
'gap:0.5rem',
|
'gap:0.5rem',
|
||||||
'padding:0.125rem 0.25rem',
|
'padding:0 0.25rem',
|
||||||
'font-size:0.625rem',
|
'font-size:0.625rem',
|
||||||
'font-family:ui-sans-serif, system-ui, sans-serif',
|
'font-family:ui-sans-serif, system-ui, sans-serif',
|
||||||
`color:${ACCENT}`,
|
`color:${ACCENT}`,
|
||||||
'background:var(--site-bg, #fff)',
|
'background:var(--site-bg, #fff)',
|
||||||
|
// Above the page, below the section bar, and never a target when invisible.
|
||||||
|
'z-index:5',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for (const [label, run] of [
|
for (const [label, run] of [
|
||||||
|
|||||||
Reference in New Issue
Block a user