sunbeam_ir/class/
modifiers.rs

1/// Modifiers for the class, such as "hover:" or "visited:"
2#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone, Default)]
3pub struct Modifiers {
4    /// ex: hover:mr10
5    hover: bool,
6    /// ex: visited:mr10
7    visited: bool,
8    /// Put this class behind a min-width media query.
9    /// ex: gteq640:mt5
10    min_width_gteq: Option<u32>,
11}
12
13/// An error while setting modifiers.
14#[derive(Debug, thiserror::Error)]
15pub enum ModifierError {
16    /// Modifier was already enabled.
17    #[error("The {0:?} modifier was already set.")]
18    AlreadySet(ModifierKind),
19}
20
21/// The type of modifier.
22#[derive(Debug, Copy, Clone)]
23pub enum ModifierKind {
24    /// hover:
25    Hover,
26    /// visited:
27    Visited,
28    /// gteq:
29    ScreenWidthGteq,
30}
31
32impl Modifiers {
33    /// Set that the hover modifier is enabled.
34    pub fn set_hover_enabled(&mut self) -> Result<(), ModifierError> {
35        if self.hover {
36            return Err(ModifierError::AlreadySet(ModifierKind::Hover));
37        }
38
39        self.hover = true;
40        Ok(())
41    }
42
43    /// Set that the visited modifier is enabled.
44    pub fn set_visited_enabled(&mut self) -> Result<(), ModifierError> {
45        if self.hover {
46            return Err(ModifierError::AlreadySet(ModifierKind::Visited));
47        }
48
49        self.visited = true;
50        Ok(())
51    }
52
53    /// Set the media query screen width >=
54    pub fn set_min_width_gteq(&mut self, screen_width: u32) -> Result<(), ModifierError> {
55        if self.hover {
56            return Err(ModifierError::AlreadySet(ModifierKind::Visited));
57        }
58
59        self.min_width_gteq = Some(screen_width);
60        Ok(())
61    }
62
63    pub(crate) fn add_modifiers_to_class_name(&mut self, class_name: &str) -> String {
64        let maybe_hover = if self.hover { ":hover" } else { "" };
65        let maybe_visited = if self.visited { ":visited" } else { "" };
66
67        format!(
68            "{class_name}{maybe_hover}{maybe_visited}",
69            class_name = class_name,
70            maybe_hover = maybe_hover,
71            maybe_visited = maybe_visited
72        )
73    }
74
75    pub(crate) fn maybe_wrap_class_definition_in_media_query(
76        &self,
77        class_definition: String,
78    ) -> String {
79        if let Some(gteq) = self.min_width_gteq() {
80            let indented_class_definition: Vec<String> = class_definition
81                .lines()
82                .into_iter()
83                .map(|l| format!("    {l}"))
84                .collect();
85            let indented_class_definition = indented_class_definition.join("\n");
86
87            format!(
88                r#"@media (min-width: {gteq}px) {{
89{indented_class_definition}
90}}"#,
91            )
92        } else {
93            class_definition
94        }
95    }
96}
97
98impl Modifiers {
99    /// Whether or not the hover modifier is enabled.
100    pub fn hover(&self) -> bool {
101        self.hover
102    }
103
104    /// Whether or not the visited modifier is enabled.
105    pub fn visited(&self) -> bool {
106        self.visited
107    }
108
109    /// Whether or not the media query min-width modifier is enabled.
110    pub fn min_width_gteq(&self) -> Option<u32> {
111        self.min_width_gteq
112    }
113}