dioxus_document/elements/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#![doc = include_str!("../../docs/head.md")]

use std::{cell::RefCell, collections::HashSet, rc::Rc};

use dioxus_core::{prelude::*, DynamicNode};
use dioxus_core_macro::*;

mod link;
pub use link::*;
mod stylesheet;
pub use stylesheet::*;
mod meta;
pub use meta::*;
mod script;
pub use script::*;
mod style;
pub use style::*;
mod title;
pub use title::*;

/// Warn the user if they try to change props on a element that is injected into the head
#[allow(unused)]
fn use_update_warning<T: PartialEq + Clone + 'static>(value: &T, name: &'static str) {
    #[cfg(debug_assertions)]
    {
        let cloned_value = value.clone();
        let initial = use_hook(move || value.clone());

        if initial != cloned_value {
            tracing::warn!("Changing the props of `{name}` is not supported ");
        }
    }
}

/// An error that can occur when extracting a single text node from a component
pub enum ExtractSingleTextNodeError<'a> {
    /// The node contained an render error, so we can't extract the text node
    RenderError(&'a RenderError),
    /// There was only one child, but it wasn't a text node
    NonTextNode,
    /// There is multiple child nodes
    NonTemplate,
}

impl ExtractSingleTextNodeError<'_> {
    /// Log a warning depending on the error
    pub fn log(&self, component: &str) {
        match self {
            ExtractSingleTextNodeError::RenderError(err) => {
                tracing::error!("Error while rendering {component}: {err}");
            }
            ExtractSingleTextNodeError::NonTextNode => {
                tracing::error!(
                    "Error while rendering {component}: The children of {component} must be a single text node"
                );
            }
            ExtractSingleTextNodeError::NonTemplate => {
                tracing::error!(
                    "Error while rendering {component}: The children of {component} must be a single text node"
                );
            }
        }
    }
}

fn extract_single_text_node(children: &Element) -> Result<String, ExtractSingleTextNodeError<'_>> {
    let vnode = match children {
        Element::Ok(vnode) => vnode,
        Element::Err(err) => {
            return Err(ExtractSingleTextNodeError::RenderError(err));
        }
    };
    // The title's children must be in one of two forms:
    // 1. rsx! { "static text" }
    // 2. rsx! { "title: {dynamic_text}" }
    match vnode.template {
        // rsx! { "static text" }
        Template {
            roots: &[TemplateNode::Text { text }],
            node_paths: &[],
            attr_paths: &[],
            ..
        } => Ok(text.to_string()),
        // rsx! { "title: {dynamic_text}" }
        Template {
            roots: &[TemplateNode::Dynamic { id }],
            node_paths: &[&[0]],
            attr_paths: &[],
            ..
        } => {
            let node = &vnode.dynamic_nodes[id];
            match node {
                DynamicNode::Text(text) => Ok(text.value.clone()),
                _ => Err(ExtractSingleTextNodeError::NonTextNode),
            }
        }
        _ => Err(ExtractSingleTextNodeError::NonTemplate),
    }
}

fn get_or_insert_root_context<T: Default + Clone + 'static>() -> T {
    match ScopeId::ROOT.has_context::<T>() {
        Some(context) => context,
        None => {
            let context = T::default();
            ScopeId::ROOT.provide_context(context.clone());
            context
        }
    }
}

#[derive(Default, Clone)]
struct DeduplicationContext(Rc<RefCell<HashSet<String>>>);

impl DeduplicationContext {
    fn should_insert(&self, href: &str) -> bool {
        let mut set = self.0.borrow_mut();
        let present = set.contains(href);
        if !present {
            set.insert(href.to_string());
            true
        } else {
            false
        }
    }
}

/// Extend a list of string attributes with a list of dioxus attribute
pub(crate) fn extend_attributes(
    attributes: &mut Vec<(&'static str, String)>,
    additional_attributes: &[Attribute],
) {
    for additional_attribute in additional_attributes {
        let attribute_value_as_string = match &additional_attribute.value {
            dioxus_core::AttributeValue::Text(v) => v.to_string(),
            dioxus_core::AttributeValue::Float(v) => v.to_string(),
            dioxus_core::AttributeValue::Int(v) => v.to_string(),
            dioxus_core::AttributeValue::Bool(v) => v.to_string(),
            dioxus_core::AttributeValue::Listener(_) | dioxus_core::AttributeValue::Any(_) => {
                tracing::error!("document::* elements do not support event listeners or any value attributes. Expected displayable attribute, found {:?}", additional_attribute.value);
                continue;
            }
            dioxus_core::AttributeValue::None => {
                continue;
            }
        };
        attributes.push((additional_attribute.name, attribute_value_as_string));
    }
}