textiler_core/context/
style_manager_context.rs

1use std::ops::Deref;
2use std::rc::Rc;
3
4use cfg_if::cfg_if;
5use gloo::utils::document;
6use stylist::ast::ToStyleStr;
7use stylist::manager::StyleManager;
8use wasm_bindgen::JsCast;
9use web_sys::{HtmlStyleElement, Node};
10
11use crate::{Error, Sx};
12use crate::theme::Theme;
13use crate::theme::theme_mode::ThemeMode;
14
15#[derive(Default, Debug, Clone, PartialEq)]
16pub struct StyleManagerContext {
17    manager: Rc<StyleManager>,
18}
19
20impl StyleManagerContext {
21    pub fn new(manager: Rc<StyleManager>) -> Self {
22        Self { manager }
23    }
24
25    #[cfg(target_arch = "wasm32")]
26    pub(crate) fn mount_wasm(
27        &self,
28        theme: &Theme,
29        mode: &ThemeMode,
30        to_mount: Sx,
31    ) -> Result<(), Error> {
32        let document = document();
33        let container = self.container().ok_or(Error::Web(None))?;
34
35        (|| {
36            let css = to_mount.to_css(&mode, theme);
37            let style_element = document.create_element("style")?;
38            let theme_name = format!("theme-{}-main", theme.prefix);
39            style_element.set_attribute("data-style", &theme_name)?;
40            let base_css = css.to_style_str(None);
41
42            if option_env!("MINIFY_CSS").is_some() {
43                match minifier::css::minify(&base_css) {
44                    Ok(minified) => {
45                        style_element.set_text_content(Some(&minified.to_string()));
46                    }
47                    Err(non_minified) => {
48                        style_element.set_text_content(Some(non_minified));
49                    }
50                }
51            } else {
52                style_element.set_text_content(Some(&base_css));
53            }
54
55            let list = container.child_nodes();
56            let len = list.length();
57            let mut existing: Option<Node> = None;
58            for i in 0..len {
59                if let Some(child) = list.get(i) {
60                    if let Some(style_element) = child.dyn_ref::<HtmlStyleElement>() {
61                        if style_element.get_attribute("data-style").as_ref() == Some(&theme_name) {
62                            existing = Some(child);
63                            break;
64                        }
65                    }
66                }
67            }
68
69            if let Some(ref existing) = existing {
70                container.replace_child(&style_element, existing)?;
71            } else {
72                container.append_child(&style_element)?;
73            }
74
75            Ok(())
76        })()
77        .map_err(|e| Error::Web(Some(e)))
78    }
79    pub fn mount(&self, theme: &Theme, mode: &ThemeMode, to_mount: Sx) -> Result<(), crate::Error> {
80        cfg_if! {
81            if #[cfg(target_arch="wasm32")] {
82                self.mount_wasm(theme, mode, to_mount)
83            } else {
84                Err(Error::MountingUnsupported)
85            }
86        }
87    }
88}
89
90impl Deref for StyleManagerContext {
91    type Target = StyleManager;
92
93    fn deref(&self) -> &Self::Target {
94        &*self.manager
95    }
96}