tailwind_rs_core/classes/
class_set.rs1use crate::responsive::Breakpoint;
28use std::collections::{HashMap, HashSet};
29
30#[derive(Debug, Clone, PartialEq)]
32pub struct ClassSet {
33 pub classes: HashSet<String>,
35 pub responsive: HashMap<Breakpoint, HashSet<String>>,
37 pub conditional: HashMap<String, HashSet<String>>,
39 pub custom: HashMap<String, String>,
41}
42
43impl ClassSet {
44 pub fn new() -> Self {
46 Self {
47 classes: HashSet::new(),
48 responsive: HashMap::new(),
49 conditional: HashMap::new(),
50 custom: HashMap::new(),
51 }
52 }
53
54 pub fn add_class(&mut self, class: impl Into<String>) {
56 self.classes.insert(class.into());
57 }
58
59 pub fn add_classes(&mut self, classes: impl IntoIterator<Item = String>) {
61 for class in classes {
62 self.classes.insert(class);
63 }
64 }
65
66 pub fn add_responsive_class(&mut self, breakpoint: Breakpoint, class: impl Into<String>) {
68 self.responsive
69 .entry(breakpoint)
70 .or_default()
71 .insert(class.into());
72 }
73
74 pub fn add_conditional_class(
76 &mut self,
77 condition: impl Into<String>,
78 class: impl Into<String>,
79 ) {
80 self.conditional
81 .entry(condition.into())
82 .or_default()
83 .insert(class.into());
84 }
85
86 pub fn add_custom(&mut self, property: impl Into<String>, value: impl Into<String>) {
88 self.custom.insert(property.into(), value.into());
89 }
90
91 pub fn remove_class(&mut self, class: &str) {
93 self.classes.remove(class);
94 }
95
96 pub fn has_class(&self, class: &str) -> bool {
98 self.classes.contains(class)
99 }
100
101 pub fn get_classes(&self) -> Vec<String> {
103 self.classes.iter().cloned().collect()
104 }
105
106 pub fn get_responsive_classes(&self, breakpoint: Breakpoint) -> Vec<String> {
108 self.responsive
109 .get(&breakpoint)
110 .map(|classes| classes.iter().cloned().collect())
111 .unwrap_or_default()
112 }
113
114 pub fn get_all_responsive_classes(&self) -> HashMap<Breakpoint, Vec<String>> {
116 self.responsive
117 .iter()
118 .map(|(breakpoint, classes)| (*breakpoint, classes.iter().cloned().collect()))
119 .collect()
120 }
121
122 pub fn get_conditional_classes(&self, condition: &str) -> Vec<String> {
124 self.conditional
125 .get(condition)
126 .map(|classes| classes.iter().cloned().collect())
127 .unwrap_or_default()
128 }
129
130 pub fn get_all_conditional_classes(&self) -> HashMap<String, Vec<String>> {
132 self.conditional
133 .iter()
134 .map(|(condition, classes)| (condition.clone(), classes.iter().cloned().collect()))
135 .collect()
136 }
137
138 pub fn get_custom_properties(&self) -> HashMap<String, String> {
140 self.custom.clone()
141 }
142
143 pub fn to_css_classes(&self) -> String {
145 let mut result = Vec::new();
146
147 let mut base_classes: Vec<String> = self.classes.iter().cloned().collect();
149 base_classes.sort();
150 result.extend(base_classes);
151
152 let mut responsive_classes: Vec<(Breakpoint, String)> = self
154 .responsive
155 .iter()
156 .flat_map(|(breakpoint, classes)| {
157 classes
158 .iter()
159 .map(|class| (*breakpoint, format!("{}{}", breakpoint.prefix(), class)))
160 })
161 .collect();
162 responsive_classes.sort_by(|a, b| a.0.min_width().cmp(&b.0.min_width()));
163 result.extend(responsive_classes.into_iter().map(|(_, class)| class));
164
165 let mut custom_variant_classes: Vec<String> = self
167 .conditional
168 .iter()
169 .flat_map(|(variant, classes)| {
170 let variant = variant.clone();
171 classes
172 .iter()
173 .map(move |class| format!("{}:{}", variant, class))
174 })
175 .collect();
176 custom_variant_classes.sort();
177 result.extend(custom_variant_classes);
178
179 result.join(" ")
180 }
181
182 pub fn to_css_custom_properties(&self) -> String {
184 if self.custom.is_empty() {
185 return String::new();
186 }
187
188 let properties: Vec<String> = self
189 .custom
190 .iter()
191 .map(|(property, value)| format!("--{}: {}", property, value))
192 .collect();
193
194 format!("style=\"{}\"", properties.join("; "))
195 }
196
197 pub fn merge(&mut self, other: ClassSet) {
199 self.classes.extend(other.classes);
200
201 for (breakpoint, classes) in other.responsive {
202 self.responsive
203 .entry(breakpoint)
204 .or_default()
205 .extend(classes);
206 }
207
208 for (condition, classes) in other.conditional {
209 self.conditional
210 .entry(condition)
211 .or_default()
212 .extend(classes);
213 }
214
215 self.custom.extend(other.custom);
216 }
217
218 pub fn is_empty(&self) -> bool {
220 self.classes.is_empty()
221 && self.responsive.is_empty()
222 && self.conditional.is_empty()
223 && self.custom.is_empty()
224 }
225
226 pub fn len(&self) -> usize {
228 self.classes.len()
229 + self.responsive.values().map(|classes| classes.len()).sum::<usize>()
230 + self.conditional.values().map(|classes| classes.len()).sum::<usize>()
231 }
232}
233
234impl Default for ClassSet {
235 fn default() -> Self {
236 Self::new()
237 }
238}