Skip to content

Commit

Permalink
Merge branch 'master' into cut-down-unsafe
Browse files Browse the repository at this point in the history
  • Loading branch information
voidpumpkin committed Nov 24, 2021
2 parents be68efb + 47fad81 commit 4dd7372
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/publish-examples.yml
Expand Up @@ -56,6 +56,11 @@ jobs:
continue
fi
# web_worker_fib does not compile with trunk. See <https://github.com/thedodd/trunk/issues/46>.
if [[ "$example" == "web_worker_fib" ]]; then
continue
fi
echo "building: $example"
(
cd "$path"
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -33,6 +33,7 @@ members = [
"examples/todomvc",
"examples/two_apps",
"examples/webgl",
"examples/web_worker_fib",

# Release tools
"packages/changelog",
Expand Down
8 changes: 7 additions & 1 deletion 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

Expand Down
16 changes: 16 additions & 0 deletions 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"
27 changes: 27 additions & 0 deletions 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
8 changes: 8 additions & 0 deletions 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
9 changes: 9 additions & 0 deletions 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
55 changes: 55 additions & 0 deletions 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<Self>,
}

#[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<Self>;
type Message = ();
type Input = WorkerInput;
type Output = WorkerOutput;

fn create(link: AgentLink<Self>) -> 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"
}
}
73 changes: 73 additions & 0 deletions 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<dyn Bridge<Worker>>,
fibonacci_output: String,
}

pub enum Message {
Click,
RunWorker,
WorkerMsg(WorkerOutput),
}

impl Component for Model {
type Message = Message;
type Properties = ();

fn create(ctx: &Context<Self>) -> 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<Self>, msg: Self::Message) -> bool {
match msg {
Self::Message::Click => {
self.clicker_value += 1;
}
Self::Message::RunWorker => {
if let Some(input) = self.input_ref.cast::<HtmlInputElement>() {
// 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<Self>) -> Html {
html! {
<>
<h1>{ "Web worker demo" }</h1>
<p>{ "Submit a value to calculate, then increase the counter on the main thread!"} </p>
<p>{ "Large numbers will take some time!" }</p>
<h3>{ "Output: " } { &self.fibonacci_output }</h3>
<br />
<input ref={self.input_ref.clone()} type="number" value="44" max="50"/>
<button onclick={ctx.link().callback(|_| Message::RunWorker)}>{ "submit" }</button>
<br /> <br />
<h3>{ "Main thread value: " } { self.clicker_value }</h3>
<button onclick={ctx.link().callback(|_| Message::Click)}>{ "click!" }</button>
</>
}
}
}
19 changes: 19 additions & 0 deletions 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::<Model>();
} else {
agent::Worker::register();
}
}
12 changes: 12 additions & 0 deletions examples/web_worker_fib/static/index.html
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Yew web worker demo</title>
<script src="wasm.js"></script>
<script type="application/javascript">wasm_bindgen('wasm_bg.wasm');</script>
</head>

<body>
</body>
</html>
45 changes: 40 additions & 5 deletions packages/yew/src/virtual_dom/listeners.rs
Expand Up @@ -501,16 +501,13 @@ impl Registry {

run_handler(&target);

if BUBBLE_EVENTS.load(Ordering::Relaxed) && !event.cancel_bubble() {
if BUBBLE_EVENTS.load(Ordering::Relaxed) {
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);
}
}
Expand Down Expand Up @@ -827,6 +824,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<C>(ctx: &Context<C>, state: &State) -> Html
where
C: Component<Message = Message>,
{
html! {
<div onclick={ctx.link().callback(|_| Message::Action)}>
<div onclick={ctx.link().callback(|event: MouseEvent| {
event.stop_propagation();
Message::Action
})}>
<a>
{state.action}
</a>
</div>
</div>
}
}
}

let (_, el) = init::<CancelBubbling>("a");

assert_count(&el, 0);

el.click();
assert_count(&el, 1);

el.click();
assert_count(&el, 2);
}

fn test_input_listener<E>(make_event: impl Fn() -> E)
where
E: JsCast + std::fmt::Debug,
Expand Down
13 changes: 6 additions & 7 deletions website/docs/concepts/router.md
Expand Up @@ -209,22 +209,21 @@ 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.
:::

```rust ,ignore
fn create(ctx: &Context<Self>) -> 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
}
}
```
Expand Down
10 changes: 8 additions & 2 deletions website/docs/more/css.md
Expand Up @@ -4,22 +4,28 @@ title: "CSS"

&lt;TODO&gt;

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.

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!

Expand Down

0 comments on commit 4dd7372

Please sign in to comment.