@@ -74,13 +74,13 @@ web_sys::window()
7474 .clear_timeout_with_handle(timeout_id);
7575```
7676
77- ## The `callback ` Layer
77+ ## The `callbacks ` Layer
7878
7979When we look at the raw `web-sys` usage, there is a bit of type conversion
8080noise, some unfortunate method names, and a handful of `unwrap`s for ignoring
8181edge-case scenarios where we prefer to fail loudly rather than limp along. We
8282can clean all these things up with the first of our " mid - level " API layers,
83- which in the case of timers is the `callback ` module in the `gloo_timers` crate
83+ which in the case of timers is the `callbacks ` module in the `gloo_timers` crate
8484(which is also re-exported from the `gloo` umbrella crate as `gloo::timers`).
8585
8686The first " mid - level " API built on top of the `-sys` bindings exposes all the
@@ -90,9 +90,9 @@ functions with `js_sys::Function`, we take any `F: FnOnce()`. This layer is
9090essentially the least opinionated direct API translation to Rust.
9191
9292```rust
93- use gloo::timers::callback ::Timeout;
93+ use gloo::timers::callbacks ::Timeout;
9494// Alternatively, we could use the `gloo_timers` crate without the rest of Gloo:
95- // use gloo_timers::callback ::Timeout;
95+ // use gloo_timers::callbacks ::Timeout;
9696
9797// Already, much nicer!
9898let timeout = Timeout::new(500, move || {
@@ -116,7 +116,7 @@ example, this means we implement a `Future` backed by `setTimeout`, and a
116116
117117```rust
118118use futures::prelude::*;
119- use gloo::timers::future ::TimeoutFuture;
119+ use gloo::timers::futures ::TimeoutFuture;
120120
121121// By using futures, we can use all the future combinator methods to build up a
122122// description of some asynchronous task.
@@ -150,7 +150,7 @@ excited for `async`/`await` as well!
150150That's all the layers we have for the `setTimeout` and `setInterval`
151151APIs. Different Web APIs will have different sets of layers, and this is
152152fine. Not every Web API uses callbacks, so it doesn't make sense to always have
153- a `callback ` module in every Gloo crate. The important part is that we are
153+ a `callbacks ` module in every Gloo crate. The important part is that we are
154154actively identifying layers, making them public and reusable, and building
155155higher-level layers on top of lower-level layers.
156156
@@ -166,8 +166,8 @@ correctness!
166166Another future direction is adding more integration layers with more parts of
167167the larger Rust crates ecosystem. For example, adding functional reactive
168168programming-style layers via [the `futures-signals`
169- crate][integrate-futures-signals] that the [`dominator`][dominator] framework is
170- built upon .
169+ crate][integrate-futures-signals] which is also used by the
170+ [`dominator`][dominator] framework .
171171
172172## Events
173173
@@ -181,27 +181,65 @@ On top of [`web_sys::Event`][web-sys-event] and
181181[`web_sys::EventTarget::add_event_listener_with_callback`][web-sys-add-listener],
182182we are building a layer for [adding and removing event
183183listeners][raii-listeners] and managing their lifetimes from Rust via RAII-style
184- automatic cleanup upon drop. This will enable usage like this:
184+ automatic cleanup upon drop.
185+
186+ We can use this API to make idiomatic Rust types that attach event listeners
187+ that automatically get removed from the DOM when the types is dropped:
185188
186189```rust
190+ use futures::sync::oneshot;
187191use gloo::events::EventListener;
188192
189- // Get an event target from somewhere. Maybe a DOM element or maybe the window
190- // or document.
191- let target: web_sys::EventTarget = unimplemented!();
192-
193- let listener = EventListener::new(&target, " click " , |event: web_sys::Event| {
194- // Cast the `Event` into a `MouseEvent`
195- let mouse_event = event.dyn_into::<web_sys::MouseEvent>().unwrap();
196-
197- // Do stuff on click...
198- });
199-
200- // If we want to remove the listener, we just drop it:
201- drop(listener);
202-
203- // Alternatively, if we want to listen forever, we can use `forget`:
204- listener.forget();
193+ // A prompt for the user.
194+ pub struct Prompt {
195+ receiver: oneshot::Receiver<String>,
196+
197+ // Automatically removed from the DOM on drop!
198+ listener: EventListener,
199+ }
200+
201+ impl Prompt {
202+ pub fn new() -> Prompt {
203+ // Create an `<input>` to prompt the user for something and attach it to the DOM.
204+ let input: web_sys::HtmlInputElement = unimplemented!();
205+
206+ // Create a oneshot channel for sending/receiving the user's input.
207+ let (sender, receiver) = oneshot::channel();
208+
209+ // Attach an event listener to the input element.
210+ let listener = EventListener::new(&input, " input " , move |_event: &web_sys::Event| {
211+ // Get the input element's value.
212+ let value = input.value();
213+
214+ // Send the input value over the oneshot channel.
215+ sender.send(value)
216+ .expect_throw(
217+ " receiver should not be dropped without first removing DOM listener "
218+ );
219+ });
220+
221+ Prompt {
222+ receiver,
223+ listener,
224+ }
225+ }
226+ }
227+
228+ // A `Prompt` is also a future, that resolves after the user input!
229+ impl Future for Prompt {
230+ type Item = String;
231+ type Error = ();
232+
233+ fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
234+ self.receiver
235+ .poll()
236+ .map_err(|_| {
237+ unreachable!(
238+ " we don 't drop the sender without either sending a value or dropping the whole Prompt "
239+ )
240+ })
241+ }
242+ }
205243```
206244
207245On top of that layer, we are using Rust's trait system to design [a
@@ -212,12 +250,18 @@ event types that you listen to:
212250```rust
213251use gloo::events::{ClickEvent, on};
214252
215- // Again, get an event target from somewhere.
253+ // Get an event target from somewhere.
216254let target: web_sys::EventTarget = unimplemented!();
217255
218- // Listen to the " click " event, and get a nicer event type!
256+ // Listen to the " click " event, know that you didn't misspell the event as
257+ // " clik " , and also get a nicer event type!
219258let click_listener = on(&target, move |e: &ClickEvent| {
220- // Do stuff on click...
259+ // The `ClickEvent` type has nice getters for the `MouseEvent` that
260+ // `" click " ` events are guaranteed to yield. No need to dynamically cast
261+ // an `Event` to a `MouseEvent`.
262+ let (x, y) = event.mouse_position();
263+
264+ // ...
221265});
222266```
223267
0 commit comments