yew/dom_bundle/
fragment.rs1use std::collections::VecDeque;
2use std::ops::{Deref, DerefMut};
3
4use wasm_bindgen::JsCast;
5use web_sys::{Element, Node};
6
7use super::{BSubtree, DomSlot};
8use crate::virtual_dom::Collectable;
9
10#[derive(Default, Debug, Clone, PartialEq, Eq)]
12pub(crate) struct Fragment(VecDeque<Node>, Option<Node>);
13
14impl Deref for Fragment {
15 type Target = VecDeque<Node>;
16
17 fn deref(&self) -> &Self::Target {
18 &self.0
19 }
20}
21
22impl DerefMut for Fragment {
23 fn deref_mut(&mut self) -> &mut Self::Target {
24 &mut self.0
25 }
26}
27
28impl Fragment {
29 pub fn collect_children(parent: &Element) -> Self {
31 let mut fragment = VecDeque::with_capacity(parent.child_nodes().length() as usize);
32
33 let mut current_node = parent.first_child();
34
35 while let Some(m) = current_node {
38 current_node = m.next_sibling();
39 fragment.push_back(m);
40 }
41
42 Self(fragment, None)
43 }
44
45 pub fn collect_between(
47 collect_from: &mut Fragment,
48 collect_for: &Collectable,
49 parent: &Element,
50 ) -> Self {
51 let is_open_tag = |node: &Node| {
52 let comment_text = node.text_content().unwrap_or_default();
53
54 comment_text.starts_with(collect_for.open_start_mark())
55 && comment_text.ends_with(collect_for.end_mark())
56 };
57
58 let is_close_tag = |node: &Node| {
59 let comment_text = node.text_content().unwrap_or_default();
60
61 comment_text.starts_with(collect_for.close_start_mark())
62 && comment_text.ends_with(collect_for.end_mark())
63 };
64
65 collect_from.trim_start_text_nodes();
67
68 let first_node = collect_from
69 .pop_front()
70 .unwrap_or_else(|| panic!("expected {} opening tag, found EOF", collect_for.name()));
71
72 assert_eq!(
73 first_node.node_type(),
74 Node::COMMENT_NODE,
75 "expected {} start, found node type {}",
77 collect_for.name(),
78 first_node.node_type()
79 );
80
81 let mut nodes = VecDeque::new();
82
83 if !is_open_tag(&first_node) {
84 panic!(
85 "expected {} opening tag, found comment node",
86 collect_for.name()
87 );
88 }
89
90 parent.remove_child(&first_node).unwrap();
92
93 let mut nested_layers = 1;
94
95 loop {
96 let current_node = collect_from.pop_front().unwrap_or_else(|| {
97 panic!("expected {} closing tag, found EOF", collect_for.name())
98 });
99
100 if current_node.node_type() == Node::COMMENT_NODE {
101 if is_open_tag(¤t_node) {
102 nested_layers += 1;
104 } else if is_close_tag(¤t_node) {
105 nested_layers -= 1;
107 if nested_layers == 0 {
108 parent.remove_child(¤t_node).unwrap();
113 break;
114 }
115 }
116 }
117
118 nodes.push_back(current_node);
119 }
120
121 let next_child = collect_from.0.front().cloned();
122 Self(nodes, next_child)
123 }
124
125 pub fn trim_start_text_nodes(&mut self) {
127 while let Some(ref m) = self.front().cloned() {
128 if m.node_type() == Node::TEXT_NODE {
129 self.pop_front();
130
131 m.unchecked_ref::<web_sys::Text>().remove();
132 } else {
133 break;
134 }
135 }
136 }
137
138 pub fn deep_clone(&self) -> Self {
140 let nodes = self
141 .iter()
142 .map(|m| m.clone_node_with_deep(true).expect("failed to clone node."))
143 .collect::<VecDeque<_>>();
144
145 Self(nodes, None)
147 }
148
149 pub fn detach(self, _root: &BSubtree, parent: &Element, parent_to_detach: bool) {
151 if !parent_to_detach {
152 for node in self.iter() {
153 parent
154 .remove_child(node)
155 .expect("failed to remove child element");
156 }
157 }
158 }
159
160 pub fn shift(&self, next_parent: &Element, slot: DomSlot) -> DomSlot {
162 for node in self.iter() {
163 slot.insert(next_parent, node);
164 }
165
166 self.front().cloned().map(DomSlot::at).unwrap_or(slot)
167 }
168
169 pub fn sibling_at_end(&self) -> Option<&Node> {
171 self.1.as_ref()
172 }
173}