use futures::channel::mpsc; use futures::stream::Stream; use gloo::events::EventListener; use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use wasm_bindgen::prelude::*; use web_sys::{EventTarget, Node}; // From: https://docs.rs/gloo-events/latest/gloo_events/struct.EventListener.html pub struct OnClick { receiver: mpsc::UnboundedReceiver<()>, // Automatically removed from the DOM on drop. listener: EventListener, } impl OnClick { pub fn new(target: &EventTarget) -> Self { Self::new_with_filter(target, |_| true) } pub fn new_with_filter(target: &EventTarget, filter: F) -> Self where F: Fn(&Node) -> bool + 'static, { let (sender, receiver) = mpsc::unbounded(); let listener = EventListener::new(target, "click", move |event| { let mut send = true; if let Some(event_target) = event.target() { if let Some(node) = event_target.dyn_ref::() { send = filter(node); } } if send { sender.unbounded_send(()).unwrap_throw(); } }); Self { receiver, listener } } } // Multiple clicks. impl Stream for OnClick { type Item = (); fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { Pin::new(&mut self.receiver).poll_next(cx) } } // Just one click. impl Future for OnClick { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.receiver) .poll_next(cx) .map(Option::unwrap) } }