fenrix_dom/
lib.rs

1use fenrix_core::create_effect;
2use std::cell::RefCell;
3use std::rc::Rc;
4use web_sys::{window, Document, Element, Node, Text};
5
6/// Gets the `document` object from the browser.
7fn document() -> Document {
8    window()
9        .expect("should have a window")
10        .document()
11        .expect("window should have a document")
12}
13
14/// Creates a new HTML element with the given tag name.
15pub fn create_element(tag: &str) -> Element {
16    document()
17        .create_element(tag)
18        .expect("failed to create element")
19}
20
21/// Creates a new text node with the given content.
22pub fn create_text_node(text: &str) -> Text {
23    document().create_text_node(text)
24}
25
26/// Appends a child node to a parent element.
27pub fn append_child(parent: &Element, child: &Node) {
28    parent
29        .append_child(child)
30        .expect("failed to append child");
31}
32
33/// Creates a text node that reactively updates when its source changes.
34///
35/// # Arguments
36///
37/// * `source`: A closure that returns the string to be displayed. This closure
38///   is tracked by the reactive system.
39pub fn create_reactive_text_node(source: impl FnMut() -> String + 'static) -> Text {
40    // Wrap the source closure in Rc<RefCell<>> to allow for shared mutable access
41    // from within the `Fn` closure required by `create_effect`.
42    let source = Rc::new(RefCell::new(source));
43
44    // Get the initial value by calling the closure.
45    let initial_value = source.borrow_mut()();
46    let text_node = create_text_node(&initial_value);
47
48    // Create an effect that updates the node's text content whenever the source changes.
49    let node_clone = text_node.clone();
50    create_effect({
51        let source = Rc::clone(&source);
52        move || {
53            // When the effect runs, borrow the closure again to get the new value.
54            let new_value = source.borrow_mut()();
55            node_clone.set_node_value(Some(&new_value));
56        }
57    });
58
59    text_node
60}
61
62/// Renders a root node to the document body.
63pub fn render(root_node: Node) {
64    let body = document().body().expect("document should have a body");
65    body.append_child(&root_node)
66        .expect("failed to append to body");
67}