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