sycamore_web/
utils.rs

1//! Utility functions. Intended for internal use only.
2
3use crate::*;
4
5/// Get all nodes between `start` and `end`.
6///
7/// If `end` is before `start`, all nodes after `start` will be returned.
8///
9/// The range is exclusive so `start` and `end` will not be included.
10#[must_use]
11pub fn get_nodes_between(start: &web_sys::Node, end: &web_sys::Node) -> Vec<web_sys::Node> {
12    let parent = start.parent_node().unwrap();
13    debug_assert_eq!(
14        parent,
15        end.parent_node().unwrap(),
16        "parents of `start` and `end` do not match"
17    );
18
19    let mut nodes = Vec::new();
20
21    let mut next = start.next_sibling();
22    while let Some(current) = next {
23        let tmp = current.next_sibling();
24        if &current == end {
25            break;
26        } else {
27            nodes.push(current);
28        }
29        next = tmp;
30    }
31
32    nodes
33}
34
35/// Wrap all the nodes in a [`View`] in a document fragment.
36///
37/// This is useful when the view is dynamically changed without being mounted since this will not
38/// update the DOM. Wrapping the nodes in a document fragment will allow you to dynamically update
39/// the view while it is detatched, and then insert it into the DOM later.
40///
41/// This only works on the client side.
42pub fn wrap_in_document_fragment(view: View) -> View {
43    let fragment = web_sys::window()
44        .unwrap()
45        .document()
46        .unwrap()
47        .create_document_fragment();
48
49    for node in view.as_web_sys() {
50        fragment.append_child(&node).unwrap();
51    }
52
53    View::from_node(HtmlNode::from_web_sys(fragment.into()))
54}
55
56/// Unwraps all the nodes from a document fragment into a flat [`View`].
57///
58/// If the first node of the view is not a document fragment, returns the view.
59pub fn unwrap_from_document_fragment(view: View) -> View {
60    if view.nodes.len() != 1 {
61        return view;
62    }
63    let node = view.nodes[0].as_web_sys();
64    if node.node_type() != web_sys::Node::DOCUMENT_FRAGMENT_NODE {
65        return view;
66    }
67
68    let fragment = node.unchecked_ref::<web_sys::DocumentFragment>();
69
70    let mut nodes = Vec::new();
71    let mut next = fragment.first_child();
72    while let Some(current) = next {
73        next = current.next_sibling();
74        nodes.push(current);
75    }
76
77    View::from_nodes(nodes.into_iter().map(HtmlNode::from_web_sys).collect())
78}
79
80/// Create a shallow copy of a view by converting the nodes to web-sys and then converting them
81/// back.
82pub fn clone_nodes_via_web_sys(view: &View) -> View {
83    let nodes = view
84        .as_web_sys()
85        .into_iter()
86        .map(HtmlNode::from_web_sys)
87        .collect();
88    View::from_nodes(nodes)
89}