kagura 0.13.2

Frontend frame-work for wasm on Rust.
Documentation
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::{Rc, Weak};
mod render;
use super::*;
use crate::kagura::Node;

pub trait AssembledDemirootComponent {
    type ThisComp: Component;

    fn post(&mut self, msg: <Self::ThisComp as Component>::Msg);
    fn update(&mut self, msg: <Self::ThisComp as Component>::Msg);
    fn ref_node(&mut self, name: String, node: web_sys::Node);
}

pub trait AssembledChildComponent {
    type DemirootComp: Component;

    fn as_any(&mut self) -> Option<Rc<RefCell<dyn std::any::Any + 'static>>>;

    fn set_demiroot(
        &mut self,
        demiroot: Option<
            Weak<RefCell<dyn AssembledDemirootComponent<ThisComp = Self::DemirootComp>>>,
        >,
    );

    fn on_assemble(&mut self);
    fn on_load(&mut self);

    fn load_lazy_cmd(&mut self) -> Vec<<Self::DemirootComp as Component>::Msg>;

    fn render(&mut self, children: Vec<Html<Self::DemirootComp>>) -> VecDeque<Node>;
}

pub struct AssembledComponentInstance<ThisComp: Update + Render, DemirootComp: Component> {
    demiroot: Option<Weak<RefCell<dyn AssembledDemirootComponent<ThisComp = DemirootComp>>>>,
    this: Weak<RefCell<Self>>,
    data: Rc<RefCell<ThisComp>>,
    props: ThisComp::Props,
    sub_mapper: sub::Mapper<ThisComp::Sub, DemirootComp::Msg>,
    is_updated: bool,
    lazy_cmd: VecDeque<AssembledCmd<ThisComp, DemirootComp::Msg>>,
    children_tree: ComponentTree<ThisComp, DemirootComp>,
    children: Vec<ComponentTree<ThisComp, DemirootComp>>,
}

enum ComponentTree<ThisComp: Component, DemirootComp: Component> {
    None,
    TextNode,
    Fragment(VecDeque<Self>),
    Element(VecDeque<Self>),
    ThisComp(Rc<RefCell<dyn AssembledChildComponent<DemirootComp = ThisComp>>>),
    DemirootComp(Rc<RefCell<dyn AssembledChildComponent<DemirootComp = DemirootComp>>>),
}

enum AssembledCmd<ThisComp: Component, DemirootMsg> {
    None,
    Sub(ThisComp::Sub),
    Task(Box<dyn FnOnce(TaskResolver<ThisComp::Msg>)>),
    Batch(Box<dyn FnOnce(BatchResolver<ThisComp::Msg>)>),
    List(Vec<Cmd<ThisComp>>),
    Msg(DemirootMsg),
}

impl<ThisComp: Update + Render, DemirootComp: Component>
    AssembledComponentInstance<ThisComp, DemirootComp>
{
    pub fn new_ref(
        data: Rc<RefCell<ThisComp>>,
        props: ThisComp::Props,
        sub_mapper: Sub<ThisComp::Sub, DemirootComp::Msg>,
    ) -> Rc<RefCell<Self>> {
        let this = Self {
            demiroot: None,
            data: data,
            props,
            sub_mapper: sub::Mapper::from(sub_mapper),
            this: Weak::new(),
            is_updated: true,
            lazy_cmd: VecDeque::new(),
            children_tree: ComponentTree::None,
            children: vec![],
        };

        let this = Rc::new(RefCell::new(this));
        this.borrow_mut().this = Rc::downgrade(&this);
        this
    }

    pub fn set_props(&mut self, props: ThisComp::Props) {
        self.props = props;
    }

    pub fn set_sub_mapper(&mut self, sub_mapper: Sub<ThisComp::Sub, DemirootComp::Msg>) {
        self.sub_mapper = sub::Mapper::from(sub_mapper);
    }

    fn force_update(&mut self, msg: ThisComp::Msg) {
        let cmd = self.data.borrow_mut().update(&self.props, msg);
        self.is_updated = true;
        self.load_cmd(cmd, false);
    }

    fn send_sub(&mut self, sub: ThisComp::Sub) {
        if let Some(demiroot) = self.demiroot() {
            if let Some(msg) = self.sub_mapper.try_map(sub) {
                demiroot.borrow_mut().post(msg);
            }
        }
    }

    fn lazy_update(&mut self, msg: ThisComp::Msg) {
        let cmd = self.data.borrow_mut().update(&self.props, msg);
        self.is_updated = true;
        self.lazy_cmd.push_back(AssembledCmd::from(cmd));
    }

    fn load_cmd(&mut self, cmd: Cmd<ThisComp>, is_lazy_sub: bool) -> Vec<ThisComp::Sub> {
        match cmd {
            Cmd::None => vec![],
            Cmd::Sub(sub) => {
                if is_lazy_sub {
                    vec![sub]
                } else {
                    self.send_sub(sub);
                    vec![]
                }
            }
            Cmd::Task(task) => {
                let this = Weak::clone(&self.this);
                let resolver = Box::new(move |msg| {
                    if let Some(this) = this.upgrade() {
                        this.borrow_mut().update(msg);
                        crate::state::render();
                    }
                });
                crate::env::add_task(move || {
                    task(resolver);
                });
                vec![]
            }
            Cmd::Batch(batch) => {
                let this = Weak::clone(&self.this);
                let resolver = Box::new(move |msg| {
                    if let Some(this) = this.upgrade() {
                        this.borrow_mut().update(msg);
                        crate::state::render();
                    }
                });
                crate::env::add_task(move || {
                    batch(resolver);
                });
                vec![]
            }
            Cmd::List(cmds) => {
                let mut subs = vec![];

                for cmd in cmds {
                    subs.append(&mut self.load_cmd(cmd, is_lazy_sub));
                }

                subs
            }
        }
    }

    fn demiroot(
        &self,
    ) -> Option<Rc<RefCell<dyn AssembledDemirootComponent<ThisComp = DemirootComp>>>> {
        self.demiroot
            .as_ref()
            .and_then(|demiroot| demiroot.upgrade())
    }

    fn demiroot_clone(
        &self,
    ) -> Option<Weak<RefCell<dyn AssembledDemirootComponent<ThisComp = DemirootComp>>>> {
        self.demiroot
            .as_ref()
            .map(|demiroot| Weak::clone(&demiroot))
    }

    fn this_as_demiroot(
        &self,
    ) -> Option<Weak<RefCell<dyn AssembledDemirootComponent<ThisComp = ThisComp>>>> {
        let this = Weak::clone(&self.this);
        Some(this)
    }

    pub fn this(&self) -> Weak<RefCell<Self>> {
        Weak::clone(&self.this)
    }
}

