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