Merge branch 'many_optional_attributes' into pr/atty303/1349
This commit is contained in:
commit
9e167dfdb7
16 files changed
+553
-319
No files matched your search
+43
-170
@@ -8,7 +8,7 @@ use syn::{
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
Error, Expr, Ident, LitStr, Result, Token,
|
||||
Ident, LitStr, Result, Token,
|
||||
};
|
||||
|
||||
// =======================================
|
||||
@@ -19,6 +19,7 @@ pub struct Element {
|
||||
pub name: ElementName,
|
||||
pub key: Option<IfmtInput>,
|
||||
pub attributes: Vec<ElementAttrNamed>,
|
||||
pub merged_attributes: Vec<ElementAttrNamed>,
|
||||
pub children: Vec<BodyNode>,
|
||||
pub brace: syn::token::Brace,
|
||||
pub extra_attributes: Option<Expr>,
|
||||
@@ -35,8 +36,6 @@ impl Parse for Element {
|
||||
let mut attributes: Vec<ElementAttrNamed> = vec![];
|
||||
let mut children: Vec<BodyNode> = vec![];
|
||||
let mut key = None;
|
||||
let mut _el_ref = None;
|
||||
let mut extra_attributes = None;
|
||||
|
||||
// parse fields with commas
|
||||
// break when we don't get this pattern anymore
|
||||
@@ -56,19 +55,14 @@ impl Parse for Element {
|
||||
|
||||
content.parse::<Token![:]>()?;
|
||||
|
||||
if content.peek(LitStr) {
|
||||
let value = content.parse()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::CustomAttrText { name, value },
|
||||
});
|
||||
} else {
|
||||
let value = content.parse::<Expr>()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::CustomAttrExpression { name, value },
|
||||
});
|
||||
}
|
||||
let value = content.parse::<ElementAttrValue>()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::Custom(name),
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
if content.is_empty() {
|
||||
break;
|
||||
@@ -93,9 +87,9 @@ impl Parse for Element {
|
||||
if name_str.starts_with("on") {
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::EventTokens {
|
||||
name,
|
||||
tokens: content.parse()?,
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(name),
|
||||
value: ElementAttrValue::EventTokens(content.parse()?),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -103,29 +97,15 @@ impl Parse for Element {
|
||||
"key" => {
|
||||
key = Some(content.parse()?);
|
||||
}
|
||||
"classes" => todo!("custom class list not supported yet"),
|
||||
// "namespace" => todo!("custom namespace not supported yet"),
|
||||
"node_ref" => {
|
||||
_el_ref = Some(content.parse::<Expr>()?);
|
||||
}
|
||||
_ => {
|
||||
if content.peek(LitStr) {
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::AttrText {
|
||||
name,
|
||||
value: content.parse()?,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr::AttrExpression {
|
||||
name,
|
||||
value: content.parse()?,
|
||||
},
|
||||
});
|
||||
}
|
||||
let value = content.parse::<ElementAttrValue>()?;
|
||||
attributes.push(ElementAttrNamed {
|
||||
el_name: el_name.clone(),
|
||||
attr: ElementAttr {
|
||||
name: ElementAttrName::BuiltIn(name),
|
||||
value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,6 +124,23 @@ impl Parse for Element {
|
||||
break;
|
||||
}
|
||||
|
||||
// Deduplicate any attributes that can be combined
|
||||
// For example, if there are two `class` attributes, combine them into one
|
||||
let mut merged_attributes: Vec<ElementAttrNamed> = Vec::new();
|
||||
for attr in &attributes {
|
||||
if let Some(old_attr_index) = merged_attributes
|
||||
.iter()
|
||||
.position(|a| a.attr.name == attr.attr.name)
|
||||
{
|
||||
let old_attr = &mut merged_attributes[old_attr_index];
|
||||
if let Some(combined) = old_attr.try_combine(attr) {
|
||||
*old_attr = combined;
|
||||
}
|
||||
} else {
|
||||
merged_attributes.push(attr.clone());
|
||||
}
|
||||
}
|
||||
|
||||
while !content.is_empty() {
|
||||
if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
|
||||
attr_after_element!(content.span());
|
||||
@@ -165,6 +162,7 @@ impl Parse for Element {
|
||||
key,
|
||||
name: el_name,
|
||||
attributes,
|
||||
merged_attributes,
|
||||
children,
|
||||
brace,
|
||||
extra_attributes,
|
||||
@@ -183,14 +181,14 @@ impl ToTokens for Element {
|
||||
};
|
||||
|
||||
let listeners = self
|
||||
.attributes
|
||||
.merged_attributes
|
||||
.iter()
|
||||
.filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. }));
|
||||
.filter(|f| matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
|
||||
|
||||
let attr = self
|
||||
.attributes
|
||||
.merged_attributes
|
||||
.iter()
|
||||
.filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. }));
|
||||
.filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
|
||||
|
||||
tokens.append_all(quote! {
|
||||
__cx.element(
|
||||
@@ -272,128 +270,3 @@ impl ToTokens for ElementName {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementAttr {
|
||||
/// `attribute: "value"`
|
||||
AttrText { name: Ident, value: IfmtInput },
|
||||
|
||||
/// `attribute: true`
|
||||
AttrExpression { name: Ident, value: Expr },
|
||||
|
||||
/// `"attribute": "value"`
|
||||
CustomAttrText { name: LitStr, value: IfmtInput },
|
||||
|
||||
/// `"attribute": true`
|
||||
CustomAttrExpression { name: LitStr, value: Expr },
|
||||
|
||||
// /// onclick: move |_| {}
|
||||
// EventClosure { name: Ident, closure: ExprClosure },
|
||||
/// onclick: {}
|
||||
EventTokens { name: Ident, tokens: Expr },
|
||||
}
|
||||
|
||||
impl ElementAttr {
|
||||
pub fn start(&self) -> Span {
|
||||
match self {
|
||||
ElementAttr::AttrText { name, .. } => name.span(),
|
||||
ElementAttr::AttrExpression { name, .. } => name.span(),
|
||||
ElementAttr::CustomAttrText { name, .. } => name.span(),
|
||||
ElementAttr::CustomAttrExpression { name, .. } => name.span(),
|
||||
ElementAttr::EventTokens { name, .. } => name.span(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_expr(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub struct ElementAttrNamed {
|
||||
pub el_name: ElementName,
|
||||
pub attr: ElementAttr,
|
||||
}
|
||||
|
||||
impl ToTokens for ElementAttrNamed {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ElementAttrNamed { el_name, attr } = self;
|
||||
|
||||
let ns = |name| match el_name {
|
||||
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 },
|
||||
ElementName::Custom(_) => quote! { None },
|
||||
};
|
||||
let volitile = |name| match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.2 },
|
||||
ElementName::Custom(_) => quote! { false },
|
||||
};
|
||||
let attribute = |name: &Ident| match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.0 },
|
||||
ElementName::Custom(_) => {
|
||||
let as_string = name.to_string();
|
||||
quote!(#as_string)
|
||||
}
|
||||
};
|
||||
|
||||
let attribute = match attr {
|
||||
ElementAttr::AttrText { name, value } => {
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#attribute,
|
||||
#value,
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::AttrExpression { name, value } => {
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#attribute,
|
||||
#value,
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::CustomAttrText { name, value } => {
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#name,
|
||||
#value,
|
||||
None,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::CustomAttrExpression { name, value } => {
|
||||
quote! {
|
||||
__cx.attr(
|
||||
#name,
|
||||
#value,
|
||||
None,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::EventTokens { name, tokens } => {
|
||||
quote! {
|
||||
dioxus_elements::events::#name(__cx, #tokens)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(attribute);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user