element.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. use super::*;
  2. use proc_macro2::{Span, TokenStream as TokenStream2};
  3. use quote::{quote, ToTokens, TokenStreamExt};
  4. use syn::{
  5. parse::{Parse, ParseBuffer, ParseStream},
  6. Error, Expr, Ident, LitStr, Result, Token,
  7. };
  8. // =======================================
  9. // Parse the VNode::Element type
  10. // =======================================
  11. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  12. pub struct Element {
  13. pub name: Ident,
  14. pub key: Option<IfmtInput>,
  15. pub attributes: Vec<ElementAttrNamed>,
  16. pub children: Vec<BodyNode>,
  17. pub _is_static: bool,
  18. }
  19. impl Parse for Element {
  20. fn parse(stream: ParseStream) -> Result<Self> {
  21. let el_name = Ident::parse(stream)?;
  22. // parse the guts
  23. let content: ParseBuffer;
  24. syn::braced!(content in stream);
  25. let mut attributes: Vec<ElementAttrNamed> = vec![];
  26. let mut children: Vec<BodyNode> = vec![];
  27. let mut key = None;
  28. let mut _el_ref = None;
  29. // parse fields with commas
  30. // break when we don't get this pattern anymore
  31. // start parsing bodynodes
  32. // "def": 456,
  33. // abc: 123,
  34. loop {
  35. // Parse the raw literal fields
  36. if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  37. let name = content.parse::<LitStr>()?;
  38. let ident = name.clone();
  39. content.parse::<Token![:]>()?;
  40. if content.peek(LitStr) {
  41. let value = content.parse()?;
  42. attributes.push(ElementAttrNamed {
  43. el_name: el_name.clone(),
  44. attr: ElementAttr::CustomAttrText { name, value },
  45. });
  46. } else {
  47. let value = content.parse::<Expr>()?;
  48. attributes.push(ElementAttrNamed {
  49. el_name: el_name.clone(),
  50. attr: ElementAttr::CustomAttrExpression { name, value },
  51. });
  52. }
  53. if content.is_empty() {
  54. break;
  55. }
  56. if content.parse::<Token![,]>().is_err() {
  57. missing_trailing_comma!(ident.span());
  58. }
  59. continue;
  60. }
  61. if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  62. let name = content.parse::<Ident>()?;
  63. let ident = name.clone();
  64. let name_str = name.to_string();
  65. content.parse::<Token![:]>()?;
  66. if name_str.starts_with("on") {
  67. attributes.push(ElementAttrNamed {
  68. el_name: el_name.clone(),
  69. attr: ElementAttr::EventTokens {
  70. name,
  71. tokens: content.parse()?,
  72. },
  73. });
  74. } else {
  75. match name_str.as_str() {
  76. "key" => {
  77. key = Some(content.parse()?);
  78. }
  79. "classes" => todo!("custom class list not supported yet"),
  80. // "namespace" => todo!("custom namespace not supported yet"),
  81. "node_ref" => {
  82. _el_ref = Some(content.parse::<Expr>()?);
  83. }
  84. _ => {
  85. if content.peek(LitStr) {
  86. attributes.push(ElementAttrNamed {
  87. el_name: el_name.clone(),
  88. attr: ElementAttr::AttrText {
  89. name,
  90. value: content.parse()?,
  91. },
  92. });
  93. } else {
  94. attributes.push(ElementAttrNamed {
  95. el_name: el_name.clone(),
  96. attr: ElementAttr::AttrExpression {
  97. name,
  98. value: content.parse()?,
  99. },
  100. });
  101. }
  102. }
  103. }
  104. }
  105. if content.is_empty() {
  106. break;
  107. }
  108. // todo: add a message saying you need to include commas between fields
  109. if content.parse::<Token![,]>().is_err() {
  110. missing_trailing_comma!(ident.span());
  111. }
  112. continue;
  113. }
  114. break;
  115. }
  116. while !content.is_empty() {
  117. if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
  118. attr_after_element!(content.span());
  119. }
  120. if (content.peek(Ident) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
  121. attr_after_element!(content.span());
  122. }
  123. children.push(content.parse::<BodyNode>()?);
  124. // consume comma if it exists
  125. // we don't actually care if there *are* commas after elements/text
  126. if content.peek(Token![,]) {
  127. let _ = content.parse::<Token![,]>();
  128. }
  129. }
  130. Ok(Self {
  131. key,
  132. name: el_name,
  133. attributes,
  134. children,
  135. _is_static: false,
  136. })
  137. }
  138. }
  139. impl ToTokens for Element {
  140. fn to_tokens(&self, tokens: &mut TokenStream2) {
  141. let name = &self.name;
  142. let children = &self.children;
  143. let key = match &self.key {
  144. Some(ty) => quote! { Some(#ty) },
  145. None => quote! { None },
  146. };
  147. let listeners = self
  148. .attributes
  149. .iter()
  150. .filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. }));
  151. let attr = self
  152. .attributes
  153. .iter()
  154. .filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. }));
  155. tokens.append_all(quote! {
  156. __cx.element(
  157. dioxus_elements::#name,
  158. __cx.bump().alloc([ #(#listeners),* ]),
  159. __cx.bump().alloc([ #(#attr),* ]),
  160. __cx.bump().alloc([ #(#children),* ]),
  161. #key,
  162. )
  163. });
  164. }
  165. }
  166. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  167. pub enum ElementAttr {
  168. /// `attribute: "value"`
  169. AttrText { name: Ident, value: IfmtInput },
  170. /// `attribute: true`
  171. AttrExpression { name: Ident, value: Expr },
  172. /// `"attribute": "value"`
  173. CustomAttrText { name: LitStr, value: IfmtInput },
  174. /// `"attribute": true`
  175. CustomAttrExpression { name: LitStr, value: Expr },
  176. // /// onclick: move |_| {}
  177. // EventClosure { name: Ident, closure: ExprClosure },
  178. /// onclick: {}
  179. EventTokens { name: Ident, tokens: Expr },
  180. }
  181. impl ElementAttr {
  182. pub fn flart(&self) -> Span {
  183. match self {
  184. ElementAttr::AttrText { name, .. } => name.span(),
  185. ElementAttr::AttrExpression { name, .. } => name.span(),
  186. ElementAttr::CustomAttrText { name, .. } => name.span(),
  187. ElementAttr::CustomAttrExpression { name, .. } => name.span(),
  188. ElementAttr::EventTokens { name, .. } => name.span(),
  189. }
  190. }
  191. pub fn is_expr(&self) -> bool {
  192. matches!(
  193. self,
  194. ElementAttr::AttrExpression { .. }
  195. | ElementAttr::CustomAttrExpression { .. }
  196. | ElementAttr::EventTokens { .. }
  197. )
  198. }
  199. }
  200. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  201. pub struct ElementAttrNamed {
  202. pub el_name: Ident,
  203. pub attr: ElementAttr,
  204. }
  205. impl ToTokens for ElementAttrNamed {
  206. fn to_tokens(&self, tokens: &mut TokenStream2) {
  207. let ElementAttrNamed { el_name, attr } = self;
  208. tokens.append_all(match attr {
  209. ElementAttr::AttrText { name, value } => {
  210. quote! {
  211. __cx.attr(
  212. dioxus_elements::#el_name::#name.0,
  213. #value,
  214. None,
  215. false
  216. )
  217. }
  218. }
  219. ElementAttr::AttrExpression { name, value } => {
  220. quote! {
  221. __cx.attr(
  222. dioxus_elements::#el_name::#name.0,
  223. #value,
  224. None,
  225. false
  226. )
  227. }
  228. }
  229. ElementAttr::CustomAttrText { name, value } => {
  230. quote! {
  231. __cx.attr(
  232. dioxus_elements::#el_name::#name.0,
  233. #value,
  234. None,
  235. false
  236. )
  237. }
  238. }
  239. ElementAttr::CustomAttrExpression { name, value } => {
  240. quote! {
  241. __cx.attr(
  242. dioxus_elements::#el_name::#name.0,
  243. #value,
  244. None,
  245. false
  246. )
  247. }
  248. }
  249. ElementAttr::EventTokens { name, tokens } => {
  250. quote! {
  251. dioxus_elements::events::#name(__cx, #tokens)
  252. }
  253. }
  254. });
  255. }
  256. }
  257. // ::dioxus::core::Attribute {
  258. // name: stringify!(#name),
  259. // namespace: None,
  260. // volatile: false,
  261. // mounted_node: Default::default(),
  262. // value: ::dioxus::core::AttributeValue::Text(#value),
  263. // }