wasm_css/
lib.rs

1// Authors: Robert Lopez
2
3pub mod components;
4pub mod delete;
5pub mod error;
6pub mod extend_from_css;
7pub mod extend_from_style;
8pub mod macros;
9pub mod remove_keys;
10
11mod generate_random_identity;
12mod render;
13
14#[cfg(test)]
15mod tests;
16
17use crate::error::WasmCssError;
18use components::Components;
19use error::format_error;
20use generate_random_identity::generate_random_identity;
21
22/// Represents a `<style>` child in the `<head>` element containing CSS.
23///
24/// `css`: formatted css String.
25#[derive(Debug, Clone, PartialEq)]
26pub struct Style {
27    pub css: String,
28    css_name: String,
29    components: Components,
30}
31
32impl Style {
33    /// Create a `Style` from `css`, rendering it to the DOM as a `<style>` in `<head>`.
34    /// The `css` is formatted, minfied, and small errors like invalid semi-colons can be
35    /// corrected, however invalid CSS itself is not handled by the parser.
36    ///
37    /// Effects remain there order as defined, but their inner fields, as well as the Style's
38    /// fields will be ordered in a deterministic fashion to avoid duplication.
39    ///
40    /// *Note*: Trailing semi-colons are required.
41    ///
42    /// Returns error if missing access to: `Head`, `Crypto`(If generating a random ID),
43    /// `Window`, `Document`.
44    ///
45    /// Provide `css_name` to define a class, ID, or to target a global identifier,
46    /// E.G.: `div`, `#id`, `.class`.
47    ///
48    /// ---
49    /// Example Usage:
50    /// ```
51    ///
52    /// let style = Style::new(
53    ///     "
54    ///         display: flex;
55    ///
56    ///         &:hover {
57    ///             background-color: rgba(111, 111, 111, 0.1);
58    ///         }
59    ///
60    ///         @media (max-width: 800px) {
61    ///             flex-direction: column;
62    ///         }
63    ///     ",
64    ///     Some(".my_class"),
65    /// )?;
66    /// ```
67    pub fn new(css: &str, css_name: Option<&str>) -> Result<Self, WasmCssError> {
68        if css_name.is_some_and(|i| i.is_empty()) {
69            return Err(format_error!(
70                "A Styles css_name cannot be empty. Use: `body`, `.class`, `#id`, ect."
71            ));
72        }
73
74        let css_name = css_name
75            .map(|id| id.to_string())
76            .unwrap_or(format!(".{}", generate_random_identity()?));
77        let components = Components::from_css(css);
78
79        let _self = Self {
80            css: components.to_css(&css_name),
81            css_name,
82            components,
83        };
84        _self.render()?;
85
86        Ok(_self)
87    }
88
89    /// Returns the `identity` of the definition inside the `<style>` element.
90    ///
91    /// Example Usage:
92    /// ```
93    ///
94    /// let style = named_style!(".myStyle", "font-size: 5rem;")?;
95    ///
96    /// // "myStyle"
97    /// let identity = style.identity();
98    /// ```
99    ///
100    /// ```
101    ///
102    /// let style = named_style!("#myStyle", "font-size: 5rem;")?;
103    ///
104    /// // "myStyle"
105    /// let identity = style.identity();
106    /// ```
107    ///
108    /// ```
109    ///
110    /// let style = named_style!("body", "font-size: 5rem;")?;
111    ///
112    /// // "body"
113    /// let identity = style.identity();
114    pub fn identity(&self) -> &str {
115        if self.css_name.starts_with('#') || self.css_name.starts_with('.') {
116            self.css_name.split_at(1).1
117        } else {
118            &self.css_name
119        }
120    }
121}