tailwind_rs_core/responsive/
breakpoints.rs

1//! # Breakpoint Definitions and Utilities
2//!
3//! This module provides breakpoint definitions and utilities for responsive design.
4
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8/// Breakpoint definitions for responsive design
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum Breakpoint {
11    /// Base breakpoint (no prefix)
12    Base,
13    /// Small screens (640px and up)
14    Sm,
15    /// Medium screens (768px and up)
16    Md,
17    /// Large screens (1024px and up)
18    Lg,
19    /// Extra large screens (1280px and up)
20    Xl,
21    /// 2X large screens (1536px and up)
22    Xl2,
23}
24
25impl Breakpoint {
26    /// Get the minimum width for this breakpoint
27    pub fn min_width(&self) -> u32 {
28        match self {
29            Breakpoint::Base => 0,
30            Breakpoint::Sm => 640,
31            Breakpoint::Md => 768,
32            Breakpoint::Lg => 1024,
33            Breakpoint::Xl => 1280,
34            Breakpoint::Xl2 => 1536,
35        }
36    }
37    
38    /// Get the CSS media query for this breakpoint
39    pub fn media_query(&self) -> &'static str {
40        match self {
41            Breakpoint::Base => "",
42            Breakpoint::Sm => "(min-width: 640px)",
43            Breakpoint::Md => "(min-width: 768px)",
44            Breakpoint::Lg => "(min-width: 1024px)",
45            Breakpoint::Xl => "(min-width: 1280px)",
46            Breakpoint::Xl2 => "(min-width: 1536px)",
47        }
48    }
49    
50    /// Get the Tailwind CSS prefix for this breakpoint
51    pub fn prefix(&self) -> &'static str {
52        match self {
53            Breakpoint::Base => "",
54            Breakpoint::Sm => "sm:",
55            Breakpoint::Md => "md:",
56            Breakpoint::Lg => "lg:",
57            Breakpoint::Xl => "xl:",
58            Breakpoint::Xl2 => "2xl:",
59        }
60    }
61    
62    /// Get all breakpoints in order
63    pub fn all() -> Vec<Breakpoint> {
64        vec![
65            Breakpoint::Base,
66            Breakpoint::Sm,
67            Breakpoint::Md,
68            Breakpoint::Lg,
69            Breakpoint::Xl,
70            Breakpoint::Xl2,
71        ]
72    }
73    
74    /// Get the next breakpoint in the sequence
75    pub fn next(&self) -> Option<Breakpoint> {
76        match self {
77            Breakpoint::Base => Some(Breakpoint::Sm),
78            Breakpoint::Sm => Some(Breakpoint::Md),
79            Breakpoint::Md => Some(Breakpoint::Lg),
80            Breakpoint::Lg => Some(Breakpoint::Xl),
81            Breakpoint::Xl => Some(Breakpoint::Xl2),
82            Breakpoint::Xl2 => None,
83        }
84    }
85    
86    /// Get the previous breakpoint in the sequence
87    pub fn previous(&self) -> Option<Breakpoint> {
88        match self {
89            Breakpoint::Base => None,
90            Breakpoint::Sm => Some(Breakpoint::Base),
91            Breakpoint::Md => Some(Breakpoint::Sm),
92            Breakpoint::Lg => Some(Breakpoint::Md),
93            Breakpoint::Xl => Some(Breakpoint::Lg),
94            Breakpoint::Xl2 => Some(Breakpoint::Xl),
95        }
96    }
97}
98
99impl FromStr for Breakpoint {
100    type Err = String;
101    
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        match s.to_lowercase().as_str() {
104            "" | "base" => Ok(Breakpoint::Base),
105            "sm" => Ok(Breakpoint::Sm),
106            "md" => Ok(Breakpoint::Md),
107            "lg" => Ok(Breakpoint::Lg),
108            "xl" => Ok(Breakpoint::Xl),
109            "2xl" | "xl2" => Ok(Breakpoint::Xl2),
110            _ => Err(format!("Invalid breakpoint: {}", s)),
111        }
112    }
113}
114
115impl std::fmt::Display for Breakpoint {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        match self {
118            Breakpoint::Base => write!(f, "base"),
119            Breakpoint::Sm => write!(f, "sm"),
120            Breakpoint::Md => write!(f, "md"),
121            Breakpoint::Lg => write!(f, "lg"),
122            Breakpoint::Xl => write!(f, "xl"),
123            Breakpoint::Xl2 => write!(f, "2xl"),
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_breakpoint_min_width() {
134        assert_eq!(Breakpoint::Base.min_width(), 0);
135        assert_eq!(Breakpoint::Sm.min_width(), 640);
136        assert_eq!(Breakpoint::Md.min_width(), 768);
137        assert_eq!(Breakpoint::Lg.min_width(), 1024);
138        assert_eq!(Breakpoint::Xl.min_width(), 1280);
139        assert_eq!(Breakpoint::Xl2.min_width(), 1536);
140    }
141
142    #[test]
143    fn test_breakpoint_media_query() {
144        assert_eq!(Breakpoint::Base.media_query(), "");
145        assert_eq!(Breakpoint::Sm.media_query(), "(min-width: 640px)");
146        assert_eq!(Breakpoint::Md.media_query(), "(min-width: 768px)");
147        assert_eq!(Breakpoint::Lg.media_query(), "(min-width: 1024px)");
148        assert_eq!(Breakpoint::Xl.media_query(), "(min-width: 1280px)");
149        assert_eq!(Breakpoint::Xl2.media_query(), "(min-width: 1536px)");
150    }
151
152    #[test]
153    fn test_breakpoint_prefix() {
154        assert_eq!(Breakpoint::Base.prefix(), "");
155        assert_eq!(Breakpoint::Sm.prefix(), "sm:");
156        assert_eq!(Breakpoint::Md.prefix(), "md:");
157        assert_eq!(Breakpoint::Lg.prefix(), "lg:");
158        assert_eq!(Breakpoint::Xl.prefix(), "xl:");
159        assert_eq!(Breakpoint::Xl2.prefix(), "2xl:");
160    }
161
162    #[test]
163    fn test_breakpoint_from_str() {
164        assert_eq!(Breakpoint::from_str("").unwrap(), Breakpoint::Base);
165        assert_eq!(Breakpoint::from_str("base").unwrap(), Breakpoint::Base);
166        assert_eq!(Breakpoint::from_str("sm").unwrap(), Breakpoint::Sm);
167        assert_eq!(Breakpoint::from_str("md").unwrap(), Breakpoint::Md);
168        assert_eq!(Breakpoint::from_str("lg").unwrap(), Breakpoint::Lg);
169        assert_eq!(Breakpoint::from_str("xl").unwrap(), Breakpoint::Xl);
170        assert_eq!(Breakpoint::from_str("2xl").unwrap(), Breakpoint::Xl2);
171        assert_eq!(Breakpoint::from_str("xl2").unwrap(), Breakpoint::Xl2);
172    }
173
174    #[test]
175    fn test_breakpoint_display() {
176        assert_eq!(format!("{}", Breakpoint::Base), "base");
177        assert_eq!(format!("{}", Breakpoint::Sm), "sm");
178        assert_eq!(format!("{}", Breakpoint::Md), "md");
179        assert_eq!(format!("{}", Breakpoint::Lg), "lg");
180        assert_eq!(format!("{}", Breakpoint::Xl), "xl");
181        assert_eq!(format!("{}", Breakpoint::Xl2), "2xl");
182    }
183
184    #[test]
185    fn test_breakpoint_all() {
186        let all = Breakpoint::all();
187        assert_eq!(all.len(), 6);
188        assert_eq!(all[0], Breakpoint::Base);
189        assert_eq!(all[1], Breakpoint::Sm);
190        assert_eq!(all[2], Breakpoint::Md);
191        assert_eq!(all[3], Breakpoint::Lg);
192        assert_eq!(all[4], Breakpoint::Xl);
193        assert_eq!(all[5], Breakpoint::Xl2);
194    }
195
196    #[test]
197    fn test_breakpoint_next() {
198        assert_eq!(Breakpoint::Base.next(), Some(Breakpoint::Sm));
199        assert_eq!(Breakpoint::Sm.next(), Some(Breakpoint::Md));
200        assert_eq!(Breakpoint::Md.next(), Some(Breakpoint::Lg));
201        assert_eq!(Breakpoint::Lg.next(), Some(Breakpoint::Xl));
202        assert_eq!(Breakpoint::Xl.next(), Some(Breakpoint::Xl2));
203        assert_eq!(Breakpoint::Xl2.next(), None);
204    }
205
206    #[test]
207    fn test_breakpoint_previous() {
208        assert_eq!(Breakpoint::Base.previous(), None);
209        assert_eq!(Breakpoint::Sm.previous(), Some(Breakpoint::Base));
210        assert_eq!(Breakpoint::Md.previous(), Some(Breakpoint::Sm));
211        assert_eq!(Breakpoint::Lg.previous(), Some(Breakpoint::Md));
212        assert_eq!(Breakpoint::Xl.previous(), Some(Breakpoint::Lg));
213        assert_eq!(Breakpoint::Xl2.previous(), Some(Breakpoint::Xl));
214    }
215}