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
use super::Web;
use crate::{view::View, Modify, Platform};
use web_sys::Element;

/// Html element view.
pub struct Html<'a, A, V> {
    tag: &'a str,
    modify: A,
    view: V,
}

impl<'a, A, V> Html<'a, A, V> {
    pub fn new(tag: &'a str, modify: A, view: V) -> Self {
        Self { tag, modify, view }
    }

    pub fn modify<A2>(self, attributes: A2) -> Html<'a, A2, V> {
        Html::new(self.tag, attributes, self.view)
    }

    pub fn then<V2>(self, view: V2) -> Html<'a, A, (V, V2)> {
        Html::new(self.tag, self.modify, (self.view, view))
    }
}

macro_rules! html_tags {
    ($($tag:ident),+) => {
        $(
            pub fn $tag(attrs: A, view: V) -> Self {
                Html::new(stringify!($tag), attrs, view)
            }
        )+
    };
}

impl<'a, A, V> Html<'static, A, V> {
    html_tags!(
        a, abbr, address, area, article, aside, audio, b, base, bdi, bdo, blockquote, body, br,
        button, canvas, caption, cite, code, col, colgroup, data, datalist, dd, del, details, dfn,
        dialog, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4,
        h5, h6, head, header, hgroup, hr, html, i, iframe, img, input, ins, kbd, label, legend, li,
        link, main, map, mark, meta, meter, nav, noscript, object, ol, optgroup, option, output, p,
        param, picture, pre, progress, q, rp, rt, ruby, s, samp, script, section, select, small,
        source, span, strong, style, sub, summary, sup, table, tbody, td, template, textarea,
        tfoot, th, thead, time, title, tr, track, u, ul, var, video, wbr
    );
}

impl<'a, A, V, E> View<Web<E>> for Html<'a, A, V>
where
    A: Modify<Web<E>, Element>,
    V: View<Web<E>>,
    E: 'static,
{
    type State = (A::State, Element, V::State);

    fn build(self, cx: &mut Web<E>) -> Self::State {
        let mut elem = cx.document.create_element(self.tag).unwrap();
        cx.insert(&elem);

        let attrs = self.modify.build(cx, &mut elem);
        let (elem, _, state) = cx.with_nested(elem, |cx| self.view.build(cx));
        (attrs, elem, state)
    }

    fn rebuild(self, cx: &mut Web<E>, state: &mut Self::State) {
        self.modify.rebuild(cx, &mut state.1, &mut state.0);

        cx.advance();
        cx.with_nested(state.1.clone(), |cx| {
            self.view.rebuild(cx, &mut state.2);
        });
    }

    fn remove(_cx: &mut Web<E>, state: &mut Self::State) {
        state.1.remove();
    }
}