vertigo 0.2.0-alpha

Reactive Real-DOM library for Rust
Documentation
use std::rc::Rc;

use crate::{
    dom::css::{Css, CssGroup},
    struct_mut::HashMapMut,
};

use super::{
    get_selector::get_selector,
    next_id::NextId,
    transform_css::transform_css,
};

type InsertFn = dyn Fn(&str, &str);

struct CssManagerInner {
    insert_css: Box<InsertFn>,
    next_id: NextId,
    ids_static: HashMapMut<&'static str, u64>,
    ids_dynamic: HashMapMut<String, u64>,
}

impl CssManagerInner {
    pub fn new(insert_css: Box<InsertFn>) -> CssManagerInner {
        CssManagerInner {
            insert_css,
            next_id: NextId::new(),
            ids_static: HashMapMut::new(),
            ids_dynamic: HashMapMut::new(),
        }
    }

    fn insert_css(&self, css: &str) -> u64 {
        let (class_id, css_selectors) = transform_css(css, &self.next_id);

        for (selector, selector_data) in css_selectors {
            (self.insert_css)(&selector, &selector_data);
        }

        class_id
    }
}

#[derive(Clone)]
pub struct CssManager {
    inner: Rc<CssManagerInner>,
}

impl CssManager {
    pub fn new(insert_css: impl Fn(&str, &str) + 'static) -> CssManager {
        CssManager {
            inner: Rc::new(CssManagerInner::new(Box::new(insert_css))),
        }
    }

    fn get_static(&self, css: &'static str) -> String {
        if let Some(class_id) = self.inner.ids_static.get(&css) {
            return get_selector(&class_id);
        }

        let class_id = self.inner.insert_css(css);
        self.inner.ids_static.insert(css, class_id);

        get_selector(&class_id)
    }

    fn get_dynamic(&self, css: impl Into<String>) -> String {
        let css = css.into();
        if let Some(class_id) = self.inner.ids_dynamic.get(&css) {
            return get_selector(&class_id);
        }

        let class_id = self.inner.insert_css(css.as_str());
        self.inner.ids_dynamic.insert(css, class_id);

        get_selector(&class_id)
    }

    pub fn get_class_name(&self, css: &Css) -> String {
        let mut out: Vec<String> = Vec::new();

        for item in css.groups.iter() {
            match item {
                CssGroup::CssStatic { value } => {
                    out.push(self.get_static(value));
                }
                CssGroup::CssDynamic { value } => {
                    out.push(self.get_dynamic(value));
                }
                CssGroup::CssMedia { query, rules } => {
                    out.push("@media".to_string());
                    out.push(query.clone());
                    for rule in rules {
                        out.push(self.get_dynamic(rule));
                    }
                }
            }
        }

        out.join(" ")
    }
}