textiler_core/theme/
breakpoint.rs1use 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 fn new(abbrev: &str, width: u16) -> Self {
14 Self {
15 abbrev: abbrev.to_string(),
16 width,
17 }
18 }
19
20 pub fn abbrev(&self) -> &str {
22 &self.abbrev
23 }
24
25 pub fn width(&self) -> u16 {
27 self.width
28 }
29
30 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 pub fn new() -> Self {
87 Self {
88 points: Default::default(),
89 }
90 }
91
92 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 pub fn get(&self, breakpoint: &str) -> Option<&Breakpoint> {
105 self.points.iter().find(|b| b.abbrev == breakpoint)
106 }
107
108 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 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}