Skip to main content

virtual_node/velement/
special_attributes.rs

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