textiler_core/theme/
breakpoint.rs

1use std::cmp::Ordering;
2use std::collections::{BTreeMap, BTreeSet, HashSet};
3use std::ops::{Deref, DerefMut};
4
5#[derive(Debug, Clone)]
6pub struct Breakpoint {
7    abbrev: String,
8    width: u16,
9}
10
11impl Breakpoint {
12    /// Creates a new breakpoint
13    fn new(abbrev: &str, width: u16) -> Self {
14        Self {
15            abbrev: abbrev.to_string(),
16            width,
17        }
18    }
19
20    /// Gets the abbreviation used to reference this breakpoint
21    pub fn abbrev(&self) -> &str {
22        &self.abbrev
23    }
24
25    /// Gets the width for this breakpoint
26    pub fn width(&self) -> u16 {
27        self.width
28    }
29
30    /// Gets a mutable reference to the width of this breakpoint
31    pub fn width_mut(&mut self) -> &mut u16 {
32        &mut self.width
33    }
34}
35
36impl Default for Breakpoints {
37    fn default() -> Self {
38        Self::from_iter([
39            ("xs", 0),
40            ("sm", 600),
41            ("md", 768),
42            ("lg", 992),
43            ("xl", 1200),
44        ])
45    }
46}
47
48impl<'a> FromIterator<(&'a str, u16)> for Breakpoints {
49    fn from_iter<T: IntoIterator<Item = (&'a str, u16)>>(iter: T) -> Self {
50        let mut bp = Breakpoints::new();
51        for (key, width) in iter {
52            bp.set(key, width);
53        }
54
55        bp
56    }
57}
58
59impl PartialEq for Breakpoint {
60    fn eq(&self, other: &Self) -> bool {
61        self.width == other.width
62    }
63}
64
65impl Eq for Breakpoint {}
66
67impl PartialOrd for Breakpoint {
68    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
69        Some(self.cmp(other))
70    }
71}
72
73impl Ord for Breakpoint {
74    fn cmp(&self, other: &Self) -> Ordering {
75        self.width.cmp(&other.width)
76    }
77}
78
79#[derive(Debug, Clone, PartialEq)]
80pub struct Breakpoints {
81    points: BTreeSet<Breakpoint>,
82}
83
84impl Breakpoints {
85    /// Creates a new set of breakpoints
86    pub fn new() -> Self {
87        Self {
88            points: Default::default(),
89        }
90    }
91
92    /// Sets a breakpoint with a given name
93    pub fn set(&mut self, breakpoint: &str, width: u16) {
94        {
95            if let Some(mut bp) = self.get_mut(breakpoint) {
96                bp.width = width;
97                return;
98            }
99        }
100        let _ = self.points.insert(Breakpoint::new(breakpoint, width));
101    }
102
103    /// Gets the breakpoint at given value
104    pub fn get(&self, breakpoint: &str) -> Option<&Breakpoint> {
105        self.points.iter().find(|b| b.abbrev == breakpoint)
106    }
107
108    /// Gets a mutable reference to a breakpoint
109
110    pub fn get_mut(&mut self, breakpoint: &str) -> Option<BreakpointMutRef> {
111        if let Some(ref found) = self.get(breakpoint).cloned() {
112            let took = self.points.take(found).unwrap();
113            Some(BreakpointMutRef {
114                bp: took,
115                points: self,
116            })
117        } else {
118            None
119        }
120    }
121
122    /// Gets all the breakpoints
123
124    pub fn points(&self) -> impl IntoIterator<Item = &Breakpoint> {
125        self.points.iter()
126    }
127}
128
129#[derive(Debug)]
130pub struct BreakpointMutRef<'a> {
131    bp: Breakpoint,
132    points: &'a mut Breakpoints,
133}
134
135impl<'a> Deref for BreakpointMutRef<'a> {
136    type Target = Breakpoint;
137
138    fn deref(&self) -> &Self::Target {
139        &self.bp
140    }
141}
142
143impl<'a> DerefMut for BreakpointMutRef<'a> {
144    fn deref_mut(&mut self) -> &mut Self::Target {
145        &mut self.bp
146    }
147}
148
149impl Drop for BreakpointMutRef<'_> {
150    fn drop(&mut self) {
151        let bp = std::mem::replace(&mut self.bp, Breakpoint::new("", 0));
152        self.points.points.insert(bp);
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use crate::theme::breakpoint::Breakpoints;
159
160    #[test]
161    fn create_default() {
162        let bps = Breakpoints::default();
163        assert_eq!(bps.get("xs").unwrap().width(), 0);
164        assert_eq!(bps.get("sm").unwrap().width(), 600);
165        assert_eq!(bps.get("md").unwrap().width(), 768);
166        assert_eq!(bps.get("lg").unwrap().width(), 992);
167        assert_eq!(bps.get("xl").unwrap().width(), 1200);
168    }
169}