seed 0.9.2

A Rust framework for creating web apps, using WebAssembly
Documentation
use crate::app::MessageMapper;
use crate::virtual_dom::{Ev, Mailbox};
use std::{cell::RefCell, collections::BTreeMap, rc::Rc};

pub mod event_handler;
pub mod listener;

pub use event_handler::EventHandler;
pub use listener::Listener;

// ------ EventHandlerManager ------

#[derive(Debug, Default)]
/// Manages event handlers and listeners for elements.
pub struct EventHandlerManager<Ms> {
    groups: BTreeMap<Ev, Group<Ms>>,
}

// @TODO remove custom impl once https://github.com/rust-lang/rust/issues/26925 is fixed
impl<Ms> Clone for EventHandlerManager<Ms> {
    fn clone(&self) -> Self {
        Self {
            groups: self.groups.clone(),
        }
    }
}

impl<Ms> EventHandlerManager<Ms> {
    /// Creates an empty manager instance.
    // @TODO Remove the line below once https://github.com/rust-lang/rust/issues/71835 is resolved.
    #[allow(clippy::missing_const_for_fn)]
    pub fn new() -> Self {
        Self {
            groups: BTreeMap::new(),
        }
    }

    /// Creates a new manager instance with given event handlers.
    /// It doesn't create listeners automatically - you have to call `attach_listeners`.
    pub fn with_event_handlers(event_handlers: Vec<EventHandler<Ms>>) -> Self {
        let mut manager = Self::new();
        manager.add_event_handlers(event_handlers);
        manager
    }

    /// Creates missing listeners and attaches them to the given `event_target`.
    /// It can reuse listeners from the `old_manager`.
    pub fn attach_listeners(
        &mut self,
        event_target: impl Into<web_sys::EventTarget> + 'static,
        mut old_manager: Option<&mut EventHandlerManager<Ms>>,
        mailbox: &Mailbox<Ms>,
    ) {
        let event_target = event_target.into();

        for (trigger, group) in &mut self.groups {
            if group.listener.is_none() {
                group.listener = old_manager
                    .as_mut()
                    .and_then(|old_manager| {
                        old_manager
                            .take_and_setup_listener(trigger, Rc::clone(&group.event_handlers))
                    })
                    .or_else(|| {
                        Some(Listener::new(
                            trigger.clone(),
                            event_target.clone(),
                            Rc::clone(&group.event_handlers),
                            mailbox.clone(),
                        ))
                    });
            }
        }
    }

    /// Add new event handlers into the manager.
    /// It doesn't create listeners automatically - you have to call `attach_listeners`.
    pub fn add_event_handlers(&mut self, event_handlers: Vec<EventHandler<Ms>>) {
        for handler in event_handlers {
            if let Some(group) = self.groups.get_mut(&handler.trigger) {
                group.event_handlers.borrow_mut().push(handler);
            } else {
                self.groups.insert(
                    handler.trigger.clone(),
                    Group {
                        event_handlers: Rc::new(RefCell::new(vec![handler])),
                        listener: None,
                    },
                );
            }
        }
    }

    /// This method is used in `attach_listeners` method to move listeners from the old manager.
    pub fn take_and_setup_listener(
        &mut self,
        trigger: &Ev,
        event_handlers: Rc<RefCell<Vec<EventHandler<Ms>>>>,
    ) -> Option<Listener<Ms>> {
        self.groups
            .get_mut(trigger)
            .and_then(|group| group.listener.take())
            .map(|listener| {
                listener.set_event_handlers(event_handlers);
                listener
            })
    }
}

impl<Ms: 'static, OtherMs: 'static> MessageMapper<Ms, OtherMs> for EventHandlerManager<Ms> {
    type SelfWithOtherMs = EventHandlerManager<OtherMs>;
    /// _Note:_ Listeners will be automatically detached and removed.
    /// You have to call `attach_listeners` to recreate them.
    fn map_msg(
        self,
        f: impl FnOnce(Ms) -> OtherMs + 'static + Clone,
    ) -> EventHandlerManager<OtherMs> {
        EventHandlerManager {
            groups: self
                .groups
                .into_iter()
                .map(|(trigger, group)| (trigger, group.map_msg(f.clone())))
                .collect(),
        }
    }
}

// ------ Group ------

#[derive(Debug)]
/// A group of event handlers and a listener with the same trigger (event).
struct Group<Ms> {
    // `event_handlers` are wrapped in `Rc` & `RefCell`
    // because they are sent to callback in `listener`.
    event_handlers: Rc<RefCell<Vec<EventHandler<Ms>>>>,
    // `listener` is optional because the element where the manager is placed may be pure virtual
    // - i.e. the element hasn't been associated with the DOM yet.
    listener: Option<Listener<Ms>>,
}

impl<T> Clone for Group<T> {
    /// _Note:_  The group's `listener` will be set to `None` and automatically detached.
    fn clone(&self) -> Self {
        Self {
            event_handlers: Rc::clone(&self.event_handlers),
            // We can't clone `listener` because it's tightly connected to the specific DOM element.
            listener: None,
        }
    }
}

impl<Ms: 'static, OtherMs: 'static> MessageMapper<Ms, OtherMs> for Group<Ms> {
    type SelfWithOtherMs = Group<OtherMs>;
    /// _Note:_  The group's `listener` will be set to `None` and automatically detached.
    fn map_msg(self, f: impl FnOnce(Ms) -> OtherMs + 'static + Clone) -> Group<OtherMs> {
        let mapped_event_handlers = self
            .event_handlers
            .replace(Vec::new())
            .into_iter()
            .map(|handler| handler.map_msg(f.clone()))
            .collect();

        Group {
            event_handlers: Rc::new(RefCell::new(mapped_event_handlers)),
            // `listener` has to be set to `None` because we had to create new `event handlers`.
            // (The reference to the old handlers in the `listener` has become invalid).
            listener: None,
        }
    }
}