feat(hemx): reuse todo row partials
Make the canonical v0 todo flow use one TodoRow partial shape for initial list rendering and generated keyed append/replace/remove helpers. Teach hemx-build to treat collection hosts with keyed h-for children as keyed targets, so beginner templates can keep the DRY outer slot + child partial composition. req: canonical_authoring/002 req: canonical_authoring/003 req: examples/004 req: list/001
This commit is contained in:
+38
-2
@@ -175,7 +175,9 @@ impl Resources {
|
||||
|
||||
if let Some(name) = static_attr(&node.attrs, "data-hemx-slot") {
|
||||
reject_unkeyed_loop(surface, node.scope, path, "slot", &name)?;
|
||||
let keyed = is_inside_keyed_for(surface, node.scope);
|
||||
let keyed = is_inside_keyed_for(surface, node.scope)
|
||||
|| (can_host_keyed_collection(tag)
|
||||
&& has_descendant_keyed_for_scope(surface, node.scope));
|
||||
let canonical = canonical_symbol(root, path, &name);
|
||||
self.insert_slot(canonical, name, component.clone(), keyed)?;
|
||||
}
|
||||
@@ -1275,6 +1277,40 @@ fn event_ident(name: &str) -> Option<String> {
|
||||
rust_ident(&name.replace(['-', ':'], "_"))
|
||||
}
|
||||
|
||||
fn can_host_keyed_collection(tag: &str) -> bool {
|
||||
matches!(
|
||||
tag,
|
||||
"ul" | "ol" | "tbody" | "thead" | "tfoot" | "table" | "select" | "datalist"
|
||||
)
|
||||
}
|
||||
|
||||
fn has_descendant_keyed_for_scope(surface: &SurfaceDocument, scope: ScopeId) -> bool {
|
||||
surface.scopes.iter().enumerate().any(|(index, current)| {
|
||||
matches!(
|
||||
current.kind,
|
||||
ScopeKind::For {
|
||||
key_expr: Some(_),
|
||||
..
|
||||
}
|
||||
) && is_descendant_scope(surface, ScopeId(index as u32), scope)
|
||||
})
|
||||
}
|
||||
|
||||
fn is_descendant_scope(surface: &SurfaceDocument, mut scope: ScopeId, ancestor: ScopeId) -> bool {
|
||||
loop {
|
||||
let Some(current) = surface.scopes.get(scope.0 as usize) else {
|
||||
return false;
|
||||
};
|
||||
let Some(parent) = current.parent else {
|
||||
return false;
|
||||
};
|
||||
if parent == ancestor {
|
||||
return true;
|
||||
}
|
||||
scope = parent;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_inside_keyed_for(surface: &SurfaceDocument, mut scope: ScopeId) -> bool {
|
||||
loop {
|
||||
let Some(current) = surface.scopes.get(scope.0 as usize) else {
|
||||
@@ -1732,7 +1768,7 @@ mod tests {
|
||||
std::fs::create_dir_all(&templates).unwrap();
|
||||
std::fs::write(
|
||||
templates.join("todos.heml"),
|
||||
r#"<ul data-hemx-slot="row"><template h-for="todo in &self.todos" h-key="todo.id"><li data-hemx-slot="row" +data-key="todo.id">{+ todo.title +}</li></template></ul>"#,
|
||||
r#"<ul data-hemx-slot="row"><template h-for="todo in &self.todos" h-key="todo.id"><li +data-key="todo.id">{+ todo +}</li></template></ul>"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user