dioxus_document/elements/
script.rs

1use super::*;
2use crate::document;
3use dioxus_core::{use_hook, VNode};
4use dioxus_html as dioxus_elements;
5
6#[non_exhaustive]
7#[derive(Clone, Props, PartialEq)]
8pub struct ScriptProps {
9    /// The contents of the script tag. If present, the children must be a single text node.
10    pub children: Element,
11    /// Scripts are deduplicated by their src attribute
12    pub src: Option<String>,
13    pub defer: Option<bool>,
14    pub crossorigin: Option<String>,
15    pub fetchpriority: Option<String>,
16    pub integrity: Option<String>,
17    pub nomodule: Option<bool>,
18    pub nonce: Option<String>,
19    pub referrerpolicy: Option<String>,
20    pub r#type: Option<String>,
21    #[props(extends = script, extends = GlobalAttributes)]
22    pub additional_attributes: Vec<Attribute>,
23}
24
25impl ScriptProps {
26    /// Get all the attributes for the script tag
27    pub fn attributes(&self) -> Vec<(&'static str, String)> {
28        let mut attributes = Vec::new();
29        extend_attributes(&mut attributes, &self.additional_attributes);
30        if let Some(defer) = &self.defer {
31            attributes.push(("defer", defer.to_string()));
32        }
33        if let Some(crossorigin) = &self.crossorigin {
34            attributes.push(("crossorigin", crossorigin.clone()));
35        }
36        if let Some(fetchpriority) = &self.fetchpriority {
37            attributes.push(("fetchpriority", fetchpriority.clone()));
38        }
39        if let Some(integrity) = &self.integrity {
40            attributes.push(("integrity", integrity.clone()));
41        }
42        if let Some(nomodule) = &self.nomodule {
43            attributes.push(("nomodule", nomodule.to_string()));
44        }
45        if let Some(nonce) = &self.nonce {
46            attributes.push(("nonce", nonce.clone()));
47        }
48        if let Some(referrerpolicy) = &self.referrerpolicy {
49            attributes.push(("referrerpolicy", referrerpolicy.clone()));
50        }
51        if let Some(r#type) = &self.r#type {
52            attributes.push(("type", r#type.clone()));
53        }
54        if let Some(src) = &self.src {
55            attributes.push(("src", src.clone()));
56        }
57        attributes
58    }
59
60    pub fn script_contents(&self) -> Result<String, ExtractSingleTextNodeError<'_>> {
61        extract_single_text_node(&self.children)
62    }
63}
64
65/// Render a [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script) tag into the head of the page.
66///
67///
68/// If present, the children of the script component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the script will not be added.
69///
70///
71/// Any scripts you add will be deduplicated by their `src` attribute (if present).
72///
73/// # Example
74/// ```rust, no_run
75/// # use dioxus::prelude::*;
76/// fn LoadScript() -> Element {
77///     rsx! {
78///         // You can use the Script component to render a script tag into the head of the page
79///         document::Script {
80///             src: asset!("/assets/script.js"),
81///         }
82///     }
83/// }
84/// ```
85///
86/// <div class="warning">
87///
88/// Any updates to the props after the first render will not be reflected in the head.
89///
90/// </div>
91#[component]
92pub fn Script(props: ScriptProps) -> Element {
93    use_update_warning(&props, "Script {}");
94
95    use_hook(|| {
96        let document = document();
97        let mut insert_script = document.create_head_component();
98        if let Some(src) = &props.src {
99            if !should_insert_script(src) {
100                insert_script = false;
101            }
102        }
103
104        if !insert_script {
105            return;
106        }
107
108        // Make sure the props are in a valid form - they must either have a source or children
109        if let (None, Err(err)) = (&props.src, props.script_contents()) {
110            // If the script has neither contents nor src, log an error
111            err.log("Script")
112        }
113
114        document.create_script(props);
115    });
116
117    VNode::empty()
118}
119
120#[derive(Default, Clone)]
121struct ScriptContext(DeduplicationContext);
122
123fn should_insert_script(src: &str) -> bool {
124    get_or_insert_root_context::<ScriptContext>()
125        .0
126        .should_insert(src)
127}