Cheaper, clearer dropzones: memoize, hatch refused, label slots as words

DropZones re-ran the accept predicates and a DOM walk on every overlay
render (pointermove-driven). Matching now runs once per drag via
matchZones(); scroll/resize ticks only re-measure the cached rects (new
test pins the predicate count across ticks).

Refused slots get a diagonal hatch on top of the red tint so the state is
not color-only, slot tags show handles as words (`3d-accessories` ->
"3d accessories", raw key kept in title), and the ghost pop honours
prefers-reduced-motion.

The mock section template now marks its sidebar slot like a real
template, so slot tests use the fixture instead of hand-injected DOM
(the old palette test asserting "no tagged slots" now expects both
sections' slots).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
mats committed 2026-11-09 09:35:16 +01:00
1 parent a798bc1ad3
commit 7362e4730f
5 files changed
+114 -52

No files matched your search

+3 -1
View File
@@ -191,9 +191,11 @@ export function render(g: Graph, id: string): string {
const attrs = `data-cms="${n.id}" data-cms-type="${n.dataType}"`
switch (n.dataType) {
case 'section':
// The schema declares a `sidebar` connection, so the template marks the zone
// (empty when nothing is slotted) the way a real template would.
return `<section class="sec sec--${esc(n.data.tone ?? 'light')}${n.data.padded ? ' sec--padded' : ''}" ${attrs} data-cms-container="true">${
n.data.title ? `<p class="sec__title">${esc(n.data.title)}</p>` : ''
}\n${kids()}\n</section>`
}\n${kids()}\n<div data-cms-slot="sidebar" data-cms-slot-parent="${n.id}"></div>\n</section>`
case 'heading': {
const l = n.data.level ?? 2
return `<h${l} ${attrs} data-cms-field="text" data-cms-editor="text">${esc(n.data.text)}</h${l}>`