Browse Source

Components participate in event handlers

Jonathan Kelley 1 year ago
parent
commit
1527b81e02

+ 2 - 2
examples/drops.rs

@@ -14,9 +14,9 @@ fn app(cx: Scope) -> Element {
     }
 
     render! {
-        {(0..count).map(|_| rsx!{
+        for _ in 0..count {
             drop_child {}
-        })}
+        }
     }
 }
 

+ 3 - 5
examples/dynamic_asset.rs

@@ -17,11 +17,9 @@ fn app(cx: Scope) -> Element {
         response.respond(Response::new(include_bytes!("./assets/logo.png").to_vec()));
     });
 
-    cx.render(rsx! {
+    render! {
         div {
-            img {
-                src: "/logos/logo.png"
-            }
+            img { src: "/logos/logo.png" }
         }
-    })
+    }
 }

+ 3 - 5
examples/error_handle.rs

@@ -22,9 +22,7 @@ fn DemoC(cx: Scope, x: i32) -> Element {
 
     result.throw()?;
 
-    cx.render(rsx! {
-        h1 {
-            "{x}"
-        }
-    })
+    render! {
+        h1 { "{x}" }
+    }
 }

+ 5 - 6
examples/file_explorer.rs

@@ -35,18 +35,17 @@ fn app(cx: Scope) -> Element {
             main {
                 {files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
                     let path_end = path.split('/').last().unwrap_or(path.as_str());
-                    let icon_type = if path_end.contains('.') {
-                        "description"
-                    } else {
-                        "folder"
-                    };
                     rsx! (
                         div {
                             class: "folder",
                             key: "{path}",
                             i { class: "material-icons",
                                 onclick: move |_| files.write().enter_dir(dir_id),
-                                "{icon_type}"
+                                if path_end.contains('.') {
+                                    "description"
+                                } else {
+                                    "folder"
+                                }
                                 p { class: "cooltip", "0 folders / 0 files" }
                             }
                             h1 { "{path_end}" }

+ 6 - 8
examples/generic_component.rs

@@ -7,9 +7,9 @@ fn main() {
 }
 
 fn app(cx: Scope) -> Element {
-    cx.render(rsx! { generic_child {
-        data: 0i32
-    } })
+    render! {
+        generic_child { data: 0 }
+    }
 }
 
 #[derive(PartialEq, Props)]
@@ -18,9 +18,7 @@ struct GenericChildProps<T: Display + PartialEq> {
 }
 
 fn generic_child<T: Display + PartialEq>(cx: Scope<GenericChildProps<T>>) -> Element {
-    let data = &cx.props.data;
-
-    cx.render(rsx! { div {
-        "{data}"
-    } })
+    render! {
+        div { "{&cx.props.data}" }
+    }
 }

+ 2 - 2
examples/hello_world.rs

@@ -5,7 +5,7 @@ fn main() {
 }
 
 fn app(cx: Scope) -> Element {
-    cx.render(rsx! (
+    render! {
         div { "Hello, world!" }
-    ))
+    }
 }

+ 3 - 1
examples/pattern_model.rs

@@ -35,11 +35,13 @@ fn main() {
     dioxus_desktop::launch_cfg(app, cfg);
 }
 
+const STYLE: &str = include_str!("./assets/calculator.css");
+
 fn app(cx: Scope) -> Element {
     let state = use_ref(cx, Calculator::new);
 
     cx.render(rsx! {
-        style { {include_str!("./assets/calculator.css")} }
+        style { {STYLE} }
         div { id: "wrapper",
             div { class: "app",
                 div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),

+ 13 - 16
examples/shared_state.rs

@@ -51,26 +51,23 @@ pub fn App(cx: Scope) -> Element {
 
 #[component]
 fn DataEditor(cx: Scope, id: usize) -> Element {
-    let cool_data = use_shared_state::<CoolData>(cx).unwrap().read();
+    let data = use_shared_state::<CoolData>(cx)?;
 
-    let my_data = &cool_data.view(id).unwrap();
-
-    render!(p {
-        "{my_data}"
-    })
+    render! {
+        p {
+            {data.read().view(id)?}
+        }
+    }
 }
 
 #[component]
 fn DataView(cx: Scope, id: usize) -> Element {
-    let cool_data = use_shared_state::<CoolData>(cx).unwrap();
-
-    let oninput = |e: FormEvent| cool_data.write().set(*id, e.value());
+    let data = use_shared_state::<CoolData>(cx)?;
 
-    let cool_data = cool_data.read();
-    let my_data = &cool_data.view(id).unwrap();
-
-    render!(input {
-        oninput: oninput,
-        value: "{my_data}"
-    })
+    render! {
+        input {
+            oninput: move |e: FormEvent| data.write().set(*id, e.value()),
+            value: data.read().view(id)?
+        }
+    }
 }

+ 17 - 4
examples/shorthand.rs

@@ -11,22 +11,35 @@ fn app(cx: Scope) -> Element {
     let class = "class";
     let id = "id";
 
-    // todo: i'd like it for children to be inferred
+    // todo: i'd like it for children on elements to be inferred as the children of the element
+    // also should shorthands understand references/dereferences?
+    // ie **a, *a, &a, &mut a, etc
     let children = render! { "Child" };
+    let onclick = move |_| println!("Clicked!");
 
     render! {
         div { class, id, {&children} }
-        Component { a, b, c, children }
-        Component { a, ..ComponentProps { a: 1, b: 2, c: 3, children: None } }
+        Component { a, b, c, children, onclick }
+        Component { a, ..ComponentProps { a: 1, b: 2, c: 3, children: None, onclick: Default::default() } }
     }
 }
 
 #[component]
-fn Component<'a>(cx: Scope<'a>, a: i32, b: i32, c: i32, children: Element<'a>) -> Element {
+fn Component<'a>(
+    cx: Scope<'a>,
+    a: i32,
+    b: i32,
+    c: i32,
+    children: Element<'a>,
+    onclick: EventHandler<'a, ()>,
+) -> Element {
     render! {
         div { "{a}" }
         div { "{b}" }
         div { "{c}" }
         div { {children} }
+        div {
+            onclick: move |_| onclick.call(()),
+        }
     }
 }

+ 15 - 0
packages/core/src/nodes.rs

@@ -804,6 +804,15 @@ impl<'a, 'b> IntoDynNode<'b> for &'a str {
     }
 }
 
+impl IntoDynNode<'_> for &String {
+    fn into_dyn_node(self, cx: &ScopeState) -> DynamicNode {
+        DynamicNode::Text(VText {
+            value: cx.bump().alloc_str(&self),
+            id: Default::default(),
+        })
+    }
+}
+
 impl IntoDynNode<'_> for String {
     fn into_dyn_node(self, cx: &ScopeState) -> DynamicNode {
         DynamicNode::Text(VText {
@@ -898,6 +907,12 @@ impl<'a> IntoAttributeValue<'a> for String {
     }
 }
 
+impl<'a> IntoAttributeValue<'a> for &String {
+    fn into_value(self, cx: &'a Bump) -> AttributeValue<'a> {
+        AttributeValue::Text(cx.alloc_str(&self))
+    }
+}
+
 impl<'a> IntoAttributeValue<'a> for f64 {
     fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
         AttributeValue::Float(self)

+ 14 - 5
packages/rsx/src/attribute.rs

@@ -109,17 +109,25 @@ impl ToTokens for ElementAttrNamed {
         };
 
         let attribute = {
+            let value = &self.attr.value;
+            let is_shorthand_event = match &attr.value {
+                ElementAttrValue::Shorthand(s) => s.to_string().starts_with("on"),
+                _ => false,
+            };
+
             match &attr.value {
                 ElementAttrValue::AttrLiteral(_)
                 | ElementAttrValue::AttrExpr(_)
                 | ElementAttrValue::Shorthand(_)
-                | ElementAttrValue::AttrOptionalExpr { .. } => {
+                | ElementAttrValue::AttrOptionalExpr { .. }
+                    if !is_shorthand_event =>
+                {
                     let name = &self.attr.name;
                     let ns = ns(name);
                     let volitile = volitile(name);
                     let attribute = attribute(name);
-                    let value = &self.attr.value;
                     let value = quote! { #value };
+
                     quote! {
                         __cx.attr(
                             #attribute,
@@ -131,12 +139,13 @@ impl ToTokens for ElementAttrNamed {
                 }
                 ElementAttrValue::EventTokens(tokens) => match &self.attr.name {
                     ElementAttrName::BuiltIn(name) => {
-                        quote! {
-                            dioxus_elements::events::#name(__cx, #tokens)
-                        }
+                        quote! { dioxus_elements::events::#name(__cx, #tokens) }
                     }
                     ElementAttrName::Custom(_) => todo!(),
                 },
+                _ => {
+                    quote! { dioxus_elements::events::#value(__cx, #value) }
+                }
             }
         };
 

+ 3 - 0
packages/rsx/src/component.rs

@@ -241,6 +241,9 @@ impl ContentField {
 impl ToTokens for ContentField {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match self {
+            ContentField::Shorthand(i) if i.to_string().starts_with("on") => {
+                tokens.append_all(quote! { __cx.event_handler(#i) })
+            }
             ContentField::Shorthand(i) => tokens.append_all(quote! { #i }),
             ContentField::ManExpr(e) => e.to_tokens(tokens),
             ContentField::Formatted(s) => tokens.append_all(quote! { __cx.raw_text(#s) }),