impl<ThisComp: Update + Render, DemirootComp: Component> AssembledDemirootComponent
    for AssembledComponentInstance<ThisComp, DemirootComp>
{
    type ThisComp = ThisComp;

    fn post(&mut self, msg: ThisComp::Msg) {
        self.force_update(msg);
    }

    fn update(&mut self, msg: ThisComp::Msg) {
        self.force_update(msg);
    }

    fn ref_node(&mut self, name: String, node: web_sys::Node) {
        let cmd = self.data.borrow_mut().ref_node(&self.props, name, node);
        self.load_cmd(cmd, false);
    }
}

impl<ThisComp: Update + Render, DemirootComp: Component> AssembledChildComponent
    for AssembledComponentInstance<ThisComp, DemirootComp>
{
    type DemirootComp = DemirootComp;

    fn as_any(&mut self) -> Option<Rc<RefCell<dyn std::any::Any>>> {
        if let Some(this) = self.this.upgrade() {
            Some(this as Rc<RefCell<dyn std::any::Any>>)
        } else {
            None
        }
    }

    fn set_demiroot(
        &mut self,
        demiroot: Option<
            Weak<RefCell<dyn AssembledDemirootComponent<ThisComp = Self::DemirootComp>>>,
        >,
    ) {
        self.demiroot = demiroot;
    }

    fn on_assemble(&mut self) {
        let cmd = self.data.borrow_mut().on_assemble(&self.props);
        self.is_updated = true;
        self.lazy_cmd.push_back(AssembledCmd::from(cmd));
    }

    fn on_load(&mut self) {
        let cmd = self.data.borrow_mut().on_load(&self.props);
        self.is_updated = true;
        self.lazy_cmd.push_back(AssembledCmd::from(cmd));
    }

    fn load_lazy_cmd(&mut self) -> Vec<DemirootComp::Msg> {
        let mut res = vec![];
        while let Some(cmd) = self.lazy_cmd.pop_front() {
            if let AssembledCmd::Msg(msg) = cmd {
                res.push(msg);
            } else {
                let subs = self.load_cmd(cmd.into(), true);
                for sub in subs {
                    if let Some(msg) = self.sub_mapper.try_map(sub) {
                        res.push(msg);
                    }
                }
            }
        }
        res
    }

    fn render(&mut self, children: Vec<Html<Self::DemirootComp>>) -> VecDeque<Node> {
        self.render(children)
    }
}

impl<ThisComp: Component, DemirootComp: Component> ComponentTree<ThisComp, DemirootComp> {
    fn into_deq(self) -> VecDeque<Self> {
        match self {
            Self::Fragment(x) => x,
            Self::None => VecDeque::new(),
            _ => vec![self].into_iter().collect(),
        }
    }
}

impl<ThisComp: Component, DemirootMsg> From<Cmd<ThisComp>> for AssembledCmd<ThisComp, DemirootMsg> {
    fn from(cmd: Cmd<ThisComp>) -> Self {
        match cmd {
            Cmd::None => Self::None,
            Cmd::Sub(sub) => Self::Sub(sub),
            Cmd::Task(task) => Self::Task(task),
            Cmd::Batch(batch) => Self::Batch(batch),
            Cmd::List(cmds) => Self::List(cmds),
        }
    }
}

impl<ThisComp: Component, DemirootMsg> Into<Cmd<ThisComp>> for AssembledCmd<ThisComp, DemirootMsg> {
    fn into(self) -> Cmd<ThisComp> {
        match self {
            Self::None => Cmd::None,
            Self::Sub(sub) => Cmd::Sub(sub),
            Self::Task(task) => Cmd::Task(task),
            Self::Batch(batch) => Cmd::Batch(batch),
            Self::List(cmds) => Cmd::List(cmds),
            Self::Msg(..) => Cmd::None,
        }
    }
}