ftd_rt/
dnode.rs

1#[derive(serde::Deserialize)]
2#[cfg_attr(
3    not(feature = "wasm"),
4    derive(serde::Serialize, PartialEq, Debug, Default, Clone)
5)]
6pub struct DNode {
7    pub classes: Vec<String>,
8    pub node: String,
9    pub attrs: ftd_rt::Map,
10    pub style: ftd_rt::Map,
11    pub children: Vec<DNode>,
12    pub text: Option<String>,
13    pub null: bool,
14    pub visible: bool,
15    pub events: Vec<ftd_rt::Event>, // $event-click$: toggle foo | "click: toggle foo"
16}
17
18impl DNode {
19    fn attrs_to_html(&self) -> String {
20        self.attrs
21            .iter()
22            .map(|(k, v)| format!("{}={}", *k, quote(v))) // TODO: escape needed?
23            .collect::<Vec<String>>()
24            .join(" ")
25    }
26
27    pub fn style_to_html(&self, visible: bool) -> String {
28        let mut styles = self.style.to_owned();
29        if !visible {
30            styles.insert("display".to_string(), "none".to_string());
31        }
32        styles
33            .iter()
34            .map(|(k, v)| format!("{}: {}", *k, ftd_rt::html::escape(v))) // TODO: escape needed?
35            .collect::<Vec<String>>()
36            .join("; ")
37    }
38
39    pub fn class_to_html(&self) -> String {
40        self.classes
41            .iter()
42            .map(|k| k.to_string())
43            .collect::<Vec<String>>()
44            .join(" ")
45    }
46
47    pub fn to_html(&self, id: &str) -> String {
48        let style = format!("style=\"{}\"", self.style_to_html(self.visible));
49        let classes = format!("class=\"{}\"", self.class_to_html());
50
51        let attrs = {
52            let mut attr = self.attrs_to_html();
53            let events = ftd_rt::event::group_by_js_event(&self.events);
54            for (name, actions) in events {
55                let event = format!(
56                    "window.ftd.handle_event(event, '{}', '{}')",
57                    id,
58                    actions.replace("\"", "&quot;")
59                );
60                attr.push(' ');
61                attr.push_str(&format!("{}={}", name, quote(&event)));
62            }
63            attr
64        };
65
66        if self.node == "img" {
67            return format!("<img {attrs} {style}>", attrs = attrs, style = style);
68        }
69        let body = match self.text.as_ref() {
70            Some(v) => v.to_string(),
71            None => self
72                .children
73                .iter()
74                .map(|v| v.to_html(id))
75                .collect::<Vec<String>>()
76                .join("\n"),
77        };
78
79        // TODO: indent things properly
80        format!(
81            "<{node} {attrs} {style} {classes}>{body}</{node}>",
82            node = self.node.as_str(),
83            attrs = attrs,
84            style = style,
85            classes = classes,
86            body = body,
87        )
88    }
89}
90
91fn quote(i: &str) -> String {
92    format!("{:?}", i)
93}