From 209a55da694ae306b9216589431c5c62cf9bfc3b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 19 Mar 2023 20:37:40 -0500 Subject: [PATCH 01/16] allow raw elements if they include a dash --- examples/web_component.rs | 15 +++++ packages/rsx-rosetta/src/lib.rs | 4 +- packages/rsx/src/element.rs | 107 ++++++++++++++++++++++++++++---- packages/rsx/src/lib.rs | 23 +++++-- packages/rsx/src/node.rs | 10 +++ 5 files changed, 141 insertions(+), 18 deletions(-) create mode 100644 examples/web_component.rs diff --git a/examples/web_component.rs b/examples/web_component.rs new file mode 100644 index 000000000..7a719b41c --- /dev/null +++ b/examples/web_component.rs @@ -0,0 +1,15 @@ + + +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! { + web-component { + "my-prop": "5%", + } + }) +} diff --git a/packages/rsx-rosetta/src/lib.rs b/packages/rsx-rosetta/src/lib.rs index 64fed1e18..4f30c0081 100644 --- a/packages/rsx-rosetta/src/lib.rs +++ b/packages/rsx-rosetta/src/lib.rs @@ -1,6 +1,6 @@ use convert_case::{Case, Casing}; use dioxus_rsx::{ - BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, IfmtInput, + BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, ElementName, IfmtInput, }; pub use html_parser::{Dom, Node}; use proc_macro2::{Ident, Span}; @@ -21,7 +21,7 @@ pub fn rsx_node_from_html(node: &Node) -> Option { Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))), Node::Element(el) => { let el_name = el.name.to_case(Case::Snake); - let el_name = Ident::new(el_name.as_str(), Span::call_site()); + let el_name = ElementName::Ident(Ident::new(el_name.as_str(), Span::call_site())); let mut attributes: Vec<_> = el .attributes diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index e47c2e02a..5b0c12a98 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -1,9 +1,12 @@ +use std::fmt::{Display, Formatter}; + use super::*; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, + punctuated::Punctuated, Error, Expr, Ident, LitStr, Result, Token, }; @@ -12,7 +15,7 @@ use syn::{ // ======================================= #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub struct Element { - pub name: Ident, + pub name: ElementName, pub key: Option, pub attributes: Vec, pub children: Vec, @@ -22,7 +25,7 @@ pub struct Element { impl Parse for Element { fn parse(stream: ParseStream) -> Result { - let el_name = Ident::parse(stream)?; + let el_name = ElementName::parse(stream)?; // parse the guts let content: ParseBuffer; @@ -181,7 +184,7 @@ impl ToTokens for Element { tokens.append_all(quote! { __cx.element( - dioxus_elements::#name, + #name, __cx.bump().alloc([ #(#listeners),* ]), __cx.bump().alloc([ #(#attr),* ]), __cx.bump().alloc([ #(#children),* ]), @@ -191,6 +194,60 @@ impl ToTokens for Element { } } +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub enum ElementName { + Ident(Ident), + Custom(String), +} + +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 PartialEq<&str> for ElementName { + fn eq(&self, other: &&str) -> bool { + match self { + ElementName::Ident(i) => i == *other, + ElementName::Custom(s) => s == *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), + } + } +} + +impl Parse for ElementName { + fn parse(stream: ParseStream) -> Result { + let raw = Punctuated::::parse_separated_nonempty(stream)?; + if raw.len() == 1 { + Ok(ElementName::Ident(raw.into_iter().next().unwrap())) + } else { + let tag = raw.into_iter().map(|ident| ident.to_string()).collect::>().join("-"); + Ok(ElementName::Custom(tag)) + } + } +} + +impl ToTokens for ElementName { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + ElementName::Ident(i) => tokens.append_all(quote! { dioxus_elements::#i }), + ElementName::Custom(s) => tokens.append_all(quote! { #s }), + } + } +} + #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum ElementAttr { /// `attribute: "value"` @@ -234,7 +291,7 @@ impl ElementAttr { #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub struct ElementAttrNamed { - pub el_name: Ident, + pub el_name: ElementName, pub attr: ElementAttr, } @@ -242,24 +299,48 @@ impl ToTokens for ElementAttrNamed { fn to_tokens(&self, tokens: &mut TokenStream2) { let ElementAttrNamed { el_name, attr } = self; - tokens.append_all(match attr { + let ns = |name| match el_name { + ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 }, + ElementName::Custom(_) => quote! { None }, + }; + let volitile = |name| match el_name { + ElementName::Ident(_) => quote! { #el_name::#name.2 }, + ElementName::Custom(_) => quote! { false }, + }; + let attribute=|name: &Ident| match el_name{ + ElementName::Ident(_) => quote! { #el_name::#name.0 }, + ElementName::Custom(_) => { + let as_string=name.to_string(); + quote!(#as_string) + } + }; + + let attribute= + + match attr { ElementAttr::AttrText { name, value } => { + let ns = ns(name); + let volitile = volitile(name); + let attribute=attribute(name); quote! { __cx.attr( - dioxus_elements::#el_name::#name.0, + #attribute, #value, - dioxus_elements::#el_name::#name.1, - dioxus_elements::#el_name::#name.2 + #ns, + #volitile ) } } ElementAttr::AttrExpression { name, value } => { + let ns = ns(name); + let volitile = volitile(name); + let attribute=attribute(name); quote! { __cx.attr( - dioxus_elements::#el_name::#name.0, + #attribute, #value, - dioxus_elements::#el_name::#name.1, - dioxus_elements::#el_name::#name.2 + #ns, + #volitile ) } } @@ -288,7 +369,9 @@ impl ToTokens for ElementAttrNamed { dioxus_elements::events::#name(__cx, #tokens) } } - }); + }; + + tokens.append_all(attribute); } } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index d22b122e9..c54014527 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -426,13 +426,25 @@ impl<'a> DynamicContext<'a> { match root { BodyNode::Element(el) => { let el_name = &el.name; + let ns = |name| match el_name { + ElementName::Ident(i) => quote! { dioxus_elements::#i::#name }, + ElementName::Custom(_) => quote! { None }, + }; let static_attrs = el.attributes.iter().map(|attr| match &attr.attr { ElementAttr::AttrText { name, value } if value.is_static() => { let value = value.to_static().unwrap(); + let ns = ns(quote!(#name.1)); + let name=match el_name { + ElementName::Ident(_) => quote! { #el_name::#name.0 }, + ElementName::Custom(_) => { + let as_string=name.to_string(); + quote! { #as_string } + }, + }; quote! { ::dioxus::core::TemplateAttribute::Static { - name: dioxus_elements::#el_name::#name.0, - namespace: dioxus_elements::#el_name::#name.1, + name: #name, + namespace: #ns, value: #value, // todo: we don't diff these so we never apply the volatile flag @@ -479,10 +491,13 @@ impl<'a> DynamicContext<'a> { let _opt = el.children.len() == 1; let children = quote! { #(#children),* }; + let ns = ns(quote!(NAME_SPACE)); + let el_name = el_name.tag_name(); + quote! { ::dioxus::core::TemplateNode::Element { - tag: dioxus_elements::#el_name::TAG_NAME, - namespace: dioxus_elements::#el_name::NAME_SPACE, + tag: #el_name, + namespace: #ns, attrs: &[ #attrs ], children: &[ #children ], } diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index 0d5556552..a5f9e50e0 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -50,7 +50,17 @@ impl Parse for BodyNode { return Ok(BodyNode::Text(stream.parse()?)); } + // if this is a dash-separated path, it's a web component (custom element) let body_stream = stream.fork(); + if let Ok(ElementName::Custom(name)) = body_stream.parse::() { + println!("name: {}", name); + if name.contains('-') && body_stream.peek(token::Brace) { + return Ok(BodyNode::Element(stream.parse::()?)); + } + } + + let body_stream = stream.fork(); + if let Ok(path) = body_stream.parse::() { // this is an Element if path match of: // - one ident From 03413a68ab53b6ab1bb5c692820b208905bbf274 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 19 Mar 2023 20:47:16 -0500 Subject: [PATCH 02/16] fix formatting --- examples/web_component.rs | 2 -- packages/rsx/src/element.rs | 18 ++++++++++-------- packages/rsx/src/lib.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/web_component.rs b/examples/web_component.rs index 7a719b41c..bd0f7eb15 100644 --- a/examples/web_component.rs +++ b/examples/web_component.rs @@ -1,5 +1,3 @@ - - use dioxus::prelude::*; fn main() { diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 5b0c12a98..1d74a08bc 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -233,7 +233,11 @@ impl Parse for ElementName { if raw.len() == 1 { Ok(ElementName::Ident(raw.into_iter().next().unwrap())) } else { - let tag = raw.into_iter().map(|ident| ident.to_string()).collect::>().join("-"); + let tag = raw + .into_iter() + .map(|ident| ident.to_string()) + .collect::>() + .join("-"); Ok(ElementName::Custom(tag)) } } @@ -307,21 +311,19 @@ impl ToTokens for ElementAttrNamed { ElementName::Ident(_) => quote! { #el_name::#name.2 }, ElementName::Custom(_) => quote! { false }, }; - let attribute=|name: &Ident| match el_name{ + let attribute = |name: &Ident| match el_name { ElementName::Ident(_) => quote! { #el_name::#name.0 }, ElementName::Custom(_) => { - let as_string=name.to_string(); + let as_string = name.to_string(); quote!(#as_string) } }; - let attribute= - - match attr { + let attribute = match attr { ElementAttr::AttrText { name, value } => { let ns = ns(name); let volitile = volitile(name); - let attribute=attribute(name); + let attribute = attribute(name); quote! { __cx.attr( #attribute, @@ -334,7 +336,7 @@ impl ToTokens for ElementAttrNamed { ElementAttr::AttrExpression { name, value } => { let ns = ns(name); let volitile = volitile(name); - let attribute=attribute(name); + let attribute = attribute(name); quote! { __cx.attr( #attribute, diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index c54014527..07c887e65 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -434,12 +434,12 @@ impl<'a> DynamicContext<'a> { ElementAttr::AttrText { name, value } if value.is_static() => { let value = value.to_static().unwrap(); let ns = ns(quote!(#name.1)); - let name=match el_name { + let name = match el_name { ElementName::Ident(_) => quote! { #el_name::#name.0 }, ElementName::Custom(_) => { - let as_string=name.to_string(); + let as_string = name.to_string(); quote! { #as_string } - }, + } }; quote! { ::dioxus::core::TemplateAttribute::Static { From f74854b38c5fddde6cb46af004778d3856df342f Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 20 Mar 2023 07:21:31 -0500 Subject: [PATCH 03/16] fix formatting with raw elements --- packages/rsx/src/element.rs | 18 +++++++++++++++--- packages/rsx/src/node.rs | 3 +-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 1d74a08bc..466200eca 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -7,6 +7,7 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, punctuated::Punctuated, + spanned::Spanned, Error, Expr, Ident, LitStr, Result, Token, }; @@ -197,7 +198,7 @@ impl ToTokens for Element { #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum ElementName { Ident(Ident), - Custom(String), + Custom(LitStr), } impl ElementName { @@ -209,11 +210,20 @@ impl ElementName { } } +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 == *other, + ElementName::Custom(s) => s.value() == *other, } } } @@ -222,7 +232,7 @@ 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), + ElementName::Custom(s) => write!(f, "{}", s.value()), } } } @@ -233,11 +243,13 @@ impl Parse for ElementName { 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::>() .join("-"); + let tag = LitStr::new(&tag, span); Ok(ElementName::Custom(tag)) } } diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index a5f9e50e0..54448c883 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -53,8 +53,7 @@ impl Parse for BodyNode { // if this is a dash-separated path, it's a web component (custom element) let body_stream = stream.fork(); if let Ok(ElementName::Custom(name)) = body_stream.parse::() { - println!("name: {}", name); - if name.contains('-') && body_stream.peek(token::Brace) { + if name.value().contains('-') && body_stream.peek(token::Brace) { return Ok(BodyNode::Element(stream.parse::()?)); } } From 28ef3f89687a58c61c798dff039a4a09fd0df0b1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 23 Apr 2023 12:07:38 -0500 Subject: [PATCH 04/16] fix liveview links --- packages/desktop/src/protocol.rs | 2 +- packages/interpreter/src/bindings.rs | 6 +++- packages/interpreter/src/interpreter.js | 48 +++++++++++++++---------- packages/liveview/src/main.js | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index efdc849a5..40cdfc88a 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -17,7 +17,7 @@ fn module_loader(root_name: &str) -> String { let rootname = "{root_name}"; let root = window.document.getElementById(rootname); if (root != null) {{ - window.interpreter = new Interpreter(root); + window.interpreter = new Interpreter(root, new InterpreterConfig(true)); window.ipc.postMessage(serializeIpcMessage("initialize")); }} diff --git a/packages/interpreter/src/bindings.rs b/packages/interpreter/src/bindings.rs index 478b8ca5c..4fd9016f1 100644 --- a/packages/interpreter/src/bindings.rs +++ b/packages/interpreter/src/bindings.rs @@ -6,10 +6,14 @@ use web_sys::Element; #[wasm_bindgen(module = "/src/interpreter.js")] extern "C" { + pub type InterpreterConfig; + #[wasm_bindgen(constructor)] + pub fn new(intercept_link_redirects: bool) -> InterpreterConfig; + pub type Interpreter; #[wasm_bindgen(constructor)] - pub fn new(arg: Element) -> Interpreter; + pub fn new(arg: Element, config: InterpreterConfig) -> Interpreter; #[wasm_bindgen(method)] pub fn SaveTemplate(this: &Interpreter, template: JsValue); diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index b921c7c22..df9dd85e0 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -17,8 +17,7 @@ class ListenerMap { } else { this.global[event_name].active++; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); if (!this.local[id]) { this.local[id] = {}; @@ -32,11 +31,13 @@ class ListenerMap { if (bubbles) { this.global[event_name].active--; if (this.global[event_name].active === 0) { - this.root.removeEventListener(event_name, this.global[event_name].callback); + this.root.removeEventListener( + event_name, + this.global[event_name].callback + ); delete this.global[event_name]; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); delete this.local[id][event_name]; if (this.local[id].length === 0) { @@ -52,8 +53,15 @@ class ListenerMap { } } +class InterpreterConfig { + constructor(intercept_link_redirects) { + this.intercept_link_redirects = intercept_link_redirects; + } +} + class Interpreter { - constructor(root) { + constructor(root, config) { + this.config = config; this.root = root; this.listeners = new ListenerMap(root); this.nodes = [root]; @@ -143,8 +151,7 @@ class Interpreter { SetAttribute(id, field, value, ns) { if (value === null) { this.RemoveAttribute(id, field, ns); - } - else { + } else { const node = this.nodes[id]; this.SetAttributeInner(node, field, value, ns); } @@ -342,7 +349,6 @@ class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": - let bubbles = event_bubbles(edit.name); // this handler is only provided on desktop implementations since this @@ -357,15 +363,21 @@ class Interpreter { if (event.type === "click") { // todo call prevent default if it's the right type of event - let a_element = target.closest("a"); - if (a_element != null) { - event.preventDefault(); - if (shouldPreventDefault !== `onclick` && a_element.getAttribute(`dioxus-prevent-default`) !== `onclick`) { - const href = a_element.getAttribute("href"); - if (href !== "" && href !== null && href !== undefined) { - window.ipc.postMessage( - serializeIpcMessage("browser_open", { href }) - ); + if (this.config.intercept_link_redirects) { + let a_element = target.closest("a"); + if (a_element != null) { + event.preventDefault(); + if ( + shouldPreventDefault !== `onclick` && + a_element.getAttribute(`dioxus-prevent-default`) !== + `onclick` + ) { + const href = a_element.getAttribute("href"); + if (href !== "" && href !== null && href !== undefined) { + window.ipc.postMessage( + serializeIpcMessage("browser_open", { href }) + ); + } } } } diff --git a/packages/liveview/src/main.js b/packages/liveview/src/main.js index 7dd9758d6..a3e5136ca 100644 --- a/packages/liveview/src/main.js +++ b/packages/liveview/src/main.js @@ -8,7 +8,7 @@ function main() { class IPC { constructor(root) { // connect to the websocket - window.interpreter = new Interpreter(root); + window.interpreter = new Interpreter(root, new InterpreterConfig(false)); let ws = new WebSocket(WS_ADDR); From e03e0647eed98f4b15e91663ecac59cfd99ad1c4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 23 May 2023 09:09:05 -0500 Subject: [PATCH 05/16] fix url encoded desktop assets --- packages/desktop/Cargo.toml | 1 + packages/desktop/src/protocol.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 092676322..35e63bd9d 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -38,6 +38,7 @@ slab = "0.4" futures-util = "0.3.25" rfd = "0.11.3" +urlencoding = "2.1.2" [target.'cfg(target_os = "ios")'.dependencies] objc = "0.2.7" diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 2e407b258..7c84a59aa 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -87,7 +87,9 @@ pub(super) fn desktop_handler( } // 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. let mut asset = get_asset_root() From 71888f484e75201496e778f85ba56819822e15f9 Mon Sep 17 00:00:00 2001 From: Nikita Pekin Date: Wed, 24 May 2023 18:11:17 +0200 Subject: [PATCH 06/16] add default-tls and rustls features --- packages/fullstack/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index 581bf2f52..5e7da25ca 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"] [dependencies] # 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" } # warp @@ -56,9 +56,11 @@ dioxus-hot-reload = { path = "../hot-reload" } web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } [features] -default = ["hot-reload"] +default = ["hot-reload", "default-tls"] hot-reload = ["serde_json", "tokio-stream", "futures-util"] warp = ["dep:warp", "http-body", "ssr"] axum = ["dep:axum", "tower-http", "ssr"] salvo = ["dep:salvo", "ssr"] ssr = ["server_fn/ssr", "tokio", "dioxus-ssr", "hyper", "http"] +default-tls = ["server_fn/default-tls"] +rustls = ["server_fn/rustls"] From 47f508fd2d3e42759cec93eacc569fe2f05ce95a Mon Sep 17 00:00:00 2001 From: elliotwaite <1767836+elliotwaite@users.noreply.github.com> Date: Wed, 24 May 2023 15:30:39 -0700 Subject: [PATCH 07/16] Clarify hooks rules to mention early returns --- docs/guide/src/en/interactivity/hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/hooks.md b/docs/guide/src/en/interactivity/hooks.md index 3539e5884..083f04b49 100644 --- a/docs/guide/src/en/interactivity/hooks.md +++ b/docs/guide/src/en/interactivity/hooks.md @@ -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) 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 3. Hooks name's should start with `use_` so you don't accidentally confuse them with regular functions From 0df6a8f2ac2995687b225e9b72e33db85625cdf0 Mon Sep 17 00:00:00 2001 From: serzhiio Date: Sun, 28 May 2023 20:09:09 +0400 Subject: [PATCH 08/16] Added `.with_mut_silent()` fn --- packages/fermi/src/hooks/atom_ref.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/fermi/src/hooks/atom_ref.rs b/packages/fermi/src/hooks/atom_ref.rs index d2fa3f213..600a0e028 100644 --- a/packages/fermi/src/hooks/atom_ref.rs +++ b/packages/fermi/src/hooks/atom_ref.rs @@ -4,6 +4,7 @@ use std::{ cell::{Ref, RefCell, RefMut}, rc::Rc, }; +use std::ops::DerefMut; /// /// @@ -70,6 +71,12 @@ impl UseAtomRef { 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(self.write_silent().deref_mut()) + } + pub fn write(&self) -> RefMut { self.root.force_update(self.ptr); self.value.borrow_mut() From 0439072a45d5cadcf9d6197dc99aa73d2f504871 Mon Sep 17 00:00:00 2001 From: serzhiio Date: Sun, 28 May 2023 20:24:39 +0400 Subject: [PATCH 09/16] upd --- packages/fermi/src/hooks/atom_ref.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/fermi/src/hooks/atom_ref.rs b/packages/fermi/src/hooks/atom_ref.rs index 600a0e028..95732c491 100644 --- a/packages/fermi/src/hooks/atom_ref.rs +++ b/packages/fermi/src/hooks/atom_ref.rs @@ -4,7 +4,6 @@ use std::{ cell::{Ref, RefCell, RefMut}, rc::Rc, }; -use std::ops::DerefMut; /// /// @@ -74,7 +73,7 @@ impl UseAtomRef { /// This is silent operation /// call `.force_update()` manually if required pub fn with_mut_silent(&self, cb: impl FnOnce(&mut T)) { - cb(self.write_silent().deref_mut()) + cb(&mut *self.write_silent()) } pub fn write(&self) -> RefMut { From 52742b30ae81b83ad0e79a0748e7d515130e180c Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 29 May 2023 12:20:33 +1200 Subject: [PATCH 10/16] Update `pretty_env_logger` to address security advisory. --- packages/liveview/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 354b0f28c..302e4c486 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -46,7 +46,7 @@ once_cell = "1.17.1" # actix-ws = { version = "0.2.5", optional = true } [dev-dependencies] -pretty_env_logger = { version = "0.4.0" } +pretty_env_logger = { version = "0.5.0" } tokio = { version = "1.22.0", features = ["full"] } dioxus = { path = "../dioxus", version = "0.3.0" } warp = "0.3.3" From 4611df014e0bca87fa2ec88b2b303035e84c3ccc Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 29 May 2023 13:13:55 +1200 Subject: [PATCH 11/16] Add notes to `CONTRIBUTING.md`. --- notes/CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/notes/CONTRIBUTING.md b/notes/CONTRIBUTING.md index e69de29bb..150b60c88 100644 --- a/notes/CONTRIBUTING.md +++ b/notes/CONTRIBUTING.md @@ -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 +``` From c7321a0389408d4e875eb02ee52a65692f4bbacb Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 29 May 2023 13:14:05 +1200 Subject: [PATCH 12/16] Don't depend on `"oldtime"` feature in `chrono` which produces security advisory. --- packages/hot-reload/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml index 2d88bf029..4809df480 100644 --- a/packages/hot-reload/Cargo.toml +++ b/packages/hot-reload/Cargo.toml @@ -18,7 +18,7 @@ dioxus-html = { path = "../html", features = ["hot-reload-context"], version = " interprocess = { version = "1.2.1" } notify = "5.0.0" -chrono = "0.4.23" +chrono = { version = "0.4.24", default-features = false, features = ["clock"] } serde_json = "1.0.91" serde = { version = "1", features = ["derive"] } execute = "0.2.11" From 23dcd0657cdfa9e0778c2eb911daf9c8f2605c47 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 29 May 2023 13:19:08 +1200 Subject: [PATCH 13/16] Update `env-logger` to `0.10.0`. --- Cargo.toml | 2 +- packages/dioxus/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d9dcc7af..03b5c1296 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ tokio = { version = "1.16.1", features = ["full"] } reqwest = { version = "0.11.9", features = ["json"] } fern = { version = "0.6.0", features = ["colored"] } thiserror = "1.0.30" -env_logger = "0.9.0" +env_logger = "0.10.0" simple_logger = "4.0.0" [profile.release] diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index bc2098285..8f0cc2184 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -35,7 +35,7 @@ log = "0.4.14" rand = { version = "0.8.4", features = ["small_rng"] } criterion = "0.3.5" thiserror = "1.0.30" -env_logger = "0.9.0" +env_logger = "0.10.0" tokio = { version = "1.21.2", features = ["full"] } # dioxus-edit-stream = { path = "../edit-stream" } From 0684f738b4eb30688666cb4a46b042b2e8c4cec4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 28 May 2023 20:54:00 -0500 Subject: [PATCH 14/16] revert control r shortcut --- packages/desktop/src/shortcut.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index 5232bdb8b..e5cc05315 100644 --- a/packages/desktop/src/shortcut.rs +++ b/packages/desktop/src/shortcut.rs @@ -41,17 +41,10 @@ impl Shortcut { impl ShortcutRegistry { pub fn new(target: &EventLoopWindowTarget) -> Self { - let myself = Self { + Self { manager: Rc::new(RefCell::new(ShortcutManager::new(target))), 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) { From 151a0ac34b14a0e10caaeadb792f254c0188c9b0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 28 May 2023 21:22:39 -0500 Subject: [PATCH 15/16] fix ssr guide examples --- docs/guide/examples/hello_world_ssr.rs | 61 ++++++++++++++++++++++++ docs/guide/src/en/getting_started/ssr.md | 48 +++++-------------- 2 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 docs/guide/examples/hello_world_ssr.rs diff --git a/docs/guide/examples/hello_world_ssr.rs b/docs/guide/examples/hello_world_ssr.rs new file mode 100644 index 000000000..8039e9fed --- /dev/null +++ b/docs/guide/examples/hello_world_ssr.rs @@ -0,0 +1,61 @@ +// 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 { + // 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 { + // 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 diff --git a/docs/guide/src/en/getting_started/ssr.md b/docs/guide/src/en/getting_started/ssr.md index f78623003..7a1954191 100644 --- a/docs/guide/src/en/getting_started/ssr.md +++ b/docs/guide/src/en/getting_started/ssr.md @@ -40,54 +40,30 @@ tokio = { version = "1.15.0", features = ["full"] } Now, set up your Axum app to respond on an endpoint. ```rust -use axum::{response::Html, routing::get, Router}; -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(); -} +{{#include ../../../examples/hello_world_ssr.rs:main}} ``` And then add our endpoint. We can either render `rsx!` directly: ```rust -async fn app_endpoint() -> Html { - // render the rsx! macro to HTML - Html(dioxus_ssr::render_lazy(rsx! { - div { "hello world!" } - })) -} +{{#include ../../../examples/hello_world_ssr.rs:endpoint}} ``` Or we can render VirtualDoms. ```rust -async fn app_endpoint() -> Html { - // 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)) -} +{{#include ../../../examples/hello_world_ssr.rs:second_endpoint}} ``` +And then add our app component: + +```rust +{{#include ../../../examples/hello_world_ssr.rs:component}} +``` + +And that's it! + + ## Multithreaded Support The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. From f963e006c7fbaf2962358ae09ed47469ee142eda Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 28 May 2023 21:23:43 -0500 Subject: [PATCH 16/16] fix clippy --- docs/guide/examples/hello_world_ssr.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide/examples/hello_world_ssr.rs b/docs/guide/examples/hello_world_ssr.rs index 8039e9fed..0f8db0b46 100644 --- a/docs/guide/examples/hello_world_ssr.rs +++ b/docs/guide/examples/hello_world_ssr.rs @@ -1,3 +1,5 @@ +#![allow(unused)] +#![allow(non_snake_case)] // ANCHOR: all // ANCHOR: main