1use crate::*;
2
3pub(crate) fn convert_web_event(event: &Event, event_name: &str) -> NativeEvent {
14 match event_name {
15 "click" | "mousedown" | "mouseup" | "mousemove" | "mouseenter" | "mouseleave"
16 | "mouseover" | "mouseout" | "dblclick" | "contextmenu" => {
17 if let Some(mouse_event) = event.dyn_ref::<MouseEvent>() {
18 NativeEvent::Mouse(NativeMouseEvent {
19 client_x: mouse_event.client_x(),
20 client_y: mouse_event.client_y(),
21 screen_x: mouse_event.screen_x(),
22 screen_y: mouse_event.screen_y(),
23 button: mouse_event.button(),
24 buttons: mouse_event.buttons(),
25 ctrl_key: mouse_event.ctrl_key(),
26 shift_key: mouse_event.shift_key(),
27 alt_key: mouse_event.alt_key(),
28 meta_key: mouse_event.meta_key(),
29 })
30 } else {
31 NativeEvent::Generic
32 }
33 }
34 "input" => {
35 if let Some(input_event) = event.dyn_ref::<InputEvent>() {
36 let value: String = get_input_value(event);
37 NativeEvent::Input(NativeInputEvent::new(value, input_event.input_type()))
38 } else {
39 NativeEvent::Input(NativeInputEvent::new(get_input_value(event), String::new()))
40 }
41 }
42 "keydown" | "keyup" | "keypress" => {
43 if let Some(key_event) = event.dyn_ref::<KeyboardEvent>() {
44 NativeEvent::Keyboard(NativeKeyboardEvent {
45 key: key_event.key(),
46 code: key_event.code(),
47 location: key_event.location(),
48 ctrl_key: key_event.ctrl_key(),
49 shift_key: key_event.shift_key(),
50 alt_key: key_event.alt_key(),
51 meta_key: key_event.meta_key(),
52 repeat: key_event.repeat(),
53 })
54 } else {
55 NativeEvent::Generic
56 }
57 }
58 "focus" | "blur" | "focusin" | "focusout" => {
59 let is_focus: bool = event_name == "focus" || event_name == "focusin";
60 NativeEvent::Focus(NativeFocusEvent::new(is_focus, !is_focus))
61 }
62 "submit" => {
63 if let Some(submit_event) = event.dyn_ref::<SubmitEvent>() {
64 let submitter: Option<String> = submit_event
65 .submitter()
66 .and_then(|s| s.dyn_into::<HtmlElement>().ok())
67 .map(|el| el.id());
68 NativeEvent::Submit(NativeSubmitEvent::new(submitter))
69 } else {
70 NativeEvent::Generic
71 }
72 }
73 "change" => {
74 let (value, checked) = get_change_value(event);
75 NativeEvent::Change(NativeChangeEvent::new(value, checked))
76 }
77 "drag" | "dragstart" | "dragend" | "dragover" | "dragenter" | "dragleave" | "drop" => {
78 if let Some(drag_event) = event.dyn_ref::<DragEvent>() {
79 let types: Vec<String> = drag_event
80 .data_transfer()
81 .map(|dt| {
82 let len: u32 = dt.types().length();
83 (0..len)
84 .filter_map(|i: u32| dt.types().get(i).as_string())
85 .collect()
86 })
87 .unwrap_or_default();
88 NativeEvent::Drag(NativeDragEvent::new(
89 drag_event.client_x(),
90 drag_event.client_y(),
91 types,
92 ))
93 } else {
94 NativeEvent::Generic
95 }
96 }
97 "touchstart" | "touchend" | "touchmove" | "touchcancel" => {
98 if let Some(touch_event) = event.dyn_ref::<TouchEvent>() {
99 let touches: TouchList = touch_event.touches();
100 let first: Option<Touch> = touches.get(0);
101 NativeEvent::Touch(NativeTouchEvent::new(
102 touches.length(),
103 first.as_ref().map(|t| t.client_x()).unwrap_or(0),
104 first.as_ref().map(|t| t.client_y()).unwrap_or(0),
105 ))
106 } else {
107 NativeEvent::Generic
108 }
109 }
110 "wheel" => {
111 if let Some(wheel_event) = event.dyn_ref::<WheelEvent>() {
112 NativeEvent::Wheel(NativeWheelEvent::new(
113 wheel_event.delta_x(),
114 wheel_event.delta_y(),
115 wheel_event.delta_mode(),
116 ))
117 } else {
118 NativeEvent::Generic
119 }
120 }
121 "copy" | "cut" | "paste" => {
122 if let Some(clipboard_event) = event.dyn_ref::<ClipboardEvent>() {
123 let data: Option<String> = clipboard_event
124 .clipboard_data()
125 .and_then(|cd| cd.get_data("text").ok());
126 NativeEvent::Clipboard(NativeClipboardEvent::new(data))
127 } else {
128 NativeEvent::Generic
129 }
130 }
131 "play" | "pause" | "ended" | "loadeddata" | "canplay" | "volumechange" | "timeupdate" => {
132 NativeEvent::Media(NativeMediaEvent::new(event_name.to_string()))
133 }
134 _ => NativeEvent::Generic,
135 }
136}
137
138fn get_input_value(event: &Event) -> String {
148 if let Some(target) = event.target() {
149 if let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
150 return input.value();
151 }
152 if let Ok(textarea) = target.clone().dyn_into::<HtmlTextAreaElement>() {
153 return textarea.value();
154 }
155 if let Ok(select) = target.clone().dyn_into::<HtmlSelectElement>() {
156 return select.value();
157 }
158 }
159 String::new()
160}
161
162fn get_change_value(event: &Event) -> (String, bool) {
172 if let Some(target) = event.target() {
173 if let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
174 return (input.value(), input.checked());
175 }
176 if let Ok(textarea) = target.clone().dyn_into::<HtmlTextAreaElement>() {
177 return (textarea.value(), false);
178 }
179 if let Ok(select) = target.clone().dyn_into::<HtmlSelectElement>() {
180 return (select.value(), false);
181 }
182 }
183 (String::new(), false)
184}
185
186pub fn mount_body<F>(render_fn: F)
196where
197 F: FnOnce() -> VirtualNode,
198{
199 mount("body", render_fn);
200}
201
202pub fn mount<F>(selector: &str, render_fn: F)
218where
219 F: FnOnce() -> VirtualNode,
220{
221 let window: Window = web_sys::window().expect("no global window exists");
222 let document: Document = window.document().expect("should have a document");
223 let target: Element = if selector == "body" {
224 document.body().expect("document should have a body").into()
225 } else if let Some(id) = selector.strip_prefix('#') {
226 document
227 .get_element_by_id(id)
228 .unwrap_or_else(|| panic!("no element found with id '{}'", id))
229 } else if let Some(class) = selector.strip_prefix('.') {
230 document
231 .get_elements_by_class_name(class)
232 .item(0)
233 .unwrap_or_else(|| panic!("no element found with class '{}'", class))
234 } else {
235 document
236 .get_elements_by_tag_name(selector)
237 .item(0)
238 .unwrap_or_else(|| panic!("no element found with tag '{}'", selector))
239 };
240 let mut renderer: Renderer = Renderer::new(target);
241 let vnode: VirtualNode = render_fn();
242 renderer.render(vnode);
243}
244
245pub(crate) fn get_handler_registry() -> &'static mut HashMap<(usize, String), HandlerEntry> {
258 unsafe {
259 if HANDLER_REGISTRY.is_null() {
260 let registry: Box<HashMap<(usize, String), HandlerEntry>> = Box::default();
261 HANDLER_REGISTRY = Box::leak(registry) as *mut HashMap<(usize, String), HandlerEntry>;
262 }
263 &mut *HANDLER_REGISTRY
264 }
265}
266
267#[cfg(target_arch = "wasm32")]
277pub(crate) fn get_dynamic_listener_registry() -> &'static mut HashMap<usize, JsValue> {
278 unsafe {
279 if DYNAMIC_LISTENER_REGISTRY.is_null() {
280 let registry: Box<HashMap<usize, JsValue>> = Box::default();
281 DYNAMIC_LISTENER_REGISTRY = Box::leak(registry) as *mut HashMap<usize, JsValue>;
282 }
283 &mut *DYNAMIC_LISTENER_REGISTRY
284 }
285}
286
287#[cfg(target_arch = "wasm32")]
297pub(crate) fn get_attr_signal_listener_registry() -> &'static mut HashMap<usize, JsValue> {
298 unsafe {
299 if ATTR_SIGNAL_LISTENER_REGISTRY.is_null() {
300 let registry: Box<HashMap<usize, JsValue>> = Box::default();
301 ATTR_SIGNAL_LISTENER_REGISTRY = Box::leak(registry) as *mut HashMap<usize, JsValue>;
302 }
303 &mut *ATTR_SIGNAL_LISTENER_REGISTRY
304 }
305}
306
307#[cfg(target_arch = "wasm32")]
318pub(crate) fn register_dynamic_listener(dynamic_id: usize, closure: Closure<dyn FnMut()>) {
319 let event_name: String = NativeEventName::EuvSignalUpdate.to_string();
320 let registry: &mut HashMap<usize, JsValue> = get_dynamic_listener_registry();
321 if let Some(old_js_value) = registry.remove(&dynamic_id) {
322 let window: Window = window().unwrap();
323 let _ =
324 window.remove_event_listener_with_callback(&event_name, old_js_value.unchecked_ref());
325 }
326 let js_value: JsValue = closure.as_ref().clone();
327 let window: Window = window().unwrap();
328 window
329 .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
330 .unwrap();
331 closure.forget();
332 registry.insert(dynamic_id, js_value);
333}
334
335#[cfg(target_arch = "wasm32")]
346pub(crate) fn register_attr_signal_listener(signal_key: usize, closure: Closure<dyn FnMut()>) {
347 let event_name: String = NativeEventName::EuvSignalUpdate.to_string();
348 let registry: &mut HashMap<usize, JsValue> = get_attr_signal_listener_registry();
349 if let Some(old_js_value) = registry.remove(&signal_key) {
350 let window: Window = window().unwrap();
351 let _ =
352 window.remove_event_listener_with_callback(&event_name, old_js_value.unchecked_ref());
353 }
354 let js_value: JsValue = closure.as_ref().clone();
355 let window: Window = window().unwrap();
356 window
357 .add_event_listener_with_callback(&event_name, closure.as_ref().unchecked_ref())
358 .unwrap();
359 closure.forget();
360 registry.insert(signal_key, js_value);
361}
362
363#[cfg(not(target_arch = "wasm32"))]
364pub(crate) fn register_dynamic_listener(_dynamic_id: usize, _closure: Closure<dyn FnMut()>) {}
365
366#[cfg(not(target_arch = "wasm32"))]
367#[allow(dead_code)]
368pub(crate) fn register_attr_signal_listener(_signal_key: usize, _closure: Closure<dyn FnMut()>) {}