dioxus_document/elements/
mod.rs1#![doc = include_str!("../../docs/head.md")]
2
3use std::{cell::RefCell, collections::HashSet, rc::Rc};
4
5use dioxus_core::{Attribute, DynamicNode, Element, RenderError, Runtime, ScopeId, VNodeChild};
6use dioxus_core_macro::*;
7use dioxus_html::extensions::*;
8mod link;
9pub use link::*;
10mod stylesheet;
11pub use stylesheet::*;
12mod meta;
13pub use meta::*;
14mod script;
15pub use script::*;
16mod style;
17pub use style::*;
18mod title;
19pub use title::*;
20
21#[allow(unused)]
23fn use_update_warning<T: PartialEq + Clone + 'static>(value: &T, name: &'static str) {
24 #[cfg(debug_assertions)]
25 {
26 use dioxus_core::use_hook;
27
28 let cloned_value = value.clone();
29 let initial = use_hook(move || value.clone());
30
31 if initial != cloned_value {
32 tracing::warn!("Changing the props of `{name}` is not supported ");
33 }
34 }
35}
36
37#[derive(Debug)]
39pub enum ExtractSingleTextNodeError<'a> {
40 RenderError(&'a RenderError),
42 NonTextNode,
44 NonTemplate,
46}
47
48impl ExtractSingleTextNodeError<'_> {
49 pub fn log(&self, component: &str) {
51 match self {
52 ExtractSingleTextNodeError::RenderError(err) => {
53 tracing::error!("Error while rendering {component}: {err}");
54 }
55 ExtractSingleTextNodeError::NonTextNode => {
56 tracing::error!(
57 "Error while rendering {component}: The children of {component} must be a single text node"
58 );
59 }
60 ExtractSingleTextNodeError::NonTemplate => {
61 tracing::error!(
62 "Error while rendering {component}: The children of {component} must be a single text node"
63 );
64 }
65 }
66 }
67}
68
69fn extract_single_text_node(children: &Element) -> Result<String, ExtractSingleTextNodeError<'_>> {
70 let vnode = match children {
71 Element::Ok(vnode) => vnode,
72 Element::Err(err) => {
73 return Err(ExtractSingleTextNodeError::RenderError(err));
74 }
75 };
76 let mut children = vnode.children();
80 let Some(child) = children.next() else {
81 return Err(ExtractSingleTextNodeError::NonTemplate);
82 };
83
84 if children.next().is_some() {
85 return Err(ExtractSingleTextNodeError::NonTemplate);
86 }
87
88 match child {
89 VNodeChild::Text(text) => Ok(text.text().to_string()),
91 VNodeChild::Dynamic(anchor) => {
93 let mut dynamic_nodes = anchor.nodes();
94 let dynamic_node = dynamic_nodes.next();
95 if dynamic_nodes.next().is_some() {
96 return Err(ExtractSingleTextNodeError::NonTemplate);
97 }
98 match dynamic_node {
99 Some(slot) => match &*slot {
100 DynamicNode::Text(text) => Ok(text.value.clone()),
101 _ => Err(ExtractSingleTextNodeError::NonTextNode),
102 },
103 _ => Err(ExtractSingleTextNodeError::NonTemplate),
104 }
105 }
106 _ => Err(ExtractSingleTextNodeError::NonTemplate),
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use dioxus_core::{DynamicNode, DynamicValues, VNode, VText};
113 use dioxus_core_template::{TemplateRawTree, TemplateStorage};
114
115 use super::*;
116
117 #[test]
118 fn extracts_static_text() {
119 static TREE: TemplateRawTree = TemplateRawTree::StaticText("static text");
120 static STORAGE: TemplateStorage<2, 1, 1> = TemplateStorage::build_from_tree(&TREE);
121 let children = Ok(VNode::new(
122 STORAGE.as_template(),
123 DynamicValues::from_parts(None, Box::new([]), Box::new([])),
124 ));
125
126 assert_eq!(
127 extract_single_text_node(&children).expect("static text should extract"),
128 "static text"
129 );
130 }
131
132 #[test]
133 fn extracts_formatted_text() {
134 static TREE: TemplateRawTree = TemplateRawTree::DynamicNode;
135 static STORAGE: TemplateStorage<1, 1, 1> = TemplateStorage::build_from_tree(&TREE);
136 let children = Ok(VNode::new(
137 STORAGE.as_template(),
138 DynamicValues::from_parts(
139 None,
140 Box::new([DynamicNode::Text(VText::new("title: dynamic text"))]),
141 Box::new([]),
142 ),
143 ));
144
145 assert_eq!(
146 extract_single_text_node(&children).expect("formatted text should extract"),
147 "title: dynamic text"
148 );
149 }
150}
151
152fn get_or_insert_root_context<T: Default + Clone + 'static>() -> T {
153 let rt = Runtime::current();
154 match rt.has_context::<T>(ScopeId::ROOT) {
155 Some(context) => context,
156 None => {
157 let context = T::default();
158 rt.provide_context(ScopeId::ROOT, context.clone());
159 context
160 }
161 }
162}
163
164#[derive(Default, Clone)]
165struct DeduplicationContext(Rc<RefCell<HashSet<String>>>);
166
167impl DeduplicationContext {
168 fn should_insert(&self, href: &str) -> bool {
169 let mut set = self.0.borrow_mut();
170 let present = set.contains(href);
171 if !present {
172 set.insert(href.to_string());
173 true
174 } else {
175 false
176 }
177 }
178}
179
180pub(crate) fn extend_attributes(
182 attributes: &mut Vec<(&'static str, String)>,
183 additional_attributes: &[Attribute],
184) {
185 for additional_attribute in additional_attributes {
186 let attribute_value_as_string = match &additional_attribute.value {
187 dioxus_core::AttributeValue::Text(v) => v.to_string(),
188 dioxus_core::AttributeValue::Float(v) => v.to_string(),
189 dioxus_core::AttributeValue::Int(v) => v.to_string(),
190 dioxus_core::AttributeValue::Bool(v) => v.to_string(),
191 dioxus_core::AttributeValue::Listener(_) | dioxus_core::AttributeValue::Any(_) => {
192 tracing::error!(
193 "document::* elements do not support event listeners or any value attributes. Expected displayable attribute, found {:?}",
194 additional_attribute.value
195 );
196 continue;
197 }
198 dioxus_core::AttributeValue::None => {
199 continue;
200 }
201 };
202 attributes.push((additional_attribute.name, attribute_value_as_string));
203 }
204}