///|
/// A declarative page layer. Its content must be enclosed by `layer_root`.
/// Layers remain part of their original Val scope and event ownership; only
/// their host location changes. No registry retains removed branch content.
pub fn[C : IsChildren] layer(id~ : String, children : C) -> Node {
guard id != "" else { abort("layer id must not be empty") }
let children = children.to_nodes()
wrap_node(
@ui.element("$minimoon-layer", id~, children=child_raw(children)),
child_bindings(children),
)
}
///|
fn collect_layers(
node : @ui.Node,
layers : Array[@ui.Node],
ids : Map[String, Bool],
) -> @ui.Node {
match node {
@ui.Text(_) | @ui.Empty => node
@ui.Retained(identity, child) =>
@ui.retained(identity, collect_layers(child, layers, ids))
@ui.Fragment(children) =>
@ui.fragment(children.map(child => collect_layers(child, layers, ids)))
@ui.KeyedFragment(children) =>
@ui.keyed_fragment(
children.map(child => {
@ui.keyed(child.key(), collect_layers(child.node(), layers, ids))
}),
)
@ui.Element(tag, attrs, children) =>
if tag == "$minimoon-layer" {
let mut id = ""
for attr in attrs {
if attr.name == "id" {
id = attr.value
}
}
guard !ids.contains(id) else { abort("duplicate layer id: " + id) }
ids[id] = true
// Reserve the parent before visiting nested layers: nested surfaces
// paint above their parent without escaping its graph ownership.
let index = layers.length()
layers.push(@ui.nothing())
let children = children.map(child => collect_layers(child, layers, ids))
layers[index] = @ui.element("view", attrs~, children~)
@ui.nothing()
} else {
@ui.element(
tag,
attrs~,
children=children.map(child => collect_layers(child, layers, ids)),
)
}
}
}
///|
/// Place all declarative layers after the content, outside scrolling and
/// clipped containers. Call once at the page's visual root.
pub fn[C : IsChildren] layer_root(
id? : String = "",
class? : String = "",
style? : String = "",
children : C,
) -> Node {
let children = children.to_nodes()
let layers : Array[@ui.Node] = []
let ids : Map[String, Bool] = Map([])
let body = children.map(child => collect_layers(child.raw, layers, ids))
let keyed_layers : Array[@ui.KeyedNode] = []
for layer in layers {
let mut key = ""
if layer is @ui.Element(_, attrs, _) {
for attr in attrs {
if attr.name == "id" {
key = attr.value
}
}
}
keyed_layers.push(@ui.keyed(key, layer))
}
body.push(@ui.keyed_fragment(keyed_layers))
wrap_node(
@ui.element(
"view",
id~,
class~,
attrs=raw_element_attrs(style, "layer-root"),
children=body,
),
child_bindings(children),
)
}