virtual_node/velement/
special_attributes.rs

1use std::borrow::Cow;
2use std::cell::RefCell;
3use std::ops::DerefMut;
4
5/// A specially supported attributes.
6#[derive(Default, PartialEq)]
7pub struct SpecialAttributes {
8    /// A a function that gets called when the virtual node is first turned into a real node.
9    ///
10    /// See [`SpecialAttributes.set_on_create_element`] for more documentation.
11    on_create_element: Option<KeyAndElementFn>,
12    /// A a function that gets called when the virtual node is first turned into a real node.
13    ///
14    /// See [`SpecialAttributes.set_on_remove_element`] for more documentation.
15    on_remove_element: Option<KeyAndElementFn>,
16    /// Allows setting the innerHTML of an element.
17    ///
18    /// # Danger
19    ///
20    /// Be sure to escape all untrusted input to avoid cross site scripting attacks.
21    pub dangerous_inner_html: Option<String>,
22}
23
24impl SpecialAttributes {
25    /// The key for the on create element function
26    pub fn on_create_element_key(&self) -> Option<&Cow<'static, str>> {
27        self.on_create_element.as_ref().map(|k| &k.key)
28    }
29
30    /// Set the [`SpecialAttributes.on_create_element`] function.
31    ///
32    /// # Key
33    ///
34    /// The key is used when one virtual-node is being patched over another.
35    ///
36    /// If the new node's key is different from the old node's key, the on create element function
37    /// gets called.
38    ///
39    /// If the keys are the same, the function does not get called.
40    ///
41    /// # Examples
42    ///
43    /// ```no_run
44    /// # use virtual_node::VirtualNode;
45    /// use wasm_bindgen::JsValue;
46    ///
47    /// let mut node = VirtualNode::element("div");
48    ///
49    /// // A key can be any `Into<Cow<'static, str>>`.
50    /// let key = "some-key";
51    ///
52    /// let on_create_elem = move |elem: web_sys::Element| {
53    ///     assert_eq!(elem.id(), "");
54    /// };
55    ///
56    /// node
57    ///     .as_velement_mut()
58    ///     .unwrap()
59    ///     .special_attributes
60    ///     .set_on_create_element(key, on_create_elem);
61    /// ```
62    pub fn set_on_create_element<Key, Func>(&mut self, key: Key, func: Func)
63    where
64        Key: Into<Cow<'static, str>>,
65        Func: FnMut(web_sys::Element) + 'static,
66    {
67        self.on_create_element = Some(KeyAndElementFn {
68            key: key.into(),
69            func: RefCell::new(ElementFunc::OneArg(Box::new(func))),
70        });
71    }
72
73    // Used by the html-macro
74    #[doc(hidden)]
75    pub fn set_on_create_element_no_args<Key, Func>(&mut self, key: Key, func: Func)
76    where
77        Key: Into<Cow<'static, str>>,
78        Func: FnMut() + 'static,
79    {
80        self.on_create_element = Some(KeyAndElementFn {
81            key: key.into(),
82            func: RefCell::new(ElementFunc::NoArgs(Box::new(func))),
83        });
84    }
85
86    /// If an `on_create_element` function was set, call it.
87    pub fn maybe_call_on_create_element(&self, element: &web_sys::Element) {
88        if let Some(on_create_elem) = &self.on_create_element {
89            on_create_elem.call(element.clone());
90        }
91
92        let _ = element;
93    }
94}
95
96impl SpecialAttributes {
97    /// The key for the on remove element function
98    pub fn on_remove_element_key(&self) -> Option<&Cow<'static, str>> {
99        self.on_remove_element.as_ref().map(|k| &k.key)
100    }
101
102    /// Set the [`SpecialAttributes.on_remove_element`] function.
103    ///
104    /// # Key
105    ///
106    /// The key is used when one virtual-node is being patched over another.
107    ///
108    /// If the old node's key is different from the new node's key, the on remove element function
109    /// gets called for the old element.
110    ///
111    /// If the keys are the same, the function does not get called.
112    ///
113    /// # Examples
114    ///
115    /// ```no_run
116    /// # use virtual_node::VirtualNode;
117    /// use wasm_bindgen::JsValue;
118    ///
119    /// let mut node = VirtualNode::element("div");
120    ///
121    /// // A key can be any `Into<Cow<'static, str>>`.
122    /// let key = "some-key";
123    ///
124    /// let on_remove_elem = move |elem: web_sys::Element| {
125    ///     assert_eq!(elem.id(), "");
126    /// };
127    ///
128    /// node
129    ///     .as_velement_mut()
130    ///     .unwrap()
131    ///     .special_attributes
132    ///     .set_on_remove_element(key, on_remove_elem);
133    /// ```
134    pub fn set_on_remove_element<Key, Func>(&mut self, key: Key, func: Func)
135    where
136        Key: Into<Cow<'static, str>>,
137        Func: FnMut(web_sys::Element) + 'static,
138    {
139        self.on_remove_element = Some(KeyAndElementFn {
140            key: key.into(),
141            func: RefCell::new(ElementFunc::OneArg(Box::new(func))),
142        });
143    }
144
145    // Used by the html-macro
146    #[doc(hidden)]
147    pub fn set_on_remove_element_no_args<Key, Func>(&mut self, key: Key, func: Func)
148    where
149        Key: Into<Cow<'static, str>>,
150        Func: FnMut() + 'static,
151    {
152        self.on_remove_element = Some(KeyAndElementFn {
153            key: key.into(),
154            func: RefCell::new(ElementFunc::NoArgs(Box::new(func))),
155        });
156    }
157
158    /// If an `on_remove_element` function was set, call it.
159    pub fn maybe_call_on_remove_element(&self, element: &web_sys::Element) {
160        if let Some(on_remove_elem) = &self.on_remove_element {
161            on_remove_elem.call(element.clone());
162        }
163
164        let _ = element;
165    }
166}
167
168struct KeyAndElementFn {
169    key: Cow<'static, str>,
170    func: RefCell<ElementFunc>,
171}
172
173enum ElementFunc {
174    NoArgs(Box<dyn FnMut()>),
175    OneArg(Box<dyn FnMut(web_sys::Element)>),
176}
177
178impl KeyAndElementFn {
179    fn call(&self, element: web_sys::Element) {
180        match self.func.borrow_mut().deref_mut() {
181            ElementFunc::NoArgs(func) => (func)(),
182            ElementFunc::OneArg(func) => (func)(element),
183        };
184    }
185}
186
187impl PartialEq for KeyAndElementFn {
188    fn eq(&self, rhs: &Self) -> bool {
189        self.key == rhs.key
190    }
191}