dioxus_document/elements/style.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
use super::*;
use crate::document;
use dioxus_html as dioxus_elements;
#[non_exhaustive]
#[derive(Clone, Props, PartialEq)]
pub struct StyleProps {
/// Styles are deduplicated by their href attribute
pub href: Option<String>,
pub media: Option<String>,
pub nonce: Option<String>,
pub title: Option<String>,
/// The contents of the style tag. If present, the children must be a single text node.
pub children: Element,
#[props(extends = style, extends = GlobalAttributes)]
pub additional_attributes: Vec<Attribute>,
}
impl StyleProps {
/// Get all the attributes for the style tag
pub fn attributes(&self) -> Vec<(&'static str, String)> {
let mut attributes = Vec::new();
extend_attributes(&mut attributes, &self.additional_attributes);
if let Some(href) = &self.href {
attributes.push(("href", href.clone()));
}
if let Some(media) = &self.media {
attributes.push(("media", media.clone()));
}
if let Some(nonce) = &self.nonce {
attributes.push(("nonce", nonce.clone()));
}
if let Some(title) = &self.title {
attributes.push(("title", title.clone()));
}
attributes
}
pub fn style_contents(&self) -> Result<String, ExtractSingleTextNodeError<'_>> {
extract_single_text_node(&self.children)
}
}
/// Render a [`style`](crate::elements::style) or [`link`](crate::elements::link) tag into the head of the page.
///
/// 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.
///
/// # Example
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// fn RedBackground() -> Element {
/// rsx! {
/// // You can use the style component to render a style tag into the head of the page
/// // This style tag will set the background color of the page to red
/// document::Style {
/// r#"
/// body {{
/// background-color: red;
/// }}
/// "#
/// }
/// // You could also use a style with a href to load a stylesheet asset
/// document::Style {
/// href: asset!("/assets/style.css")
/// }
/// }
/// }
/// ```
///
/// <div class="warning">
///
/// Any updates to the props after the first render will not be reflected in the head.
///
/// </div>
#[component]
pub fn Style(props: StyleProps) -> Element {
use_update_warning(&props, "Style {}");
use_hook(|| {
let document = document();
let mut insert_style = document.create_head_component();
if let Some(href) = &props.href {
if !should_insert_style(href) {
insert_style = false;
}
}
if !insert_style {
return;
}
let mut attributes = props.attributes();
match (&props.href, props.style_contents()) {
// The style has inline contents, render it as a style tag
(_, Ok(_)) => document.create_style(props),
// The style has a src, render it as a link tag
(Some(_), _) => {
attributes.push(("type", "text/css".into()));
attributes.push(("rel", "stylesheet".into()));
document.create_link(LinkProps {
media: props.media,
title: props.title,
r#type: Some("text/css".to_string()),
additional_attributes: props.additional_attributes,
href: props.href,
rel: Some("stylesheet".to_string()),
disabled: None,
r#as: None,
sizes: None,
crossorigin: None,
referrerpolicy: None,
fetchpriority: None,
hreflang: None,
integrity: None,
blocking: None,
});
}
// The style has neither contents nor src, log an error
(None, Err(err)) => err.log("Style"),
};
});
VNode::empty()
}
#[derive(Default, Clone)]
struct StyleContext(DeduplicationContext);
fn should_insert_style(href: &str) -> bool {
get_or_insert_root_context::<StyleContext>()
.0
.should_insert(href)
}