dioxus_document/elements/
mod.rs

1#![doc = include_str!("../../docs/head.md")]
2
3use std::{cell::RefCell, collections::HashSet, rc::Rc};
4
5use dioxus_core::{
6    Attribute, DynamicNode, Element, RenderError, Runtime, ScopeId, Template, TemplateNode,
7};
8use dioxus_core_macro::*;
9
10mod link;
11pub use link::*;
12mod stylesheet;
13pub use stylesheet::*;
14mod meta;
15pub use meta::*;
16mod script;
17pub use script::*;
18mod style;
19pub use style::*;
20mod title;
21pub use title::*;
22
23/// Warn the user if they try to change props on a element that is injected into the head
24#[allow(unused)]
25fn use_update_warning<T: PartialEq + Clone + 'static>(value: &T, name: &'static str) {
26    #[cfg(debug_assertions)]
27    {
28        use dioxus_core::use_hook;
29
30        let cloned_value = value.clone();
31        let initial = use_hook(move || value.clone());
32
33        if initial != cloned_value {
34            tracing::warn!("Changing the props of `{name}` is not supported ");
35        }
36    }
37}
38
39/// An error that can occur when extracting a single text node from a component
40pub enum ExtractSingleTextNodeError<'a> {
41    /// The node contained an render error, so we can't extract the text node
42    RenderError(&'a RenderError),
43    /// There was only one child, but it wasn't a text node
44    NonTextNode,
45    /// There is multiple child nodes
46    NonTemplate,
47}
48
49impl ExtractSingleTextNodeError<'_> {
50    /// Log a warning depending on the error
51    pub fn log(&self, component: &str) {
52        match self {
53            ExtractSingleTextNodeError::RenderError(err) => {
54                tracing::error!("Error while rendering {component}: {err}");
55            }
56            ExtractSingleTextNodeError::NonTextNode => {
57                tracing::error!(
58                    "Error while rendering {component}: The children of {component} must be a single text node"
59                );
60            }
61            ExtractSingleTextNodeError::NonTemplate => {
62                tracing::error!(
63                    "Error while rendering {component}: The children of {component} must be a single text node"
64                );
65            }
66        }
67    }
68}
69
70fn extract_single_text_node(children: &Element) -> Result<String, ExtractSingleTextNodeError<'_>> {
71    let vnode = match children {
72        Element::Ok(vnode) => vnode,
73        Element::Err(err) => {
74            return Err(ExtractSingleTextNodeError::RenderError(err));
75        }
76    };
77    // The title's children must be in one of two forms:
78    // 1. rsx! { "static text" }
79    // 2. rsx! { "title: {dynamic_text}" }
80    match vnode.template {
81        // rsx! { "static text" }
82        Template {
83            roots: &[TemplateNode::Text { text }],
84            node_paths: &[],
85            attr_paths: &[],
86            ..
87        } => Ok(text.to_string()),
88        // rsx! { "title: {dynamic_text}" }
89        Template {
90            roots: &[TemplateNode::Dynamic { id }],
91            node_paths: &[&[0]],
92            attr_paths: &[],
93            ..
94        } => {
95            let node = &vnode.dynamic_nodes[id];
96            match node {
97                DynamicNode::Text(text) => Ok(text.value.clone()),
98                _ => Err(ExtractSingleTextNodeError::NonTextNode),
99            }
100        }
101        _ => Err(ExtractSingleTextNodeError::NonTemplate),
102    }
103}
104
105fn get_or_insert_root_context<T: Default + Clone + 'static>() -> T {
106    let rt = Runtime::current();
107    match rt.has_context::<T>(ScopeId::ROOT) {
108        Some(context) => context,
109        None => {
110            let context = T::default();
111            rt.provide_context(ScopeId::ROOT, context.clone());
112            context
113        }
114    }
115}
116
117#[derive(Default, Clone)]
118struct DeduplicationContext(Rc<RefCell<HashSet<String>>>);
119
120impl DeduplicationContext {
121    fn should_insert(&self, href: &str) -> bool {
122        let mut set = self.0.borrow_mut();
123        let present = set.contains(href);
124        if !present {
125            set.insert(href.to_string());
126            true
127        } else {
128            false
129        }
130    }
131}
132
133/// Extend a list of string attributes with a list of dioxus attribute
134pub(crate) fn extend_attributes(
135    attributes: &mut Vec<(&'static str, String)>,
136    additional_attributes: &[Attribute],
137) {
138    for additional_attribute in additional_attributes {
139        let attribute_value_as_string = match &additional_attribute.value {
140            dioxus_core::AttributeValue::Text(v) => v.to_string(),
141            dioxus_core::AttributeValue::Float(v) => v.to_string(),
142            dioxus_core::AttributeValue::Int(v) => v.to_string(),
143            dioxus_core::AttributeValue::Bool(v) => v.to_string(),
144            dioxus_core::AttributeValue::Listener(_) | dioxus_core::AttributeValue::Any(_) => {
145                tracing::error!("document::* elements do not support event listeners or any value attributes. Expected displayable attribute, found {:?}", additional_attribute.value);
146                continue;
147            }
148            dioxus_core::AttributeValue::None => {
149                continue;
150            }
151        };
152        attributes.push((additional_attribute.name, attribute_value_as_string));
153    }
154}