Browse Source

Merge branch 'upstream' into disable-browser-shortcuts

Evan Almloff 2 years ago
parent
commit
bc014f7107

+ 36 - 0
examples/counter.rs

@@ -0,0 +1,36 @@
+//! Comparison example with leptos' counter example
+//! https://github.com/leptos-rs/leptos/blob/main/examples/counters/src/lib.rs
+
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus_desktop::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    let counters = use_state(cx, || vec![0, 0, 0]);
+    let sum: usize = counters.iter().copied().sum();
+
+    render! {
+        div {
+            button { onclick: move |_| counters.make_mut().push(0), "Add counter" }
+            button { onclick: move |_| { counters.make_mut().pop(); }, "Remove counter" }
+            p { "Total: {sum}" }
+            for (i, counter) in counters.iter().enumerate() {
+                li {
+                    button { onclick: move |_| counters.make_mut()[i] -= 1, "-1" }
+                    input {
+                        value: "{counter}",
+                        oninput: move |e| {
+                            if let Ok(value) = e.value.parse::<usize>() {
+                                counters.make_mut()[i] = value;
+                            }
+                        }
+                    }
+                    button { onclick: move |_| counters.make_mut()[i] += 1, "+1" }
+                    button { onclick: move |_| { counters.make_mut().remove(i); }, "x" }
+                }
+            }
+        }
+    }
+}

+ 4 - 2
packages/desktop/src/lib.rs

@@ -36,8 +36,8 @@ use tao::{
 };
 pub use wry;
 pub use wry::application as tao;
-use wry::application::window::WindowId;
 use wry::webview::WebView;
+use wry::{application::window::WindowId, webview::WebContext};
 
 /// Launch the WebView and run the event loop.
 ///
@@ -281,7 +281,7 @@ fn create_new_window(
     event_handlers: &WindowEventHandlers,
     shortcut_manager: ShortcutRegistry,
 ) -> WebviewHandler {
-    let webview = webview::build(&mut cfg, event_loop, proxy.clone());
+    let (webview, web_context) = webview::build(&mut cfg, event_loop, proxy.clone());
 
     dom.base_scope().provide_context(DesktopContext::new(
         webview.clone(),
@@ -299,6 +299,7 @@ fn create_new_window(
         webview,
         dom,
         waker: waker::tao_waker(proxy, id),
+        web_context,
     }
 }
 
@@ -306,6 +307,7 @@ struct WebviewHandler {
     dom: VirtualDom,
     webview: Rc<wry::webview::WebView>,
     waker: Waker,
+    web_context: WebContext,
 }
 
 /// Poll the virtualdom until it's pending

+ 7 - 4
packages/desktop/src/webview.rs

@@ -7,13 +7,13 @@ use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget};
 pub use wry;
 pub use wry::application as tao;
 use wry::application::window::Window;
-use wry::webview::{WebView, WebViewBuilder};
+use wry::webview::{WebContext, WebView, WebViewBuilder};
 
 pub fn build(
     cfg: &mut Config,
     event_loop: &EventLoopWindowTarget<UserWindowEvent>,
     proxy: EventLoopProxy<UserWindowEvent>,
-) -> Rc<WebView> {
+) -> (Rc<WebView>, WebContext) {
     let builder = cfg.window.clone();
     let window = builder.build(event_loop).unwrap();
     let file_handler = cfg.file_drop_handler.take();
@@ -33,6 +33,8 @@ pub fn build(
         ));
     }
 
+    let mut web_context = WebContext::new(cfg.data_dir.clone());
+
     let mut webview = WebViewBuilder::new(window)
         .unwrap()
         .with_transparent(cfg.window.window.transparent)
@@ -52,7 +54,8 @@ pub fn build(
                 .as_ref()
                 .map(|handler| handler(window, evet))
                 .unwrap_or_default()
-        });
+        })
+        .with_web_context(&mut web_context);
 
     #[cfg(windows)]
     {
@@ -89,5 +92,5 @@ pub fn build(
         webview = webview.with_devtools(true);
     }
 
-    Rc::new(webview.build().unwrap())
+    (Rc::new(webview.build().unwrap()), web_context)
 }

+ 15 - 1
packages/ssr/src/cache.rs

@@ -23,6 +23,8 @@ pub enum Segment {
         // This will be true if there are static styles
         inside_style_tag: bool,
     },
