createDom function
Creates a real DOM Node from a resolved Morphic tree.
The optional componentOwner identifies which Component this element
is the root output of. When provided, the created node is registered with
the ComponentRuntime so that requestUpdateFor can diff it directly on
future morphs without traversing the full tree.
Pass componentOwner only for the root element of a component's render
output — never for nested elements within the same component.
Implementation
Node createDom(Morphic node, {Component? componentOwner}) {
if (node is TextMorphic) {
return document.createTextNode(node.value);
}
if (node is ElementMorphic) {
final element = document.createElement(node.tag);
// ── Attributes ───────────────────────────────────────────────────────
node.attributes.forEach((key, attr) {
if (attr is StringAttribute) {
element.setAttribute(key, attr.value);
} else if (attr is BooleanAttribute) {
if (attr.value) element.setAttribute(key, '');
} else if (attr is ClassAttribute) {
element.setAttribute('class', attr.classes);
} else if (attr is StyleAttribute) {
final htmlElement = element as HTMLElement;
attr.styles.forEach((k, value) {
htmlElement.style.setProperty(_toKebabCase(k), value);
});
} else if (attr is EventAttribute) {
// Register via mutable wrapper so patch can update the callback
// without touching the DOM listener registration.
_registerHandler(element, key, attr);
}
});
// ── Children ─────────────────────────────────────────────────────────
final morphicChildren = node.children.cast<Morphic>();
for (final child in morphicChildren) {
element.append(createDom(child));
}
// ── Component node registration ───────────────────────────────────────
if (componentOwner != null) {
try {
RenderContext.runtime.registerComponentDomNode(componentOwner, element);
} catch (_) {
// RenderContext not active in static render paths or tests.
}
}
return element;
}
throw UnsupportedError('Unknown node type: ${node.runtimeType}');
}