feat: namespaced attributes
this commit adds namespaced attributes. This lets us support attribute groups, and thus, inline styles. This namespaced attribute stuff is only available for styles at the moment, though it theoretically could be enabled for any other attributes.
This commit is contained in:
1 parent
a4bf3d9862
commit
22e659c2bd
18 files changed
+612
-3129
No files matched your search
@@ -10,6 +10,7 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-core = { path="./packages/core", version="0.1.0" }
|
dioxus-core = { path="./packages/core", version="0.1.0" }
|
||||||
dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
|
dioxus-core-macro = { path="./packages/core-macro", version="0.1.0" }
|
||||||
|
dioxus-html-namespace = { path="./packages/html-namespace" }
|
||||||
|
|
||||||
dioxus-web = { path="./packages/web", optional=true }
|
dioxus-web = { path="./packages/web", optional=true }
|
||||||
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
# dioxus-hooks = { path="./packages/hooks", version="0.0.0" }
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an
|
|||||||
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
|
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
|
||||||
| Manual Props | ✅ | ✅ | Manually pass in props with spread syntax |
|
| Manual Props | ✅ | ✅ | Manually pass in props with spread syntax |
|
||||||
| Controlled Inputs | ✅ | ✅ | stateful wrappers around inputs |
|
| Controlled Inputs | ✅ | ✅ | stateful wrappers around inputs |
|
||||||
|
| CSS/Inline Styles | ✅ | ✅ | syntax for inline styles/attribute groups[2] |
|
||||||
| NodeRef | 🛠 | ✅ | gain direct access to nodes [1] |
|
| NodeRef | 🛠 | ✅ | gain direct access to nodes [1] |
|
||||||
| CSS/Inline Styles | 🛠 | ✅ | syntax for inline styles/attribute groups[2] |
|
|
||||||
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
||||||
| Suspense | 🛠 | ✅ | schedule future render from future/promise |
|
| Suspense | 🛠 | ✅ | schedule future render from future/promise |
|
||||||
| Cooperative Scheduling | 🛠 | ✅ | Prioritize important events over non-important events |
|
| Cooperative Scheduling | 🛠 | ✅ | Prioritize important events over non-important events |
|
||||||
|
|||||||
+6
-2
@@ -2,7 +2,9 @@
|
|||||||
//!
|
//!
|
||||||
//! The example from the README.md
|
//! The example from the README.md
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::{events::on::MouseEvent, prelude::*};
|
||||||
|
use dioxus_html_namespace::{button, div, h1};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::web::launch(Example)
|
dioxus::web::launch(Example)
|
||||||
}
|
}
|
||||||
@@ -10,9 +12,11 @@ fn main() {
|
|||||||
fn Example(cx: Context<()>) -> VNode {
|
fn Example(cx: Context<()>) -> VNode {
|
||||||
let name = use_state(&cx, || "..?");
|
let name = use_state(&cx, || "..?");
|
||||||
|
|
||||||
|
let handler = move |e: MouseEvent| e.cl;
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 { "Hello, {name}" }
|
h1 { "Hello, {name}" }
|
||||||
button { "?", onclick: move |_| name.set("world!")}
|
button { "?", onclick: move |event| name.set("world!")}
|
||||||
button { "?", onclick: move |_| name.set("Dioxus 🎉")}
|
button { "?", onclick: move |_| name.set("Dioxus 🎉")}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -13,15 +13,55 @@ use syn::{
|
|||||||
// =======================================
|
// =======================================
|
||||||
pub struct Element {
|
pub struct Element {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
attrs: Vec<ElementAttr>,
|
key: Option<AttrType>,
|
||||||
|
attributes: Vec<ElementAttr>,
|
||||||
|
listeners: Vec<ElementAttr>,
|
||||||
children: Vec<Node>,
|
children: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Element {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
let name = &self.name;
|
||||||
|
// let name = &self.name.to_string();
|
||||||
|
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
__cx.element(#name)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add attributes
|
||||||
|
// TODO: conver to the "attrs" syntax for compile-time known sizes
|
||||||
|
if self.attributes.len() > 0 {
|
||||||
|
let attr = &self.attributes;
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
.attributes([ #(#attr),* ])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.children.len() > 0 {
|
||||||
|
let childs = &self.children;
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
.children([ #(#childs),* ])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.listeners.len() > 0 {
|
||||||
|
let listeners = &self.listeners;
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
.listeners([ #(#listeners),* ])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.append_all(quote! {
|
||||||
|
.finish()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for Element {
|
impl Parse for Element {
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
fn parse(stream: ParseStream) -> Result<Self> {
|
||||||
//
|
|
||||||
let name = Ident::parse(stream)?;
|
let name = Ident::parse(stream)?;
|
||||||
|
|
||||||
|
// TODO: remove this in favor of the compile-time validation system
|
||||||
if !crate::util::is_valid_tag(&name.to_string()) {
|
if !crate::util::is_valid_tag(&name.to_string()) {
|
||||||
return Err(Error::new(name.span(), "Not a valid Html tag"));
|
return Err(Error::new(name.span(), "Not a valid Html tag"));
|
||||||
}
|
}
|
||||||
@@ -30,20 +70,19 @@ impl Parse for Element {
|
|||||||
let content: ParseBuffer;
|
let content: ParseBuffer;
|
||||||
syn::braced!(content in stream);
|
syn::braced!(content in stream);
|
||||||
|
|
||||||
let mut attrs: Vec<ElementAttr> = vec![];
|
let mut attributes: Vec<ElementAttr> = vec![];
|
||||||
|
let mut listeners: Vec<ElementAttr> = vec![];
|
||||||
let mut children: Vec<Node> = vec![];
|
let mut children: Vec<Node> = vec![];
|
||||||
|
let mut key = None;
|
||||||
|
|
||||||
'parsing: loop {
|
'parsing: loop {
|
||||||
// [1] Break if empty
|
// [1] Break if empty
|
||||||
if content.is_empty() {
|
if content.is_empty() {
|
||||||
break 'parsing;
|
break 'parsing;
|
||||||
}
|
}
|
||||||
|
|
||||||
let forked = content.fork();
|
if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
|
||||||
if forked.call(Ident::parse_any).is_ok()
|
parse_element_body(&content, &mut attributes, &mut listeners, &mut key)?;
|
||||||
&& forked.parse::<Token![:]>().is_ok()
|
|
||||||
&& forked.parse::<Token![:]>().is_err()
|
|
||||||
{
|
|
||||||
attrs.push(content.parse::<ElementAttr>()?);
|
|
||||||
} else {
|
} else {
|
||||||
children.push(content.parse::<Node>()?);
|
children.push(content.parse::<Node>()?);
|
||||||
}
|
}
|
||||||
@@ -56,69 +95,22 @@ impl Parse for Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
key,
|
||||||
name,
|
name,
|
||||||
attrs,
|
attributes,
|
||||||
children,
|
children,
|
||||||
|
listeners,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for Element {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let name = &self.name.to_string();
|
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
__cx.element(#name)
|
|
||||||
});
|
|
||||||
// dioxus::builder::ElementBuilder::new(__cx, #name)
|
|
||||||
// dioxus::builder::ElementBuilder::new(__cx, #name)
|
|
||||||
|
|
||||||
// Add attributes
|
|
||||||
// TODO: conver to the "attrs" syntax for compile-time known sizes
|
|
||||||
for attr in self.attrs.iter() {
|
|
||||||
attr.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let mut children = self.children.iter();
|
|
||||||
// while let Some(child) = children.next() {
|
|
||||||
// let inner_toks = child.to_token_stream();
|
|
||||||
// tokens.append_all(quote! {
|
|
||||||
// .iter_child(#inner_toks)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut childs = quote! {};
|
|
||||||
for child in &self.children {
|
|
||||||
match child {
|
|
||||||
Node::Text(e) => e.to_tokens(&mut childs),
|
|
||||||
Node::Element(e) => e.to_tokens(&mut childs),
|
|
||||||
Node::RawExpr(e) => quote! {
|
|
||||||
__cx.fragment_from_iter(#e)
|
|
||||||
}
|
|
||||||
.to_tokens(&mut childs),
|
|
||||||
}
|
|
||||||
childs.append_all(quote! {,})
|
|
||||||
}
|
|
||||||
if self.children.len() > 0 {
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
.children([
|
|
||||||
#childs
|
|
||||||
])
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
.finish()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// =======================================
|
/// =======================================
|
||||||
/// Parse a VElement's Attributes
|
/// Parse a VElement's Attributes
|
||||||
/// =======================================
|
/// =======================================
|
||||||
struct ElementAttr {
|
struct ElementAttr {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
ty: AttrType,
|
value: AttrType,
|
||||||
|
namespace: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AttrType {
|
enum AttrType {
|
||||||
@@ -128,132 +120,142 @@ enum AttrType {
|
|||||||
Event(ExprClosure),
|
Event(ExprClosure),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ElementAttr {
|
// We parse attributes and dump them into the attribute vec
|
||||||
fn parse(s: ParseStream) -> Result<Self> {
|
// This is because some tags might be namespaced (IE style)
|
||||||
let mut name = Ident::parse_any(s)?;
|
// These dedicated tags produce multiple name-spaced attributes
|
||||||
let name_str = name.to_string();
|
fn parse_element_body(
|
||||||
s.parse::<Token![:]>()?;
|
stream: ParseStream,
|
||||||
|
attrs: &mut Vec<ElementAttr>,
|
||||||
|
listeners: &mut Vec<ElementAttr>,
|
||||||
|
key: &mut Option<AttrType>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut name = Ident::parse_any(stream)?;
|
||||||
|
let name_str = name.to_string();
|
||||||
|
stream.parse::<Token![:]>()?;
|
||||||
|
|
||||||
// Check if this is an event handler
|
// Return early if the field is a listener
|
||||||
// If so, parse into literal tokens
|
if name_str.starts_with("on") {
|
||||||
let ty = if name_str.starts_with("on") {
|
// remove the "on" bit
|
||||||
// remove the "on" bit
|
// name = Ident::new(&name_str.trim_start_matches("on"), name.span());
|
||||||
name = Ident::new(&name_str.trim_start_matches("on"), name.span());
|
|
||||||
|
|
||||||
if s.peek(token::Brace) {
|
let ty = if stream.peek(token::Brace) {
|
||||||
let content;
|
let content;
|
||||||
syn::braced!(content in s);
|
syn::braced!(content in stream);
|
||||||
|
|
||||||
// Try to parse directly as a closure
|
// Try to parse directly as a closure
|
||||||
let fork = content.fork();
|
let fork = content.fork();
|
||||||
if let Ok(event) = fork.parse::<ExprClosure>() {
|
if let Ok(event) = fork.parse::<ExprClosure>() {
|
||||||
content.advance_to(&fork);
|
content.advance_to(&fork);
|
||||||
AttrType::Event(event)
|
AttrType::Event(event)
|
||||||
} else {
|
|
||||||
AttrType::EventTokens(content.parse()?)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AttrType::Event(s.parse()?)
|
AttrType::EventTokens(content.parse()?)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match name_str.as_str() {
|
AttrType::Event(stream.parse()?)
|
||||||
"key" => {
|
};
|
||||||
// todo: better error here
|
listeners.push(ElementAttr {
|
||||||
AttrType::BumpText(s.parse::<LitStr>()?)
|
name,
|
||||||
}
|
value: ty,
|
||||||
// "style" => {
|
namespace: None,
|
||||||
// //
|
});
|
||||||
// todo!("inline style not yet supported")
|
return Ok(());
|
||||||
// }
|
}
|
||||||
"classes" => {
|
|
||||||
//
|
let ty: AttrType = match name_str.as_str() {
|
||||||
todo!("custom class lsit not supported")
|
// short circuit early if style is using the special syntax
|
||||||
}
|
"style" if stream.peek(token::Brace) => {
|
||||||
"namespace" => {
|
let inner;
|
||||||
//
|
syn::braced!(inner in stream);
|
||||||
todo!("custom namespace not supported")
|
|
||||||
}
|
while !inner.is_empty() {
|
||||||
"ref" => {
|
let name = Ident::parse_any(&inner)?;
|
||||||
//
|
inner.parse::<Token![:]>()?;
|
||||||
todo!("custom ref not supported")
|
let ty = if inner.peek(LitStr) {
|
||||||
}
|
let rawtext = inner.parse::<LitStr>().unwrap();
|
||||||
_ => {
|
AttrType::BumpText(rawtext)
|
||||||
if s.peek(LitStr) {
|
} else {
|
||||||
let rawtext = s.parse::<LitStr>().unwrap();
|
let toks = inner.parse::<Expr>()?;
|
||||||
AttrType::BumpText(rawtext)
|
AttrType::FieldTokens(toks)
|
||||||
} else {
|
};
|
||||||
let toks = s.parse::<Expr>()?;
|
if inner.peek(Token![,]) {
|
||||||
AttrType::FieldTokens(toks)
|
let _ = inner.parse::<Token![,]>();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
attrs.push(ElementAttr {
|
||||||
|
name,
|
||||||
|
value: ty,
|
||||||
|
namespace: Some("style".to_string()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// let lit_str = if name_str == "style" && s.peek(token::Brace) {
|
return Ok(());
|
||||||
// // special-case to deal with literal styles.
|
}
|
||||||
// let outer;
|
"key" => {
|
||||||
// syn::braced!(outer in s);
|
*key = Some(AttrType::BumpText(stream.parse::<LitStr>()?));
|
||||||
// // double brace for inline style.
|
return Ok(());
|
||||||
// // todo!("Style support not ready yet");
|
}
|
||||||
|
"classes" => {
|
||||||
// // if outer.peek(token::Brace) {
|
todo!("custom class lsit not supported")
|
||||||
// // let inner;
|
}
|
||||||
// // syn::braced!(inner in outer);
|
"namespace" => {
|
||||||
// // let styles: Styles = inner.parse()?;
|
todo!("custom namespace not supported")
|
||||||
// // MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
|
}
|
||||||
// // } else {
|
"ref" => {
|
||||||
// // just parse as an expression
|
todo!("NodeRefs are currently not supported! This is currently a reserved keyword.")
|
||||||
// outer.parse()?
|
|
||||||
// // }
|
|
||||||
// } else {
|
|
||||||
// s.parse()?
|
|
||||||
// };
|
|
||||||
};
|
|
||||||
|
|
||||||
// consume comma if it exists
|
|
||||||
// we don't actually care if there *are* commas between attrs
|
|
||||||
if s.peek(Token![,]) {
|
|
||||||
let _ = s.parse::<Token![,]>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ElementAttr { name, ty })
|
// Fall through
|
||||||
|
_ => {
|
||||||
|
if stream.peek(LitStr) {
|
||||||
|
let rawtext = stream.parse::<LitStr>().unwrap();
|
||||||
|
AttrType::BumpText(rawtext)
|
||||||
|
} else {
|
||||||
|
let toks = stream.parse::<Expr>()?;
|
||||||
|
AttrType::FieldTokens(toks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// consume comma if it exists
|
||||||
|
// we don't actually care if there *are* commas between attrs
|
||||||
|
if stream.peek(Token![,]) {
|
||||||
|
let _ = stream.parse::<Token![,]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attrs.push(ElementAttr {
|
||||||
|
name,
|
||||||
|
value: ty,
|
||||||
|
namespace: None,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for ElementAttr {
|
impl ToTokens for ElementAttr {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
let name = self.name.to_string();
|
let name = self.name.to_string();
|
||||||
let nameident = &self.name;
|
let nameident = &self.name;
|
||||||
let _attr_stream = TokenStream2::new();
|
|
||||||
|
|
||||||
match &self.ty {
|
let namespace = match &self.namespace {
|
||||||
AttrType::BumpText(value) => match name.as_str() {
|
Some(t) => quote! { Some(#t) },
|
||||||
"key" => {
|
None => quote! { None },
|
||||||
tokens.append_all(quote! {
|
};
|
||||||
.key2(format_args_f!(#value))
|
|
||||||
});
|
match &self.value {
|
||||||
}
|
AttrType::BumpText(value) => tokens.append_all(quote! {
|
||||||
_ => {
|
__cx.attr(#name, format_args_f!(#value), #namespace)
|
||||||
tokens.append_all(quote! {
|
}),
|
||||||
.attr(#name, format_args_f!(#value))
|
|
||||||
});
|
AttrType::FieldTokens(exp) => tokens.append_all(quote! {
|
||||||
}
|
__cx.attr(#name, #exp, #namespace)
|
||||||
},
|
}),
|
||||||
AttrType::FieldTokens(exp) => {
|
|
||||||
tokens.append_all(quote! {
|
// todo: move event handlers on to the elements or onto the nodefactory
|
||||||
.attr(#name, #exp)
|
AttrType::Event(event) => tokens.append_all(quote! {
|
||||||
});
|
dioxus::events::on::#nameident(__cx, #event)
|
||||||
}
|
}),
|
||||||
AttrType::Event(event) => {
|
|
||||||
tokens.append_all(quote! {
|
AttrType::EventTokens(event) => tokens.append_all(quote! {
|
||||||
.add_listener(dioxus::events::on::#nameident(__cx, #event))
|
dioxus::events::on::#nameident(__cx, #event)
|
||||||
});
|
}),
|
||||||
}
|
|
||||||
AttrType::EventTokens(event) => {
|
|
||||||
//
|
|
||||||
tokens.append_all(quote! {
|
|
||||||
.add_listener(dioxus::events::on::#nameident(__cx, #event))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,33 +80,19 @@ impl ToTokens for RsxRender {
|
|||||||
quote! {#inner}
|
quote! {#inner}
|
||||||
} else {
|
} else {
|
||||||
let childs = &self.roots;
|
let childs = &self.roots;
|
||||||
let children = quote! {
|
quote! { __cx.fragment_from_iter(&[ #(#childs),* ]) }
|
||||||
ChildrenList::new(__cx)
|
|
||||||
#( .add_child(#childs) )*
|
|
||||||
.finish()
|
|
||||||
};
|
|
||||||
quote! {
|
|
||||||
// #key_token,
|
|
||||||
dioxus::builder::vfragment(
|
|
||||||
__cx,
|
|
||||||
None,
|
|
||||||
#children
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match &self.custom_context {
|
match &self.custom_context {
|
||||||
// The `in cx` pattern allows directly rendering
|
// The `in cx` pattern allows directly rendering
|
||||||
Some(ident) => out_tokens.append_all(quote! {
|
Some(ident) => out_tokens.append_all(quote! {
|
||||||
#ident.render(dioxus::prelude::LazyNodes::new(move |__cx|{
|
#ident.render(dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory|{
|
||||||
// let bump = &__cx.bump();
|
|
||||||
#inner
|
#inner
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
// Otherwise we just build the LazyNode wrapper
|
// Otherwise we just build the LazyNode wrapper
|
||||||
None => out_tokens.append_all(quote! {
|
None => out_tokens.append_all(quote! {
|
||||||
dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory|{
|
dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory|{
|
||||||
// let bump = &__cx.bump();
|
|
||||||
#inner
|
#inner
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ impl ToTokens for Node {
|
|||||||
match &self {
|
match &self {
|
||||||
Node::Element(el) => el.to_tokens(tokens),
|
Node::Element(el) => el.to_tokens(tokens),
|
||||||
Node::Text(txt) => txt.to_tokens(tokens),
|
Node::Text(txt) => txt.to_tokens(tokens),
|
||||||
Node::RawExpr(exp) => exp.to_tokens(tokens),
|
Node::RawExpr(exp) => tokens.append_all(quote! {
|
||||||
|
__cx.fragment_from_iter(#exp)
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-63
@@ -1326,69 +1326,69 @@ mod tests {
|
|||||||
use crate::util::DebugDom;
|
use crate::util::DebugDom;
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_child_iterator() {
|
// fn test_child_iterator() {
|
||||||
static App: FC<()> = |cx| {
|
// static App: FC<()> = |cx| {
|
||||||
cx.render(rsx! {
|
// cx.render(rsx! {
|
||||||
Fragment {
|
// Fragment {
|
||||||
div {}
|
// div {}
|
||||||
h1 {}
|
// h1 {}
|
||||||
h2 {}
|
// h2 {}
|
||||||
h3 {}
|
// h3 {}
|
||||||
Fragment {
|
// Fragment {
|
||||||
"internal node"
|
// "internal node"
|
||||||
div {
|
// div {
|
||||||
"baller text shouldn't show up"
|
// "baller text shouldn't show up"
|
||||||
}
|
// }
|
||||||
p {
|
// p {
|
||||||
|
|
||||||
}
|
// }
|
||||||
Fragment {
|
// Fragment {
|
||||||
Fragment {
|
// Fragment {
|
||||||
"wow you really like framgents"
|
// "wow you really like framgents"
|
||||||
Fragment {
|
// Fragment {
|
||||||
"why are you like this"
|
// "why are you like this"
|
||||||
Fragment {
|
// Fragment {
|
||||||
"just stop now please"
|
// "just stop now please"
|
||||||
Fragment {
|
// Fragment {
|
||||||
"this hurts"
|
// "this hurts"
|
||||||
Fragment {
|
// Fragment {
|
||||||
"who needs this many fragments?????"
|
// "who needs this many fragments?????"
|
||||||
Fragment {
|
// Fragment {
|
||||||
"just... fine..."
|
// "just... fine..."
|
||||||
Fragment {
|
// Fragment {
|
||||||
"no"
|
// "no"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
"my text node 1"
|
// "my text node 1"
|
||||||
"my text node 2"
|
// "my text node 2"
|
||||||
"my text node 3"
|
// "my text node 3"
|
||||||
"my text node 4"
|
// "my text node 4"
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
};
|
// };
|
||||||
let mut dom = VirtualDom::new(App);
|
// let mut dom = VirtualDom::new(App);
|
||||||
let mut renderer = DebugDom::new();
|
// let mut renderer = DebugDom::new();
|
||||||
dom.rebuild(&mut renderer).unwrap();
|
// dom.rebuild(&mut renderer).unwrap();
|
||||||
let starter = dom.base_scope().root();
|
// let starter = dom.base_scope().root();
|
||||||
let ite = RealChildIterator::new(starter, &dom.components);
|
// let ite = RealChildIterator::new(starter, &dom.components);
|
||||||
for child in ite {
|
// for child in ite {
|
||||||
match child {
|
// match child {
|
||||||
VNode::Element(el) => println!("Found: Element {}", el.tag_name),
|
// VNode::Element(el) => println!("Found: Element {}", el.tag_name),
|
||||||
VNode::Text(t) => println!("Found: Text {:?}", t.text),
|
// VNode::Text(t) => println!("Found: Text {:?}", t.text),
|
||||||
|
|
||||||
// These would represent failing cases.
|
// // These would represent failing cases.
|
||||||
VNode::Fragment(_) => panic!("Found: Fragment"),
|
// VNode::Fragment(_) => panic!("Found: Fragment"),
|
||||||
VNode::Suspended { real } => panic!("Found: Suspended"),
|
// VNode::Suspended { real } => panic!("Found: Suspended"),
|
||||||
VNode::Component(_) => panic!("Found: Component"),
|
// VNode::Component(_) => panic!("Found: Component"),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
+141
-28
@@ -123,10 +123,25 @@ pub mod on {
|
|||||||
use super::VirtualEvent;
|
use super::VirtualEvent;
|
||||||
|
|
||||||
macro_rules! event_directory {
|
macro_rules! event_directory {
|
||||||
( $( $eventdata:ident($wrapper:ident): [ $( $name:ident )* ]; )* ) => {
|
( $(
|
||||||
$(
|
$(
|
||||||
|
#[$attr:meta]
|
||||||
|
)*
|
||||||
|
$eventdata:ident($wrapper:ident): [
|
||||||
|
$(
|
||||||
|
#[$method_attr:meta]
|
||||||
|
)*
|
||||||
|
$( $name:ident )*
|
||||||
|
];
|
||||||
|
)* ) => {
|
||||||
|
$(
|
||||||
|
$(#[$attr])*
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct $wrapper(pub Rc<dyn $eventdata>);
|
pub struct $wrapper(pub Rc<dyn $eventdata>);
|
||||||
|
|
||||||
|
// todo: derefing to the event is fine (and easy) but breaks some IDE stuff like (go to source)
|
||||||
|
// going to source in fact takes you to the source of Rc which is... less than useful
|
||||||
|
// Either we ask for this to be changed in Rust-analyzer or manually impkement the trait
|
||||||
impl Deref for $wrapper {
|
impl Deref for $wrapper {
|
||||||
type Target = Rc<dyn $eventdata>;
|
type Target = Rc<dyn $eventdata>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -134,6 +149,7 @@ pub mod on {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(#[$method_attr])*
|
||||||
$(
|
$(
|
||||||
pub fn $name<'a>(
|
pub fn $name<'a>(
|
||||||
c: &'_ NodeFactory<'a>,
|
c: &'_ NodeFactory<'a>,
|
||||||
@@ -155,17 +171,112 @@ pub mod on {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Dioxus Synthetic event system
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
event_directory! {
|
event_directory! {
|
||||||
ClipboardEventInner(ClipboardEvent): [copy cut paste];
|
|
||||||
CompositionEventInner(CompositionEvent): [compositionend compositionstart compositionupdate];
|
|
||||||
KeyboardEventInner(KeyboardEvent): [keydown keypress keyup];
|
|
||||||
FocusEventInner(FocusEvent): [focus blur];
|
|
||||||
FormEventInner(FormEvent): [change input invalid reset submit];
|
ClipboardEventInner(ClipboardEvent): [
|
||||||
MouseEventInner(MouseEvent): [
|
/// Called when "copy"
|
||||||
click contextmenu doubleclick drag dragend dragenter dragexit
|
oncopy
|
||||||
dragleave dragover dragstart drop mousedown mouseenter mouseleave
|
oncut
|
||||||
mousemove mouseout mouseover mouseup
|
onpaste
|
||||||
];
|
];
|
||||||
|
CompositionEventInner(CompositionEvent): [
|
||||||
|
oncompositionend
|
||||||
|
oncompositionstart
|
||||||
|
oncompositionupdate
|
||||||
|
];
|
||||||
|
KeyboardEventInner(KeyboardEvent): [
|
||||||
|
onkeydown
|
||||||
|
onkeypress
|
||||||
|
onkeyup
|
||||||
|
];
|
||||||
|
FocusEventInner(FocusEvent): [
|
||||||
|
onfocus
|
||||||
|
onblur
|
||||||
|
];
|
||||||
|
FormEventInner(FormEvent): [
|
||||||
|
onchange
|
||||||
|
oninput
|
||||||
|
oninvalid
|
||||||
|
onreset
|
||||||
|
onsubmit
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// The MouseEvent interface represents events that occur due to the user interacting with a pointing device (such as a mouse).
|
||||||
|
///
|
||||||
|
/// ## Trait implementation:
|
||||||
|
/// ```rust
|
||||||
|
/// fn alt_key(&self) -> bool;
|
||||||
|
/// fn button(&self) -> i16;
|
||||||
|
/// fn buttons(&self) -> u16;
|
||||||
|
/// fn client_x(&self) -> i32;
|
||||||
|
/// fn client_y(&self) -> i32;
|
||||||
|
/// fn ctrl_key(&self) -> bool;
|
||||||
|
/// fn meta_key(&self) -> bool;
|
||||||
|
/// fn page_x(&self) -> i32;
|
||||||
|
/// fn page_y(&self) -> i32;
|
||||||
|
/// fn screen_x(&self) -> i32;
|
||||||
|
/// fn screen_y(&self) -> i32;
|
||||||
|
/// fn shift_key(&self) -> bool;
|
||||||
|
/// fn get_modifier_state(&self, key_code: &str) -> bool;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Event Handlers
|
||||||
|
/// - click
|
||||||
|
/// - contextmenu
|
||||||
|
/// - doubleclick
|
||||||
|
/// - drag
|
||||||
|
/// - dragend
|
||||||
|
/// - dragenter
|
||||||
|
/// - dragexit
|
||||||
|
/// - dragleave
|
||||||
|
/// - dragover
|
||||||
|
/// - dragstart
|
||||||
|
/// - drop
|
||||||
|
/// - mousedown
|
||||||
|
/// - mouseenter
|
||||||
|
/// - mouseleave
|
||||||
|
/// - mousemove
|
||||||
|
/// - mouseout
|
||||||
|
/// - mouseover
|
||||||
|
/// - mouseup
|
||||||
|
MouseEventInner(MouseEvent): [
|
||||||
|
/// Onclick!
|
||||||
|
onclick
|
||||||
|
oncontextmenu
|
||||||
|
ondoubleclick
|
||||||
|
ondrag
|
||||||
|
ondragend
|
||||||
|
ondragenter
|
||||||
|
ondragexit
|
||||||
|
ondragleave
|
||||||
|
ondragover
|
||||||
|
ondragstart
|
||||||
|
ondrop
|
||||||
|
onmousedown
|
||||||
|
onmouseenter
|
||||||
|
onmouseleave
|
||||||
|
onmousemove
|
||||||
|
onmouseout
|
||||||
|
onmouseover
|
||||||
|
onmouseup
|
||||||
|
];
|
||||||
|
|
||||||
PointerEventInner(PointerEvent): [
|
PointerEventInner(PointerEvent): [
|
||||||
pointerdown pointermove pointerup pointercancel gotpointercapture
|
pointerdown pointermove pointerup pointercancel gotpointercapture
|
||||||
lostpointercapture pointerenter pointerleave pointerover pointerout
|
lostpointercapture pointerenter pointerleave pointerover pointerout
|
||||||
@@ -293,6 +404,8 @@ pub mod on {
|
|||||||
fn alt_key(&self) -> bool;
|
fn alt_key(&self) -> bool;
|
||||||
fn button(&self) -> i16;
|
fn button(&self) -> i16;
|
||||||
fn buttons(&self) -> u16;
|
fn buttons(&self) -> u16;
|
||||||
|
|
||||||
|
/// Get the X coordinate of the mouse relative to the window
|
||||||
fn client_x(&self) -> i32;
|
fn client_x(&self) -> i32;
|
||||||
fn client_y(&self) -> i32;
|
fn client_y(&self) -> i32;
|
||||||
fn ctrl_key(&self) -> bool;
|
fn ctrl_key(&self) -> bool;
|
||||||
@@ -597,25 +710,25 @@ pub mod on {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod tests {
|
// mod tests {
|
||||||
|
|
||||||
use std::rc::Rc;
|
// use std::rc::Rc;
|
||||||
|
|
||||||
use crate as dioxus;
|
// use crate as dioxus;
|
||||||
use crate::events::on::MouseEvent;
|
// use crate::events::on::MouseEvent;
|
||||||
use crate::prelude::*;
|
// use crate::prelude::*;
|
||||||
|
|
||||||
fn autocomplete() {
|
// fn autocomplete() {
|
||||||
// let v = move |evt| {
|
// // let v = move |evt| {
|
||||||
// let r = evt.alt_key();
|
// // let r = evt.alt_key();
|
||||||
// };
|
// // };
|
||||||
|
|
||||||
let g = rsx! {
|
// let g = rsx! {
|
||||||
button {
|
// button {
|
||||||
onclick: move |evt| {
|
// onclick: move |evt| {
|
||||||
let r = evt.alt_key();
|
// let r = evt.alt_key();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
@@ -13,6 +13,7 @@ pub mod serialize;
|
|||||||
|
|
||||||
pub mod arena;
|
pub mod arena;
|
||||||
pub mod component;
|
pub mod component;
|
||||||
|
pub mod styles;
|
||||||
pub mod util; // Logic for extending FC
|
pub mod util; // Logic for extending FC
|
||||||
|
|
||||||
pub mod debug_renderer;
|
pub mod debug_renderer;
|
||||||
@@ -54,6 +55,7 @@ pub(crate) mod innerlude {
|
|||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
||||||
use crate::nodes;
|
use crate::nodes;
|
||||||
|
pub use crate::styles::{AsAttr, StyleBuilder};
|
||||||
pub use crate::virtual_dom::Context;
|
pub use crate::virtual_dom::Context;
|
||||||
pub use crate::virtual_dom::Scoped;
|
pub use crate::virtual_dom::Scoped;
|
||||||
pub use nodes::*;
|
pub use nodes::*;
|
||||||
@@ -61,7 +63,7 @@ pub mod prelude {
|
|||||||
pub use crate::nodebuilder::LazyNodes;
|
pub use crate::nodebuilder::LazyNodes;
|
||||||
|
|
||||||
pub use crate::nodebuilder::ChildrenList;
|
pub use crate::nodebuilder::ChildrenList;
|
||||||
pub use crate::nodebuilder::NodeFactory;
|
pub use crate::nodebuilder::{DioxusElement, NodeFactory};
|
||||||
// pub use nodes::iterables::IterableNodes;
|
// pub use nodes::iterables::IterableNodes;
|
||||||
/// This type alias is an internal way of abstracting over the static functions that represent components.
|
/// This type alias is an internal way of abstracting over the static functions that represent components.
|
||||||
pub use crate::innerlude::FC;
|
pub use crate::innerlude::FC;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use bumpalo::Bump;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::VirtualEvent,
|
events::VirtualEvent,
|
||||||
innerlude::{Properties, VComponent, FC},
|
innerlude::{Properties, VComponent, VText, FC},
|
||||||
nodes::{Attribute, Listener, NodeKey, VNode},
|
nodes::{Attribute, Listener, NodeKey, VNode},
|
||||||
prelude::{VElement, VFragment},
|
prelude::{VElement, VFragment},
|
||||||
virtual_dom::{RealDomNode, Scope},
|
virtual_dom::{RealDomNode, Scope},
|
||||||
@@ -34,7 +34,6 @@ where
|
|||||||
cx: &'b NodeFactory<'a>,
|
cx: &'b NodeFactory<'a>,
|
||||||
key: NodeKey<'a>,
|
key: NodeKey<'a>,
|
||||||
tag_name: &'static str,
|
tag_name: &'static str,
|
||||||
// tag_name: &'a str,
|
|
||||||
listeners: Listeners,
|
listeners: Listeners,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
children: Children,
|
children: Children,
|
||||||
@@ -422,7 +421,11 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.attributes.push(Attribute { name, value });
|
self.attributes.push(Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
namespace: None,
|
||||||
|
});
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +458,11 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn bool_attr(mut self, name: &'static str, should_add: bool) -> Self {
|
pub fn bool_attr(mut self, name: &'static str, should_add: bool) -> Self {
|
||||||
if should_add {
|
if should_add {
|
||||||
self.attributes.push(Attribute { name, value: "" });
|
self.attributes.push(Attribute {
|
||||||
|
name,
|
||||||
|
value: "",
|
||||||
|
namespace: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -659,13 +666,17 @@ pub fn text3<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> VNode<'a
|
|||||||
// ```
|
// ```
|
||||||
// div {"abc"}
|
// div {"abc"}
|
||||||
// ```
|
// ```
|
||||||
|
VNode::text(raw_text(bump, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_text<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> &'a str {
|
||||||
match args.as_str() {
|
match args.as_str() {
|
||||||
Some(static_str) => VNode::text(static_str),
|
Some(static_str) => static_str,
|
||||||
None => {
|
None => {
|
||||||
use bumpalo::core_alloc::fmt::Write;
|
use bumpalo::core_alloc::fmt::Write;
|
||||||
let mut s = bumpalo::collections::String::new_in(bump);
|
let mut s = bumpalo::collections::String::new_in(bump);
|
||||||
s.write_fmt(args).unwrap();
|
s.write_fmt(args).unwrap();
|
||||||
VNode::text(s.into_bump_str())
|
s.into_bump_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,7 +694,11 @@ pub fn text3<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> VNode<'a
|
|||||||
/// let my_id_attr = attr("id", "my-id");
|
/// let my_id_attr = attr("id", "my-id");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
|
pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
|
||||||
Attribute { name, value }
|
Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
namespace: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn virtual_child<'a, T: Properties + 'a>(
|
pub fn virtual_child<'a, T: Properties + 'a>(
|
||||||
@@ -762,7 +777,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
/// Create an element builder
|
/// Create an element builder
|
||||||
pub fn element<'b>(
|
pub fn element<'b>(
|
||||||
&'b self,
|
&'b self,
|
||||||
tag_name: &'static str,
|
tag: impl DioxusElement,
|
||||||
) -> ElementBuilder<
|
) -> ElementBuilder<
|
||||||
'a,
|
'a,
|
||||||
'b,
|
'b,
|
||||||
@@ -770,7 +785,21 @@ impl<'a> NodeFactory<'a> {
|
|||||||
bumpalo::collections::Vec<'a, Attribute<'a>>,
|
bumpalo::collections::Vec<'a, Attribute<'a>>,
|
||||||
bumpalo::collections::Vec<'a, VNode<'a>>,
|
bumpalo::collections::Vec<'a, VNode<'a>>,
|
||||||
> {
|
> {
|
||||||
ElementBuilder::new(self, tag_name)
|
ElementBuilder::new(self, tag.tag_name())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attr(
|
||||||
|
&self,
|
||||||
|
name: &'static str,
|
||||||
|
val: Arguments<'a>,
|
||||||
|
namespace: Option<&'static str>,
|
||||||
|
) -> Attribute<'a> {
|
||||||
|
let value = raw_text(self.bump(), val);
|
||||||
|
Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
namespace,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn child_list(&self) -> ChildrenList {
|
pub fn child_list(&self) -> ChildrenList {
|
||||||
@@ -813,3 +842,11 @@ impl Debug for NodeFactory<'_> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait DioxusElement {
|
||||||
|
const TAG_NAME: &'static str;
|
||||||
|
const NAME_SPACE: Option<&'static str>;
|
||||||
|
fn tag_name(&self) -> &'static str {
|
||||||
|
Self::TAG_NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -216,6 +216,11 @@ pub struct VElement<'a> {
|
|||||||
pub struct Attribute<'a> {
|
pub struct Attribute<'a> {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
|
|
||||||
|
/// If an attribute is "namespaced", then it belongs to a group
|
||||||
|
/// The most common namespace is the "style" namespace
|
||||||
|
// pub is_dynamic: bool,
|
||||||
|
pub namespace: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Attribute<'a> {
|
impl<'a> Attribute<'a> {
|
||||||
|
|||||||
+108
-72
@@ -1,6 +1,10 @@
|
|||||||
//! Dedicated styling system for Components
|
//! Dedicated styling system for Components
|
||||||
//! ---------------------------------------
|
//! ---------------------------------------
|
||||||
//!
|
//!
|
||||||
|
//! In the future, we'd like to move this out of Dioxus core or build a better, more general abstraction. For now, dedicated
|
||||||
|
//! styling is more-or-less hardcoded into Dioxus.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
@@ -8,76 +12,108 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
|
|
||||||
enum Styles {
|
use crate::{
|
||||||
background,
|
innerlude::Attribute,
|
||||||
backgroundAttachment,
|
nodebuilder::{text3, NodeFactory},
|
||||||
backgroundColor,
|
};
|
||||||
backgroundImage,
|
|
||||||
backgroundPosition,
|
pub struct StyleBuilder;
|
||||||
backgroundRepeat,
|
pub trait AsAttr<'a> {
|
||||||
border,
|
fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a>;
|
||||||
borderBottom,
|
}
|
||||||
borderBottomColor,
|
impl<'a> AsAttr<'a> for std::fmt::Arguments<'a> {
|
||||||
borderBottomStyle,
|
fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a> {
|
||||||
borderBottomWidth,
|
fac.attr(field, self, Some("style"))
|
||||||
borderColor,
|
}
|
||||||
borderLeft,
|
}
|
||||||
borderLeftColor,
|
|
||||||
borderLeftStyle,
|
macro_rules! build_styles {
|
||||||
borderLeftWidth,
|
($ ($name:ident: $lit:literal,)* ) => {
|
||||||
borderRight,
|
impl StyleBuilder {
|
||||||
borderRightColor,
|
$(
|
||||||
borderRightStyle,
|
pub fn $name<'a>(f: &NodeFactory<'a>, args: impl AsAttr<'a>) -> Attribute<'a> {
|
||||||
borderRightWidth,
|
args.to_attr($lit, f)
|
||||||
borderStyle,
|
}
|
||||||
borderTop,
|
)*
|
||||||
borderTopColor,
|
}
|
||||||
borderTopStyle,
|
};
|
||||||
borderTopWidth,
|
}
|
||||||
borderWidth,
|
|
||||||
clear,
|
build_styles! {
|
||||||
clip,
|
background: "background",
|
||||||
color,
|
background_attachment: "background-attachment",
|
||||||
cursor,
|
background_color: "background-color",
|
||||||
display,
|
background_image: "background-image",
|
||||||
filter,
|
background_position: "background-position",
|
||||||
cssFloat,
|
background_repeat: "background-repeat",
|
||||||
font,
|
border: "border",
|
||||||
fontFamily,
|
border_bottom: "border-bottom",
|
||||||
fontSize,
|
border_bottom_color: "border-bottom-color",
|
||||||
fontVariant,
|
border_bottom_style: "border-bottom-style",
|
||||||
fontWeight,
|
border_bottom_width: "border-bottom-width",
|
||||||
height,
|
border_color: "border-color",
|
||||||
left,
|
border_left: "border-left",
|
||||||
letterSpacing,
|
border_left_color: "border-left-color",
|
||||||
lineHeight,
|
border_left_style: "border-left-style",
|
||||||
listStyle,
|
border_left_width: "border-left-width",
|
||||||
listStyleImage,
|
border_right: "border-right",
|
||||||
listStylePosition,
|
border_right_color: "border-right-color",
|
||||||
listStyleType,
|
border_right_style: "border-right-style",
|
||||||
margin,
|
border_right_width: "border-right-width",
|
||||||
marginBottom,
|
border_style: "border-style",
|
||||||
marginLeft,
|
border_top: "border-top",
|
||||||
marginRight,
|
border_top_color: "border-top-color",
|
||||||
marginTop,
|
border_top_style: "border-top-style",
|
||||||
overflow,
|
border_top_width: "border-top-width",
|
||||||
padding,
|
border_width: "border-width",
|
||||||
paddingBottom,
|
clear: "clear",
|
||||||
paddingLeft,
|
clip: "clip",
|
||||||
paddingRight,
|
color: "color",
|
||||||
paddingTop,
|
cursor: "cursor",
|
||||||
pageBreakAfter,
|
display: "display",
|
||||||
pageBreakBefore,
|
filter: "filter",
|
||||||
position,
|
css_float: "css-float",
|
||||||
strokeDasharray,
|
font: "font",
|
||||||
strokeDashoffset,
|
font_family: "font-family",
|
||||||
textAlign,
|
font_size: "font-size",
|
||||||
textDecoration,
|
font_variant: "font-variant",
|
||||||
textIndent,
|
font_weight: "font-weight",
|
||||||
textTransform,
|
height: "height",
|
||||||
top,
|
left: "left",
|
||||||
verticalAlign,
|
letter_spacing: "letter-spacing",
|
||||||
visibility,
|
line_height: "line-height",
|
||||||
width,
|
list_style: "list-style",
|
||||||
zIndex,
|
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",
|
||||||
|
overflow: "overflow",
|
||||||
|
padding: "padding",
|
||||||
|
padding_bottom: "padding-bottom",
|
||||||
|
padding_left: "padding-left",
|
||||||
|
padding_right: "padding-right",
|
||||||
|
padding_top: "padding-top",
|
||||||
|
page_break_after: "page-break-after",
|
||||||
|
page_break_before: "page-break-before",
|
||||||
|
position: "position",
|
||||||
|
stroke_dasharray: "stroke-dasharray",
|
||||||
|
stroke_dashoffset: "stroke-dashoffset",
|
||||||
|
text_align: "text-align",
|
||||||
|
text_decoration: "text-decoration",
|
||||||
|
text_indent: "text-indent",
|
||||||
|
text_transform: "text-transform",
|
||||||
|
top: "top",
|
||||||
|
vertical_align: "vertical-align",
|
||||||
|
visibility: "visibility",
|
||||||
|
width: "width",
|
||||||
|
z_index: "z-index",
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example(f: &NodeFactory) {
|
||||||
|
let style_list = &[("width", "10"), ("text-decoration", "")];
|
||||||
|
let styles = &[StyleBuilder::background(f, format_args!("10"))];
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dioxus-core = { path="../core" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
scraper = "0.12.0"
|
scraper = "0.12.0"
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,42 +0,0 @@
|
|||||||
use scraper::{Html, Selector};
|
|
||||||
|
|
||||||
struct AttrClass {
|
|
||||||
attr_name: String,
|
|
||||||
attr_doc_link: String,
|
|
||||||
target_elements: Vec<String>,
|
|
||||||
docs: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
const contents: &str = "";
|
|
||||||
// let contents = include_str!("./attrlist.html");
|
|
||||||
fn main() {
|
|
||||||
let mut items: Vec<AttrClass> = Vec::new();
|
|
||||||
|
|
||||||
let fragment = Html::parse_fragment(contents);
|
|
||||||
|
|
||||||
let ul_selector = Selector::parse("tbody").unwrap();
|
|
||||||
let li_selector = Selector::parse("tr").unwrap();
|
|
||||||
|
|
||||||
let ul = fragment.select(&ul_selector).next().unwrap();
|
|
||||||
for element in ul.select(&li_selector) {
|
|
||||||
let mut childs = element.children().into_iter();
|
|
||||||
|
|
||||||
let attr_field_node = childs.next().unwrap();
|
|
||||||
let elements_node = childs.next().unwrap();
|
|
||||||
let description_node = childs.next().unwrap();
|
|
||||||
|
|
||||||
// let attr_name = { todo!() };
|
|
||||||
// let attr_doc_link = { todo!() };
|
|
||||||
// let target_elements = { todo!() };
|
|
||||||
|
|
||||||
// let docs = description_node.text();
|
|
||||||
|
|
||||||
// todo!()
|
|
||||||
// items.push(AttrClass {
|
|
||||||
// attr_name,
|
|
||||||
// attr_doc_link,
|
|
||||||
// target_elements,
|
|
||||||
// docs,
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
//! POC: Planning the layout of a single element type
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! The ultimate goal with a dedicated namespace is three-fold:
|
|
||||||
//! - compile-time correct templates preventing misuse of elemnents
|
|
||||||
//! - deep integration of DSL with IDE
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
|
|
||||||
struct NodeFactory {}
|
|
||||||
|
|
||||||
struct div<'a>(&NodeFactory);
|
|
||||||
impl<'a> div<'a> {
|
|
||||||
fn new(cx: &NodeFactory) -> Self {
|
|
||||||
div(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
||||||
fn factory(
|
|
||||||
// this is your mom
|
|
||||||
cx: &NodeFactory,
|
|
||||||
) {
|
|
||||||
div::new(cx);
|
|
||||||
rsx! {
|
|
||||||
div {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,22 @@
|
|||||||
trait DioxusElement {
|
//! # Dioxus Namespace for HTML
|
||||||
const TAG_NAME: &'static str;
|
//!
|
||||||
const NAME_SPACE: Option<&'static str>;
|
//! This crate provides a set of compile-time correct HTML elements that can be used with the Rsx and Html macros.
|
||||||
}
|
//! This system allows users to easily build new tags, new types, and customize the output of the Rsx and Html macros.
|
||||||
|
//!
|
||||||
|
//! An added benefit of this approach is the ability to lend comprehensive documentation on how to use these elements inside
|
||||||
|
//! of the Rsx and Html macros. Each element comes with a substantial amount of documentation on how to best use it, hopefully
|
||||||
|
//! making the development cycle quick.
|
||||||
|
|
||||||
struct myinput {}
|
use dioxus_core::prelude::{DioxusElement, NodeFactory};
|
||||||
|
|
||||||
struct NodeBuilder {}
|
|
||||||
impl NodeBuilder {
|
|
||||||
// fn add_attr()
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! builder_constructors {
|
macro_rules! builder_constructors {
|
||||||
( $(
|
( $( $(#[$attr:meta])* $name:ident; )* ) => {
|
||||||
$(#[$attr:meta])*
|
|
||||||
$name:ident;
|
|
||||||
// {
|
|
||||||
// attrs: [
|
|
||||||
// $(
|
|
||||||
// $(#[$attr2:meta])*
|
|
||||||
// $attr3:ident
|
|
||||||
// ,
|
|
||||||
// )*
|
|
||||||
// ];
|
|
||||||
// }
|
|
||||||
)* ) => {
|
|
||||||
$(
|
$(
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
$(#[$attr])*
|
$(#[$attr])*
|
||||||
pub struct $name<'a> {
|
pub struct $name;
|
||||||
// the we wrap the builder, shielding what can be added
|
|
||||||
builder: &'a NodeBuilder
|
impl DioxusElement for $name {
|
||||||
}
|
|
||||||
impl<'a> DioxusElement for $name<'a> {
|
|
||||||
const TAG_NAME: &'static str = stringify!($name);
|
const TAG_NAME: &'static str = stringify!($name);
|
||||||
const NAME_SPACE: Option<&'static str> = None;
|
const NAME_SPACE: Option<&'static str> = None;
|
||||||
}
|
}
|
||||||
@@ -48,6 +33,10 @@ macro_rules! builder_constructors {
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
||||||
//
|
//
|
||||||
// Does not include obsolete elements.
|
// Does not include obsolete elements.
|
||||||
|
|
||||||
|
// This namespace represents a collection of modern HTML-5 compatiable elements.
|
||||||
|
//
|
||||||
|
// This list does not include obsolete, deprecated, experimental, or poorly supported elements.
|
||||||
builder_constructors! {
|
builder_constructors! {
|
||||||
// Document metadata
|
// Document metadata
|
||||||
|
|
||||||
@@ -109,18 +98,57 @@ builder_constructors! {
|
|||||||
/// element.
|
/// element.
|
||||||
header;
|
header;
|
||||||
|
|
||||||
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
|
/// [`<h1>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1)
|
||||||
/// element.
|
/// element.
|
||||||
|
///
|
||||||
|
/// # About
|
||||||
|
/// - The HTML `<h1>` element is found within the `<body>` tag.
|
||||||
|
/// - Headings can range from `<h1>` to `<h6>`.
|
||||||
|
/// - The most important heading is `<h1>` and the least important heading is `<h6>`.
|
||||||
|
/// - The `<h1>` heading is the first heading in the document.
|
||||||
|
/// - The `<h1>` heading is usually a large bolded font.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// ```
|
||||||
|
/// html!(<h1> A header element </h1>)
|
||||||
|
/// rsx!(h1 { "A header element" })
|
||||||
|
/// LazyNodes::new(|f| f.el(h1).children([f.text("A header element")]).finish())
|
||||||
|
/// ```
|
||||||
h1;
|
h1;
|
||||||
|
|
||||||
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
|
/// [`<h2>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2)
|
||||||
/// element.
|
/// element.
|
||||||
|
///
|
||||||
|
/// # About
|
||||||
|
/// - The HTML `<h2>` element is found within the `<body>` tag.
|
||||||
|
/// - Headings can range from `<h1>` to `<h6>`.
|
||||||
|
/// - The most important heading is `<h1>` and the least important heading is `<h6>`.
|
||||||
|
/// - The `<h2>` heading is the second heading in the document.
|
||||||
|
/// - The `<h2>` heading is usually a large bolded font.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// ```
|
||||||
|
/// html!(<h2> A header element </h2>)
|
||||||
|
/// rsx!(h2 { "A header element" })
|
||||||
|
/// LazyNodes::new(|f| f.el(h2).children([f.text("A header element")]).finish())
|
||||||
|
/// ```
|
||||||
h2;
|
h2;
|
||||||
|
|
||||||
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
|
/// [`<h3>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3)
|
||||||
/// element.
|
/// element.
|
||||||
|
///
|
||||||
|
/// # About
|
||||||
|
/// - The HTML <h1> element is found within the <body> tag.
|
||||||
|
/// - Headings can range from <h1> to <h6>.
|
||||||
|
/// - The most important heading is <h1> and the least important heading is <h6>.
|
||||||
|
/// - The <h1> heading is the first heading in the document.
|
||||||
|
/// - The <h1> heading is usually a large bolded font.
|
||||||
h3;
|
h3;
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
|
/// [`<h4>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4)
|
||||||
@@ -134,10 +162,7 @@ builder_constructors! {
|
|||||||
/// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
|
/// [`<h6>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6)
|
||||||
/// element.
|
/// element.
|
||||||
h6;
|
h6;
|
||||||
/// Build a
|
|
||||||
/// [`<hgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup)
|
|
||||||
/// element.
|
|
||||||
hgroup;
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
|
/// [`<main>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main)
|
||||||
/// element.
|
/// element.
|
||||||
@@ -268,22 +293,17 @@ builder_constructors! {
|
|||||||
/// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
|
/// [`<q>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q)
|
||||||
/// element.
|
/// element.
|
||||||
q;
|
q;
|
||||||
/// Build a
|
|
||||||
/// [`<rb>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rb)
|
|
||||||
/// element.
|
|
||||||
rb;
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
|
/// [`<rp>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp)
|
||||||
/// element.
|
/// element.
|
||||||
rp;
|
rp;
|
||||||
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
|
/// [`<rt>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rt)
|
||||||
/// element.
|
/// element.
|
||||||
rt;
|
rt;
|
||||||
/// Build a
|
|
||||||
/// [`<rtc>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rtc)
|
|
||||||
/// element.
|
|
||||||
rtc;
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
|
/// [`<ruby>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby)
|
||||||
/// element.
|
/// element.
|
||||||
@@ -521,18 +541,8 @@ builder_constructors! {
|
|||||||
/// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
|
/// [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
|
||||||
/// element.
|
/// element.
|
||||||
details;
|
details;
|
||||||
/// Build a
|
|
||||||
/// [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
|
|
||||||
/// element.
|
|
||||||
dialog;
|
|
||||||
/// Build a
|
|
||||||
/// [`<menu>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu)
|
|
||||||
/// element.
|
|
||||||
menu;
|
|
||||||
/// Build a
|
|
||||||
/// [`<menuitem>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menuitem)
|
|
||||||
/// element.
|
|
||||||
menuitem;
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
|
/// [`<summary>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
|
||||||
/// element.
|
/// element.
|
||||||
@@ -552,7 +562,6 @@ builder_constructors! {
|
|||||||
|
|
||||||
builder_constructors! {
|
builder_constructors! {
|
||||||
// SVG components
|
// SVG components
|
||||||
|
|
||||||
/// Build a
|
/// Build a
|
||||||
/// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
|
/// [`<svg>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg)
|
||||||
/// element.
|
/// element.
|
||||||
@@ -590,38 +599,3 @@ builder_constructors! {
|
|||||||
/// element.
|
/// element.
|
||||||
image <> "http://www.w3.org/2000/svg";
|
image <> "http://www.w3.org/2000/svg";
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_attrs {
|
|
||||||
( $(
|
|
||||||
$(#[$attr:meta])*
|
|
||||||
$name:ident: [$($f:ident)*];
|
|
||||||
)* ) => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_attrs! {
|
|
||||||
/// Indicates whether the user can interact with the element
|
|
||||||
disabled: [button command fieldset input keygen optgroup option select textarea];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
fn r#type() {}
|
|
||||||
|
|
||||||
struct MyThing {}
|
|
||||||
impl MyThing {
|
|
||||||
/// eat my socks
|
|
||||||
fn r#type(&self) -> &Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn nothing(&self) -> &Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn test() {
|
|
||||||
let t = MyThing {};
|
|
||||||
t.r#type().r#nothing();
|
|
||||||
|
|
||||||
// let g = div::TAG_NAME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,16 +11,6 @@ use dioxus_core::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use DomEdits::*;
|
use DomEdits::*;
|
||||||
|
|
||||||
fn test() {
|
|
||||||
const App: FC<()> = |cx| cx.render(rsx! { div {}});
|
|
||||||
let mut vi = VirtualDom::new(App);
|
|
||||||
let mut real = WebviewDom {
|
|
||||||
edits: Vec::new(),
|
|
||||||
node_counter: 0,
|
|
||||||
};
|
|
||||||
vi.rebuild(&mut real);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WebviewDom<'bump> {
|
pub struct WebviewDom<'bump> {
|
||||||
pub edits: Vec<DomEdits<'bump>>,
|
pub edits: Vec<DomEdits<'bump>>,
|
||||||
pub node_counter: u64,
|
pub node_counter: u64,
|
||||||
|
|||||||
Reference in new issue
Block a user