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}