wal_css/
css_manager.rs

1use std::{cell::RefCell, rc::Rc};
2use web_sys::{window, Document, Element};
3
4use crate::{css::Css, id_generator::IdGenerator, parser::process_css};
5
6thread_local! {
7    static ID_GENERATOR: Rc<RefCell<IdGenerator>> = Rc::new(RefCell::new(IdGenerator::new()));
8}
9
10/// CssManager struct allows to attach new css stylesheets to the application.
11///
12/// Using CssManager directly is not recommended, insted use the [css_styleseet macro](../macro.css_stylesheet.html).
13///
14/// To attach css as a stylesheet directly, call the attach_css method with a correct CSS string as an argument.
15/// The selectors will be prepended with a newly generated id and the resulting CSS will be added
16/// to the application as a new style tag. The method will return the [Css](../css/struct.Css.html) object, which can be used to reference the stylesheet selectors
17/// inside the [rsx macro](../../wal_rsx/macro.rsx.html).
18///
19/// # Example usage
20/// ```
21/// use wal_css::css_manager::CssManager;
22///
23/// let manager = CssManager::new();
24/// manager.attach_css(".class1 { background-color: red; }");
25/// ```
26pub struct CssManager {
27    document: Document,
28}
29
30#[allow(dead_code)]
31impl CssManager {
32    /// Creates new instance of CssManager struct.
33    ///
34    /// # Example usage
35    /// ```
36    /// use wal_css::css_manager::CssManager;
37    /// let manager = CssManager::new();
38    /// ```
39    pub fn new() -> Self {
40        CssManager {
41            document: window().unwrap().document().unwrap(),
42        }
43    }
44
45    /// Attaches css string passed as an argument as a new stylesheet.
46    /// The selectors will be prepended with a newly generated id and the resulting CSS will be added
47    /// to the application as a new style tag. The method will return the [Css](../css/struct.Css.html) object, which can be used to reference the stylesheet selectors
48    /// inside the [rsx macro](../../wal_rsx/macro.rsx.html).
49    ///
50    /// # Example usage
51    /// ```
52    /// use wal_css::css_manager::CssManager;
53    ///
54    /// let manager = CssManager::new();
55    /// manager.attach_css(".class1 { background-color: red; }");
56    /// ```
57    pub fn attach_css(&mut self, css: &str) -> Css {
58        // generate new id and prefix for the css stylesheet
59        let id = ID_GENERATOR.with(|gen| gen.as_ref().borrow_mut().get_new_id());
60        let prefix = generate_prefix(id);
61
62        // parse the css and generate new css with mapping
63        let (new_css, mapping) = process_css(css, &prefix);
64
65        // generate new style element
66        let style: Element = add_new_style_element(&self.document);
67        style.set_text_content(Some(&new_css));
68
69        // return new Css object
70        Css::new(id, style, mapping)
71    }
72}
73
74impl Default for CssManager {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80fn add_new_style_element(document: &Document) -> Element {
81    let style = document.create_element("style").unwrap();
82    style.append_child(&document.create_text_node("")).unwrap();
83    document.head().unwrap().append_child(&style).unwrap();
84    style
85}
86
87fn generate_prefix(id: u8) -> String {
88    format!("_{}-", id)
89}
90
91#[cfg(test)]
92mod tests {
93
94    use gloo::utils::document;
95    use wasm_bindgen_test::*;
96    use web_sys::Node;
97
98    use super::*;
99    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
100
101    #[wasm_bindgen_test]
102    fn manager_creates_properly() {
103        CssManager::new();
104    }
105
106    #[wasm_bindgen_test]
107    fn add_new_style_element_adds_element_properly() {
108        let _new_element = add_new_style_element(&document());
109
110        let style_element = document().head().unwrap().last_element_child().unwrap();
111
112        assert_eq!(style_element.local_name(), "style");
113        assert_eq!(
114            style_element.first_child().unwrap().node_type(),
115            Node::TEXT_NODE
116        );
117    }
118
119    #[wasm_bindgen_test]
120    fn manager_attaches_css_properly() {
121        let mut manager = CssManager::new();
122
123        let _css = manager.attach_css(".class1 { color: red; }");
124
125        let style_element = manager
126            .document
127            .head()
128            .unwrap()
129            .last_element_child()
130            .unwrap();
131
132        let css_txt = style_element.first_child().unwrap().text_content().unwrap();
133
134        assert_eq!(css_txt, "._0-class1 { color: red; }");
135    }
136}