WIP hot reload
This commit is contained in:
1 parent
284860f470
commit
c7c84da8ab
19 files changed
+2531
-693
No files matched your search
+5
-1
@@ -28,6 +28,7 @@ dioxus-tui = { path = "./packages/tui", version = "^0.2.0", optional = true }
|
|||||||
|
|
||||||
dioxus-liveview = { path = "./packages/liveview", optional = true }
|
dioxus-liveview = { path = "./packages/liveview", optional = true }
|
||||||
dioxus-rsx = { path = "./packages/rsx", optional = true }
|
dioxus-rsx = { path = "./packages/rsx", optional = true }
|
||||||
|
dioxus-rsx-interperter = { path = "./packages/rsx_interperter", optional = true }
|
||||||
dioxus-autofmt = { path = "./packages/autofmt", optional = true }
|
dioxus-autofmt = { path = "./packages/autofmt", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@@ -44,6 +45,7 @@ router = ["dioxus-router"]
|
|||||||
tui = ["dioxus-tui"]
|
tui = ["dioxus-tui"]
|
||||||
liveview = ["dioxus-liveview"]
|
liveview = ["dioxus-liveview"]
|
||||||
autofmt = ["dioxus-autofmt"]
|
autofmt = ["dioxus-autofmt"]
|
||||||
|
hot_reload = ["dioxus-core-macro/hot_reload"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
@@ -61,6 +63,7 @@ members = [
|
|||||||
"packages/liveview",
|
"packages/liveview",
|
||||||
"packages/rsx",
|
"packages/rsx",
|
||||||
"packages/autofmt",
|
"packages/autofmt",
|
||||||
|
"packages/rsx_interperter",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -75,7 +78,8 @@ serde_json = "1.0.79"
|
|||||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||||
tokio = { version = "1.16.1", features = ["full"] }
|
tokio = { version = "1.16.1", features = ["full"] }
|
||||||
reqwest = { version = "0.11.9", features = ["json"] }
|
reqwest = { version = "0.11.9", features = ["json"] }
|
||||||
dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] }
|
# dioxus = { path = ".", features = ["desktop", "ssr", "router", "fermi", "tui"] }
|
||||||
|
dioxus = { path = ".", features = ["desktop"] }
|
||||||
fern = { version = "0.6.0", features = ["colored"] }
|
fern = { version = "0.6.0", features = ["colored"] }
|
||||||
criterion = "0.3.5"
|
criterion = "0.3.5"
|
||||||
thiserror = "1.0.30"
|
thiserror = "1.0.30"
|
||||||
|
|||||||
@@ -20,8 +20,12 @@ proc-macro2 = { version = "1.0.6" }
|
|||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "1.0.11", features = ["full", "extra-traits"] }
|
syn = { version = "1.0.11", features = ["full", "extra-traits"] }
|
||||||
dioxus-rsx = { path = "../rsx" }
|
dioxus-rsx = { path = "../rsx" }
|
||||||
|
dioxus-rsx-interperter = { path = "../rsx_interperter", optional = true }
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hot_reload = ["dioxus-rsx-interperter"]
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
use rsx::{BodyNode, Component, ContentField, ElementAttr, IfmtInput};
|
||||||
|
use std::collections::HashMap;
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
mod ifmt;
|
|
||||||
mod inlineprops;
|
mod inlineprops;
|
||||||
mod props;
|
mod props;
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ use dioxus_rsx as rsx;
|
|||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn format_args_f(input: TokenStream) -> TokenStream {
|
pub fn format_args_f(input: TokenStream) -> TokenStream {
|
||||||
use ifmt::*;
|
use rsx::*;
|
||||||
let item = parse_macro_input!(input as IfmtInput);
|
let item = parse_macro_input!(input as IfmtInput);
|
||||||
format_args_f_impl(item)
|
format_args_f_impl(item)
|
||||||
.unwrap_or_else(|err| err.to_compile_error())
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
@@ -183,7 +184,17 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||||||
pub fn rsx(s: TokenStream) -> TokenStream {
|
pub fn rsx(s: TokenStream) -> TokenStream {
|
||||||
match syn::parse::<rsx::CallBody>(s) {
|
match syn::parse::<rsx::CallBody>(s) {
|
||||||
Err(err) => err.to_compile_error().into(),
|
Err(err) => err.to_compile_error().into(),
|
||||||
Ok(stream) => stream.to_token_stream().into(),
|
Ok(body) => {
|
||||||
|
#[cfg(feature = "hot_reload")]
|
||||||
|
{
|
||||||
|
use dioxus_rsx_interperter::captuered_context::CapturedContextBuilder;
|
||||||
|
|
||||||
|
let captured = CapturedContextBuilder::from_call_body(body);
|
||||||
|
captured.to_token_stream().into()
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "hot_reload"))]
|
||||||
|
body.to_token_stream().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
//! Parse components into the VComponent VNode
|
|
||||||
//! ==========================================
|
|
||||||
//!
|
|
||||||
//! This parsing path emerges from [`AmbiguousElement`] which supports validation of the vcomponent format.
|
|
||||||
//! We can be reasonably sure that whatever enters this parsing path is in the right format.
|
|
||||||
//! This feature must support
|
|
||||||
//! - [x] Namespaced components
|
|
||||||
//! - [x] Fields
|
|
||||||
//! - [x] Componentbuilder synax
|
|
||||||
//! - [x] Optional commas
|
|
||||||
//! - [ ] Children
|
|
||||||
//! - [ ] Keys
|
|
||||||
//! - [ ] Properties spreading with with `..` syntax
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
|
||||||
use syn::{
|
|
||||||
ext::IdentExt,
|
|
||||||
parse::{Parse, ParseBuffer, ParseStream},
|
|
||||||
token, Expr, Ident, LitStr, Result, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Component {
|
|
||||||
pub name: syn::Path,
|
|
||||||
pub body: Vec<ComponentField>,
|
|
||||||
pub children: Vec<BodyNode>,
|
|
||||||
pub manual_props: Option<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Component {
|
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
|
||||||
let name = syn::Path::parse_mod_style(stream)?;
|
|
||||||
|
|
||||||
let content: ParseBuffer;
|
|
||||||
|
|
||||||
// if we see a `{` then we have a block
|
|
||||||
// else parse as a function-like call
|
|
||||||
if stream.peek(token::Brace) {
|
|
||||||
syn::braced!(content in stream);
|
|
||||||
} else {
|
|
||||||
syn::parenthesized!(content in stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut body = Vec::new();
|
|
||||||
let mut children = Vec::new();
|
|
||||||
let mut manual_props = None;
|
|
||||||
|
|
||||||
while !content.is_empty() {
|
|
||||||
// if we splat into a component then we're merging properties
|
|
||||||
if content.peek(Token![..]) {
|
|
||||||
content.parse::<Token![..]>()?;
|
|
||||||
manual_props = Some(content.parse::<Expr>()?);
|
|
||||||
} else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
|
|
||||||
body.push(content.parse::<ComponentField>()?);
|
|
||||||
} else {
|
|
||||||
children.push(content.parse::<BodyNode>()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.peek(Token![,]) {
|
|
||||||
let _ = content.parse::<Token![,]>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
name,
|
|
||||||
body,
|
|
||||||
children,
|
|
||||||
manual_props,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for Component {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let name = &self.name;
|
|
||||||
|
|
||||||
let mut has_key = None;
|
|
||||||
|
|
||||||
let builder = match &self.manual_props {
|
|
||||||
Some(manual_props) => {
|
|
||||||
let mut toks = quote! {
|
|
||||||
let mut __manual_props = #manual_props;
|
|
||||||
};
|
|
||||||
for field in &self.body {
|
|
||||||
if field.name == "key" {
|
|
||||||
has_key = Some(field);
|
|
||||||
} else {
|
|
||||||
let name = &field.name;
|
|
||||||
let val = &field.content;
|
|
||||||
toks.append_all(quote! {
|
|
||||||
__manual_props.#name = #val;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toks.append_all(quote! {
|
|
||||||
__manual_props
|
|
||||||
});
|
|
||||||
quote! {{
|
|
||||||
#toks
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut toks = quote! { fc_to_builder(#name) };
|
|
||||||
for field in &self.body {
|
|
||||||
match field.name.to_string().as_str() {
|
|
||||||
"key" => {
|
|
||||||
//
|
|
||||||
has_key = Some(field);
|
|
||||||
}
|
|
||||||
_ => toks.append_all(quote! {#field}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.children.is_empty() {
|
|
||||||
let childs = &self.children;
|
|
||||||
toks.append_all(quote! {
|
|
||||||
.children(__cx.create_children([ #( #childs ),* ]))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toks.append_all(quote! {
|
|
||||||
.build()
|
|
||||||
});
|
|
||||||
toks
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let key_token = match has_key {
|
|
||||||
Some(field) => {
|
|
||||||
let inners = &field.content;
|
|
||||||
quote! { Some(format_args_f!(#inners)) }
|
|
||||||
}
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
|
|
||||||
let fn_name = self.name.segments.last().unwrap().ident.to_string();
|
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
__cx.component(
|
|
||||||
#name,
|
|
||||||
#builder,
|
|
||||||
#key_token,
|
|
||||||
#fn_name
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the struct's fields info
|
|
||||||
pub struct ComponentField {
|
|
||||||
name: Ident,
|
|
||||||
content: ContentField,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ContentField {
|
|
||||||
ManExpr(Expr),
|
|
||||||
Formatted(LitStr),
|
|
||||||
OnHandlerRaw(Expr),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for ContentField {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
match self {
|
|
||||||
ContentField::ManExpr(e) => e.to_tokens(tokens),
|
|
||||||
ContentField::Formatted(s) => tokens.append_all(quote! {
|
|
||||||
__cx.raw_text(format_args_f!(#s)).0
|
|
||||||
}),
|
|
||||||
ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
|
|
||||||
__cx.event_handler(#e)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for ComponentField {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let name = Ident::parse_any(input)?;
|
|
||||||
input.parse::<Token![:]>()?;
|
|
||||||
|
|
||||||
if name.to_string().starts_with("on") {
|
|
||||||
let content = ContentField::OnHandlerRaw(input.parse()?);
|
|
||||||
return Ok(Self { name, content });
|
|
||||||
}
|
|
||||||
|
|
||||||
if name == "key" {
|
|
||||||
let content = ContentField::ManExpr(input.parse()?);
|
|
||||||
return Ok(Self { name, content });
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.peek(LitStr) && input.peek2(Token![,]) {
|
|
||||||
let t: LitStr = input.fork().parse()?;
|
|
||||||
|
|
||||||
if is_literal_foramtted(&t) {
|
|
||||||
let content = ContentField::Formatted(input.parse()?);
|
|
||||||
return Ok(Self { name, content });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.peek(LitStr) && input.peek2(LitStr) {
|
|
||||||
missing_trailing_comma!(input.span());
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = ContentField::ManExpr(input.parse()?);
|
|
||||||
Ok(Self { name, content })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for ComponentField {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let ComponentField { name, content, .. } = self;
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
.#name(#content)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_literal_foramtted(lit: &LitStr) -> bool {
|
|
||||||
let s = lit.value();
|
|
||||||
let mut chars = s.chars();
|
|
||||||
|
|
||||||
while let Some(next) = chars.next() {
|
|
||||||
if next == '{' {
|
|
||||||
let nen = chars.next();
|
|
||||||
if nen != Some('{') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseBuffer, ParseStream},
|
|
||||||
Expr, Ident, LitStr, Result, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
// =======================================
|
|
||||||
// Parse the VNode::Element type
|
|
||||||
// =======================================
|
|
||||||
pub struct Element {
|
|
||||||
pub name: Ident,
|
|
||||||
pub key: Option<LitStr>,
|
|
||||||
pub attributes: Vec<ElementAttrNamed>,
|
|
||||||
pub children: Vec<BodyNode>,
|
|
||||||
pub _is_static: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Element {
|
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
|
||||||
let el_name = Ident::parse(stream)?;
|
|
||||||
|
|
||||||
// parse the guts
|
|
||||||
let content: ParseBuffer;
|
|
||||||
syn::braced!(content in stream);
|
|
||||||
|
|
||||||
let mut attributes: Vec<ElementAttrNamed> = vec![];
|
|
||||||
let mut children: Vec<BodyNode> = vec![];
|
|
||||||
let mut key = None;
|
|
||||||
let mut _el_ref = None;
|
|
||||||
|
|
||||||
// parse fields with commas
|
|
||||||
// break when we don't get this pattern anymore
|
|
||||||
// start parsing bodynodes
|
|
||||||
// "def": 456,
|
|
||||||
// abc: 123,
|
|
||||||
loop {
|
|
||||||
// Parse the raw literal fields
|
|
||||||
if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
|
|
||||||
let name = content.parse::<LitStr>()?;
|
|
||||||
let ident = name.clone();
|
|
||||||
|
|
||||||
content.parse::<Token![:]>()?;
|
|
||||||
|
|
||||||
if content.peek(LitStr) && content.peek2(Token![,]) {
|
|
||||||
let value = content.parse::<LitStr>()?;
|
|
||||||
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 },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.parse::<Token![,]>().is_err() {
|
|
||||||
missing_trailing_comma!(ident);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
|
|
||||||
let name = content.parse::<Ident>()?;
|
|
||||||
let ident = name.clone();
|
|
||||||
|
|
||||||
let name_str = name.to_string();
|
|
||||||
content.parse::<Token![:]>()?;
|
|
||||||
|
|
||||||
if name_str.starts_with("on") {
|
|
||||||
attributes.push(ElementAttrNamed {
|
|
||||||
el_name: el_name.clone(),
|
|
||||||
attr: ElementAttr::EventTokens {
|
|
||||||
name,
|
|
||||||
tokens: content.parse()?,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
match name_str.as_str() {
|
|
||||||
"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()?,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: add a message saying you need to include commas between fields
|
|
||||||
if content.parse::<Token![,]>().is_err() {
|
|
||||||
missing_trailing_comma!(ident);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while !content.is_empty() {
|
|
||||||
if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
|
|
||||||
attr_after_element!(content.span());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.peek(Ident) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
|
|
||||||
attr_after_element!(content.span());
|
|
||||||
}
|
|
||||||
|
|
||||||
children.push(content.parse::<BodyNode>()?);
|
|
||||||
// consume comma if it exists
|
|
||||||
// we don't actually care if there *are* commas after elements/text
|
|
||||||
if content.peek(Token![,]) {
|
|
||||||
let _ = content.parse::<Token![,]>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
key,
|
|
||||||
name: el_name,
|
|
||||||
attributes,
|
|
||||||
children,
|
|
||||||
_is_static: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for Element {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let name = &self.name;
|
|
||||||
let children = &self.children;
|
|
||||||
|
|
||||||
let key = match &self.key {
|
|
||||||
Some(ty) => quote! { Some(format_args_f!(#ty)) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
|
|
||||||
let listeners = self
|
|
||||||
.attributes
|
|
||||||
.iter()
|
|
||||||
.filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. }));
|
|
||||||
|
|
||||||
let attr = self
|
|
||||||
.attributes
|
|
||||||
.iter()
|
|
||||||
.filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. }));
|
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
__cx.element(
|
|
||||||
dioxus_elements::#name,
|
|
||||||
__cx.bump().alloc([ #(#listeners),* ]),
|
|
||||||
__cx.bump().alloc([ #(#attr),* ]),
|
|
||||||
__cx.bump().alloc([ #(#children),* ]),
|
|
||||||
#key,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ElementAttr {
|
|
||||||
/// attribute: "valuee {}"
|
|
||||||
AttrText { name: Ident, value: LitStr },
|
|
||||||
|
|
||||||
/// attribute: true,
|
|
||||||
AttrExpression { name: Ident, value: Expr },
|
|
||||||
|
|
||||||
/// "attribute": "value {}"
|
|
||||||
CustomAttrText { name: LitStr, value: LitStr },
|
|
||||||
|
|
||||||
/// "attribute": true,
|
|
||||||
CustomAttrExpression { name: LitStr, value: Expr },
|
|
||||||
|
|
||||||
// /// onclick: move |_| {}
|
|
||||||
// EventClosure { name: Ident, closure: ExprClosure },
|
|
||||||
/// onclick: {}
|
|
||||||
EventTokens { name: Ident, tokens: Expr },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ElementAttrNamed {
|
|
||||||
pub el_name: Ident,
|
|
||||||
pub attr: ElementAttr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for ElementAttrNamed {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let ElementAttrNamed { el_name, attr } = self;
|
|
||||||
|
|
||||||
tokens.append_all(match attr {
|
|
||||||
ElementAttr::AttrText { name, value } => {
|
|
||||||
quote! {
|
|
||||||
dioxus_elements::#el_name.#name(__cx, format_args_f!(#value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
|
||||||
quote! {
|
|
||||||
dioxus_elements::#el_name.#name(__cx, #value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
|
||||||
quote! {
|
|
||||||
__cx.attr( #name, format_args_f!(#value), None, false )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
|
||||||
quote! {
|
|
||||||
__cx.attr( #name, format_args_f!(#value), None, false )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ElementAttr::EventClosure { name, closure } => {
|
|
||||||
// quote! {
|
|
||||||
// dioxus_elements::on::#name(__cx, #closure)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
ElementAttr::EventTokens { name, tokens } => {
|
|
||||||
quote! {
|
|
||||||
dioxus_elements::on::#name(__cx, #tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
macro_rules! missing_trailing_comma {
|
|
||||||
($span:expr) => {
|
|
||||||
proc_macro_error::emit_error!($span, "missing trailing comma")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! attr_after_element {
|
|
||||||
($span:expr) => {
|
|
||||||
proc_macro_error::emit_error!(
|
|
||||||
$span,
|
|
||||||
"expected element";
|
|
||||||
help = "move the attribute above all the children and text elements"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
//! Parse the root tokens in the rsx!{} macro
|
|
||||||
//! =========================================
|
|
||||||
//!
|
|
||||||
//! This parsing path emerges directly from the macro call, with `RsxRender` being the primary entrance into parsing.
|
|
||||||
//! This feature must support:
|
|
||||||
//! - [x] Optionally rendering if the `in XYZ` pattern is present
|
|
||||||
//! - [x] Fragments as top-level element (through ambiguous)
|
|
||||||
//! - [x] Components as top-level element (through ambiguous)
|
|
||||||
//! - [x] Tags as top-level elements (through ambiguous)
|
|
||||||
//! - [x] Good errors if parsing fails
|
|
||||||
//!
|
|
||||||
//! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod errors;
|
|
||||||
|
|
||||||
mod component;
|
|
||||||
mod element;
|
|
||||||
mod node;
|
|
||||||
|
|
||||||
pub mod pretty;
|
|
||||||
|
|
||||||
// Re-export the namespaces into each other
|
|
||||||
pub use component::*;
|
|
||||||
pub use element::*;
|
|
||||||
pub use node::*;
|
|
||||||
|
|
||||||
// imports
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
Ident, Result, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct CallBody {
|
|
||||||
pub custom_context: Option<Ident>,
|
|
||||||
pub roots: Vec<BodyNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for CallBody {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let custom_context = if input.peek(Ident) && input.peek2(Token![,]) {
|
|
||||||
let name = input.parse::<Ident>()?;
|
|
||||||
input.parse::<Token![,]>()?;
|
|
||||||
|
|
||||||
Some(name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut roots = Vec::new();
|
|
||||||
|
|
||||||
while !input.is_empty() {
|
|
||||||
let node = input.parse::<BodyNode>()?;
|
|
||||||
|
|
||||||
if input.peek(Token![,]) {
|
|
||||||
let _ = input.parse::<Token![,]>();
|
|
||||||
}
|
|
||||||
|
|
||||||
roots.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
custom_context,
|
|
||||||
roots,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize the same way, regardless of flavor
|
|
||||||
impl ToTokens for CallBody {
|
|
||||||
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
|
|
||||||
let inner = if self.roots.len() == 1 {
|
|
||||||
let inner = &self.roots[0];
|
|
||||||
quote! { #inner }
|
|
||||||
} else {
|
|
||||||
let childs = &self.roots;
|
|
||||||
quote! { __cx.fragment_root([ #(#childs),* ]) }
|
|
||||||
};
|
|
||||||
|
|
||||||
match &self.custom_context {
|
|
||||||
// The `in cx` pattern allows directly rendering
|
|
||||||
Some(ident) => out_tokens.append_all(quote! {
|
|
||||||
#ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
|
|
||||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
|
||||||
#inner
|
|
||||||
}))
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Otherwise we just build the LazyNode wrapper
|
|
||||||
None => out_tokens.append_all(quote! {
|
|
||||||
LazyNodes::new(move |__cx: NodeFactory| -> VNode {
|
|
||||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
|
||||||
#inner
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
token, Expr, LitStr, Result, Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Parse
|
|
||||||
-> div {}
|
|
||||||
-> Component {}
|
|
||||||
-> component()
|
|
||||||
-> "text {with_args}"
|
|
||||||
-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
|
|
||||||
*/
|
|
||||||
pub enum BodyNode {
|
|
||||||
Element(Element),
|
|
||||||
Component(Component),
|
|
||||||
Text(LitStr),
|
|
||||||
RawExpr(Expr),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for BodyNode {
|
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
|
||||||
if stream.peek(LitStr) {
|
|
||||||
return Ok(BodyNode::Text(stream.parse()?));
|
|
||||||
}
|
|
||||||
|
|
||||||
// div {} -> el
|
|
||||||
// Div {} -> comp
|
|
||||||
if stream.peek(syn::Ident) && stream.peek2(token::Brace) {
|
|
||||||
if stream
|
|
||||||
.fork()
|
|
||||||
.parse::<Ident>()?
|
|
||||||
.to_string()
|
|
||||||
.chars()
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.is_ascii_uppercase()
|
|
||||||
{
|
|
||||||
return Ok(BodyNode::Component(stream.parse()?));
|
|
||||||
} else {
|
|
||||||
return Ok(BodyNode::Element(stream.parse::<Element>()?));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// component() -> comp
|
|
||||||
// ::component {} -> comp
|
|
||||||
// ::component () -> comp
|
|
||||||
if (stream.peek(syn::Ident) && stream.peek2(token::Paren))
|
|
||||||
|| (stream.peek(Token![::]))
|
|
||||||
|| (stream.peek(Token![:]) && stream.peek2(Token![:]))
|
|
||||||
{
|
|
||||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
|
||||||
}
|
|
||||||
|
|
||||||
// crate::component{} -> comp
|
|
||||||
// crate::component() -> comp
|
|
||||||
if let Ok(pat) = stream.fork().parse::<syn::Path>() {
|
|
||||||
if pat.segments.len() > 1 {
|
|
||||||
return Ok(BodyNode::Component(stream.parse::<Component>()?));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for BodyNode {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
match &self {
|
|
||||||
BodyNode::Element(el) => el.to_tokens(tokens),
|
|
||||||
BodyNode::Component(comp) => comp.to_tokens(tokens),
|
|
||||||
BodyNode::Text(txt) => tokens.append_all(quote! {
|
|
||||||
__cx.text(format_args_f!(#txt))
|
|
||||||
}),
|
|
||||||
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
|
||||||
__cx.fragment_from_iter(#exp)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//! pretty printer for rsx!
|
|
||||||
@@ -85,6 +85,8 @@ pub mod prelude {
|
|||||||
fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,
|
fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,
|
||||||
LazyNodes, NodeFactory, Properties, Scope, ScopeId, ScopeState, VNode, VirtualDom,
|
LazyNodes, NodeFactory, Properties, Scope, ScopeId, ScopeState, VNode, VirtualDom,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "hot_reload")]
|
||||||
|
pub use dioxus_rsx_interperter::captuered_context::{CapturedContext, IfmtArgs};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod exports {
|
pub mod exports {
|
||||||
|
|||||||
@@ -140,9 +140,9 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
|
|||||||
|
|
||||||
#[allow(dead_code)] // dumb compiler does not see the struct being used...
|
#[allow(dead_code)] // dumb compiler does not see the struct being used...
|
||||||
pub struct IfmtInput {
|
pub struct IfmtInput {
|
||||||
format_literal: LitStr,
|
pub format_literal: LitStr,
|
||||||
positional_args: Vec<Expr>,
|
pub positional_args: Vec<Expr>,
|
||||||
named_args: Vec<(Ident, Expr)>,
|
pub named_args: Vec<(Ident, Expr)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for IfmtInput {
|
impl Parse for IfmtInput {
|
||||||
@@ -16,11 +16,13 @@ mod errors;
|
|||||||
|
|
||||||
mod component;
|
mod component;
|
||||||
mod element;
|
mod element;
|
||||||
|
mod ifmt;
|
||||||
mod node;
|
mod node;
|
||||||
|
|
||||||
// Re-export the namespaces into each other
|
// Re-export the namespaces into each other
|
||||||
pub use component::*;
|
pub use component::*;
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
|
pub use ifmt::*;
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
|
||||||
// imports
|
// imports
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "dioxus-rsx-interperter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
syn = "1.0"
|
||||||
|
quote = "1.0"
|
||||||
|
dioxus-rsx = { path = "../rsx" }
|
||||||
|
dioxus-ssr = { path = "../ssr" }
|
||||||
|
dioxus-core = { path = "../core" }
|
||||||
|
dioxus-html = { path = "../html" }
|
||||||
@@ -0,0 +1,869 @@
|
|||||||
|
use crate::elements::*;
|
||||||
|
|
||||||
|
// find the mapped attribute name
|
||||||
|
pub fn attrbute_to_static_str(attr: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||||
|
NO_NAMESPACE_ATTRIBUTES
|
||||||
|
.iter()
|
||||||
|
.find(|&a| *a == attr)
|
||||||
|
.map(|a| (*a, None))
|
||||||
|
.or_else(|| {
|
||||||
|
STYLE_ATTRIBUTES
|
||||||
|
.iter()
|
||||||
|
.find(|(a, _)| *a == attr)
|
||||||
|
.map(|(_, b)| (*b, Some("style")))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
MAPPED_ATTRIBUTES
|
||||||
|
.iter()
|
||||||
|
.find(|(a, _)| *a == attr)
|
||||||
|
.map(|(_, b)| (*b, None))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
svg::MAPPED_ATTRIBUTES
|
||||||
|
.iter()
|
||||||
|
.find(|(a, _)| *a == attr)
|
||||||
|
.map(|(_, b)| (*b, None))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
ELEMENTS_WITH_MAPPED_ATTRIBUTES
|
||||||
|
.iter()
|
||||||
|
.find_map(|(_, attrs)| {
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.find(|(a, _)| *a == attr)
|
||||||
|
.map(|(_, b)| (*b, None))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
ELEMENTS_WITH_NAMESPACE
|
||||||
|
.iter()
|
||||||
|
.find_map(|(_, _, attrs)| attrs.iter().find(|a| **a == attr).map(|a| (*a, None)))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
ELEMENTS_WITHOUT_NAMESPACE
|
||||||
|
.iter()
|
||||||
|
.find_map(|(_, attrs)| attrs.iter().find(|a| **a == attr).map(|a| (*a, None)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! no_namespace_trait_methods {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident;
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
pub const NO_NAMESPACE_ATTRIBUTES: &'static [&'static str] = &[
|
||||||
|
$(
|
||||||
|
stringify!($name),
|
||||||
|
)*
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! style_trait_methods {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident: $lit:literal,
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
pub const STYLE_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[
|
||||||
|
$(
|
||||||
|
(stringify!($name), $lit),
|
||||||
|
)*
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! mapped_trait_methods {
|
||||||
|
(
|
||||||
|
$(
|
||||||
|
$(#[$attr:meta])*
|
||||||
|
$name:ident: $lit:literal,
|
||||||
|
)*
|
||||||
|
) => {
|
||||||
|
pub const MAPPED_ATTRIBUTES: &'static [(&'static str, &'static str)] = &[
|
||||||
|
$(
|
||||||
|
(stringify!($name), $lit),
|
||||||
|
)*
|
||||||
|
("prevent_default", "dioxus-prevent-default"),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
no_namespace_trait_methods! {
|
||||||
|
accesskey;
|
||||||
|
|
||||||
|
/// The HTML class attribute is used to specify a class for an HTML element.
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
/// Multiple HTML elements can share the same class.
|
||||||
|
///
|
||||||
|
/// The class global attribute is a space-separated list of the case-sensitive classes of the element.
|
||||||
|
/// Classes allow CSS and Javascript to select and access specific elements via the class selectors or
|
||||||
|
/// functions like the DOM method document.getElementsByClassName.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ### HTML:
|
||||||
|
/// ```html
|
||||||
|
/// <p class="note editorial">Above point sounds a bit obvious. Remove/rewrite?</p>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### CSS:
|
||||||
|
/// ```css
|
||||||
|
/// .note {
|
||||||
|
/// font-style: italic;
|
||||||
|
/// font-weight: bold;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// .editorial {
|
||||||
|
/// background: rgb(255, 0, 0, .25);
|
||||||
|
/// padding: 10px;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
class;
|
||||||
|
contenteditable;
|
||||||
|
data;
|
||||||
|
dir;
|
||||||
|
draggable;
|
||||||
|
hidden;
|
||||||
|
id;
|
||||||
|
lang;
|
||||||
|
spellcheck;
|
||||||
|
style;
|
||||||
|
tabindex;
|
||||||
|
title;
|
||||||
|
translate;
|
||||||
|
|
||||||
|
role;
|
||||||
|
|
||||||
|
/// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting
|
||||||
|
/// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS)
|
||||||
|
/// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind
|
||||||
|
/// yourself that it’s dangerous
|
||||||
|
dangerous_inner_html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This macro creates an explicit method call for each of the style attributes.
|
||||||
|
//
|
||||||
|
// The left token specifies the name of the attribute in the rsx! macro, and the right string literal specifies the
|
||||||
|
// actual name of the attribute generated.
|
||||||
|
//
|
||||||
|
// This roughly follows the html spec
|
||||||
|
style_trait_methods! {
|
||||||
|
align_content: "align-content",
|
||||||
|
align_items: "align-items",
|
||||||
|
align_self: "align-self",
|
||||||
|
alignment_adjust: "alignment-adjust",
|
||||||
|
alignment_baseline: "alignment-baseline",
|
||||||
|
all: "all",
|
||||||
|
alt: "alt",
|
||||||
|
animation: "animation",
|
||||||
|
animation_delay: "animation-delay",
|
||||||
|
animation_direction: "animation-direction",
|
||||||
|
animation_duration: "animation-duration",
|
||||||
|
animation_fill_mode: "animation-fill-mode",
|
||||||
|
animation_iteration_count: "animation-iteration-count",
|
||||||
|
animation_name: "animation-name",
|
||||||
|
animation_play_state: "animation-play-state",
|
||||||
|
animation_timing_function: "animation-timing-function",
|
||||||
|
azimuth: "azimuth",
|
||||||
|
backface_visibility: "backface-visibility",
|
||||||
|
background: "background",
|
||||||
|
background_attachment: "background-attachment",
|
||||||
|
background_clip: "background-clip",
|
||||||
|
background_color: "background-color",
|
||||||
|
background_image: "background-image",
|
||||||
|
background_origin: "background-origin",
|
||||||
|
background_position: "background-position",
|
||||||
|
background_repeat: "background-repeat",
|
||||||
|
background_size: "background-size",
|
||||||
|
background_blend_mode: "background-blend-mode",
|
||||||
|
baseline_shift: "baseline-shift",
|
||||||
|
bleed: "bleed",
|
||||||
|
bookmark_label: "bookmark-label",
|
||||||
|
bookmark_level: "bookmark-level",
|
||||||
|
bookmark_state: "bookmark-state",
|
||||||
|
border: "border",
|
||||||
|
border_color: "border-color",
|
||||||
|
border_style: "border-style",
|
||||||
|
border_width: "border-width",
|
||||||
|
border_bottom: "border-bottom",
|
||||||
|
border_bottom_color: "border-bottom-color",
|
||||||
|
border_bottom_style: "border-bottom-style",
|
||||||
|
border_bottom_width: "border-bottom-width",
|
||||||
|
border_left: "border-left",
|
||||||
|
border_left_color: "border-left-color",
|
||||||
|
border_left_style: "border-left-style",
|
||||||
|
border_left_width: "border-left-width",
|
||||||
|
border_right: "border-right",
|
||||||
|
border_right_color: "border-right-color",
|
||||||
|
border_right_style: "border-right-style",
|
||||||
|
border_right_width: "border-right-width",
|
||||||
|
border_top: "border-top",
|
||||||
|
border_top_color: "border-top-color",
|
||||||
|
border_top_style: "border-top-style",
|
||||||
|
border_top_width: "border-top-width",
|
||||||
|
border_collapse: "border-collapse",
|
||||||
|
border_image: "border-image",
|
||||||
|
border_image_outset: "border-image-outset",
|
||||||
|
border_image_repeat: "border-image-repeat",
|
||||||
|
border_image_slice: "border-image-slice",
|
||||||
|
border_image_source: "border-image-source",
|
||||||
|
border_image_width: "border-image-width",
|
||||||
|
border_radius: "border-radius",
|
||||||
|
border_bottom_left_radius: "border-bottom-left-radius",
|
||||||
|
border_bottom_right_radius: "border-bottom-right-radius",
|
||||||
|
border_top_left_radius: "border-top-left-radius",
|
||||||
|
border_top_right_radius: "border-top-right-radius",
|
||||||
|
border_spacing: "border-spacing",
|
||||||
|
bottom: "bottom",
|
||||||
|
box_decoration_break: "box-decoration-break",
|
||||||
|
box_shadow: "box-shadow",
|
||||||
|
box_sizing: "box-sizing",
|
||||||
|
box_snap: "box-snap",
|
||||||
|
break_after: "break-after",
|
||||||
|
break_before: "break-before",
|
||||||
|
break_inside: "break-inside",
|
||||||
|
buffered_rendering: "buffered-rendering",
|
||||||
|
caption_side: "caption-side",
|
||||||
|
clear: "clear",
|
||||||
|
clear_side: "clear-side",
|
||||||
|
clip: "clip",
|
||||||
|
clip_path: "clip-path",
|
||||||
|
clip_rule: "clip-rule",
|
||||||
|
color: "color",
|
||||||
|
color_adjust: "color-adjust",
|
||||||
|
color_correction: "color-correction",
|
||||||
|
color_interpolation: "color-interpolation",
|
||||||
|
color_interpolation_filters: "color-interpolation-filters",
|
||||||
|
color_profile: "color-profile",
|
||||||
|
color_rendering: "color-rendering",
|
||||||
|
column_fill: "column-fill",
|
||||||
|
column_gap: "column-gap",
|
||||||
|
column_rule: "column-rule",
|
||||||
|
column_rule_color: "column-rule-color",
|
||||||
|
column_rule_style: "column-rule-style",
|
||||||
|
column_rule_width: "column-rule-width",
|
||||||
|
column_span: "column-span",
|
||||||
|
columns: "columns",
|
||||||
|
column_count: "column-count",
|
||||||
|
column_width: "column-width",
|
||||||
|
contain: "contain",
|
||||||
|
content: "content",
|
||||||
|
counter_increment: "counter-increment",
|
||||||
|
counter_reset: "counter-reset",
|
||||||
|
counter_set: "counter-set",
|
||||||
|
cue: "cue",
|
||||||
|
cue_after: "cue-after",
|
||||||
|
cue_before: "cue-before",
|
||||||
|
cursor: "cursor",
|
||||||
|
direction: "direction",
|
||||||
|
display: "display",
|
||||||
|
display_inside: "display-inside",
|
||||||
|
display_outside: "display-outside",
|
||||||
|
display_extras: "display-extras",
|
||||||
|
display_box: "display-box",
|
||||||
|
dominant_baseline: "dominant-baseline",
|
||||||
|
elevation: "elevation",
|
||||||
|
empty_cells: "empty-cells",
|
||||||
|
enable_background: "enable-background",
|
||||||
|
fill: "fill",
|
||||||
|
fill_opacity: "fill-opacity",
|
||||||
|
fill_rule: "fill-rule",
|
||||||
|
filter: "filter",
|
||||||
|
float: "float",
|
||||||
|
float_defer_column: "float-defer-column",
|
||||||
|
float_defer_page: "float-defer-page",
|
||||||
|
float_offset: "float-offset",
|
||||||
|
float_wrap: "float-wrap",
|
||||||
|
flow_into: "flow-into",
|
||||||
|
flow_from: "flow-from",
|
||||||
|
flex: "flex",
|
||||||
|
flex_basis: "flex-basis",
|
||||||
|
flex_grow: "flex-grow",
|
||||||
|
flex_shrink: "flex-shrink",
|
||||||
|
flex_flow: "flex-flow",
|
||||||
|
flex_direction: "flex-direction",
|
||||||
|
flex_wrap: "flex-wrap",
|
||||||
|
flood_color: "flood-color",
|
||||||
|
flood_opacity: "flood-opacity",
|
||||||
|
font: "font",
|
||||||
|
font_family: "font-family",
|
||||||
|
font_size: "font-size",
|
||||||
|
font_stretch: "font-stretch",
|
||||||
|
font_style: "font-style",
|
||||||
|
font_weight: "font-weight",
|
||||||
|
font_feature_settings: "font-feature-settings",
|
||||||
|
font_kerning: "font-kerning",
|
||||||
|
font_language_override: "font-language-override",
|
||||||
|
font_size_adjust: "font-size-adjust",
|
||||||
|
font_synthesis: "font-synthesis",
|
||||||
|
font_variant: "font-variant",
|
||||||
|
font_variant_alternates: "font-variant-alternates",
|
||||||
|
font_variant_caps: "font-variant-caps",
|
||||||
|
font_variant_east_asian: "font-variant-east-asian",
|
||||||
|
font_variant_ligatures: "font-variant-ligatures",
|
||||||
|
font_variant_numeric: "font-variant-numeric",
|
||||||
|
font_variant_position: "font-variant-position",
|
||||||
|
footnote_policy: "footnote-policy",
|
||||||
|
glyph_orientation_horizontal: "glyph-orientation-horizontal",
|
||||||
|
glyph_orientation_vertical: "glyph-orientation-vertical",
|
||||||
|
grid: "grid",
|
||||||
|
grid_auto_flow: "grid-auto-flow",
|
||||||
|
grid_auto_columns: "grid-auto-columns",
|
||||||
|
grid_auto_rows: "grid-auto-rows",
|
||||||
|
grid_template: "grid-template",
|
||||||
|
grid_template_areas: "grid-template-areas",
|
||||||
|
grid_template_columns: "grid-template-columns",
|
||||||
|
grid_template_rows: "grid-template-rows",
|
||||||
|
grid_area: "grid-area",
|
||||||
|
grid_column: "grid-column",
|
||||||
|
grid_column_start: "grid-column-start",
|
||||||
|
grid_column_end: "grid-column-end",
|
||||||
|
grid_row: "grid-row",
|
||||||
|
grid_row_start: "grid-row-start",
|
||||||
|
grid_row_end: "grid-row-end",
|
||||||
|
hanging_punctuation: "hanging-punctuation",
|
||||||
|
height: "height",
|
||||||
|
hyphenate_character: "hyphenate-character",
|
||||||
|
hyphenate_limit_chars: "hyphenate-limit-chars",
|
||||||
|
hyphenate_limit_last: "hyphenate-limit-last",
|
||||||
|
hyphenate_limit_lines: "hyphenate-limit-lines",
|
||||||
|
hyphenate_limit_zone: "hyphenate-limit-zone",
|
||||||
|
hyphens: "hyphens",
|
||||||
|
icon: "icon",
|
||||||
|
image_orientation: "image-orientation",
|
||||||
|
image_resolution: "image-resolution",
|
||||||
|
image_rendering: "image-rendering",
|
||||||
|
ime: "ime",
|
||||||
|
ime_align: "ime-align",
|
||||||
|
ime_mode: "ime-mode",
|
||||||
|
ime_offset: "ime-offset",
|
||||||
|
ime_width: "ime-width",
|
||||||
|
initial_letters: "initial-letters",
|
||||||
|
inline_box_align: "inline-box-align",
|
||||||
|
isolation: "isolation",
|
||||||
|
justify_content: "justify-content",
|
||||||
|
justify_items: "justify-items",
|
||||||
|
justify_self: "justify-self",
|
||||||
|
kerning: "kerning",
|
||||||
|
left: "left",
|
||||||
|
letter_spacing: "letter-spacing",
|
||||||
|
lighting_color: "lighting-color",
|
||||||
|
line_box_contain: "line-box-contain",
|
||||||
|
line_break: "line-break",
|
||||||
|
line_grid: "line-grid",
|
||||||
|
line_height: "line-height",
|
||||||
|
line_slack: "line-slack",
|
||||||
|
line_snap: "line-snap",
|
||||||
|
list_style: "list-style",
|
||||||
|
list_style_image: "list-style-image",
|
||||||
|
list_style_position: "list-style-position",
|
||||||
|
list_style_type: "list-style-type",
|
||||||
|
margin: "margin",
|
||||||
|
margin_bottom: "margin-bottom",
|
||||||
|
margin_left: "margin-left",
|
||||||
|
margin_right: "margin-right",
|
||||||
|
margin_top: "margin-top",
|
||||||
|
marker: "marker",
|
||||||
|
marker_end: "marker-end",
|
||||||
|
marker_mid: "marker-mid",
|
||||||
|
marker_pattern: "marker-pattern",
|
||||||
|
marker_segment: "marker-segment",
|
||||||
|
marker_start: "marker-start",
|
||||||
|
marker_knockout_left: "marker-knockout-left",
|
||||||
|
marker_knockout_right: "marker-knockout-right",
|
||||||
|
marker_side: "marker-side",
|
||||||
|
marks: "marks",
|
||||||
|
marquee_direction: "marquee-direction",
|
||||||
|
marquee_play_count: "marquee-play-count",
|
||||||
|
marquee_speed: "marquee-speed",
|
||||||
|
marquee_style: "marquee-style",
|
||||||
|
mask: "mask",
|
||||||
|
mask_image: "mask-image",
|
||||||
|
mask_repeat: "mask-repeat",
|
||||||
|
mask_position: "mask-position",
|
||||||
|
mask_clip: "mask-clip",
|
||||||
|
mask_origin: "mask-origin",
|
||||||
|
mask_size: "mask-size",
|
||||||
|
mask_box: "mask-box",
|
||||||
|
mask_box_outset: "mask-box-outset",
|
||||||
|
mask_box_repeat: "mask-box-repeat",
|
||||||
|
mask_box_slice: "mask-box-slice",
|
||||||
|
mask_box_source: "mask-box-source",
|
||||||
|
mask_box_width: "mask-box-width",
|
||||||
|
mask_type: "mask-type",
|
||||||
|
max_height: "max-height",
|
||||||
|
max_lines: "max-lines",
|
||||||
|
max_width: "max-width",
|
||||||
|
min_height: "min-height",
|
||||||
|
min_width: "min-width",
|
||||||
|
mix_blend_mode: "mix-blend-mode",
|
||||||
|
nav_down: "nav-down",
|
||||||
|
nav_index: "nav-index",
|
||||||
|
nav_left: "nav-left",
|
||||||
|
nav_right: "nav-right",
|
||||||
|
nav_up: "nav-up",
|
||||||
|
object_fit: "object-fit",
|
||||||
|
object_position: "object-position",
|
||||||
|
offset_after: "offset-after",
|
||||||
|
offset_before: "offset-before",
|
||||||
|
offset_end: "offset-end",
|
||||||
|
offset_start: "offset-start",
|
||||||
|
opacity: "opacity",
|
||||||
|
order: "order",
|
||||||
|
orphans: "orphans",
|
||||||
|
outline: "outline",
|
||||||
|
outline_color: "outline-color",
|
||||||
|
outline_style: "outline-style",
|
||||||
|
outline_width: "outline-width",
|
||||||
|
outline_offset: "outline-offset",
|
||||||
|
overflow: "overflow",
|
||||||
|
overflow_x: "overflow-x",
|
||||||
|
overflow_y: "overflow-y",
|
||||||
|
overflow_style: "overflow-style",
|
||||||
|
overflow_wrap: "overflow-wrap",
|
||||||
|
padding: "padding",
|
||||||
|
padding_bottom: "padding-bottom",
|
||||||
|
padding_left: "padding-left",
|
||||||
|
padding_right: "padding-right",
|
||||||
|
padding_top: "padding-top",
|
||||||
|
page: "page",
|
||||||
|
page_break_after: "page-break-after",
|
||||||
|
page_break_before: "page-break-before",
|
||||||
|
page_break_inside: "page-break-inside",
|
||||||
|
paint_order: "paint-order",
|
||||||
|
pause: "pause",
|
||||||
|
pause_after: "pause-after",
|
||||||
|
pause_before: "pause-before",
|
||||||
|
perspective: "perspective",
|
||||||
|
perspective_origin: "perspective-origin",
|
||||||
|
pitch: "pitch",
|
||||||
|
pitch_range: "pitch-range",
|
||||||
|
play_during: "play-during",
|
||||||
|
pointer_events: "pointer-events",
|
||||||
|
position: "position",
|
||||||
|
quotes: "quotes",
|
||||||
|
region_fragment: "region-fragment",
|
||||||
|
resize: "resize",
|
||||||
|
rest: "rest",
|
||||||
|
rest_after: "rest-after",
|
||||||
|
rest_before: "rest-before",
|
||||||
|
richness: "richness",
|
||||||
|
right: "right",
|
||||||
|
ruby_align: "ruby-align",
|
||||||
|
ruby_merge: "ruby-merge",
|
||||||
|
ruby_position: "ruby-position",
|
||||||
|
scroll_behavior: "scroll-behavior",
|
||||||
|
scroll_snap_coordinate: "scroll-snap-coordinate",
|
||||||
|
scroll_snap_destination: "scroll-snap-destination",
|
||||||
|
scroll_snap_points_x: "scroll-snap-points-x",
|
||||||
|
scroll_snap_points_y: "scroll-snap-points-y",
|
||||||
|
scroll_snap_type: "scroll-snap-type",
|
||||||
|
shape_image_threshold: "shape-image-threshold",
|
||||||
|
shape_inside: "shape-inside",
|
||||||
|
shape_margin: "shape-margin",
|
||||||
|
shape_outside: "shape-outside",
|
||||||
|
shape_padding: "shape-padding",
|
||||||
|
shape_rendering: "shape-rendering",
|
||||||
|
size: "size",
|
||||||
|
speak: "speak",
|
||||||
|
speak_as: "speak-as",
|
||||||
|
speak_header: "speak-header",
|
||||||
|
speak_numeral: "speak-numeral",
|
||||||
|
speak_punctuation: "speak-punctuation",
|
||||||
|
speech_rate: "speech-rate",
|
||||||
|
stop_color: "stop-color",
|
||||||
|
stop_opacity: "stop-opacity",
|
||||||
|
stress: "stress",
|
||||||
|
string_set: "string-set",
|
||||||
|
stroke: "stroke",
|
||||||
|
stroke_dasharray: "stroke-dasharray",
|
||||||
|
stroke_dashoffset: "stroke-dashoffset",
|
||||||
|
stroke_linecap: "stroke-linecap",
|
||||||
|
stroke_linejoin: "stroke-linejoin",
|
||||||
|
stroke_miterlimit: "stroke-miterlimit",
|
||||||
|
stroke_opacity: "stroke-opacity",
|
||||||
|
stroke_width: "stroke-width",
|
||||||
|
tab_size: "tab-size",
|
||||||
|
table_layout: "table-layout",
|
||||||
|
text_align: "text-align",
|
||||||
|
text_align_all: "text-align-all",
|
||||||
|
text_align_last: "text-align-last",
|
||||||
|
text_anchor: "text-anchor",
|
||||||
|
text_combine_upright: "text-combine-upright",
|
||||||
|
text_decoration: "text-decoration",
|
||||||
|
text_decoration_color: "text-decoration-color",
|
||||||
|
text_decoration_line: "text-decoration-line",
|
||||||
|
text_decoration_style: "text-decoration-style",
|
||||||
|
text_decoration_skip: "text-decoration-skip",
|
||||||
|
text_emphasis: "text-emphasis",
|
||||||
|
text_emphasis_color: "text-emphasis-color",
|
||||||
|
text_emphasis_style: "text-emphasis-style",
|
||||||
|
text_emphasis_position: "text-emphasis-position",
|
||||||
|
text_emphasis_skip: "text-emphasis-skip",
|
||||||
|
text_height: "text-height",
|
||||||
|
text_indent: "text-indent",
|
||||||
|
text_justify: "text-justify",
|
||||||
|
text_orientation: "text-orientation",
|
||||||
|
text_overflow: "text-overflow",
|
||||||
|
text_rendering: "text-rendering",
|
||||||
|
text_shadow: "text-shadow",
|
||||||
|
text_size_adjust: "text-size-adjust",
|
||||||
|
text_space_collapse: "text-space-collapse",
|
||||||
|
text_spacing: "text-spacing",
|
||||||
|
text_transform: "text-transform",
|
||||||
|
text_underline_position: "text-underline-position",
|
||||||
|
text_wrap: "text-wrap",
|
||||||
|
top: "top",
|
||||||
|
touch_action: "touch-action",
|
||||||
|
transform: "transform",
|
||||||
|
transform_box: "transform-box",
|
||||||
|
transform_origin: "transform-origin",
|
||||||
|
transform_style: "transform-style",
|
||||||
|
transition: "transition",
|
||||||
|
transition_delay: "transition-delay",
|
||||||
|
transition_duration: "transition-duration",
|
||||||
|
transition_property: "transition-property",
|
||||||
|
unicode_bidi: "unicode-bidi",
|
||||||
|
vector_effect: "vector-effect",
|
||||||
|
vertical_align: "vertical-align",
|
||||||
|
visibility: "visibility",
|
||||||
|
voice_balance: "voice-balance",
|
||||||
|
voice_duration: "voice-duration",
|
||||||
|
voice_family: "voice-family",
|
||||||
|
voice_pitch: "voice-pitch",
|
||||||
|
voice_range: "voice-range",
|
||||||
|
voice_rate: "voice-rate",
|
||||||
|
voice_stress: "voice-stress",
|
||||||
|
voice_volumn: "voice-volumn",
|
||||||
|
volume: "volume",
|
||||||
|
white_space: "white-space",
|
||||||
|
widows: "widows",
|
||||||
|
width: "width",
|
||||||
|
will_change: "will-change",
|
||||||
|
word_break: "word-break",
|
||||||
|
word_spacing: "word-spacing",
|
||||||
|
word_wrap: "word-wrap",
|
||||||
|
wrap_flow: "wrap-flow",
|
||||||
|
wrap_through: "wrap-through",
|
||||||
|
writing_mode: "writing-mode",
|
||||||
|
gap: "gap",
|
||||||
|
list_styler_type: "list-style-type",
|
||||||
|
row_gap: "row-gap",
|
||||||
|
transition_timing_function: "transition-timing-function",
|
||||||
|
user_select: "user-select",
|
||||||
|
webkit_user_select: "-webkit-user-select",
|
||||||
|
z_index : "z-index",
|
||||||
|
}
|
||||||
|
mapped_trait_methods! {
|
||||||
|
aria_current: "aria-current",
|
||||||
|
aria_details: "aria-details",
|
||||||
|
aria_disabled: "aria-disabled",
|
||||||
|
aria_hidden: "aria-hidden",
|
||||||
|
aria_invalid: "aria-invalid",
|
||||||
|
aria_keyshortcuts: "aria-keyshortcuts",
|
||||||
|
aria_label: "aria-label",
|
||||||
|
aria_roledescription: "aria-roledescription",
|
||||||
|
// Widget Attributes
|
||||||
|
aria_autocomplete: "aria-autocomplete",
|
||||||
|
aria_checked: "aria-checked",
|
||||||
|
aria_expanded: "aria-expanded",
|
||||||
|
aria_haspopup: "aria-haspopup",
|
||||||
|
aria_level: "aria-level",
|
||||||
|
aria_modal: "aria-modal",
|
||||||
|
aria_multiline: "aria-multiline",
|
||||||
|
aria_multiselectable: "aria-multiselectable",
|
||||||
|
aria_orientation: "aria-orientation",
|
||||||
|
aria_placeholder: "aria-placeholder",
|
||||||
|
aria_pressed: "aria-pressed",
|
||||||
|
aria_readonly: "aria-readonly",
|
||||||
|
aria_required: "aria-required",
|
||||||
|
aria_selected: "aria-selected",
|
||||||
|
aria_sort: "aria-sort",
|
||||||
|
aria_valuemax: "aria-valuemax",
|
||||||
|
aria_valuemin: "aria-valuemin",
|
||||||
|
aria_valuenow: "aria-valuenow",
|
||||||
|
aria_valuetext: "aria-valuetext",
|
||||||
|
// Live Region Attributes
|
||||||
|
aria_atomic: "aria-atomic",
|
||||||
|
aria_busy: "aria-busy",
|
||||||
|
aria_live: "aria-live",
|
||||||
|
aria_relevant: "aria-relevant",
|
||||||
|
|
||||||
|
aria_dropeffect: "aria-dropeffect",
|
||||||
|
aria_grabbed: "aria-grabbed",
|
||||||
|
// Relationship Attributes
|
||||||
|
aria_activedescendant: "aria-activedescendant",
|
||||||
|
aria_colcount: "aria-colcount",
|
||||||
|
aria_colindex: "aria-colindex",
|
||||||
|
aria_colspan: "aria-colspan",
|
||||||
|
aria_controls: "aria-controls",
|
||||||
|
aria_describedby: "aria-describedby",
|
||||||
|
aria_errormessage: "aria-errormessage",
|
||||||
|
aria_flowto: "aria-flowto",
|
||||||
|
aria_labelledby: "aria-labelledby",
|
||||||
|
aria_owns: "aria-owns",
|
||||||
|
aria_posinset: "aria-posinset",
|
||||||
|
aria_rowcount: "aria-rowcount",
|
||||||
|
aria_rowindex: "aria-rowindex",
|
||||||
|
aria_rowspan: "aria-rowspan",
|
||||||
|
aria_setsize: "aria-setsize",
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod svg {
|
||||||
|
mapped_trait_methods! {
|
||||||
|
accent_height: "accent-height",
|
||||||
|
accumulate: "accumulate",
|
||||||
|
additive: "additive",
|
||||||
|
alignment_baseline: "alignment-baseline",
|
||||||
|
alphabetic: "alphabetic",
|
||||||
|
amplitude: "amplitude",
|
||||||
|
arabic_form: "arabic-form",
|
||||||
|
ascent: "ascent",
|
||||||
|
attributeName: "attributeName",
|
||||||
|
attributeType: "attributeType",
|
||||||
|
azimuth: "azimuth",
|
||||||
|
baseFrequency: "baseFrequency",
|
||||||
|
baseline_shift: "baseline-shift",
|
||||||
|
baseProfile: "baseProfile",
|
||||||
|
bbox: "bbox",
|
||||||
|
begin: "begin",
|
||||||
|
bias: "bias",
|
||||||
|
by: "by",
|
||||||
|
calcMode: "calcMode",
|
||||||
|
cap_height: "cap-height",
|
||||||
|
class: "class",
|
||||||
|
clip: "clip",
|
||||||
|
clipPathUnits: "clipPathUnits",
|
||||||
|
clip_path: "clip-path",
|
||||||
|
clip_rule: "clip-rule",
|
||||||
|
color: "color",
|
||||||
|
color_interpolation: "color-interpolation",
|
||||||
|
color_interpolation_filters: "color-interpolation-filters",
|
||||||
|
color_profile: "color-profile",
|
||||||
|
color_rendering: "color-rendering",
|
||||||
|
contentScriptType: "contentScriptType",
|
||||||
|
contentStyleType: "contentStyleType",
|
||||||
|
crossorigin: "crossorigin",
|
||||||
|
cursor: "cursor",
|
||||||
|
cx: "cx",
|
||||||
|
cy: "cy",
|
||||||
|
d: "d",
|
||||||
|
decelerate: "decelerate",
|
||||||
|
descent: "descent",
|
||||||
|
diffuseConstant: "diffuseConstant",
|
||||||
|
direction: "direction",
|
||||||
|
display: "display",
|
||||||
|
divisor: "divisor",
|
||||||
|
dominant_baseline: "dominant-baseline",
|
||||||
|
dur: "dur",
|
||||||
|
dx: "dx",
|
||||||
|
dy: "dy",
|
||||||
|
edgeMode: "edgeMode",
|
||||||
|
elevation: "elevation",
|
||||||
|
enable_background: "enable-background",
|
||||||
|
end: "end",
|
||||||
|
exponent: "exponent",
|
||||||
|
fill: "fill",
|
||||||
|
fill_opacity: "fill-opacity",
|
||||||
|
fill_rule: "fill-rule",
|
||||||
|
filter: "filter",
|
||||||
|
filterRes: "filterRes",
|
||||||
|
filterUnits: "filterUnits",
|
||||||
|
flood_color: "flood-color",
|
||||||
|
flood_opacity: "flood-opacity",
|
||||||
|
font_family: "font-family",
|
||||||
|
font_size: "font-size",
|
||||||
|
font_size_adjust: "font-size-adjust",
|
||||||
|
font_stretch: "font-stretch",
|
||||||
|
font_style: "font-style",
|
||||||
|
font_variant: "font-variant",
|
||||||
|
font_weight: "font-weight",
|
||||||
|
format: "format",
|
||||||
|
from: "from",
|
||||||
|
fr: "fr",
|
||||||
|
fx: "fx",
|
||||||
|
fy: "fy",
|
||||||
|
g1: "g1",
|
||||||
|
g2: "g2",
|
||||||
|
glyph_name: "glyph-name",
|
||||||
|
glyph_orientation_horizontal: "glyph-orientation-horizontal",
|
||||||
|
glyph_orientation_vertical: "glyph-orientation-vertical",
|
||||||
|
glyphRef: "glyphRef",
|
||||||
|
gradientTransform: "gradientTransform",
|
||||||
|
gradientUnits: "gradientUnits",
|
||||||
|
hanging: "hanging",
|
||||||
|
height: "height",
|
||||||
|
href: "href",
|
||||||
|
hreflang: "hreflang",
|
||||||
|
horiz_adv_x: "horiz-adv-x",
|
||||||
|
horiz_origin_x: "horiz-origin-x",
|
||||||
|
id: "id",
|
||||||
|
ideographic: "ideographic",
|
||||||
|
image_rendering: "image-rendering",
|
||||||
|
_in: "_in",
|
||||||
|
in2: "in2",
|
||||||
|
intercept: "intercept",
|
||||||
|
k: "k",
|
||||||
|
k1: "k1",
|
||||||
|
k2: "k2",
|
||||||
|
k3: "k3",
|
||||||
|
k4: "k4",
|
||||||
|
kernelMatrix: "kernelMatrix",
|
||||||
|
kernelUnitLength: "kernelUnitLength",
|
||||||
|
kerning: "kerning",
|
||||||
|
keyPoints: "keyPoints",
|
||||||
|
keySplines: "keySplines",
|
||||||
|
keyTimes: "keyTimes",
|
||||||
|
lang: "lang",
|
||||||
|
lengthAdjust: "lengthAdjust",
|
||||||
|
letter_spacing: "letter-spacing",
|
||||||
|
lighting_color: "lighting-color",
|
||||||
|
limitingConeAngle: "limitingConeAngle",
|
||||||
|
local: "local",
|
||||||
|
marker_end: "marker-end",
|
||||||
|
marker_mid: "marker-mid",
|
||||||
|
marker_start: "marker_start",
|
||||||
|
markerHeight: "markerHeight",
|
||||||
|
markerUnits: "markerUnits",
|
||||||
|
markerWidth: "markerWidth",
|
||||||
|
mask: "mask",
|
||||||
|
maskContentUnits: "maskContentUnits",
|
||||||
|
maskUnits: "maskUnits",
|
||||||
|
mathematical: "mathematical",
|
||||||
|
max: "max",
|
||||||
|
media: "media",
|
||||||
|
method: "method",
|
||||||
|
min: "min",
|
||||||
|
mode: "mode",
|
||||||
|
name: "name",
|
||||||
|
numOctaves: "numOctaves",
|
||||||
|
offset: "offset",
|
||||||
|
opacity: "opacity",
|
||||||
|
operator: "operator",
|
||||||
|
order: "order",
|
||||||
|
orient: "orient",
|
||||||
|
orientation: "orientation",
|
||||||
|
origin: "origin",
|
||||||
|
overflow: "overflow",
|
||||||
|
overline_position: "overline-position",
|
||||||
|
overline_thickness: "overline-thickness",
|
||||||
|
panose_1: "panose-1",
|
||||||
|
paint_order: "paint-order",
|
||||||
|
path: "path",
|
||||||
|
pathLength: "pathLength",
|
||||||
|
patternContentUnits: "patternContentUnits",
|
||||||
|
patternTransform: "patternTransform",
|
||||||
|
patternUnits: "patternUnits",
|
||||||
|
ping: "ping",
|
||||||
|
pointer_events: "pointer-events",
|
||||||
|
points: "points",
|
||||||
|
pointsAtX: "pointsAtX",
|
||||||
|
pointsAtY: "pointsAtY",
|
||||||
|
pointsAtZ: "pointsAtZ",
|
||||||
|
preserveAlpha: "preserveAlpha",
|
||||||
|
preserveAspectRatio: "preserveAspectRatio",
|
||||||
|
primitiveUnits: "primitiveUnits",
|
||||||
|
r: "r",
|
||||||
|
radius: "radius",
|
||||||
|
referrerPolicy: "referrerPolicy",
|
||||||
|
refX: "refX",
|
||||||
|
refY: "refY",
|
||||||
|
rel: "rel",
|
||||||
|
rendering_intent: "rendering-intent",
|
||||||
|
repeatCount: "repeatCount",
|
||||||
|
repeatDur: "repeatDur",
|
||||||
|
requiredExtensions: "requiredExtensions",
|
||||||
|
requiredFeatures: "requiredFeatures",
|
||||||
|
restart: "restart",
|
||||||
|
result: "result",
|
||||||
|
role: "role",
|
||||||
|
rotate: "rotate",
|
||||||
|
rx: "rx",
|
||||||
|
ry: "ry",
|
||||||
|
scale: "scale",
|
||||||
|
seed: "seed",
|
||||||
|
shape_rendering: "shape-rendering",
|
||||||
|
slope: "slope",
|
||||||
|
spacing: "spacing",
|
||||||
|
specularConstant: "specularConstant",
|
||||||
|
specularExponent: "specularExponent",
|
||||||
|
speed: "speed",
|
||||||
|
spreadMethod: "spreadMethod",
|
||||||
|
startOffset: "startOffset",
|
||||||
|
stdDeviation: "stdDeviation",
|
||||||
|
stemh: "stemh",
|
||||||
|
stemv: "stemv",
|
||||||
|
stitchTiles: "stitchTiles",
|
||||||
|
stop_color: "stop_color",
|
||||||
|
stop_opacity: "stop_opacity",
|
||||||
|
strikethrough_position: "strikethrough-position",
|
||||||
|
strikethrough_thickness: "strikethrough-thickness",
|
||||||
|
string: "string",
|
||||||
|
stroke: "stroke",
|
||||||
|
stroke_dasharray: "stroke-dasharray",
|
||||||
|
stroke_dashoffset: "stroke-dashoffset",
|
||||||
|
stroke_linecap: "stroke-linecap",
|
||||||
|
stroke_linejoin: "stroke-linejoin",
|
||||||
|
stroke_miterlimit: "stroke-miterlimit",
|
||||||
|
stroke_opacity: "stroke-opacity",
|
||||||
|
stroke_width: "stroke-width",
|
||||||
|
style: "style",
|
||||||
|
surfaceScale: "surfaceScale",
|
||||||
|
systemLanguage: "systemLanguage",
|
||||||
|
tabindex: "tabindex",
|
||||||
|
tableValues: "tableValues",
|
||||||
|
target: "target",
|
||||||
|
targetX: "targetX",
|
||||||
|
targetY: "targetY",
|
||||||
|
text_anchor: "text-anchor",
|
||||||
|
text_decoration: "text-decoration",
|
||||||
|
text_rendering: "text-rendering",
|
||||||
|
textLength: "textLength",
|
||||||
|
to: "to",
|
||||||
|
transform: "transform",
|
||||||
|
transform_origin: "transform-origin",
|
||||||
|
r#type: "_type",
|
||||||
|
u1: "u1",
|
||||||
|
u2: "u2",
|
||||||
|
underline_position: "underline-position",
|
||||||
|
underline_thickness: "underline-thickness",
|
||||||
|
unicode: "unicode",
|
||||||
|
unicode_bidi: "unicode-bidi",
|
||||||
|
unicode_range: "unicode-range",
|
||||||
|
units_per_em: "units-per-em",
|
||||||
|
v_alphabetic: "v-alphabetic",
|
||||||
|
v_hanging: "v-hanging",
|
||||||
|
v_ideographic: "v-ideographic",
|
||||||
|
v_mathematical: "v-mathematical",
|
||||||
|
values: "values",
|
||||||
|
vector_effect: "vector-effect",
|
||||||
|
version: "version",
|
||||||
|
vert_adv_y: "vert-adv-y",
|
||||||
|
vert_origin_x: "vert-origin-x",
|
||||||
|
vert_origin_y: "vert-origin-y",
|
||||||
|
view_box: "viewBox",
|
||||||
|
view_target: "viewTarget",
|
||||||
|
visibility: "visibility",
|
||||||
|
width: "width",
|
||||||
|
widths: "widths",
|
||||||
|
word_spacing: "word-spacing",
|
||||||
|
writing_mode: "writing-mode",
|
||||||
|
x: "x",
|
||||||
|
x_height: "x-height",
|
||||||
|
x1: "x1",
|
||||||
|
x2: "x2",
|
||||||
|
xmlns: "xmlns",
|
||||||
|
x_channel_selector: "xChannelSelector",
|
||||||
|
y: "y",
|
||||||
|
y1: "y1",
|
||||||
|
y2: "y2",
|
||||||
|
y_channel_selector: "yChannelSelector",
|
||||||
|
z: "z",
|
||||||
|
zoomAndPan: "zoomAndPan",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
use dioxus_core::VNode;
|
||||||
|
use dioxus_rsx::{BodyNode, CallBody, Component, ElementAttr, IfmtInput};
|
||||||
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use syn::Expr;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CapturedContextBuilder {
|
||||||
|
attributes: HashMap<String, IfmtInput>,
|
||||||
|
text: Vec<IfmtInput>,
|
||||||
|
components: Vec<Component>,
|
||||||
|
iterators: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CapturedContextBuilder {
|
||||||
|
pub fn extend(&mut self, other: CapturedContextBuilder) {
|
||||||
|
self.attributes.extend(other.attributes);
|
||||||
|
self.text.extend(other.text);
|
||||||
|
self.components.extend(other.components);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_call_body(body: CallBody) -> Self {
|
||||||
|
let mut new = Self::default();
|
||||||
|
for node in body.roots {
|
||||||
|
new.extend(Self::find_captured(node));
|
||||||
|
}
|
||||||
|
new
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_captured(node: BodyNode) -> Self {
|
||||||
|
let mut captured = CapturedContextBuilder::default();
|
||||||
|
match node {
|
||||||
|
BodyNode::Element(el) => {
|
||||||
|
for attr in el.attributes {
|
||||||
|
let (name, value_tokens) = match attr.attr {
|
||||||
|
ElementAttr::AttrText { name, value } => {
|
||||||
|
(name.to_string(), value.to_token_stream())
|
||||||
|
}
|
||||||
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
|
(name.to_string(), value.to_token_stream())
|
||||||
|
}
|
||||||
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
|
(name.value(), value.to_token_stream())
|
||||||
|
}
|
||||||
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
|
(name.value(), value.to_token_stream())
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let formated: IfmtInput = syn::parse2(value_tokens).unwrap();
|
||||||
|
captured.attributes.insert(name, formated);
|
||||||
|
}
|
||||||
|
for child in el.children {
|
||||||
|
captured.extend(Self::find_captured(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BodyNode::Component(comp) => {
|
||||||
|
let fn_name = comp.name.segments.last().unwrap().ident.to_string();
|
||||||
|
captured.components.push(comp);
|
||||||
|
}
|
||||||
|
BodyNode::Text(t) => {
|
||||||
|
let tokens = t.to_token_stream();
|
||||||
|
let formated: IfmtInput = syn::parse2(tokens).unwrap();
|
||||||
|
captured.text.push(formated);
|
||||||
|
}
|
||||||
|
BodyNode::RawExpr(expr) => captured.iterators.push(expr),
|
||||||
|
BodyNode::Meta(_) => (),
|
||||||
|
}
|
||||||
|
captured
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for CapturedContextBuilder {
|
||||||
|
fn to_tokens(&self, tokens: &mut quote::__private::TokenStream) {
|
||||||
|
let CapturedContextBuilder {
|
||||||
|
attributes,
|
||||||
|
text,
|
||||||
|
components,
|
||||||
|
iterators,
|
||||||
|
} = self;
|
||||||
|
let captured: Vec<_> = attributes
|
||||||
|
.iter()
|
||||||
|
.map(|(_, fmt)| fmt.named_args.iter())
|
||||||
|
.chain(text.iter().map(|fmt| fmt.named_args.iter()))
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
let captured_names = captured.iter().map(|(n, _)| n);
|
||||||
|
let captured_expr = captured.iter().map(|(_, e)| e);
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
CapturedContext {
|
||||||
|
captured: IfmtArgs{
|
||||||
|
named_args: &'static [#((#captured_names, #captured_expr)),*]
|
||||||
|
},
|
||||||
|
components: vec![#(#components),*],
|
||||||
|
iterators: vec![#(#iterators),*],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CapturedComponentBuilder {
|
||||||
|
name: syn::Path,
|
||||||
|
function: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CapturedContext<'a> {
|
||||||
|
// map of the attribute name to the formated value
|
||||||
|
captured: IfmtArgs,
|
||||||
|
// the only thing we can update in component is the children
|
||||||
|
components: Vec<VNode<'a>>,
|
||||||
|
// we can't reasonably interpert iterators, so they are staticly inserted
|
||||||
|
iterators: Vec<VNode<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IfmtArgs {
|
||||||
|
// live reload only supports named arguments
|
||||||
|
pub named_args: &'static [(&'static str, String)],
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IfmtSegment<'a> {
|
||||||
|
Static(&'a str),
|
||||||
|
Dynamic(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RsxNode<'a> {
|
||||||
|
Element {
|
||||||
|
name: String,
|
||||||
|
attributes: Vec<(String, IfmtSegment<'a>)>,
|
||||||
|
children: Vec<RsxNode<'a>>,
|
||||||
|
},
|
||||||
|
Text {
|
||||||
|
text: Vec<IfmtSegment<'a>>,
|
||||||
|
},
|
||||||
|
Component {
|
||||||
|
children: Vec<RsxNode<'a>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,88 @@
|
|||||||
|
use dioxus_core::{Attribute, NodeFactory, VNode};
|
||||||
|
use dioxus_rsx::{BodyNode, CallBody, ElementAttr, IfmtInput};
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::parse2;
|
||||||
|
|
||||||
|
use crate::attributes::attrbute_to_static_str;
|
||||||
|
use crate::elements::element_to_static_str;
|
||||||
|
|
||||||
|
pub fn build<'a>(rsx: CallBody, factory: &NodeFactory<'a>) -> VNode<'a> {
|
||||||
|
let children_built = factory.bump().alloc(Vec::new());
|
||||||
|
for (i, child) in rsx.roots.into_iter().enumerate() {
|
||||||
|
children_built.push(build_node(child, factory, i.to_string().as_str()));
|
||||||
|
}
|
||||||
|
factory.fragment_from_iter(children_built.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_node<'a>(node: BodyNode, factory: &NodeFactory<'a>, key: &str) -> Option<VNode<'a>> {
|
||||||
|
let bump = factory.bump();
|
||||||
|
match node {
|
||||||
|
BodyNode::Text(text) => {
|
||||||
|
let ifmt_input: IfmtInput = parse2(text.into_token_stream()).unwrap();
|
||||||
|
Some(factory.text(format_args!("{}", ifmt_input.format_literal.value())))
|
||||||
|
}
|
||||||
|
BodyNode::Element(el) => {
|
||||||
|
let attributes: &mut Vec<Attribute> = bump.alloc(Vec::new());
|
||||||
|
for attr in el.attributes {
|
||||||
|
let result: Option<(String, IfmtInput)> = match attr.attr {
|
||||||
|
ElementAttr::AttrText { name, value } => {
|
||||||
|
Some((name.to_string(), parse2(value.into_token_stream()).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
|
Some((name.to_string(), parse2(value.into_token_stream()).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
|
Some((name.value(), parse2(value.into_token_stream()).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
|
Some((name.value(), parse2(value.into_token_stream()).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementAttr::EventTokens { .. } => None,
|
||||||
|
|
||||||
|
ElementAttr::Meta(_) => None,
|
||||||
|
};
|
||||||
|
if let Some((name, value)) = result {
|
||||||
|
if let Some((name, namespace)) = attrbute_to_static_str(&name) {
|
||||||
|
let value = bump.alloc(value.format_literal.value());
|
||||||
|
attributes.push(Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
is_static: true,
|
||||||
|
is_volatile: false,
|
||||||
|
namespace,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let children = bump.alloc(Vec::new());
|
||||||
|
for (i, child) in el.children.into_iter().enumerate() {
|
||||||
|
let node = build_node(child, factory, i.to_string().as_str());
|
||||||
|
if let Some(node) = node {
|
||||||
|
children.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tag = bump.alloc(el.name.to_string());
|
||||||
|
if let Some((tag, ns)) = element_to_static_str(tag) {
|
||||||
|
Some(factory.raw_element(
|
||||||
|
tag,
|
||||||
|
ns,
|
||||||
|
&[],
|
||||||
|
attributes.as_slice(),
|
||||||
|
children.as_slice(),
|
||||||
|
Some(format_args!("{}", key)),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BodyNode::Component(_) => todo!(),
|
||||||
|
BodyNode::RawExpr(_) => todo!(),
|
||||||
|
BodyNode::Meta(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
use crate::interperter::build;
|
||||||
|
use dioxus_core::LazyNodes;
|
||||||
|
use dioxus_rsx::CallBody;
|
||||||
|
use syn::{parse_str, Result};
|
||||||
|
|
||||||
|
mod attributes;
|
||||||
|
pub mod captuered_context;
|
||||||
|
mod elements;
|
||||||
|
mod interperter;
|
||||||
|
|
||||||
|
pub fn rsx_to_html(text: &str, context: &captuered_context::CapturedContext) -> {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ pub mod events {
|
|||||||
pub use dioxus_html::{on::*, KeyCode};
|
pub use dioxus_html::{on::*, KeyCode};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use dioxus_rsx as rsx;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::hooks::*;
|
pub use crate::hooks::*;
|
||||||
pub use dioxus_core::prelude::*;
|
pub use dioxus_core::prelude::*;
|
||||||
|
|||||||
Reference in new issue
Block a user