feat: component event callbacks + function prop forwarding
- ComponentUse wraps Assign/MethodCall/Block props as arrow functions - Component prop wrapper preserves function props (typeof check) - Event handler fallthrough calls function-type identifiers - New: examples/callback-demo.ds with Button onClick callbacks - All existing examples build successfully
This commit is contained in:
parent
cbd6dfc7a6
commit
9d01f1b702
2 changed files with 47 additions and 6 deletions
|
|
@ -467,12 +467,12 @@ impl JsEmitter {
|
|||
self.emit_line(&format!("function DS_{}(props, __children) {{", comp.name));
|
||||
self.indent += 1;
|
||||
// Destructure props into local signal-compatible variables
|
||||
// Props may be raw values or signals — wrap in a signal-like accessor
|
||||
// Props may be raw values, signals, or callback functions
|
||||
for p in &comp.props {
|
||||
// Create a signal-like wrapper: if prop is already a signal, use it; otherwise wrap
|
||||
// Create a signal-like wrapper: if prop is a function, keep as-is; if already a signal, use it; otherwise wrap
|
||||
self.emit_line(&format!(
|
||||
"const {} = (props.{} !== undefined && props.{} !== null) ? (typeof props.{} === 'object' && 'value' in props.{} ? props.{} : {{ get value() {{ return props.{}; }} }}) : {{ get value() {{ return \"\"; }} }};",
|
||||
p.name, p.name, p.name, p.name, p.name, p.name, p.name
|
||||
"const {} = (typeof props.{} === 'function') ? props.{} : (props.{} !== undefined && props.{} !== null) ? (typeof props.{} === 'object' && 'value' in props.{} ? props.{} : {{ get value() {{ return props.{}; }} }}) : {{ get value() {{ return \"\"; }} }};",
|
||||
p.name, p.name, p.name, p.name, p.name, p.name, p.name, p.name, p.name
|
||||
));
|
||||
}
|
||||
let root_id = self.emit_view_expr(&comp.body, graph);
|
||||
|
|
@ -901,7 +901,18 @@ impl JsEmitter {
|
|||
Expr::ComponentUse { name, props, children } => {
|
||||
let node_var = self.next_node_id();
|
||||
let props_js: Vec<String> = props.iter()
|
||||
.map(|(k, v)| format!("{}: {}", k, self.emit_expr(v)))
|
||||
.map(|(k, v)| {
|
||||
// Detect callback props (assignments, method calls, blocks)
|
||||
let is_callback = matches!(v,
|
||||
Expr::Assign(_, _, _) | Expr::MethodCall(_, _, _) | Expr::Block(_)
|
||||
);
|
||||
if is_callback {
|
||||
let handler_js = self.emit_event_handler_expr(v);
|
||||
format!("{}: () => {{ {}; DS.flush() }}", k, handler_js)
|
||||
} else {
|
||||
format!("{}: {}", k, self.emit_expr(v))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if children.is_empty() {
|
||||
self.emit_line(&format!("const {} = DS_{}({{ {} }});", node_var, name, props_js.join(", ")));
|
||||
|
|
@ -1344,7 +1355,14 @@ impl JsEmitter {
|
|||
let stmts: Vec<String> = exprs.iter().map(|e| self.emit_event_handler_expr(e)).collect();
|
||||
stmts.join("; ")
|
||||
}
|
||||
_ => self.emit_expr(expr),
|
||||
_ => {
|
||||
// For plain identifiers that might be callback props, call them
|
||||
if let Expr::Ident(name) = expr {
|
||||
format!("if (typeof {} === 'function') {}()", name, name)
|
||||
} else {
|
||||
self.emit_expr(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
23
examples/callback-demo.ds
Normal file
23
examples/callback-demo.ds
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-- DreamStack Callback Demo
|
||||
-- Tests component event callbacks
|
||||
|
||||
import { Card } from "../registry/components/card"
|
||||
import { Button } from "../registry/components/button"
|
||||
|
||||
let count = 0
|
||||
let message = "Click a button"
|
||||
|
||||
view main = column [
|
||||
text "🔗 Component Callbacks" { variant: "title" }
|
||||
text "Components can trigger parent actions" { variant: "subtitle" }
|
||||
|
||||
Card { title: "Using Button Component", subtitle: "callback props" } [
|
||||
text "Count: {count}" { variant: "title" }
|
||||
text message { variant: "subtitle" }
|
||||
row [
|
||||
Button { label: "+1", onClick: count += 1, variant: "primary" }
|
||||
Button { label: "-1", onClick: count -= 1, variant: "secondary" }
|
||||
Button { label: "Reset", onClick: count = 0, variant: "ghost" }
|
||||
]
|
||||
]
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue