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}