From 99f0c01c30126e1a00f87bf8759f4bb98f7c582c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABlle=20Huisman?= Date: Sat, 23 Nov 2024 13:12:39 +0100 Subject: [PATCH] Fix callback and node ref in leptos-struct-component --- .../leptos-struct-component-macro/src/lib.rs | 128 +++++++--------- .../leptos-struct-component/src/attributes.rs | 140 ++++++------------ .../tests/struct_component.rs | 11 +- 3 files changed, 111 insertions(+), 168 deletions(-) diff --git a/packages/leptos-struct-component-macro/src/lib.rs b/packages/leptos-struct-component-macro/src/lib.rs index 6e19329..ecc1d3f 100644 --- a/packages/leptos-struct-component-macro/src/lib.rs +++ b/packages/leptos-struct-component-macro/src/lib.rs @@ -3,10 +3,10 @@ extern crate proc_macro; use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::quote; use syn::{ - parse_macro_input, spanned::Spanned, AttrStyle, Attribute, Data, DeriveInput, Ident, LitBool, - LitStr, Meta, Type, + parse_macro_input, spanned::Spanned, AttrStyle, Attribute, Data, DeriveInput, GenericArgument, + LitBool, LitStr, Meta, PathArguments, Type, }; #[derive(Debug, Default)] @@ -114,26 +114,28 @@ pub fn derive_struct_component(input: proc_macro::TokenStream) -> proc_macro::To } } - // if ident == "attributes" { - // attributes_map = Some(quote! { - // .chain( - // self.attributes - // .into_iter() - // .flatten() - // .flat_map(|(key, value)| value.map(|value| ( - // ::yew::virtual_dom::AttrValue::from(key), - // ::yew::virtual_dom::AttributeOrProperty::Attribute(AttrValue::from(value)), - // )), - // ), - // ) - // }); - - // continue; - // } + if ident == "attributes" { + // TODO: dynamic attributes + + // attributes_map = Some(quote! { + // .chain( + // self.attributes + // .into_iter() + // .flatten() + // .flat_map(|(key, value)| value.map(|value| ( + // ::yew::virtual_dom::AttrValue::from(key), + // ::yew::virtual_dom::AttributeOrProperty::Attribute(AttrValue::from(value)), + // )), + // ), + // ) + // }); + + continue; + } if ident == "node_ref" { node_ref = Some(quote! { - tag.node_ref = self.node_ref; + .node_ref(self.node_ref) }); continue; @@ -141,12 +143,44 @@ pub fn derive_struct_component(input: proc_macro::TokenStream) -> proc_macro::To if ident.to_string().starts_with("on") { if let Type::Path(path) = &field.ty { + let event = ident + .to_string() + .strip_prefix("on") + .expect("String should start with `on`.") + .parse::() + .expect("String should parse as TokenStream."); + let first = path.path.segments.first(); + let first_argument = first.and_then(|segment| match &segment.arguments { + PathArguments::None => None, + PathArguments::AngleBracketed(arguments) => { + arguments.args.first().and_then(|arg| match arg { + GenericArgument::Type(Type::Path(path)) => { + path.path.segments.first() + } + _ => None, + }) + } + PathArguments::Parenthesized(_) => None, + }); + if first.is_some_and(|segment| segment.ident == "Callback") { - // listeners.push(ident.clone()); + listeners.push(quote! { + .on(::leptos::tachys::html::event::#event, move |event| { + self.#ident.run(event); + }) + }); + continue; + } else if first.is_some_and(|segment| segment.ident == "Option") + && first_argument.is_some_and(|argument| argument.ident == "Callback") + { listeners.push(quote! { - .#ident(move |event| self.#ident.run(event)) + .on(::leptos::tachys::html::event::#event, move |event| { + if let Some(listener) = &self.#ident { + listener.run(event); + } + }) }); continue; @@ -154,28 +188,8 @@ pub fn derive_struct_component(input: proc_macro::TokenStream) -> proc_macro::To } } - // if ident == "checked" { - // attribute_checked = Some(quote! { - // tag.set_checked(self.checked); - // }); - // } - - // if ident == "value" { - // attribute_value = Some(quote! { - // tag.set_value(self.value.clone()); - // }); - // } - match &field.ty { Type::Path(path) => { - let name = ident.to_string().replace("_", "-"); - let name = if name.starts_with("r#") { - name.strip_prefix("r#").expect("String should have prefix.") - } else { - name.as_str() - } - .to_token_stream(); - let first = path.path.segments.first(); attributes.push( @@ -183,38 +197,9 @@ pub fn derive_struct_component(input: proc_macro::TokenStream) -> proc_macro::To quote! { .#ident(move || self.#ident.get()) } - } else if first.is_some_and(|segment| segment.ident == "Option") { - quote! { - .#ident(self.#ident) - - // self.#ident.map(|value| ( - // ::yew::virtual_dom::AttrValue::from(#name), - // ::yew::virtual_dom::AttributeOrProperty::Attribute( - // ::yew::virtual_dom::AttrValue::from(value) - // ) - // )) - } - } else if first.is_some_and(|segment| segment.ident == "bool") { - quote! { - .#ident(self.#ident) - - // self.#ident.then_some(( - // ::yew::virtual_dom::AttrValue::from(#name), - // ::yew::virtual_dom::AttributeOrProperty::Attribute( - // ::yew::virtual_dom::AttrValue::from("") - // ) - // )) - } } else { quote! { .#ident(self.#ident) - - // Some(( - // ::yew::virtual_dom::AttrValue::from(#name), - // ::yew::virtual_dom::AttributeOrProperty::Attribute( - // ::yew::virtual_dom::AttrValue::from(self.#ident) - // ) - // )) } }, ); @@ -261,6 +246,7 @@ pub fn derive_struct_component(input: proc_macro::TokenStream) -> proc_macro::To // TODO: dynamic attributes #tag + #node_ref #(#attributes)* #(#listeners)* #children diff --git a/packages/leptos-struct-component/src/attributes.rs b/packages/leptos-struct-component/src/attributes.rs index 0d69546..97696f4 100644 --- a/packages/leptos-struct-component/src/attributes.rs +++ b/packages/leptos-struct-component/src/attributes.rs @@ -1,11 +1,13 @@ -use std::{ - collections::HashMap, - ops::Deref, - option::{IntoIter, Iter, IterMut}, +use std::{collections::HashMap, ops::Deref}; + +use leptos::{ + attr::custom::{custom_attribute, CustomAttr}, + html::button, + prelude::AddAnyAttr, }; #[derive(Clone, Debug, Default, PartialEq)] -pub struct Attributes(Option>>); +pub struct Attributes(Option>>); impl Attributes { pub fn with_defaults>(mut self, defaults: I) -> Attributes { @@ -20,41 +22,39 @@ impl Attributes { self } + + pub fn to_custom_attributes(self) -> Vec>> { + self.0 + .map(|map| { + map.into_iter() + .map(|(key, value)| custom_attribute(key, value)) + .collect() + }) + .unwrap_or_default() + } + + // pub fn test(self) { + // let attrs = self.to_custom_attributes(); + + // let mut tag = button(); + + // let test = attrs + // .into_iter() + // .fold(tag, |tag, attr| tag.add_any_attr(attr)); + // } } impl Deref for Attributes { - type Target = Option>>; + type Target = Option>>; fn deref(&self) -> &Self::Target { &self.0 } } -impl From>> for Attributes { - fn from(value: HashMap>) -> Attributes { - Attributes(Some(value)) - } -} - -impl From> for Attributes { - fn from(value: HashMap) -> Attributes { - Attributes(Some( - value - .into_iter() - .map(|(key, value)| (key, Some(value))) - .collect(), - )) - } -} - impl From>> for Attributes { fn from(value: HashMap>) -> Attributes { - Attributes(Some( - value - .into_iter() - .map(|(key, value)| (AttrValue::from(key), value.map(AttrValue::from))) - .collect(), - )) + Attributes(Some(value)) } } @@ -63,106 +63,54 @@ impl From> for Attributes { Attributes(Some( value .into_iter() - .map(|(key, value)| (AttrValue::from(key), Some(AttrValue::from(value)))) + .map(|(key, value)| (key, Some(value))) .collect(), )) } } -impl From<[(AttrValue, Option); N]> for Attributes { - fn from(value: [(AttrValue, Option); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value))) - } -} - -impl From<[(AttrValue, AttrValue); N]> for Attributes { - fn from(value: [(AttrValue, AttrValue); N]) -> Attributes { - Attributes(Some(HashMap::from_iter( - value.map(|(key, value)| (key, Some(value))), - ))) - } -} - impl From<[(&str, Option<&str>); N]> for Attributes { fn from(value: [(&str, Option<&str>); N]) -> Attributes { Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - ( - AttrValue::from(key.to_string()), - value.map(|value| AttrValue::from(value.to_string())), - ) + (key.to_string(), value.map(|value| value.to_string())) })))) } } impl From<[(&str, &str); N]> for Attributes { fn from(value: [(&str, &str); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - ( - AttrValue::from(key.to_string()), - Some(AttrValue::from(value.to_string())), - ) - })))) + Attributes(Some(HashMap::from_iter( + value.map(|(key, value)| (key.to_string(), Some(value.to_string()))), + ))) } } impl From<[(&str, Option); N]> for Attributes { fn from(value: [(&str, Option); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - (AttrValue::from(key.to_string()), value.map(AttrValue::from)) - })))) + Attributes(Some(HashMap::from_iter( + value.map(|(key, value)| (key.to_string(), value)), + ))) } } impl From<[(&str, String); N]> for Attributes { fn from(value: [(&str, String); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - ( - AttrValue::from(key.to_string()), - Some(AttrValue::from(value)), - ) - })))) + Attributes(Some(HashMap::from_iter( + value.map(|(key, value)| (key.to_string(), Some(value))), + ))) } } impl From<[(String, Option); N]> for Attributes { fn from(value: [(String, Option); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - (AttrValue::from(key), value.map(AttrValue::from)) - })))) + Attributes(Some(HashMap::from_iter(value))) } } impl From<[(String, String); N]> for Attributes { fn from(value: [(String, String); N]) -> Attributes { - Attributes(Some(HashMap::from_iter(value.map(|(key, value)| { - (AttrValue::from(key), Some(AttrValue::from(value))) - })))) - } -} - -impl<'a> IntoIterator for &'a Attributes { - type Item = &'a HashMap>; - type IntoIter = Iter<'a, HashMap>>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a> IntoIterator for &'a mut Attributes { - type Item = &'a mut HashMap>; - type IntoIter = IterMut<'a, HashMap>>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } -} - -impl IntoIterator for Attributes { - type Item = HashMap>; - type IntoIter = IntoIter>>; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + Attributes(Some(HashMap::from_iter( + value.map(|(key, value)| (key, Some(value))), + ))) } } diff --git a/packages/leptos-struct-component/tests/struct_component.rs b/packages/leptos-struct-component/tests/struct_component.rs index 0391d51..c67316c 100644 --- a/packages/leptos-struct-component/tests/struct_component.rs +++ b/packages/leptos-struct-component/tests/struct_component.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -use leptos::prelude::*; +use leptos::{ev::MouseEvent, prelude::*}; use leptos_node_ref::AnyNodeRef; use leptos_struct_component::{struct_component, StructComponent}; @@ -79,6 +79,9 @@ pub struct ImageChildProps { pub class: MaybeProp, pub id: MaybeProp, pub style: MaybeProp, + + // Event handler attributes + pub onclick: Option>, } #[component] @@ -88,6 +91,9 @@ pub fn Image( #[prop(into, optional)] class: MaybeProp, #[prop(into, optional)] style: MaybeProp, + // Event handler attributes + #[prop(into, optional)] on_click: Option>, + #[prop(into, optional)] node_ref: AnyNodeRef, // #[prop(into, optional)] attributes: Attributes, #[prop(into, optional)] as_child: Option>, @@ -100,6 +106,9 @@ pub fn Image( class, id, style, + + // Event handler attributes + onclick: on_click, }; if let Some(as_child) = as_child {