mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
Merge pull request #895 from Demonthos/inline-custom-elements
Allow raw elements if they include a dash
This commit is contained in:
commit
7e96475951
5 changed files with 152 additions and 18 deletions
13
examples/web_component.rs
Normal file
13
examples/web_component.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
web-component {
|
||||
"my-prop": "5%",
|
||||
}
|
||||
})
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use dioxus_rsx::{
|
||||
BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, IfmtInput,
|
||||
BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, ElementName, IfmtInput,
|
||||
};
|
||||
pub use html_parser::{Dom, Node};
|
||||
use proc_macro2::{Ident, Span};
|
||||
|
@ -21,7 +21,7 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
|
|||
Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))),
|
||||
Node::Element(el) => {
|
||||
let el_name = el.name.to_case(Case::Snake);
|
||||
let el_name = Ident::new(el_name.as_str(), Span::call_site());
|
||||
let el_name = ElementName::Ident(Ident::new(el_name.as_str(), Span::call_site()));
|
||||
|
||||
let mut attributes: Vec<_> = el
|
||||
.attributes
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use super::*;
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::{Parse, ParseBuffer, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
Error, Expr, Ident, LitStr, Result, Token,
|
||||
};
|
||||
|
||||
|
@ -12,7 +16,7 @@ use syn::{
|
|||
// =======================================
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub struct Element {
|
||||
pub name: Ident,
|
||||
pub name: ElementName,
|
||||
pub key: Option<IfmtInput>,
|
||||
pub attributes: Vec<ElementAttrNamed>,
|
||||
pub children: Vec<BodyNode>,
|
||||
|
@ -22,7 +26,7 @@ pub struct Element {
|
|||
|
||||
impl Parse for Element {
|
||||
fn parse(stream: ParseStream) -> Result<Self> {
|
||||
let el_name = Ident::parse(stream)?;
|
||||
let el_name = ElementName::parse(stream)?;
|
||||
|
||||
// parse the guts
|
||||
let content: ParseBuffer;
|
||||
|
@ -181,7 +185,7 @@ impl ToTokens for Element {
|
|||
|
||||
tokens.append_all(quote! {
|
||||
__cx.element(
|
||||
dioxus_elements::#name,
|
||||
#name,
|
||||
__cx.bump().alloc([ #(#listeners),* ]),
|
||||
__cx.bump().alloc([ #(#attr),* ]),
|
||||
__cx.bump().alloc([ #(#children),* ]),
|
||||
|
@ -191,6 +195,75 @@ impl ToTokens for Element {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementName {
|
||||
Ident(Ident),
|
||||
Custom(LitStr),
|
||||
}
|
||||
|
||||
impl ElementName {
|
||||
pub(crate) fn tag_name(&self) -> TokenStream2 {
|
||||
match self {
|
||||
ElementName::Ident(i) => quote! { dioxus_elements::#i::TAG_NAME },
|
||||
ElementName::Custom(s) => quote! { #s },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementName {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
ElementName::Ident(i) => i.span(),
|
||||
ElementName::Custom(s) => s.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for ElementName {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
match self {
|
||||
ElementName::Ident(i) => i == *other,
|
||||
ElementName::Custom(s) => s.value() == *other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ElementName {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ElementName::Ident(i) => write!(f, "{}", i),
|
||||
ElementName::Custom(s) => write!(f, "{}", s.value()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ElementName {
|
||||
fn parse(stream: ParseStream) -> Result<Self> {
|
||||
let raw = Punctuated::<Ident, Token![-]>::parse_separated_nonempty(stream)?;
|
||||
if raw.len() == 1 {
|
||||
Ok(ElementName::Ident(raw.into_iter().next().unwrap()))
|
||||
} else {
|
||||
let span = raw.span();
|
||||
let tag = raw
|
||||
.into_iter()
|
||||
.map(|ident| ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("-");
|
||||
let tag = LitStr::new(&tag, span);
|
||||
Ok(ElementName::Custom(tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ElementName {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
ElementName::Ident(i) => tokens.append_all(quote! { dioxus_elements::#i }),
|
||||
ElementName::Custom(s) => tokens.append_all(quote! { #s }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub enum ElementAttr {
|
||||
/// `attribute: "value"`
|
||||
|
@ -234,7 +307,7 @@ impl ElementAttr {
|
|||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||
pub struct ElementAttrNamed {
|
||||
pub el_name: Ident,
|
||||
pub el_name: ElementName,
|
||||
pub attr: ElementAttr,
|
||||
}
|
||||
|
||||
|
@ -242,24 +315,46 @@ impl ToTokens for ElementAttrNamed {
|
|||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ElementAttrNamed { el_name, attr } = self;
|
||||
|
||||
tokens.append_all(match attr {
|
||||
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(
|
||||
dioxus_elements::#el_name::#name.0,
|
||||
#attribute,
|
||||
#value,
|
||||
dioxus_elements::#el_name::#name.1,
|
||||
dioxus_elements::#el_name::#name.2
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
ElementAttr::AttrExpression { name, value } => {
|
||||
let ns = ns(name);
|
||||
let volitile = volitile(name);
|
||||
let attribute = attribute(name);
|
||||
quote! {
|
||||
__cx.attr(
|
||||
dioxus_elements::#el_name::#name.0,
|
||||
#attribute,
|
||||
#value,
|
||||
dioxus_elements::#el_name::#name.1,
|
||||
dioxus_elements::#el_name::#name.2
|
||||
#ns,
|
||||
#volitile
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +383,9 @@ impl ToTokens for ElementAttrNamed {
|
|||
dioxus_elements::events::#name(__cx, #tokens)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
tokens.append_all(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -426,13 +426,25 @@ impl<'a> DynamicContext<'a> {
|
|||
match root {
|
||||
BodyNode::Element(el) => {
|
||||
let el_name = &el.name;
|
||||
let ns = |name| match el_name {
|
||||
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name },
|
||||
ElementName::Custom(_) => quote! { None },
|
||||
};
|
||||
let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
|
||||
ElementAttr::AttrText { name, value } if value.is_static() => {
|
||||
let value = value.to_static().unwrap();
|
||||
let ns = ns(quote!(#name.1));
|
||||
let name = match el_name {
|
||||
ElementName::Ident(_) => quote! { #el_name::#name.0 },
|
||||
ElementName::Custom(_) => {
|
||||
let as_string = name.to_string();
|
||||
quote! { #as_string }
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
::dioxus::core::TemplateAttribute::Static {
|
||||
name: dioxus_elements::#el_name::#name.0,
|
||||
namespace: dioxus_elements::#el_name::#name.1,
|
||||
name: #name,
|
||||
namespace: #ns,
|
||||
value: #value,
|
||||
|
||||
// todo: we don't diff these so we never apply the volatile flag
|
||||
|
@ -479,10 +491,13 @@ impl<'a> DynamicContext<'a> {
|
|||
let _opt = el.children.len() == 1;
|
||||
let children = quote! { #(#children),* };
|
||||
|
||||
let ns = ns(quote!(NAME_SPACE));
|
||||
let el_name = el_name.tag_name();
|
||||
|
||||
quote! {
|
||||
::dioxus::core::TemplateNode::Element {
|
||||
tag: dioxus_elements::#el_name::TAG_NAME,
|
||||
namespace: dioxus_elements::#el_name::NAME_SPACE,
|
||||
tag: #el_name,
|
||||
namespace: #ns,
|
||||
attrs: &[ #attrs ],
|
||||
children: &[ #children ],
|
||||
}
|
||||
|
|
|
@ -50,7 +50,16 @@ impl Parse for BodyNode {
|
|||
return Ok(BodyNode::Text(stream.parse()?));
|
||||
}
|
||||
|
||||
// if this is a dash-separated path, it's a web component (custom element)
|
||||
let body_stream = stream.fork();
|
||||
if let Ok(ElementName::Custom(name)) = body_stream.parse::<ElementName>() {
|
||||
if name.value().contains('-') && body_stream.peek(token::Brace) {
|
||||
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
||||
}
|
||||
}
|
||||
|
||||
let body_stream = stream.fork();
|
||||
|
||||
if let Ok(path) = body_stream.parse::<syn::Path>() {
|
||||
// this is an Element if path match of:
|
||||
// - one ident
|
||||
|
|
Loading…
Add table
Reference in a new issue