Skip to main content

euv_core/renderer/render/
fn.rs

1use crate::*;
2
3/// Mounts the given virtual DOM tree to the document body.
4///
5/// # Arguments
6///
7/// - `FnOnce() -> VirtualNode + 'static` - A closure that returns the virtual DOM tree to render.
8///
9/// # Panics
10///
11/// Panics if the document body cannot be found.
12pub fn mount_body<F>(render_fn: F)
13where
14    F: FnOnce() -> VirtualNode,
15{
16    mount("body", render_fn);
17}
18
19/// Mounts the given virtual DOM tree to a specific element matched by a CSS selector.
20///
21/// Supported selector syntax:
22/// - `"#id"` — select by element ID
23/// - `".class"` — select by class name (uses the first match)
24/// - `"tag"` — select by tag name (uses the first match)
25///
26/// # Arguments
27///
28/// - `&str` - A CSS selector string to locate the target element.
29/// - `FnOnce() -> VirtualNode + 'static` - A closure that returns the virtual DOM tree to render.
30///
31/// # Panics
32///
33/// Panics if no global `window` or `document` exists, or if the selector does not match any element.
34pub fn mount<F>(selector: &str, render_fn: F)
35where
36    F: FnOnce() -> VirtualNode,
37{
38    init_event_delegation();
39    let window: Window = web_sys::window().expect("no global window exists");
40    let document: Document = window.document().expect("should have a document");
41    let target: Element = if selector == "body" {
42        document.body().expect("document should have a body").into()
43    } else if let Some(id) = selector.strip_prefix('#') {
44        document
45            .get_element_by_id(id)
46            .unwrap_or_else(|| panic!("no element found with id '{}'", id))
47    } else if let Some(class) = selector.strip_prefix('.') {
48        document
49            .get_elements_by_class_name(class)
50            .item(0)
51            .unwrap_or_else(|| panic!("no element found with class '{}'", class))
52    } else {
53        document
54            .get_elements_by_tag_name(selector)
55            .item(0)
56            .unwrap_or_else(|| panic!("no element found with tag '{}'", selector))
57    };
58    let mut renderer: Renderer = Renderer::new(target);
59    let vnode: VirtualNode = render_fn();
60    renderer.render(vnode);
61}
62
63/// Destroys the application mounted at the given CSS selector.
64///
65/// Removes all child nodes from the target element, cleans up all
66/// framework-global state (handlers, signal update registry, delegated events),
67/// resets scheduling state, and clears injected CSS classes.
68///
69/// # Arguments
70///
71/// - `&str` - A CSS selector string identifying the root element to unmount.
72///
73/// # Panics
74///
75/// Panics if no global `window` or `document` exists.
76pub fn destroy(selector: &str) {
77    let window: Window = window().expect("no global window exists");
78    let document: Document = window.document().expect("should have a document");
79    let target: Option<Element> = if selector == "body" {
80        document.body().map(|body: HtmlElement| body.into())
81    } else if let Some(id) = selector.strip_prefix('#') {
82        document.get_element_by_id(id)
83    } else if let Some(class) = selector.strip_prefix('.') {
84        document.get_elements_by_class_name(class).item(0)
85    } else {
86        document.get_elements_by_tag_name(selector).item(0)
87    };
88    if let Some(element) = target {
89        while let Some(child) = element.first_child() {
90            let _ = element.remove_child(&child);
91        }
92    }
93    cleanup_all();
94    reset_schedule_state();
95    reset_injected_classes();
96}