feat(builder): items 10+12+13 — scope canvas guides, Show Guides toggle, Layers panel hover/icons, topbar unification
Item 10: dashed canvas "guide" outlines were applied via blanket tag
selectors (div/section/header/...), so a finished section showed 3-4
nested dashed boxes. RenderNode.tsx (the <Editor onRender> override) now
tags each Craft.js droppable container's real DOM node with a
`data-craft-node` attribute (node.data.isCanvas, excluding ROOT), and
editor.css's guide rules target that attribute instead — a component's own
internal wrapper markup is no longer mistaken for a drop target. Added a
"Show guides" topbar toggle (default ON, persisted to localStorage),
state lifted in EditorShell.tsx (mirrors how `device` is already lifted),
flips `.guides-off` on Canvas.tsx's `.canvas-device-frame`.
Item 12: Layers panel rows get per-type FA icons (keyed off the same
craft.displayName used for the row label, seeded from BlocksPanel's
choices), indent-guide lines connecting nested rows, and row hover
highlights the matching canvas element (`data-layer-hovered`, written
directly to the node's DOM via `query.node(id).get().dom` — NOT via
`actions.setNodeEvent`, which is stripped from useEditor()'s public
`actions` at runtime, not just in its TS type, and threw when called).
Along the way, Craft.js's own connect() was found to already wire mouse
hover to the same `hovered` node event internally (previously invisible
because the matching CSS was dead) — RenderNode now also mirrors that
onto `data-craft-hovered` for a real-mouse-hover canvas highlight, and
both hover attributes plus the guide attribute are suppressed under
`.guides-off` so the toggle stays airtight.
Item 13: unified the topbar to one button radius (`.topbar-btn` 20px pill
-> var(--radius-md)), demoted Code/Preview to icon-only with
`data-tooltip`. Fixed a latent bug the new tooltips exposed: `[data-tooltip]`
tooltips render above their trigger, but the topbar has no room above it
(`body { overflow: hidden }`) — added a `.topbar` override to render
those below instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,14 +14,36 @@ interface RenderNodeProps {
|
||||
* straight through as a Fragment, so this never touches layout, never
|
||||
* appears in `toHtml` export (that walks the Craft node tree, not this
|
||||
* portal), and doesn't wrap every node in extra DOM.
|
||||
*
|
||||
* It also imperatively tags each node's real DOM element with two
|
||||
* editor-only data attributes (never part of `toHtml` export, which walks
|
||||
* the Craft node tree, not the live DOM):
|
||||
* - `data-craft-node`: set on actual Craft.js droppable containers
|
||||
* (`node.data.isCanvas`, excluding ROOT). `editor.css`'s dashed "guide"
|
||||
* outlines target this attribute instead of blanket tag selectors
|
||||
* (div/section/header/...), so a component's own internal wrapper markup
|
||||
* no longer picks up a guide outline it isn't a real drop target for.
|
||||
* - `data-craft-hovered`: mirrors `node.events.hovered` (Craft's hover
|
||||
* event set). Craft's own `connectors.connect()` (called by every
|
||||
* component) wires a native mouseover/mouseleave listener to this event
|
||||
* internally, so a plain mouse hover over any connected node sets it --
|
||||
* this attribute is a real-mouse-hover canvas highlight. (The Layers
|
||||
* panel's row-hover -> canvas-highlight sync, item 12, uses a sibling
|
||||
* `data-layer-hovered` attribute written directly by LayersPanel.tsx
|
||||
* instead of this event, since the action that would drive it here
|
||||
* -- `actions.setNodeEvent` -- is stripped from the public `useEditor()`
|
||||
* API at runtime.) editor.css matches both attributes for the same
|
||||
* outline and suppresses both under `.guides-off`.
|
||||
*/
|
||||
export const RenderNode: React.FC<RenderNodeProps> = ({ render }) => {
|
||||
const { actions } = useEditor();
|
||||
const { id, isSelected, dom, name, parent } = useNode((node) => ({
|
||||
const { id, isSelected, dom, name, parent, isCanvas, isHovered } = useNode((node) => ({
|
||||
isSelected: node.events.selected,
|
||||
isHovered: node.events.hovered,
|
||||
dom: node.dom,
|
||||
name: (node.data.props?.aiName as string) || node.data.displayName,
|
||||
parent: node.data.parent,
|
||||
isCanvas: node.data.isCanvas,
|
||||
}));
|
||||
|
||||
const badgeRef = useRef<HTMLDivElement>(null);
|
||||
@@ -46,6 +68,24 @@ export const RenderNode: React.FC<RenderNodeProps> = ({ render }) => {
|
||||
};
|
||||
}, [active, updatePosition]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dom) return;
|
||||
if (isCanvas && id !== 'ROOT') {
|
||||
dom.setAttribute('data-craft-node', '');
|
||||
} else {
|
||||
dom.removeAttribute('data-craft-node');
|
||||
}
|
||||
}, [dom, isCanvas, id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dom) return;
|
||||
if (isHovered) {
|
||||
dom.setAttribute('data-craft-hovered', '');
|
||||
} else {
|
||||
dom.removeAttribute('data-craft-hovered');
|
||||
}
|
||||
}, [dom, isHovered]);
|
||||
|
||||
if (!active) return <>{render}</>;
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user