Feat: update readme and examples
This commit is contained in:
1 parent
c8bb392cad
commit
ffaf687896
12 files changed
+608
-63
No files matched your search
@@ -392,8 +392,6 @@ impl ToTokens for ToToksCtx<&LitStr> {
|
||||
}
|
||||
}
|
||||
|
||||
mod styles {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
fn parse(input: &str) -> super::Result<super::HtmlRender> {
|
||||
|
||||
@@ -12,6 +12,7 @@ use syn::{
|
||||
mod fc;
|
||||
mod htm;
|
||||
mod ifmt;
|
||||
mod rsxt;
|
||||
// mod styles;
|
||||
|
||||
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
|
||||
@@ -25,6 +26,18 @@ pub fn html(s: TokenStream) -> TokenStream {
|
||||
html.to_token_stream().into()
|
||||
}
|
||||
|
||||
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
|
||||
/// We aim to keep functional parity with html templates.
|
||||
#[proc_macro]
|
||||
pub fn rsx(s: TokenStream) -> TokenStream {
|
||||
let template: rsxt::RsxRender = match syn::parse(s) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
template.to_token_stream().into()
|
||||
}
|
||||
|
||||
/// Label a function or static closure as a functional component.
|
||||
/// This macro reduces the need to create a separate properties struct.
|
||||
#[proc_macro_attribute]
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
|
||||
An example usage of rsx! would look like this:
|
||||
```ignore
|
||||
ctx.render(rsx!{
|
||||
div {
|
||||
h1 { "Example" },
|
||||
p {
|
||||
tag: "type",
|
||||
abc: 123,
|
||||
enabled: true,
|
||||
class: "big small wide short",
|
||||
|
||||
a { "abcder" },
|
||||
h2 { "whatsup", class: "abc-123" },
|
||||
CustomComponent { a: 123, b: 456, key: "1" },
|
||||
{ 0..3.map(|i| rsx!{ h1 {"{:i}"} }) },
|
||||
{expr}
|
||||
|
||||
// expr can take:
|
||||
// - iterator
|
||||
// - |bump| { }
|
||||
// - value (gets formatted as &str)
|
||||
// - ... more as we upgrade it
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
each element is given by tag { [Attr] }
|
||||
|
||||
*/
|
||||
use syn::parse::ParseBuffer;
|
||||
|
||||
use {
|
||||
proc_macro::TokenStream,
|
||||
proc_macro2::{Span, TokenStream as TokenStream2},
|
||||
quote::{quote, ToTokens, TokenStreamExt},
|
||||
syn::{
|
||||
ext::IdentExt,
|
||||
parse::{Parse, ParseStream},
|
||||
token, Error, Expr, ExprClosure, Ident, LitBool, LitStr, Path, Result, Token,
|
||||
},
|
||||
};
|
||||
|
||||
// ==============================================
|
||||
// Parse any stream coming from the rsx! macro
|
||||
// ==============================================
|
||||
pub struct RsxRender {}
|
||||
|
||||
impl Parse for RsxRender {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let g: Element = input.parse()?;
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for RsxRender {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let new_toks = quote! {
|
||||
move |_| {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
new_toks.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Parse any div {} as a VElement
|
||||
// ==============================================
|
||||
enum Node {
|
||||
Element(Element),
|
||||
Text(TextNode),
|
||||
}
|
||||
|
||||
impl ToTokens for ToToksCtx<&Node> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match &self.inner {
|
||||
Node::Element(el) => self.recurse(el).to_tokens(tokens),
|
||||
Node::Text(txt) => self.recurse(txt).to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn peek(s: ParseStream) -> bool {
|
||||
(s.peek(Token![<]) && !s.peek2(Token![/])) || s.peek(token::Brace) || s.peek(LitStr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Node {
|
||||
fn parse(s: ParseStream) -> Result<Self> {
|
||||
Ok(if s.peek(Token![<]) {
|
||||
Node::Element(s.parse()?)
|
||||
} else {
|
||||
Node::Text(s.parse()?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Parse the VNode::Element type
|
||||
/// =======================================
|
||||
/// - [ ] Allow VComponent
|
||||
///
|
||||
///
|
||||
struct Element {
|
||||
name: Ident,
|
||||
attrs: Vec<Attr>,
|
||||
children: MaybeExpr<Vec<Node>>,
|
||||
}
|
||||
|
||||
impl ToTokens for ToToksCtx<&Element> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
// let ctx = self.ctx;
|
||||
let name = &self.inner.name;
|
||||
tokens.append_all(quote! {
|
||||
dioxus::builder::#name(bump)
|
||||
});
|
||||
for attr in self.inner.attrs.iter() {
|
||||
self.recurse(attr).to_tokens(tokens);
|
||||
}
|
||||
match &self.inner.children {
|
||||
MaybeExpr::Expr(expr) => tokens.append_all(quote! {
|
||||
.children(#expr)
|
||||
}),
|
||||
MaybeExpr::Literal(nodes) => {
|
||||
let mut children = nodes.iter();
|
||||
if let Some(child) = children.next() {
|
||||
let mut inner_toks = TokenStream2::new();
|
||||
self.recurse(child).to_tokens(&mut inner_toks);
|
||||
while let Some(child) = children.next() {
|
||||
quote!(,).to_tokens(&mut inner_toks);
|
||||
self.recurse(child).to_tokens(&mut inner_toks);
|
||||
}
|
||||
tokens.append_all(quote! {
|
||||
.children([#inner_toks])
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
tokens.append_all(quote! {
|
||||
.finish()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Element {
|
||||
fn parse(s: ParseStream) -> Result<Self> {
|
||||
// steps:
|
||||
// grab ident as name
|
||||
// peak to the next character
|
||||
// ensure it's a {
|
||||
|
||||
// s.parse::<Token![<]>()?;
|
||||
let name = Ident::parse_any(s)?;
|
||||
|
||||
let content: ParseBuffer;
|
||||
// parse the guts of the div {} tag into the content buffer
|
||||
syn::braced!(content in s);
|
||||
|
||||
// s.
|
||||
// s.parse::<Token!["{"]>()?;
|
||||
// s.parse()
|
||||
// s.parse_terminated(parser)
|
||||
// s.parse::<token::Brace>()?;
|
||||
|
||||
let mut attrs = vec![];
|
||||
// let mut children = vec![];
|
||||
let mut children: Vec<Node> = vec![];
|
||||
|
||||
// // keep looking for attributes
|
||||
// while !s.peek(Token![>]) {
|
||||
// // self-closing
|
||||
// if s.peek(Token![/]) {
|
||||
// s.parse::<Token![/]>()?;
|
||||
// s.parse::<Token![>]>()?;
|
||||
// return Ok(Self {
|
||||
// name,
|
||||
// attrs,
|
||||
// children: MaybeExpr::Literal(vec![]),
|
||||
// });
|
||||
// }
|
||||
// attrs.push(s.parse()?);
|
||||
// }
|
||||
// s.parse::<Token![>]>()?;
|
||||
|
||||
// // Contents of an element can either be a brace (in which case we just copy verbatim), or a
|
||||
// // sequence of nodes.
|
||||
// let children = if s.peek(token::Brace) {
|
||||
// // expr
|
||||
// let content;
|
||||
// syn::braced!(content in s);
|
||||
// MaybeExpr::Expr(content.parse()?)
|
||||
// } else {
|
||||
// // nodes
|
||||
// let mut children = vec![];
|
||||
// while !(s.peek(Token![<]) && s.peek2(Token![/])) {
|
||||
// children.push(s.parse()?);
|
||||
// }
|
||||
// MaybeExpr::Literal(children)
|
||||
// };
|
||||
|
||||
// // closing element
|
||||
// s.parse::<Token![<]>()?;
|
||||
// s.parse::<Token![/]>()?;
|
||||
// let close = Ident::parse_any(s)?;
|
||||
// if close.to_string() != name.to_string() {
|
||||
// return Err(Error::new_spanned(
|
||||
// close,
|
||||
// "closing element does not match opening",
|
||||
// ));
|
||||
// }
|
||||
// s.parse::<Token![>]>()?;
|
||||
Ok(Self {
|
||||
name,
|
||||
attrs,
|
||||
children,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Parse a VElement's Attributes
|
||||
/// =======================================
|
||||
/// - [ ] Allow expressions as attribute
|
||||
///
|
||||
///
|
||||
struct Attr {
|
||||
name: Ident,
|
||||
ty: AttrType,
|
||||
}
|
||||
|
||||
impl Parse for Attr {
|
||||
fn parse(s: ParseStream) -> Result<Self> {
|
||||
let mut name = Ident::parse_any(s)?;
|
||||
let name_str = name.to_string();
|
||||
s.parse::<Token![=]>()?;
|
||||
|
||||
// Check if this is an event handler
|
||||
// If so, parse into literal tokens
|
||||
let ty = if name_str.starts_with("on") {
|
||||
// remove the "on" bit
|
||||
name = Ident::new(&name_str.trim_start_matches("on"), name.span());
|
||||
let content;
|
||||
syn::braced!(content in s);
|
||||
// AttrType::Value(content.parse()?)
|
||||
AttrType::Event(content.parse()?)
|
||||
// AttrType::Event(content.parse()?)
|
||||
} else {
|
||||
let lit_str = if name_str == "style" && s.peek(token::Brace) {
|
||||
// special-case to deal with literal styles.
|
||||
let outer;
|
||||
syn::braced!(outer in s);
|
||||
// double brace for inline style.
|
||||
// todo!("Style support not ready yet");
|
||||
|
||||
// if outer.peek(token::Brace) {
|
||||
// let inner;
|
||||
// syn::braced!(inner in outer);
|
||||
// let styles: Styles = inner.parse()?;
|
||||
// MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
|
||||
// } else {
|
||||
// just parse as an expression
|
||||
MaybeExpr::Expr(outer.parse()?)
|
||||
// }
|
||||
} else {
|
||||
s.parse()?
|
||||
};
|
||||
AttrType::Value(lit_str)
|
||||
};
|
||||
Ok(Attr { name, ty })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ToToksCtx<&Attr> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = self.inner.name.to_string();
|
||||
let mut attr_stream = TokenStream2::new();
|
||||
match &self.inner.ty {
|
||||
AttrType::Value(value) => {
|
||||
let value = self.recurse(value);
|
||||
tokens.append_all(quote! {
|
||||
.attr(#name, #value)
|
||||
});
|
||||
}
|
||||
AttrType::Event(event) => {
|
||||
tokens.append_all(quote! {
|
||||
.on(#name, #event)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AttrType {
|
||||
Value(MaybeExpr<LitStr>),
|
||||
Event(ExprClosure),
|
||||
// todo Bool(MaybeExpr<LitBool>)
|
||||
}
|
||||
|
||||
/// =======================================
|
||||
/// Parse just plain text
|
||||
/// =======================================
|
||||
/// - [ ] Perform formatting automatically
|
||||
///
|
||||
///
|
||||
struct TextNode(MaybeExpr<LitStr>);
|
||||
|
||||
impl Parse for TextNode {
|
||||
fn parse(s: ParseStream) -> Result<Self> {
|
||||
Ok(Self(s.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ToToksCtx<&TextNode> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let mut token_stream = TokenStream2::new();
|
||||
self.recurse(&self.inner.0).to_tokens(&mut token_stream);
|
||||
tokens.append_all(quote! {
|
||||
{
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
let mut s = bumpalo::collections::String::new_in(bump);
|
||||
s.write_fmt(format_args_f!(#token_stream)).unwrap();
|
||||
dioxus::builder::text2(s)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
enum MaybeExpr<T> {
|
||||
Literal(T),
|
||||
Expr(Expr),
|
||||
}
|
||||
|
||||
impl<T: Parse> Parse for MaybeExpr<T> {
|
||||
fn parse(s: ParseStream) -> Result<Self> {
|
||||
if s.peek(token::Brace) {
|
||||
let content;
|
||||
syn::braced!(content in s);
|
||||
Ok(MaybeExpr::Expr(content.parse()?))
|
||||
} else {
|
||||
Ok(MaybeExpr::Literal(s.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ToTokens for ToToksCtx<&'a MaybeExpr<T>>
|
||||
where
|
||||
T: 'a,
|
||||
ToToksCtx<&'a T>: ToTokens,
|
||||
{
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match &self.inner {
|
||||
MaybeExpr::Literal(v) => self.recurse(v).to_tokens(tokens),
|
||||
MaybeExpr::Expr(expr) => expr.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ToTokens context
|
||||
struct ToToksCtx<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<'a, T> ToToksCtx<T> {
|
||||
fn new(inner: T) -> Self {
|
||||
ToToksCtx { inner }
|
||||
}
|
||||
|
||||
fn recurse<U>(&self, inner: U) -> ToToksCtx<U> {
|
||||
ToToksCtx { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ToToksCtx<&LitStr> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.inner.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ struct Context2<'a, P> {
|
||||
rops: &'a P, // _p: PhantomData<&'a ()>,
|
||||
}
|
||||
impl<'a, P> Context2<'a, P> {
|
||||
fn view(self, _f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree {
|
||||
fn render(self, _f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree {
|
||||
DTree {}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ impl<'a> Context<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn view(self, _f: impl FnOnce(&'a String) + 'a) {}
|
||||
fn render(self, _f: impl FnOnce(&'a String) + 'a) {}
|
||||
// fn view(self, f: impl for<'b> FnOnce(&'a String) + 'a) {}
|
||||
// fn view(self, f: impl for<'b> FnOnce(&'b String) + 'a) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn Example(ctx: Context, props: ()) -> DomTree {
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
<h1 tag="type" abc=123 class="big..."> "title1" "title2" </h1>
|
||||
|
||||
<h1 alsd> alkjsd </h1>
|
||||
|
||||
|
||||
h1 { tag: "type", abc: 123, class: "big small wide short",
|
||||
"title1"
|
||||
"title1"
|
||||
"title1"
|
||||
"title"
|
||||
}
|
||||
|
||||
h1 ("title") {
|
||||
tag: "type",
|
||||
abc: 123,
|
||||
class: "big small wide short",
|
||||
}
|
||||
|
||||
// <button
|
||||
// class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
// onclick={move |_| set_name("jill")}
|
||||
// onclick={move |_| set_name("jill")}
|
||||
// >
|
||||
// "Jill!"
|
||||
// </button>
|
||||
|
||||
|
||||
button { "Jill!",
|
||||
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick: move |_| set_name("jill"),
|
||||
onclick: move |_| set_name("jill"),
|
||||
}
|
||||
|
||||
button {
|
||||
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick: move |_| set_name("jill"),
|
||||
onclick: move |_| set_name("jill"),
|
||||
// this is valid
|
||||
"Jill!",
|
||||
// this is also valid
|
||||
{"Jill!"}
|
||||
}
|
||||
|
||||
h1 { "Text", class: "inline-block py-4 px-8 mr-6 leading-none" }
|
||||
|
||||
// <h1 class="inline-block py-4 px-8 mr-6 leading-none">
|
||||
// "Text"
|
||||
// </h1>
|
||||
|
||||
h1 {
|
||||
div {
|
||||
h1 {}
|
||||
h2 {}
|
||||
Brick {}
|
||||
|
||||
p {}
|
||||
p {
|
||||
tag: "type",
|
||||
abc: 123,
|
||||
enabled: true,
|
||||
class: "big small wide short",
|
||||
|
||||
a { "abcder" },
|
||||
h2 { "whatsup", class: "abc-123" },
|
||||
CustomComponent { a: 123, b: 456, key: "1" },
|
||||
}
|
||||
|
||||
div { class: "big small wide short",
|
||||
div {},
|
||||
div {},
|
||||
div {},
|
||||
div {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h2 {}
|
||||
h3 {}
|
||||
"abcd123"
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -116,7 +116,7 @@ pub(crate) mod innerlude {
|
||||
// Re-export the FC macro
|
||||
pub use crate as dioxus;
|
||||
pub use crate::nodebuilder as builder;
|
||||
pub use dioxus_core_macro::{fc, html};
|
||||
pub use dioxus_core_macro::{fc, html, rsx};
|
||||
}
|
||||
|
||||
/// Re-export common types for ease of development use.
|
||||
@@ -144,7 +144,7 @@ pub mod prelude {
|
||||
pub use crate::nodebuilder as builder;
|
||||
// pub use dioxus_core_macro::fc;
|
||||
pub use dioxus_core_macro::format_args_f;
|
||||
pub use dioxus_core_macro::{fc, html};
|
||||
pub use dioxus_core_macro::{fc, html, rsx};
|
||||
|
||||
// pub use crate::diff::DiffMachine;
|
||||
pub use crate::dodriodiff::DiffMachine;
|
||||
|
||||
@@ -46,12 +46,11 @@ static Example: FC<()> = |ctx, props| {
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
// onclick={move |_| log::debug!("set jill")}>
|
||||
onclick={move |_| set_name("jill")}
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</button>
|
||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
onclick={move |_| set_name("jill")}
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in new issue
Block a user