From c0f584c761edba97f6b073d2fbcf666d6883cefc Mon Sep 17 00:00:00 2001 From: Julius Lungys <32368314+voidpumpkin@users.noreply.github.com> Date: Tue, 23 Nov 2021 13:05:01 +0200 Subject: [PATCH 1/4] Add multithreading example (#2171) * correctly reflect the status of multi_thread example * commiting a partially working example ported from 0.18 to next * make the app actually start * remove example from github workflows * rust-fmt * cargo make pr-flow done succesfully :) * update per comments * fix build errors * fix cargo fmt * fix stuff * Apply suggestions from code review Co-authored-by: mc1098 * Update examples/web_worker_fib/src/app.rs Co-authored-by: mc1098 * Remove space * Update .github/workflows/publish-examples.yml Co-authored-by: WorldSEnder Co-authored-by: SpanishPear Co-authored-by: mc1098 Co-authored-by: WorldSEnder --- .github/workflows/publish-examples.yml | 5 ++ Cargo.toml | 1 + examples/multi_thread/README.md | 8 ++- examples/web_worker_fib/Cargo.toml | 16 +++++ examples/web_worker_fib/README.md | 27 +++++++++ examples/web_worker_fib/build.sh | 8 +++ examples/web_worker_fib/serve.sh | 9 +++ examples/web_worker_fib/src/agent.rs | 55 +++++++++++++++++ examples/web_worker_fib/src/app.rs | 73 +++++++++++++++++++++++ examples/web_worker_fib/src/lib.rs | 19 ++++++ examples/web_worker_fib/static/index.html | 12 ++++ 11 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 examples/web_worker_fib/Cargo.toml create mode 100644 examples/web_worker_fib/README.md create mode 100755 examples/web_worker_fib/build.sh create mode 100755 examples/web_worker_fib/serve.sh create mode 100644 examples/web_worker_fib/src/agent.rs create mode 100644 examples/web_worker_fib/src/app.rs create mode 100644 examples/web_worker_fib/src/lib.rs create mode 100644 examples/web_worker_fib/static/index.html diff --git a/.github/workflows/publish-examples.yml b/.github/workflows/publish-examples.yml index 915f2358fa7..e3114c462d9 100644 --- a/.github/workflows/publish-examples.yml +++ b/.github/workflows/publish-examples.yml @@ -56,6 +56,11 @@ jobs: continue fi + # web_worker_fib does not compile with trunk. See . + if [[ "$example" == "web_worker_fib" ]]; then + continue + fi + echo "building: $example" ( cd "$path" diff --git a/Cargo.toml b/Cargo.toml index 2bab65a51a8..6b6177b5b9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ members = [ "examples/todomvc", "examples/two_apps", "examples/webgl", + "examples/web_worker_fib", # Release tools "packages/changelog", diff --git a/examples/multi_thread/README.md b/examples/multi_thread/README.md index c5ebd7ece0b..5c565f8263c 100644 --- a/examples/multi_thread/README.md +++ b/examples/multi_thread/README.md @@ -1,8 +1,14 @@ +# Warning + +The multi-thread example is a conceptual WIP and is currently blocked on [a future Trunk feature](https://github.com/thedodd/trunk/issues/46) + +There is an alternate multi-thread example [here](https://github.com/yewstack/yew/tree/master/examples/web_worker_fib). + + # Multi-Thread Example [![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Fmulti_thread)](https://examples.yew.rs/multi_thread) -**WIP**: [thedodd/trunk#46](https://github.com/thedodd/trunk/issues/46) ## Concepts diff --git a/examples/web_worker_fib/Cargo.toml b/examples/web_worker_fib/Cargo.toml new file mode 100644 index 00000000000..609a0f6b50c --- /dev/null +++ b/examples/web_worker_fib/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "yew-worker-fib" +version = "0.1.0" +edition = "2018" +author = "Shrey Somaiya" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +yew = { path = "../../packages/yew" } +yew-agent = { path = "../../packages/yew-agent" } +wasm-bindgen = "0.2" +js-sys = "0.3" +web-sys = { version = "0.3", features = [ "HtmlInputElement" ] } +serde = "1" diff --git a/examples/web_worker_fib/README.md b/examples/web_worker_fib/README.md new file mode 100644 index 00000000000..fdadfa2a3b6 --- /dev/null +++ b/examples/web_worker_fib/README.md @@ -0,0 +1,27 @@ +# Web Worker Demo + +Calculate fibbonnaci value of a number in the worker thread, +without blocking the main thread. + +You can access a live version here: + +# Running this example + +do `./build.sh && ./serve.sh` + +## notes + +This example is NOT built with [trunk](https://github.com/thedodd/trunk). +Multi-threading in yew does not currently build with Trunk, due to issues described in the [multi_thread](/examples/multi_thread/README.md) example. + +Instead the example is built with [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) directly. + +To build, run `./build.sh`. +You can then serve the build, with `./serve.sh`. + +This example uses python3 as a server, any alternative will work. + +# Thanks to + +- [insou22](https://github.com/insou22) for writing up the demo. +- [https://github.com/yvt/img2text](https://github.com/yvt/img2text) -- for how to make web workers compile in wasm diff --git a/examples/web_worker_fib/build.sh b/examples/web_worker_fib/build.sh new file mode 100755 index 00000000000..8ffb1c10a18 --- /dev/null +++ b/examples/web_worker_fib/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +if ! command -v wasm-pack 2>&1 >/dev/null; +then + echo 'error: you must install wasm-pack' + exit +fi + +wasm-pack build --target no-modules --out-name wasm --out-dir ./static --no-typescript diff --git a/examples/web_worker_fib/serve.sh b/examples/web_worker_fib/serve.sh new file mode 100755 index 00000000000..b65aeeb5e5a --- /dev/null +++ b/examples/web_worker_fib/serve.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +if ! command -v python3 2>&1 >/dev/null; +then + echo 'error: you must install python3' + exit +fi + +cd static/ +python3 -m http.server 8080 diff --git a/examples/web_worker_fib/src/agent.rs b/examples/web_worker_fib/src/agent.rs new file mode 100644 index 00000000000..ce4e6216e6c --- /dev/null +++ b/examples/web_worker_fib/src/agent.rs @@ -0,0 +1,55 @@ +use serde::{Deserialize, Serialize}; +use yew_agent::{Agent, AgentLink, HandlerId, Public}; + +pub struct Worker { + link: AgentLink, +} + +#[derive(Serialize, Deserialize)] +pub struct WorkerInput { + pub n: u32, +} + +#[derive(Serialize, Deserialize)] +pub struct WorkerOutput { + pub value: u32, +} + +impl Agent for Worker { + type Reach = Public; + type Message = (); + type Input = WorkerInput; + type Output = WorkerOutput; + + fn create(link: AgentLink) -> Self { + Self { link } + } + + fn update(&mut self, _msg: Self::Message) { + // no messaging + } + + fn handle_input(&mut self, msg: Self::Input, id: HandlerId) { + // this runs in a web worker + // and does not block the main + // browser thread! + + let n = msg.n; + + fn fib(n: u32) -> u32 { + if n <= 1 { + 1 + } else { + fib(n - 1) + fib(n - 2) + } + } + + let output = Self::Output { value: fib(n) }; + + self.link.respond(id, output); + } + + fn name_of_resource() -> &'static str { + "wasm.js" + } +} diff --git a/examples/web_worker_fib/src/app.rs b/examples/web_worker_fib/src/app.rs new file mode 100644 index 00000000000..7ab2f9d5b1e --- /dev/null +++ b/examples/web_worker_fib/src/app.rs @@ -0,0 +1,73 @@ +use crate::agent::{Worker, WorkerInput, WorkerOutput}; + +use web_sys::HtmlInputElement; +use yew::prelude::*; +use yew_agent::{Bridge, Bridged}; + +pub struct Model { + clicker_value: u32, + input_ref: NodeRef, + worker: Box>, + fibonacci_output: String, +} + +pub enum Message { + Click, + RunWorker, + WorkerMsg(WorkerOutput), +} + +impl Component for Model { + type Message = Message; + type Properties = (); + + fn create(ctx: &Context) -> Self { + let worker = Worker::bridge(ctx.link().callback(Self::Message::WorkerMsg)); + + Self { + clicker_value: 0, + input_ref: NodeRef::default(), + worker, + fibonacci_output: String::from("Try out some fibonacci calculations!"), + } + } + + fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { + match msg { + Self::Message::Click => { + self.clicker_value += 1; + } + Self::Message::RunWorker => { + if let Some(input) = self.input_ref.cast::() { + // start the worker off! + self.worker.send(WorkerInput { + n: input.value_as_number() as u32, + }); + } + } + Self::Message::WorkerMsg(output) => { + // the worker is done! + self.fibonacci_output = format!("Fibonacci value: {}", output.value); + } + } + + true + } + + fn view(&self, ctx: &Context) -> Html { + html! { + <> +

{ "Web worker demo" }

+

{ "Submit a value to calculate, then increase the counter on the main thread!"}

+

{ "Large numbers will take some time!" }

+

{ "Output: " } { &self.fibonacci_output }

+
+ + +

+

{ "Main thread value: " } { self.clicker_value }

+ + + } + } +} diff --git a/examples/web_worker_fib/src/lib.rs b/examples/web_worker_fib/src/lib.rs new file mode 100644 index 00000000000..16c37a051af --- /dev/null +++ b/examples/web_worker_fib/src/lib.rs @@ -0,0 +1,19 @@ +#![recursion_limit = "1024"] +#![allow(clippy::large_enum_variant)] + +pub mod agent; +pub mod app; +use app::Model; +use wasm_bindgen::prelude::*; +use yew_agent::Threaded; + +#[wasm_bindgen(start)] +pub fn start() { + use js_sys::{global, Reflect}; + + if Reflect::has(&global(), &JsValue::from_str("window")).unwrap() { + yew::start_app::(); + } else { + agent::Worker::register(); + } +} diff --git a/examples/web_worker_fib/static/index.html b/examples/web_worker_fib/static/index.html new file mode 100644 index 00000000000..2f265bc1ba8 --- /dev/null +++ b/examples/web_worker_fib/static/index.html @@ -0,0 +1,12 @@ + + + + + Yew web worker demo + + + + + + + From bb36725dc27a5f14e8dd0663ab758ce49f2581b2 Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Tue, 23 Nov 2021 21:50:13 +0900 Subject: [PATCH 2/4] Add stylist to docs. (#2190) --- website/docs/more/css.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/website/docs/more/css.md b/website/docs/more/css.md index e15e06e4500..2b06f5297d3 100644 --- a/website/docs/more/css.md +++ b/website/docs/more/css.md @@ -4,7 +4,7 @@ title: "CSS" <TODO> -A proposal for integrated CSS support can be found here: +A proposal for integrated CSS support can be found here: [https://github.com/yewstack/yew/issues/533](https://github.com/yewstack/yew/issues/533) This contains a lot of discussion about how to best integrate CSS support into Yew. @@ -12,14 +12,20 @@ This contains a lot of discussion about how to best integrate CSS support into Y Currently, the approach we've adopted is to encourage developers to build a number of systems, before adopting the most popular one. -The community are currently developing a number of projects to make it easy to add styles to +The community are currently developing a number of projects to make it easy to add styles to projects. A few are given below: +#### Component Libraries + * [yew_styles](https://github.com/spielrs/yew_styles) - A styling framework for Yew without any JavaScript dependencies. * [yew-mdc](https://github.com/Follpvosten/yew-mdc) - Material Design Components. * [muicss-yew](https://github.com/AlephAlpha/muicss-yew) - MUI CSS Components. * [Yewtify](https://github.com/yewstack/yewtify) – Implements the features provided by the Vuetify framework in Yew. +#### Styling Solutions + +* [stylist](https://github.com/futursolo/stylist-rs) - A CSS-in-Rust styling solution for WebAssembly Applications. + :::important contribute If you're developing a project adding styles to Yew please submit a PR adding yourself to this list! From c348a2358e723de36573a02d0fcd2c5bdb0fa2da Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Wed, 24 Nov 2021 01:06:41 +0900 Subject: [PATCH 3/4] Fix router docs. (#2188) --- website/docs/concepts/router.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/website/docs/concepts/router.md b/website/docs/concepts/router.md index 53d6176b0d9..43bc0f15cbd 100644 --- a/website/docs/concepts/router.md +++ b/website/docs/concepts/router.md @@ -209,7 +209,7 @@ Your components will re-render when provided values change. #### Struct components -In order to react on route changes, you can pass a callback closure to the `listen()` method of `AnyHistory`. +In order to react on route changes, you can pass a callback closure to the `add_history_listener()` method of `ctx.link()`. :::note The history listener will get unregistered once it is dropped. Make sure to store the handle inside your component state. @@ -217,14 +217,13 @@ The history listener will get unregistered once it is dropped. Make sure to stor ```rust ,ignore fn create(ctx: &Context) -> Self { - let _listener = ctx.link() - .history() - .unwrap() - .listen(ctx.link().callback( + let listener = ctx.link() + .add_history_listener(ctx.link().callback( // handle event - )); + )) + .unwrap(); MyComponent { - _listener + _listener: listener } } ``` From 47fad81f19581b947ef90e955f67f2e652ec51f5 Mon Sep 17 00:00:00 2001 From: rjmac Date: Tue, 23 Nov 2021 12:38:32 -0800 Subject: [PATCH 4/4] Check event bubbling cancellation at each step of propagation (#2191) Rather than once after the innermost node is given the chance to handle it. Fixes #2185 Co-authored-by: Robert Macomber --- packages/yew/src/virtual_dom/listeners.rs | 45 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/yew/src/virtual_dom/listeners.rs b/packages/yew/src/virtual_dom/listeners.rs index 0b4e0859012..f6c48e5e30d 100644 --- a/packages/yew/src/virtual_dom/listeners.rs +++ b/packages/yew/src/virtual_dom/listeners.rs @@ -502,16 +502,13 @@ impl Registry { run_handler(&target); - if unsafe { BUBBLE_EVENTS } && !event.cancel_bubble() { + if unsafe { BUBBLE_EVENTS } { let mut el = target; - loop { + while !event.cancel_bubble() { el = match el.parent_element() { Some(el) => el, None => break, }; - // XXX: we have no way to detect, if the callback called `Event.stopPropagation()` - // or `Event.stopImmediatePropagation()` without breaking the callback API. - // It's arguably not worth the cost. run_handler(&el); } } @@ -828,6 +825,44 @@ mod tests { assert_count(&el, 2); } + #[test] + fn cancel_bubbling_nested() { + // Here an event is being delivered to a DOM node which does + // _not_ have a listener but which is contained within an + // element that does and which cancels the bubble. + struct CancelBubbling; + + impl Mixin for CancelBubbling { + fn view(ctx: &Context, state: &State) -> Html + where + C: Component, + { + html! { + + } + } + } + + let (_, el) = init::("a"); + + assert_count(&el, 0); + + el.click(); + assert_count(&el, 1); + + el.click(); + assert_count(&el, 2); + } + fn test_input_listener(make_event: impl Fn() -> E) where E: JsCast + std::fmt::Debug,