StatifierBlocks.Editor.BlockNode (StatifierBlocks v0.3.0)

Copy Markdown View Source

One block's chrome, dispatching to its slots (ADR-0005 decisions 10, 11, 12, 13).

No branch on a type name, ever

Decision 10's layout and slot_style are how nested groups, lanes and interrupt rails render distinctly without the editor branching on a type name. core.parallel declares layout: :columns, so its lane slots sit side by side and the absence of ordering between them is visible; core.resumable_group declares slot_style: %{"interrupts" => :secondary}, so its interrupt rules render as an attached rail rather than a second body. A host block type with the same structural shape gets the same rendering by declaring the same thing, which is the property that matters. There is no branch anywhere in this module on "core.parallel" or on any other type string, and the operator pre-decision behind ADR-0005 is that there never will be.

icon is a name, never markup. This component takes an icon component as an assign and passes the name to it; a host that ships heroicons renders heroicons. Accepting raw SVG from a callback would be injecting host-authored markup into this package's own render tree - an injection surface, and a guarantee that the icon set fragments across palettes.

A host that ships nothing gets StatifierBlocks.Editor.Icons, this package's own set for the names its own palette emits, and an entry that names no icon at all gets no tile. Neither is a hole in the paragraph above: the default is markup this package wrote, resolved from a name by the same seam, and the host's icon still wins wherever it is passed.

Unresolvable blocks (decision 12)

A block whose type does not resolve renders rather than vanishing: its type name, unavailable chrome, a :block finding, its config read-only as canonical JSON (there is no config_schema/1 to drive a form and inventing one would be guessing), and its existing children rendered normally, recursively - the document's slots map preserved every one of them, decoding never having consulted a registry.

It may be selected, moved and deleted. Its config may not be edited. It is never a drop target, because decision 5's first rule needs slots/1 and there is none - Edit.Targets already leaves it out of the droppable set, so nothing here has to re-derive that.

The acceptance property is preservation: open a document containing a block type the host does not have, edit an unrelated part of the tree, save, and the unresolvable block's bytes are unchanged. An editor that quietly dropped it would turn a missing palette entry into silent data loss.

The accent, and why the stylesheet still names no type

A palette entry may declare accent_token, the NAME of a --sb-* custom property (ADR-0005 decision 14's amendment). This component stamps it on the card and rebinds --sb-block-accent there; the stylesheet reads that property in exactly two rules, an icon tile and a card stripe, so adding a block type with its own identity adds no CSS and no branch. The value is the theme's - a descriptor carries a name, never a colour - and a name that does not validate resolves to the editor's accent rather than to a broken card.

The measurement anchors

Three of them per block, stamped as data-sb-anchor and read by nothing in this module: the node's own box, its card, and a zero-height outlet at the very bottom of everything it contains. The outlet exists so that flow leaves a container from BELOW its children rather than from its header - a sequence's last block and the sequence itself have to leave from the same line, or every edge out of a nested container starts in the wrong place.

They are markup, and the amendment to decision 7 is why that is all they are: the measurement hook reads these boxes and pushes them, and StatifierBlocks.Connectors decides what to draw from the numbers. A host that never imports the hook has three inert attributes and an empty div.

The badge

findings_count covers the whole subtree, so a collapsed node still shows that something inside it needs attention. Decision 11's last sentence is explicit that a finding must never hide inside something folded shut - that is the failure mode that makes tree editors feel unreliable.

Summary

Functions

One block: chrome, findings, and its slots, recursively.

Functions

block_node(assigns)

One block: chrome, findings, and its slots, recursively.

Attributes

  • node (StatifierBlocks.ViewModel.Node) (required)

  • drag (:any) - Defaults to nil.

  • selected_id (:string) - Defaults to nil.

  • target (:any) (required)

  • icon (:any) - Defaults to nil.

  • class (:string) - Defaults to nil.

  • root? (:boolean) - The document's root. It is neither draggable nor deletable, because Edit.apply/2 refuses both for it (check_not_root/2) - rendering the affordances anyway would be an interface that lies about what it can do.

    Defaults to false.