tailwind_rs_core/css_generator/
variants.rs1use crate::responsive::Breakpoint;
7
8#[derive(Debug, Clone)]
10pub struct VariantParser {
11 variants: Vec<String>,
13 breakpoints: Vec<Breakpoint>,
15}
16
17impl VariantParser {
18 pub fn new() -> Self {
20 Self {
21 variants: vec![
22 "dark".to_string(),
23 "hover".to_string(),
24 "focus".to_string(),
25 "active".to_string(),
26 "visited".to_string(),
27 "disabled".to_string(),
28 "group-hover".to_string(),
29 "group-focus".to_string(),
30 "group-active".to_string(),
31 "group-disabled".to_string(),
32 "peer-hover".to_string(),
33 "peer-focus".to_string(),
34 "peer-active".to_string(),
35 "peer-disabled".to_string(),
36 "first".to_string(),
37 "last".to_string(),
38 "odd".to_string(),
39 "even".to_string(),
40 "sm".to_string(),
41 "md".to_string(),
42 "lg".to_string(),
43 "xl".to_string(),
44 "2xl".to_string(),
45 ],
46 breakpoints: vec![
47 Breakpoint::Sm,
48 Breakpoint::Md,
49 Breakpoint::Lg,
50 Breakpoint::Xl,
51 Breakpoint::Xl2,
52 ],
53 }
54 }
55
56 pub fn parse_variants(&self, class: &str) -> (Vec<String>, String) {
58 let mut variants = Vec::new();
59 let mut remaining = class.to_string();
60
61 let compound_patterns = [
64 ("dark:hover:", vec!["dark", "hover"]),
65 ("dark:group-hover:", vec!["dark", "group-hover"]),
66 ("dark:focus:", vec!["dark", "focus"]),
67 ("dark:active:", vec!["dark", "active"]),
68 ];
69
70 for (prefix, variant_list) in compound_patterns {
71 if remaining.starts_with(prefix) {
72 variants.extend(variant_list.iter().map(|v| v.to_string()));
73 remaining = remaining.strip_prefix(prefix).unwrap_or(&remaining).to_string();
74 break;
75 }
76 }
77
78 if variants.is_empty() {
80 let variant_patterns = [
81 ("dark:", "dark"),
82 ("hover:", "hover"),
83 ("focus:", "focus"),
84 ("active:", "active"),
85 ("visited:", "visited"),
86 ("disabled:", "disabled"),
87 ("group-hover:", "group-hover"),
88 ("group-focus:", "group-focus"),
89 ("group-active:", "group-active"),
90 ("group-disabled:", "group-disabled"),
91 ("peer-hover:", "peer-hover"),
92 ("peer-focus:", "peer-focus"),
93 ("peer-active:", "peer-active"),
94 ("peer-disabled:", "peer-disabled"),
95 ("first:", "first"),
96 ("last:", "last"),
97 ("odd:", "odd"),
98 ("even:", "even"),
99 ("pointer-coarse:", "pointer-coarse"),
101 ("pointer-fine:", "pointer-fine"),
102 ("motion-reduce:", "motion-reduce"),
103 ("motion-safe:", "motion-safe"),
104 ("light:", "light"),
105 ("sm:", "sm"),
107 ("md:", "md"),
108 ("lg:", "lg"),
109 ("xl:", "xl"),
110 ("2xl:", "2xl"),
111 ];
112
113 for (prefix, variant) in variant_patterns {
114 if remaining.starts_with(prefix) {
115 variants.push(variant.to_string());
116 remaining = remaining.strip_prefix(prefix).unwrap_or(&remaining).to_string();
117 break; }
119 }
120 }
121
122 (variants, remaining)
123 }
124
125 pub fn get_variant_selector(&self, variant: &str) -> String {
127 match variant {
128 "dark" => ".dark ".to_string(),
129 "hover" => ":hover".to_string(),
130 "focus" => ":focus".to_string(),
131 "active" => ":active".to_string(),
132 "visited" => ":visited".to_string(),
133 "disabled" => ":disabled".to_string(),
134 "group-hover" => ".group:hover ".to_string(),
135 "group-focus" => ".group:focus ".to_string(),
136 "group-active" => ".group:active ".to_string(),
137 "group-disabled" => ".group:disabled ".to_string(),
138 "peer-hover" => ".peer:hover ".to_string(),
139 "peer-focus" => ".peer:focus ".to_string(),
140 "peer-active" => ".peer:active ".to_string(),
141 "peer-disabled" => ".peer:disabled ".to_string(),
142 "first" => ":first-child".to_string(),
143 "last" => ":last-child".to_string(),
144 "odd" => ":nth-child(odd)".to_string(),
145 "even" => ":nth-child(even)".to_string(),
146 "pointer-coarse" | "pointer-fine" | "motion-reduce" | "motion-safe" | "light" => String::new(),
148 _ => String::new(),
149 }
150 }
151
152 pub fn get_device_media_query(&self, variant: &str) -> Option<String> {
154 match variant {
155 "pointer-coarse" => Some("(pointer: coarse)".to_string()),
156 "pointer-fine" => Some("(pointer: fine)".to_string()),
157 "motion-reduce" => Some("(prefers-reduced-motion: reduce)".to_string()),
158 "motion-safe" => Some("(prefers-reduced-motion: no-preference)".to_string()),
159 "light" => Some("(prefers-color-scheme: light)".to_string()),
160 _ => None,
161 }
162 }
163
164 pub fn get_responsive_media_query(&self, variant: &str) -> Option<String> {
166 match variant {
167 "sm" => Some("(min-width: 640px)".to_string()),
168 "md" => Some("(min-width: 768px)".to_string()),
169 "lg" => Some("(min-width: 1024px)".to_string()),
170 "xl" => Some("(min-width: 1280px)".to_string()),
171 "2xl" => Some("(min-width: 1536px)".to_string()),
172 _ => None,
173 }
174 }
175
176 pub fn is_supported_variant(&self, variant: &str) -> bool {
178 self.variants.contains(&variant.to_string())
179 }
180
181 pub fn get_supported_variants(&self) -> &[String] {
183 &self.variants
184 }
185
186 pub fn get_supported_breakpoints(&self) -> &[Breakpoint] {
188 &self.breakpoints
189 }
190}
191
192impl Default for VariantParser {
193 fn default() -> Self {
194 Self::new()
195 }
196}