dioxus_document/elements/
style.rs

1use super::*;
2use crate::document;
3use dioxus_html as dioxus_elements;
4
5#[non_exhaustive]
6#[derive(Clone, Props, PartialEq)]
7pub struct StyleProps {
8    /// Styles are deduplicated by their href attribute
9    pub href: Option<String>,
10    pub media: Option<String>,
11    pub nonce: Option<String>,
12    pub title: Option<String>,
13    /// The contents of the style tag. If present, the children must be a single text node.
14    pub children: Element,
15    #[props(extends = style, extends = GlobalAttributes)]
16    pub additional_attributes: Vec<Attribute>,
17}
18
19impl StyleProps {
20    /// Get all the attributes for the style tag
21    pub fn attributes(&self) -> Vec<(&'static str, String)> {
22        let mut attributes = Vec::new();
23        extend_attributes(&mut attributes, &self.additional_attributes);
24        if let Some(href) = &self.href {
25            attributes.push(("href", href.clone()));
26        }
27        if let Some(media) = &self.media {
28            attributes.push(("media", media.clone()));
29        }
30        if let Some(nonce) = &self.nonce {
31            attributes.push(("nonce", nonce.clone()));
32        }
33        if let Some(title) = &self.title {
34            attributes.push(("title", title.clone()));
35        }
36        attributes
37    }
38
39    pub fn style_contents(&self) -> Result<String, ExtractSingleTextNodeError<'_>> {
40        extract_single_text_node(&self.children)
41    }
42}
43
44/// Render a [`style`](crate::elements::style) or [`link`](crate::elements::link) tag into the head of the page.
45///
46/// If present, the children of the style component must be a single static or formatted string. If there are more children or the children contain components, conditionals, loops, or fragments, the style will not be added.
47///
48/// # Example
49/// ```rust, no_run
50/// # use dioxus::prelude::*;
51/// fn RedBackground() -> Element {
52///     rsx! {
53///         // You can use the style component to render a style tag into the head of the page
54///         // This style tag will set the background color of the page to red
55///         document::Style {
56///             r#"
57///                 body {{
58///                     background-color: red;
59///                 }}
60///             "#
61///         }
62///         // You could also use a style with a href to load a stylesheet asset
63///         document::Style {
64///             href: asset!("/assets/style.css")
65///         }
66///     }
67/// }
68/// ```
69///
70/// <div class="warning">
71///
72/// Any updates to the props after the first render will not be reflected in the head.
73///
74/// </div>
75#[component]
76pub fn Style(props: StyleProps) -> Element {
77    use_update_warning(&props, "Style {}");
78
79    use_hook(|| {
80        let document = document();
81        let mut insert_style = document.create_head_component();
82        if let Some(href) = &props.href {
83            if !should_insert_style(href) {
84                insert_style = false;
85            }
86        }
87        if !insert_style {
88            return;
89        }
90        let mut attributes = props.attributes();
91        match (&props.href, props.style_contents()) {
92            // The style has inline contents, render it as a style tag
93            (_, Ok(_)) => document.create_style(props),
94            // The style has a src, render it as a link tag
95            (Some(_), _) => {
96                attributes.push(("type", "text/css".into()));
97                attributes.push(("rel", "stylesheet".into()));
98                document.create_link(LinkProps {
99                    media: props.media,
100                    title: props.title,
101                    r#type: Some("text/css".to_string()),
102                    additional_attributes: props.additional_attributes,
103                    href: props.href,
104                    rel: Some("stylesheet".to_string()),
105                    disabled: None,
106                    r#as: None,
107                    sizes: None,
108                    crossorigin: None,
109                    referrerpolicy: None,
110                    fetchpriority: None,
111                    hreflang: None,
112                    integrity: None,
113                    blocking: None,
114                });
115            }
116            // The style has neither contents nor src, log an error
117            (None, Err(err)) => err.log("Style"),
118        };
119    });
120
121    VNode::empty()
122}
123
124#[derive(Default, Clone)]
125struct StyleContext(DeduplicationContext);
126
127fn should_insert_style(href: &str) -> bool {
128    get_or_insert_root_context::<StyleContext>()
129        .0
130        .should_insert(href)
131}