tailwind_rs_core/responsive/
states.rs

1//! # State Definitions for Pseudo-classes
2//!
3//! This module provides state definitions for CSS pseudo-classes.
4
5use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8/// State definitions for pseudo-classes
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum State {
11    /// Hover state
12    Hover,
13    /// Focus state
14    Focus,
15    /// Active state
16    Active,
17    /// Visited state
18    Visited,
19    /// Disabled state
20    Disabled,
21    /// Checked state
22    Checked,
23    /// Indeterminate state
24    Indeterminate,
25    /// Required state
26    Required,
27    /// Valid state
28    Valid,
29    /// Invalid state
30    Invalid,
31    /// In-range state
32    InRange,
33    /// Out-of-range state
34    OutOfRange,
35    /// Read-only state
36    ReadOnly,
37    /// Read-write state
38    ReadWrite,
39    /// Optional state
40    Optional,
41    /// Placeholder-shown state
42    PlaceholderShown,
43    /// Autofill state
44    Autofill,
45    /// Default state
46    Default,
47    /// Indeterminate state (alternative)
48    IndeterminateAlt,
49    /// In-range state (alternative)
50    InRangeAlt,
51    /// Out-of-range state (alternative)
52    OutOfRangeAlt,
53    /// Read-only state (alternative)
54    ReadOnlyAlt,
55    /// Read-write state (alternative)
56    ReadWriteAlt,
57    /// Optional state (alternative)
58    OptionalAlt,
59    /// Placeholder-shown state (alternative)
60    PlaceholderShownAlt,
61    /// Autofill state (alternative)
62    AutofillAlt,
63}
64
65impl State {
66    /// Get the CSS pseudo-class prefix for this state
67    pub fn prefix(&self) -> &'static str {
68        match self {
69            State::Hover => "hover:",
70            State::Focus => "focus:",
71            State::Active => "active:",
72            State::Visited => "visited:",
73            State::Disabled => "disabled:",
74            State::Checked => "checked:",
75            State::Indeterminate => "indeterminate:",
76            State::Required => "required:",
77            State::Valid => "valid:",
78            State::Invalid => "invalid:",
79            State::InRange => "in-range:",
80            State::OutOfRange => "out-of-range:",
81            State::ReadOnly => "read-only:",
82            State::ReadWrite => "read-write:",
83            State::Optional => "optional:",
84            State::PlaceholderShown => "placeholder-shown:",
85            State::Autofill => "autofill:",
86            State::Default => "default:",
87            State::IndeterminateAlt => "indeterminate:",
88            State::InRangeAlt => "in-range:",
89            State::OutOfRangeAlt => "out-of-range:",
90            State::ReadOnlyAlt => "read-only:",
91            State::ReadWriteAlt => "read-write:",
92            State::OptionalAlt => "optional:",
93            State::PlaceholderShownAlt => "placeholder-shown:",
94            State::AutofillAlt => "autofill:",
95        }
96    }
97    
98    /// Get the CSS pseudo-class name for this state
99    pub fn css_name(&self) -> &'static str {
100        match self {
101            State::Hover => ":hover",
102            State::Focus => ":focus",
103            State::Active => ":active",
104            State::Visited => ":visited",
105            State::Disabled => ":disabled",
106            State::Checked => ":checked",
107            State::Indeterminate => ":indeterminate",
108            State::Required => ":required",
109            State::Valid => ":valid",
110            State::Invalid => ":invalid",
111            State::InRange => ":in-range",
112            State::OutOfRange => ":out-of-range",
113            State::ReadOnly => ":read-only",
114            State::ReadWrite => ":read-write",
115            State::Optional => ":optional",
116            State::PlaceholderShown => ":placeholder-shown",
117            State::Autofill => ":autofill",
118            State::Default => ":default",
119            State::IndeterminateAlt => ":indeterminate",
120            State::InRangeAlt => ":in-range",
121            State::OutOfRangeAlt => ":out-of-range",
122            State::ReadOnlyAlt => ":read-only",
123            State::ReadWriteAlt => ":read-write",
124            State::OptionalAlt => ":optional",
125            State::PlaceholderShownAlt => ":placeholder-shown",
126            State::AutofillAlt => ":autofill",
127        }
128    }
129    
130    /// Get all states
131    pub fn all() -> Vec<State> {
132        vec![
133            State::Hover,
134            State::Focus,
135            State::Active,
136            State::Visited,
137            State::Disabled,
138            State::Checked,
139            State::Indeterminate,
140            State::Required,
141            State::Valid,
142            State::Invalid,
143            State::InRange,
144            State::OutOfRange,
145            State::ReadOnly,
146            State::ReadWrite,
147            State::Optional,
148            State::PlaceholderShown,
149            State::Autofill,
150            State::Default,
151        ]
152    }
153    
154    /// Check if this state is interactive (user can trigger it)
155    pub fn is_interactive(&self) -> bool {
156        matches!(self, State::Hover | State::Focus | State::Active)
157    }
158    
159    /// Check if this state is form-related
160    pub fn is_form_related(&self) -> bool {
161        matches!(
162            self,
163            State::Checked
164                | State::Indeterminate
165                | State::Required
166                | State::Valid
167                | State::Invalid
168                | State::InRange
169                | State::OutOfRange
170                | State::ReadOnly
171                | State::ReadWrite
172                | State::Optional
173                | State::PlaceholderShown
174                | State::Autofill
175                | State::Default
176        )
177    }
178}
179
180impl FromStr for State {
181    type Err = String;
182    
183    fn from_str(s: &str) -> Result<Self, Self::Err> {
184        match s.to_lowercase().as_str() {
185            "hover" => Ok(State::Hover),
186            "focus" => Ok(State::Focus),
187            "active" => Ok(State::Active),
188            "visited" => Ok(State::Visited),
189            "disabled" => Ok(State::Disabled),
190            "checked" => Ok(State::Checked),
191            "indeterminate" => Ok(State::Indeterminate),
192            "required" => Ok(State::Required),
193            "valid" => Ok(State::Valid),
194            "invalid" => Ok(State::Invalid),
195            "in-range" => Ok(State::InRange),
196            "out-of-range" => Ok(State::OutOfRange),
197            "read-only" => Ok(State::ReadOnly),
198            "read-write" => Ok(State::ReadWrite),
199            "optional" => Ok(State::Optional),
200            "placeholder-shown" => Ok(State::PlaceholderShown),
201            "autofill" => Ok(State::Autofill),
202            "default" => Ok(State::Default),
203            _ => Err(format!("Invalid state: {}", s)),
204        }
205    }
206}
207
208impl std::fmt::Display for State {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        match self {
211            State::Hover => write!(f, "hover"),
212            State::Focus => write!(f, "focus"),
213            State::Active => write!(f, "active"),
214            State::Visited => write!(f, "visited"),
215            State::Disabled => write!(f, "disabled"),
216            State::Checked => write!(f, "checked"),
217            State::Indeterminate => write!(f, "indeterminate"),
218            State::Required => write!(f, "required"),
219            State::Valid => write!(f, "valid"),
220            State::Invalid => write!(f, "invalid"),
221            State::InRange => write!(f, "in-range"),
222            State::OutOfRange => write!(f, "out-of-range"),
223            State::ReadOnly => write!(f, "read-only"),
224            State::ReadWrite => write!(f, "read-write"),
225            State::Optional => write!(f, "optional"),
226            State::PlaceholderShown => write!(f, "placeholder-shown"),
227            State::Autofill => write!(f, "autofill"),
228            State::Default => write!(f, "default"),
229            State::IndeterminateAlt => write!(f, "indeterminate"),
230            State::InRangeAlt => write!(f, "in-range"),
231            State::OutOfRangeAlt => write!(f, "out-of-range"),
232            State::ReadOnlyAlt => write!(f, "read-only"),
233            State::ReadWriteAlt => write!(f, "read-write"),
234            State::OptionalAlt => write!(f, "optional"),
235            State::PlaceholderShownAlt => write!(f, "placeholder-shown"),
236            State::AutofillAlt => write!(f, "autofill"),
237        }
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    #[test]
246    fn test_state_prefix() {
247        assert_eq!(State::Hover.prefix(), "hover:");
248        assert_eq!(State::Focus.prefix(), "focus:");
249        assert_eq!(State::Active.prefix(), "active:");
250        assert_eq!(State::Disabled.prefix(), "disabled:");
251        assert_eq!(State::Checked.prefix(), "checked:");
252    }
253
254    #[test]
255    fn test_state_css_name() {
256        assert_eq!(State::Hover.css_name(), ":hover");
257        assert_eq!(State::Focus.css_name(), ":focus");
258        assert_eq!(State::Active.css_name(), ":active");
259        assert_eq!(State::Disabled.css_name(), ":disabled");
260        assert_eq!(State::Checked.css_name(), ":checked");
261    }
262
263    #[test]
264    fn test_state_from_str() {
265        assert_eq!(State::from_str("hover").unwrap(), State::Hover);
266        assert_eq!(State::from_str("focus").unwrap(), State::Focus);
267        assert_eq!(State::from_str("active").unwrap(), State::Active);
268        assert_eq!(State::from_str("disabled").unwrap(), State::Disabled);
269        assert_eq!(State::from_str("checked").unwrap(), State::Checked);
270    }
271
272    #[test]
273    fn test_state_display() {
274        assert_eq!(format!("{}", State::Hover), "hover");
275        assert_eq!(format!("{}", State::Focus), "focus");
276        assert_eq!(format!("{}", State::Active), "active");
277        assert_eq!(format!("{}", State::Disabled), "disabled");
278        assert_eq!(format!("{}", State::Checked), "checked");
279    }
280
281    #[test]
282    fn test_state_all() {
283        let all = State::all();
284        assert_eq!(all.len(), 18);
285        assert!(all.contains(&State::Hover));
286        assert!(all.contains(&State::Focus));
287        assert!(all.contains(&State::Active));
288    }
289
290    #[test]
291    fn test_state_is_interactive() {
292        assert!(State::Hover.is_interactive());
293        assert!(State::Focus.is_interactive());
294        assert!(State::Active.is_interactive());
295        assert!(!State::Disabled.is_interactive());
296        assert!(!State::Checked.is_interactive());
297    }
298
299    #[test]
300    fn test_state_is_form_related() {
301        assert!(!State::Hover.is_form_related());
302        assert!(!State::Focus.is_form_related());
303        assert!(!State::Active.is_form_related());
304        assert!(State::Checked.is_form_related());
305        assert!(State::Required.is_form_related());
306        assert!(State::Valid.is_form_related());
307    }
308}