1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use wasm_bindgen::UnwrapThrowExt;

use super::{Checklist, Comp, CompInstance, Component, RcComp};
use crate::dom::{Element, TagName};

pub type ChildComp<C> = RcComp<C>;

impl<C: Component> ChildComp<C> {
    pub fn comp_instance(&self) -> std::cell::Ref<CompInstance<C>> {
        self.0.borrow()
    }
}

pub trait AsChildComp: Sized + Component {
    const ROOT_ELEMENT_TAG: TagName;
    type Properties;
    fn init(comp: &Comp<Self>, props: Self::Properties) -> Self;
    fn with_props(props: Self::Properties) -> ChildComp<Self> {
        let root_element = match Self::ROOT_ELEMENT_TAG {
            TagName::Html(tag) => Element::new_ns(tag),
            #[cfg(feature = "svg")]
            TagName::Svg(tag) => Element::new_ns(tag),
        };
        let rc_comp = ChildComp::with_root(root_element);
        let comp = rc_comp.comp();
        let state = AsChildComp::init(&comp, props);
        rc_comp.set_state(state);
        crate::routing::register_routing_callback(&comp);
        rc_comp
    }
}

impl<C: AsChildComp + Component> ChildComp<C> {
    pub fn with_updater<P, T, G, U, Cl>(self, fn_get_value: G, cb: U) -> Child<P, C, T>
    where
        P: Component,
        G: 'static + Fn(&P) -> T,
        U: 'static + Fn(&mut C, T) -> Cl,
        Cl: 'static + Into<Checklist<C>>,
        T: 'static + Clone + PartialEq,
    {
        let child_callback = self.comp().callback_arg_mut(cb);
        Child {
            child: self,
            last_value: None,
            fn_get_value: Some(Box::new(fn_get_value)),
            child_callback: Some(child_callback),
        }
    }

    pub fn no_updater<P>(self) -> Child<P, C, ()>
    where
        P: Component,
    {
        Child {
            child: self,
            last_value: None,
            fn_get_value: None,
            child_callback: None,
        }
    }
}

// A new struct and impl Drop on it, instead of impl Drop on Comp,
// because we only want to set status to unmounted when removing
// it from its parent.
pub struct ComponentHandle<C: Component>(Comp<C>);

impl<C: Component> Drop for ComponentHandle<C> {
    fn drop(&mut self) {
        self.0.set_mount_status_to_unmounted();
    }
}

impl<C: Component> From<Comp<C>> for ComponentHandle<C> {
    fn from(comp: Comp<C>) -> Self {
        Self(comp)
    }
}

impl<C: Component> Drop for ChildComp<C> {
    fn drop(&mut self) {
        crate::routing::remove_routing_callback::<C>();
        self.0
            .try_borrow_mut()
            .expect_throw("Why unable to borrow a child component in dropping?")
            .root_element
            .ws_element()
            .set_text_content(None);
    }
}

type GetValue<P, T> = Box<dyn Fn(&P) -> T>;

pub struct Child<P, C, T>
where
    P: Component,
    C: Component,
    T: Clone + PartialEq,
{
    child: ChildComp<C>,
    last_value: Option<T>,
    fn_get_value: Option<GetValue<P, T>>,
    child_callback: Option<crate::CallbackArg<T>>,
}

impl<P, C, T> Child<P, C, T>
where
    P: Component,
    C: Component,
    T: Clone + PartialEq,
{
    // This return `true` if it queue an update
    pub fn update(&mut self, parent_state: &P) -> bool {
        let getter = match self.fn_get_value.as_ref() {
            Some(g) => g,
            None => return false,
        };

        let new_value = (getter)(parent_state);
        if let Some(old_value) = self.last_value.as_ref() {
            if new_value == *old_value {
                return false;
            }
        }
        self.last_value = Some(new_value.clone());

        if let Some(cb) = self.child_callback.as_ref() {
            cb.queue(new_value);
            return true;
        }
        false
    }

    pub fn get_root_node(&self) -> web_sys::Node {
        let v =
            self.child.0.try_borrow().expect_throw(
                "component::child_component::Child::get_root_node borrow CompInstance",
            );
        let n: &web_sys::Node = v.root_element.ws_element().ws_node();
        n.clone()
    }

    pub fn first_render(&self) {
        self.child.first_render();
    }
}