textiler_core/
theme.rs

1//! Used for theming
2
3use std::collections::HashMap;
4use std::ops::Deref;
5
6use once_cell::sync::Lazy;
7use yew::Properties;
8
9use crate::theme::breakpoint::Breakpoints;
10pub use color::Color;
11use regex::Regex;
12
13use crate::theme::palette::Palette;
14use crate::theme::typography::{TypographyLevel, TypographyScale};
15use crate::utils::to_property;
16
17pub mod color;
18
19pub mod baseline;
20pub mod breakpoint;
21pub mod gradient;
22pub mod palette;
23pub mod parsing;
24pub mod sx;
25pub mod theme_mode;
26pub mod typography;
27
28#[derive(Debug, Clone, PartialEq)]
29pub struct Theme {
30    pub prefix: String,
31    breakpoints: Breakpoints,
32    palettes: HashMap<String, Palette>,
33    typography: TypographyScale,
34}
35
36static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
37    parsing::from_str(include_str!("./theme/theme.json")).expect("could not read default theme")
38});
39
40impl Default for Theme {
41    fn default() -> Self {
42        DEFAULT_THEME.clone()
43    }
44}
45
46impl Theme {
47    pub fn new() -> Self {
48        Self::with_prefix("happy")
49    }
50
51    pub fn with_prefix(prefix: impl AsRef<str>) -> Self {
52        Self {
53            prefix: prefix.as_ref().to_string(),
54            breakpoints: Default::default(),
55            palettes: Default::default(),
56            typography: Default::default(),
57        }
58    }
59
60    /// Get a palette by name
61    pub fn get_palette(&self, name: &str) -> Option<&Palette> {
62        self.palettes.get(name)
63    }
64
65    /// Get a palette by name
66    pub fn palette_mut(&mut self, name: &str) -> Option<&mut Palette> {
67        self.palettes.get_mut(name)
68    }
69
70    /// Insert a palette into the theme
71    pub fn insert_palette(&mut self, name: impl AsRef<str>, palette: Palette) {
72        let _ = self.palettes.insert(name.as_ref().to_string(), palette);
73    }
74
75    /// Creates a new palette if not yet present, and returns a mutable reference to it.
76    pub fn palette(&mut self, name: impl AsRef<str>) -> &mut Palette {
77        self.palettes.entry(name.as_ref().to_string()).or_default()
78    }
79
80    pub fn palette_var(&self, palette: &str, selector: &str) -> String {
81        to_property(format!("--{}-palette-{palette}-{selector}", self.prefix))
82    }
83
84    pub fn class_var(&self, class: &str, var_name: &str) -> String {
85        to_property(format!("--{}-{class}-{var_name}", self.prefix))
86    }
87
88    /// Gets all palettes
89    pub fn palettes(&self) -> impl Iterator<Item = (&str, &Palette)> {
90        self.palettes.iter().map(|(key, value)| (&**key, value))
91    }
92
93    /// Gets a reference to the breakpoints object
94    pub fn breakpoints(&self) -> &Breakpoints {
95        &self.breakpoints
96    }
97
98    /// Gets a mutable reference to the breakpoints object
99    pub fn breakpoints_mut(&mut self) -> &mut Breakpoints {
100        &mut self.breakpoints
101    }
102
103    /// Gets the typography scale
104    pub fn typography(&self) -> &TypographyScale {
105        &self.typography
106    }
107
108    /// Gets the typography scale
109    pub fn typography_mut(&mut self) -> &mut TypographyScale {
110        &mut self.typography
111    }
112
113    pub fn system_class(&self) -> String {
114        format!(".{}-system", self.prefix)
115    }
116}
117
118pub static PALETTE_SELECTOR_REGEX: Lazy<Regex> = Lazy::new(|| {
119    Regex::new(r#"^(?<palette>[a-zA-Z_]\w*)\.(?<selector>\w+)$"#)
120        .expect("could not create palette selector")
121});