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