+    /// A marker for where to insert a dynamic inner html
+    InnerHtmlMarker,
 }
 
 impl std::fmt::Write for StringChain {
@@ -69,6 +71,9 @@ impl StringCache {
                 write!(chain, "<{tag}")?;
                 // we need to collect the styles and write them at the end
                 let mut styles = Vec::new();
+                // we need to collect the inner html and write it at the end
+                let mut inner_html = None;
+                // we need to keep track of if we have dynamic attrs to know if we need to insert a style and inner_html marker
                 let mut has_dynamic_attrs = false;
                 for attr in *attrs {
                     match attr {
@@ -77,7 +82,9 @@ impl StringCache {
                             value,
                             namespace,
                         } => {
-                            if let Some("style") = namespace {
+                            if *name == "dangerous_inner_html" {
+                                inner_html = Some(value);
+                            } else if let Some("style") = namespace {
                                 styles.push((name, value));
                             } else {
                                 write!(chain, " {name}=\"{value}\"")?;
@@ -110,6 +117,13 @@ impl StringCache {
                     write!(chain, "/>")?;
                 } else {
                     write!(chain, ">")?;
+                    // Write the static inner html, or insert a marker if dynamic inner html is possible
+                    if let Some(inner_html) = inner_html {
+                        chain.write_str(inner_html)?;
+                    } else if has_dynamic_attrs {
+                        chain.segments.push(Segment::InnerHtmlMarker);
+                    }
+
                     for child in *children {
                         Self::recurse(child, cur_path, root_idx, chain)?;
                     }

+ 21 - 2
packages/ssr/src/renderer.rs

@@ -70,6 +70,8 @@ impl Renderer {
             .or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap()))
             .clone();
 
+        let mut inner_html = None;
+
         // We need to keep track of the dynamic styles so we can insert them into the right place
         let mut accumulated_dynamic_styles = Vec::new();
 
@@ -77,7 +79,9 @@ impl Renderer {
             match segment {
                 Segment::Attr(idx) => {
                     let attr = &template.dynamic_attrs[*idx];
-                    if attr.namespace == Some("style") {
+                    if attr.name == "dangerous_inner_html" {
+                        inner_html = Some(attr);
+                    } else if attr.namespace == Some("style") {
                         accumulated_dynamic_styles.push(attr);
                     } else {
                         match attr.value {
@@ -165,6 +169,19 @@ impl Renderer {
                         accumulated_dynamic_styles.clear();
                     }
                 }
+
+                Segment::InnerHtmlMarker => {
+                    if let Some(inner_html) = inner_html.take() {
+                        let inner_html = &inner_html.value;
+                        match inner_html {
+                            AttributeValue::Text(value) => write!(buf, "{}", value)?,
+                            AttributeValue::Bool(value) => write!(buf, "{}", value)?,
+                            AttributeValue::Float(f) => write!(buf, "{}", f)?,
+                            AttributeValue::Int(i) => write!(buf, "{}", i)?,
+                            _ => {}
+                        }
+                    }
+                }
             }
         }
 
@@ -208,7 +225,9 @@ fn to_string_works() {
                     StyleMarker {
                         inside_style_tag: false,
                     },
-                    PreRendered(">Hello world 1 --&gt;".into(),),
+                    PreRendered(">".into()),
+                    InnerHtmlMarker,
+                    PreRendered("Hello world 1 --&gt;".into(),),
                     Node(0,),
                     PreRendered(
                         "&lt;-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),

+ 26 - 0
packages/ssr/tests/inner_html.rs

@@ -0,0 +1,26 @@
+use dioxus::prelude::*;
+
+#[test]
+fn static_inner_html() {
+    fn app(cx: Scope) -> Element {
+        render! { div { dangerous_inner_html: "<div>1234</div>" } }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(dioxus_ssr::render(&dom), r#"<div><div>1234</div></div>"#);
+}
+
+#[test]
+fn dynamic_inner_html() {
+    fn app(cx: Scope) -> Element {
+        let inner_html = "<div>1234</div>";
+        render! { div { dangerous_inner_html: "{inner_html}" } }
+    }
+
+    let mut dom = VirtualDom::new(app);
+    _ = dom.rebuild();
+
+    assert_eq!(dioxus_ssr::render(&dom), r#"<div><div>1234</div></div>"#);
+}

+ 4 - 3
packages/web/src/cfg.rs

@@ -49,15 +49,16 @@ impl Config {
 
     /// Set the name of the element that Dioxus will use as the root.
     ///
-    /// This is akint to calling React.render() on the element with the specified name.
+    /// This is akin to calling React.render() on the element with the specified name.
     pub fn rootname(mut self, name: impl Into<String>) -> Self {
         self.rootname = name.into();
         self
     }
 
-    /// Set the name of the element that Dioxus will use as the root.
+    /// Sets a string cache for wasm bindgen to [intern](https://docs.rs/wasm-bindgen/0.2.84/wasm_bindgen/fn.intern.html). This can help reduce the time it takes for wasm bindgen to pass
+    /// strings from rust to javascript. This can significantly improve pefromance when passing strings to javascript, but can have a negative impact on startup time.
     ///
-    /// This is akint to calling React.render() on the element with the specified name.
+    /// > Currently this cache is only used when creating static elements and attributes.
     pub fn with_string_cache(mut self, cache: Vec<String>) -> Self {
         self.cached_strings = cache;
         self