Merge branch 'upstream' into router-typesafe

This commit is contained in:
Evan Almloff committed 2023-05-31 17:16:27 -05:00
commit 92755a381d
21 files changed
+297 -94

No files matched your search

+1 -1
View File
@@ -72,7 +72,7 @@ tokio = { version = "1.16.1", features = ["full"] }
reqwest = { version = "0.11.9", features = ["json"] } reqwest = { version = "0.11.9", features = ["json"] }
fern = { version = "0.6.0", features = ["colored"] } fern = { version = "0.6.0", features = ["colored"] }
thiserror = "1.0.30" thiserror = "1.0.30"
env_logger = "0.9.0" env_logger = "0.10.0"
simple_logger = "4.0.0" simple_logger = "4.0.0"
[profile.release] [profile.release]
+63
View File
@@ -0,0 +1,63 @@
#![allow(unused)]
#![allow(non_snake_case)]
// ANCHOR: all
// ANCHOR: main
#![allow(non_snake_case)]
use axum::{response::Html, routing::get, Router};
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
#[tokio::main]
async fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(
Router::new()
.route("/", get(app_endpoint))
.into_make_service(),
)
.await
.unwrap();
}
// ANCHOR_END: main
// ANCHOR: endpoint
async fn app_endpoint() -> Html<String> {
// render the rsx! macro to HTML
Html(dioxus_ssr::render_lazy(rsx! {
div { "hello world!" }
}))
}
// ANCHOR_END: endpoint
// ANCHOR: second_endpoint
async fn second_app_endpoint() -> Html<String> {
// create a component that renders a div with the text "hello world"
fn app(cx: Scope) -> Element {
cx.render(rsx!(div { "hello world" }))
}
// create a VirtualDom with the app component
let mut app = VirtualDom::new(app);
// rebuild the VirtualDom before rendering
let _ = app.rebuild();
// render the VirtualDom to HTML
Html(dioxus_ssr::render(&app))
}
// ANCHOR_END: second_endpoint
// ANCHOR: component
// define a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
"Hello, world!"
}
})
}
// ANCHOR_END: component
// ANCHOR_END: all
+12 -36
View File
@@ -40,54 +40,30 @@ tokio = { version = "1.15.0", features = ["full"] }
Now, set up your Axum app to respond on an endpoint. Now, set up your Axum app to respond on an endpoint.
```rust ```rust
use axum::{response::Html, routing::get, Router}; {{#include ../../../examples/hello_world_ssr.rs:main}}
use dioxus::prelude::*;
#[tokio::main]
async fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(
Router::new()
.route("/", get(app_endpoint))
.into_make_service(),
)
.await
.unwrap();
}
``` ```
And then add our endpoint. We can either render `rsx!` directly: And then add our endpoint. We can either render `rsx!` directly:
```rust ```rust
async fn app_endpoint() -> Html<String> { {{#include ../../../examples/hello_world_ssr.rs:endpoint}}
// render the rsx! macro to HTML
Html(dioxus_ssr::render_lazy(rsx! {
div { "hello world!" }
}))
}
``` ```
Or we can render VirtualDoms. Or we can render VirtualDoms.
```rust ```rust
async fn app_endpoint() -> Html<String> { {{#include ../../../examples/hello_world_ssr.rs:second_endpoint}}
// create a component that renders a div with the text "hello world"
fn app(cx: Scope) -> Element {
cx.render(rsx!(div { "hello world" }))
}
// create a VirtualDom with the app component
let mut app = VirtualDom::new(app);
// rebuild the VirtualDom before rendering
let _ = app.rebuild();
// render the VirtualDom to HTML
Html(dioxus_ssr::render_vdom(&app))
}
``` ```
And then add our app component:
```rust
{{#include ../../../examples/hello_world_ssr.rs:component}}
```
And that's it!
## Multithreaded Support ## Multithreaded Support
The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe.
+1 -1
View File
@@ -44,7 +44,7 @@ This is only possible because the two hooks are always called in the same order,
1. Hooks may be only used in components or other hooks (we'll get to that later) 1. Hooks may be only used in components or other hooks (we'll get to that later)
2. On every call to the component function 2. On every call to the component function
1. The same hooks must be called 1. The same hooks must be called (except in the case of early returns, as explained later in the [Error Handling chapter](../best_practices/error_handling.md))
2. In the same order 2. In the same order
3. Hooks name's should start with `use_` so you don't accidentally confuse them with regular functions 3. Hooks name's should start with `use_` so you don't accidentally confuse them with regular functions
+13
View File
@@ -0,0 +1,13 @@
use dioxus::prelude::*;
fn main() {
dioxus_desktop::launch(app);
}
fn app(cx: Scope) -> Element {
cx.render(rsx! {
web-component {
"my-prop": "5%",
}
})
}
+13
View File
@@ -0,0 +1,13 @@
# Contributing
On Linux, install the following packages:
```bash
sudo apt install libgdk3.0-cil libatk1.0-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libsoup-3.0-dev libjavascriptcoregtk-4.1-dev libwebkit2gtk-4.1-dev
```
Then run:
```bash
cargo test --workspace --tests
```
+1
View File
@@ -38,6 +38,7 @@ slab = "0.4"
futures-util = "0.3.25" futures-util = "0.3.25"
rfd = "0.11.3" rfd = "0.11.3"
urlencoding = "2.1.2"
[target.'cfg(target_os = "ios")'.dependencies] [target.'cfg(target_os = "ios")'.dependencies]
objc = "0.2.7" objc = "0.2.7"
+4 -2
View File
@@ -43,7 +43,7 @@ fn module_loader(root_name: &str) -> String {
let rootname = "{root_name}"; let rootname = "{root_name}";
let root = window.document.getElementById(rootname); let root = window.document.getElementById(rootname);
if (root != null) {{ if (root != null) {{
window.interpreter = new Interpreter(root); window.interpreter = new Interpreter(root, new InterpreterConfig(true));
window.ipc.postMessage(serializeIpcMessage("initialize")); window.ipc.postMessage(serializeIpcMessage("initialize"));
}} }}
</script> </script>
@@ -87,7 +87,9 @@ pub(super) fn desktop_handler(
} }
// Else, try to serve a file from the filesystem. // Else, try to serve a file from the filesystem.
let path = PathBuf::from(request.uri().path().trim_start_matches('/')); let decoded = urlencoding::decode(request.uri().path().trim_start_matches('/'))
.expect("expected URL to be UTF-8 encoded");
let path = PathBuf::from(&*decoded);
// If the path is relative, we'll try to serve it from the assets directory. // If the path is relative, we'll try to serve it from the assets directory.
let mut asset = get_asset_root() let mut asset = get_asset_root()
+2 -9
View File
@@ -41,17 +41,10 @@ impl Shortcut {
impl ShortcutRegistry { impl ShortcutRegistry {
pub fn new<T>(target: &EventLoopWindowTarget<T>) -> Self { pub fn new<T>(target: &EventLoopWindowTarget<T>) -> Self {
let myself = Self { Self {
manager: Rc::new(RefCell::new(ShortcutManager::new(target))), manager: Rc::new(RefCell::new(ShortcutManager::new(target))),
shortcuts: Rc::new(RefCell::new(HashMap::new())), shortcuts: Rc::new(RefCell::new(HashMap::new())),
}; }
// prevent CTRL+R from reloading the page which breaks apps
let _ = myself.add_shortcut(
Some(ModifiersState::CONTROL),
KeyCode::KeyR,
Box::new(|| {}),
);
myself
} }
pub(crate) fn call_handlers(&self, id: AcceleratorId) { pub(crate) fn call_handlers(&self, id: AcceleratorId) {
+1 -1
View File
@@ -35,7 +35,7 @@ log = "0.4.14"
rand = { version = "0.8.4", features = ["small_rng"] } rand = { version = "0.8.4", features = ["small_rng"] }
criterion = "0.3.5" criterion = "0.3.5"
thiserror = "1.0.30" thiserror = "1.0.30"
env_logger = "0.9.0" env_logger = "0.10.0"
tokio = { version = "1.21.2", features = ["full"] } tokio = { version = "1.21.2", features = ["full"] }
# dioxus-edit-stream = { path = "../edit-stream" } # dioxus-edit-stream = { path = "../edit-stream" }
+6
View File
@@ -70,6 +70,12 @@ impl<T: 'static> UseAtomRef<T> {
self.value.borrow() self.value.borrow()
} }
/// This is silent operation
/// call `.force_update()` manually if required
pub fn with_mut_silent(&self, cb: impl FnOnce(&mut T)) {
cb(&mut *self.write_silent())
}
pub fn write(&self) -> RefMut<T> { pub fn write(&self) -> RefMut<T> {
self.root.force_update(self.ptr); self.root.force_update(self.ptr);
self.value.borrow_mut() self.value.borrow_mut()
+4 -2
View File
@@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"]
[dependencies] [dependencies]
# server functions # server functions
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] } server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", default-features = false, features = ["stable"] }
dioxus_server_macro = { path = "server-macro" } dioxus_server_macro = { path = "server-macro" }
# warp # warp
@@ -56,9 +56,11 @@ dioxus-hot-reload = { path = "../hot-reload" }
web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] }
[features] [features]
default = ["hot-reload"] default = ["hot-reload", "default-tls"]
hot-reload = ["serde_json", "tokio-stream", "futures-util"] hot-reload = ["serde_json", "tokio-stream", "futures-util"]
warp = ["dep:warp", "http-body", "ssr"] warp = ["dep:warp", "http-body", "ssr"]
axum = ["dep:axum", "tower-http", "ssr"] axum = ["dep:axum", "tower-http", "ssr"]
salvo = ["dep:salvo", "ssr"] salvo = ["dep:salvo", "ssr"]
ssr = ["server_fn/ssr", "tokio", "dioxus-ssr", "hyper", "http"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr", "hyper", "http"]
default-tls = ["server_fn/default-tls"]
rustls = ["server_fn/rustls"]
+1 -1
View File
@@ -18,7 +18,7 @@ dioxus-html = { path = "../html", features = ["hot-reload-context"], version = "
interprocess = { version = "1.2.1" } interprocess = { version = "1.2.1" }
notify = "5.0.0" notify = "5.0.0"
chrono = "0.4.23" chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
serde_json = "1.0.91" serde_json = "1.0.91"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
execute = "0.2.11" execute = "0.2.11"
+5 -1
View File
@@ -6,10 +6,14 @@ use web_sys::Element;
#[wasm_bindgen(module = "/src/interpreter.js")] #[wasm_bindgen(module = "/src/interpreter.js")]
extern "C" { extern "C" {
pub type InterpreterConfig;
#[wasm_bindgen(constructor)]
pub fn new(intercept_link_redirects: bool) -> InterpreterConfig;
pub type Interpreter; pub type Interpreter;
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(arg: Element) -> Interpreter; pub fn new(arg: Element, config: InterpreterConfig) -> Interpreter;
#[wasm_bindgen(method)] #[wasm_bindgen(method)]
pub fn SaveTemplate(this: &Interpreter, template: JsValue); pub fn SaveTemplate(this: &Interpreter, template: JsValue);
+29 -20
View File
@@ -53,8 +53,15 @@ class ListenerMap {
} }
} }
class InterpreterConfig {
constructor(intercept_link_redirects) {
this.intercept_link_redirects = intercept_link_redirects;
}
}
class Interpreter { class Interpreter {
constructor(root) { constructor(root, config) {
this.config = config;
this.root = root; this.root = root;
this.listeners = new ListenerMap(root); this.listeners = new ListenerMap(root);
this.nodes = [root]; this.nodes = [root];
@@ -397,7 +404,7 @@ class Interpreter {
); );
} else { } else {
this.NewEventListener(edit.name, edit.id, bubbles, (event) => { this.NewEventListener(edit.name, edit.id, bubbles, (event) => {
handler(event, edit.name, bubbles); handler(event, edit.name, bubbles, this.config);
}); });
} }
break; break;
@@ -407,32 +414,34 @@ class Interpreter {
// this handler is only provided on the desktop and liveview implementations since this // this handler is only provided on the desktop and liveview implementations since this
// method is not used by the web implementation // method is not used by the web implementation
function handler(event, name, bubbles) { function handler(event, name, bubbles, config) {
let target = event.target; let target = event.target;
if (target != null) { if (target != null) {
let preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`); let preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`);
if (event.type === "click") { if (event.type === "click") {
// todo call prevent default if it's the right type of event // todo call prevent default if it's the right type of event
let a_element = target.closest("a"); if (config.intercept_link_redirects) {
if (a_element != null) { let a_element = target.closest("a");
event.preventDefault(); if (a_element != null) {
event.preventDefault();
let elementShouldPreventDefault = let elementShouldPreventDefault =
preventDefaultRequests && preventDefaultRequests.includes(`onclick`); preventDefaultRequests && preventDefaultRequests.includes(`onclick`);
let aElementShouldPreventDefault = a_element.getAttribute( let aElementShouldPreventDefault = a_element.getAttribute(
`dioxus-prevent-default` `dioxus-prevent-default`
); );
let linkShouldPreventDefault = let linkShouldPreventDefault =
aElementShouldPreventDefault && aElementShouldPreventDefault &&
aElementShouldPreventDefault.includes(`onclick`); aElementShouldPreventDefault.includes(`onclick`);
if (!elementShouldPreventDefault && !linkShouldPreventDefault) { if (!elementShouldPreventDefault && !linkShouldPreventDefault) {
const href = a_element.getAttribute("href"); const href = a_element.getAttribute("href");
if (href !== "" && href !== null && href !== undefined) { if (href !== "" && href !== null && href !== undefined) {
window.ipc.postMessage( window.ipc.postMessage(
serializeIpcMessage("browser_open", { href }) serializeIpcMessage("browser_open", { href })
); );
}
} }
} }
} }
+1 -1
View File
@@ -46,7 +46,7 @@ once_cell = "1.17.1"
# actix-ws = { version = "0.2.5", optional = true } # actix-ws = { version = "0.2.5", optional = true }
[dev-dependencies] [dev-dependencies]
pretty_env_logger = { version = "0.4.0" } pretty_env_logger = { version = "0.5.0" }
tokio = { version = "1.22.0", features = ["full"] } tokio = { version = "1.22.0", features = ["full"] }
dioxus = { path = "../dioxus", version = "0.3.0" } dioxus = { path = "../dioxus", version = "0.3.0" }
warp = "0.3.3" warp = "0.3.3"
+1 -1
View File
@@ -8,7 +8,7 @@ function main() {
class IPC { class IPC {
constructor(root) { constructor(root) {
// connect to the websocket // connect to the websocket
window.interpreter = new Interpreter(root); window.interpreter = new Interpreter(root, new InterpreterConfig(false));
let ws = new WebSocket(WS_ADDR); let ws = new WebSocket(WS_ADDR);
+2 -2
View File
@@ -1,6 +1,6 @@
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use dioxus_rsx::{ use dioxus_rsx::{
BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, IfmtInput, BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, ElementName, IfmtInput,
}; };
pub use html_parser::{Dom, Node}; pub use html_parser::{Dom, Node};
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
@@ -21,7 +21,7 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))), Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))),
Node::Element(el) => { Node::Element(el) => {
let el_name = el.name.to_case(Case::Snake); let el_name = el.name.to_case(Case::Snake);
let el_name = Ident::new(el_name.as_str(), Span::call_site()); let el_name = ElementName::Ident(Ident::new(el_name.as_str(), Span::call_site()));
let mut attributes: Vec<_> = el let mut attributes: Vec<_> = el
.attributes .attributes
+109 -12
View File
@@ -1,9 +1,13 @@
use std::fmt::{Display, Formatter};
use super::*; use super::*;
use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt}; use quote::{quote, ToTokens, TokenStreamExt};
use syn::{ use syn::{
parse::{Parse, ParseBuffer, ParseStream}, parse::{Parse, ParseBuffer, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Error, Expr, Ident, LitStr, Result, Token, Error, Expr, Ident, LitStr, Result, Token,
}; };
@@ -12,7 +16,7 @@ use syn::{
// ======================================= // =======================================
#[derive(PartialEq, Eq, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Element { pub struct Element {
pub name: Ident, pub name: ElementName,
pub key: Option<IfmtInput>, pub key: Option<IfmtInput>,
pub attributes: Vec<ElementAttrNamed>, pub attributes: Vec<ElementAttrNamed>,
pub children: Vec<BodyNode>, pub children: Vec<BodyNode>,
@@ -22,7 +26,7 @@ pub struct Element {
impl Parse for Element { impl Parse for Element {
fn parse(stream: ParseStream) -> Result<Self> { fn parse(stream: ParseStream) -> Result<Self> {
let el_name = Ident::parse(stream)?; let el_name = ElementName::parse(stream)?;
// parse the guts // parse the guts
let content: ParseBuffer; let content: ParseBuffer;
@@ -181,7 +185,7 @@ impl ToTokens for Element {
tokens.append_all(quote! { tokens.append_all(quote! {
__cx.element( __cx.element(
dioxus_elements::#name, #name,
__cx.bump().alloc([ #(#listeners),* ]), __cx.bump().alloc([ #(#listeners),* ]),
__cx.bump().alloc([ #(#attr),* ]), __cx.bump().alloc([ #(#attr),* ]),
__cx.bump().alloc([ #(#children),* ]), __cx.bump().alloc([ #(#children),* ]),
@@ -191,6 +195,75 @@ impl ToTokens for Element {
} }
} }
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementName {
Ident(Ident),
Custom(LitStr),
}
impl ElementName {
pub(crate) fn tag_name(&self) -> TokenStream2 {
match self {
ElementName::Ident(i) => quote! { dioxus_elements::#i::TAG_NAME },
ElementName::Custom(s) => quote! { #s },
}
}
}
impl ElementName {
pub fn span(&self) -> Span {
match self {
ElementName::Ident(i) => i.span(),
ElementName::Custom(s) => s.span(),
}
}
}
impl PartialEq<&str> for ElementName {
fn eq(&self, other: &&str) -> bool {
match self {
ElementName::Ident(i) => i == *other,
ElementName::Custom(s) => s.value() == *other,
}
}
}
impl Display for ElementName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ElementName::Ident(i) => write!(f, "{}", i),
ElementName::Custom(s) => write!(f, "{}", s.value()),
}
}
}
impl Parse for ElementName {
fn parse(stream: ParseStream) -> Result<Self> {
let raw = Punctuated::<Ident, Token![-]>::parse_separated_nonempty(stream)?;
if raw.len() == 1 {
Ok(ElementName::Ident(raw.into_iter().next().unwrap()))
} else {
let span = raw.span();
let tag = raw
.into_iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join("-");
let tag = LitStr::new(&tag, span);
Ok(ElementName::Custom(tag))
}
}
}
impl ToTokens for ElementName {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
ElementName::Ident(i) => tokens.append_all(quote! { dioxus_elements::#i }),
ElementName::Custom(s) => tokens.append_all(quote! { #s }),
}
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementAttr { pub enum ElementAttr {
/// `attribute: "value"` /// `attribute: "value"`
@@ -234,7 +307,7 @@ impl ElementAttr {
#[derive(PartialEq, Eq, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ElementAttrNamed { pub struct ElementAttrNamed {
pub el_name: Ident, pub el_name: ElementName,
pub attr: ElementAttr, pub attr: ElementAttr,
} }
@@ -242,24 +315,46 @@ impl ToTokens for ElementAttrNamed {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
let ElementAttrNamed { el_name, attr } = self; let ElementAttrNamed { el_name, attr } = self;
tokens.append_all(match attr { let ns = |name| match el_name {
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 },
ElementName::Custom(_) => quote! { None },
};
let volitile = |name| match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.2 },
ElementName::Custom(_) => quote! { false },
};
let attribute = |name: &Ident| match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.0 },
ElementName::Custom(_) => {
let as_string = name.to_string();
quote!(#as_string)
}
};
let attribute = match attr {
ElementAttr::AttrText { name, value } => { ElementAttr::AttrText { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! { quote! {
__cx.attr( __cx.attr(
dioxus_elements::#el_name::#name.0, #attribute,
#value, #value,
dioxus_elements::#el_name::#name.1, #ns,
dioxus_elements::#el_name::#name.2 #volitile
) )
} }
} }
ElementAttr::AttrExpression { name, value } => { ElementAttr::AttrExpression { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! { quote! {
__cx.attr( __cx.attr(
dioxus_elements::#el_name::#name.0, #attribute,
#value, #value,
dioxus_elements::#el_name::#name.1, #ns,
dioxus_elements::#el_name::#name.2 #volitile
) )
} }
} }
@@ -288,7 +383,9 @@ impl ToTokens for ElementAttrNamed {
dioxus_elements::events::#name(__cx, #tokens) dioxus_elements::events::#name(__cx, #tokens)
} }
} }
}); };
tokens.append_all(attribute);
} }
} }
+19 -4
View File
@@ -426,13 +426,25 @@ impl<'a> DynamicContext<'a> {
match root { match root {
BodyNode::Element(el) => { BodyNode::Element(el) => {
let el_name = &el.name; let el_name = &el.name;
let ns = |name| match el_name {
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name },
ElementName::Custom(_) => quote! { None },
};
let static_attrs = el.attributes.iter().map(|attr| match &attr.attr { let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
ElementAttr::AttrText { name, value } if value.is_static() => { ElementAttr::AttrText { name, value } if value.is_static() => {
let value = value.to_static().unwrap(); let value = value.to_static().unwrap();
let ns = ns(quote!(#name.1));
let name = match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.0 },
ElementName::Custom(_) => {
let as_string = name.to_string();
quote! { #as_string }
}
};
quote! { quote! {
::dioxus::core::TemplateAttribute::Static { ::dioxus::core::TemplateAttribute::Static {
name: dioxus_elements::#el_name::#name.0, name: #name,
namespace: dioxus_elements::#el_name::#name.1, namespace: #ns,
value: #value, value: #value,
// todo: we don't diff these so we never apply the volatile flag // todo: we don't diff these so we never apply the volatile flag
@@ -479,10 +491,13 @@ impl<'a> DynamicContext<'a> {
let _opt = el.children.len() == 1; let _opt = el.children.len() == 1;
let children = quote! { #(#children),* }; let children = quote! { #(#children),* };
let ns = ns(quote!(NAME_SPACE));
let el_name = el_name.tag_name();
quote! { quote! {
::dioxus::core::TemplateNode::Element { ::dioxus::core::TemplateNode::Element {
tag: dioxus_elements::#el_name::TAG_NAME, tag: #el_name,
namespace: dioxus_elements::#el_name::NAME_SPACE, namespace: #ns,
attrs: &[ #attrs ], attrs: &[ #attrs ],
children: &[ #children ], children: &[ #children ],
} }
+9
View File
@@ -50,7 +50,16 @@ impl Parse for BodyNode {
return Ok(BodyNode::Text(stream.parse()?)); return Ok(BodyNode::Text(stream.parse()?));
} }
// if this is a dash-separated path, it's a web component (custom element)
let body_stream = stream.fork(); let body_stream = stream.fork();
if let Ok(ElementName::Custom(name)) = body_stream.parse::<ElementName>() {
if name.value().contains('-') && body_stream.peek(token::Brace) {
return Ok(BodyNode::Element(stream.parse::<Element>()?));
}
}
let body_stream = stream.fork();
if let Ok(path) = body_stream.parse::<syn::Path>() { if let Ok(path) = body_stream.parse::<syn::Path>() {
// this is an Element if path match of: // this is an Element if path match of:
// - one ident // - one ident