tailwind_rs_core/classes/
class_set.rs1use crate::responsive::Breakpoint;
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct ClassSet {
11 pub classes: HashSet<String>,
13 pub responsive: HashMap<Breakpoint, HashSet<String>>,
15 pub conditional: HashMap<String, HashSet<String>>,
17 pub custom: HashMap<String, String>,
19}
20
21impl ClassSet {
22 pub fn new() -> Self {
24 Self {
25 classes: HashSet::new(),
26 responsive: HashMap::new(),
27 conditional: HashMap::new(),
28 custom: HashMap::new(),
29 }
30 }
31
32 pub fn add_class(&mut self, class: impl Into<String>) {
34 self.classes.insert(class.into());
35 }
36
37 pub fn add_classes(&mut self, classes: impl IntoIterator<Item = String>) {
39 for class in classes {
40 self.classes.insert(class);
41 }
42 }
43
44 pub fn add_responsive_class(&mut self, breakpoint: Breakpoint, class: impl Into<String>) {
46 self.responsive
47 .entry(breakpoint)
48 .or_default()
49 .insert(class.into());
50 }
51
52 pub fn add_conditional_class(
54 &mut self,
55 condition: impl Into<String>,
56 class: impl Into<String>,
57 ) {
58 self.conditional
59 .entry(condition.into())
60 .or_default()
61 .insert(class.into());
62 }
63
64 pub fn add_custom(&mut self, property: impl Into<String>, value: impl Into<String>) {
66 self.custom.insert(property.into(), value.into());
67 }
68
69 pub fn remove_class(&mut self, class: &str) {
71 self.classes.remove(class);
72 }
73
74 pub fn has_class(&self, class: &str) -> bool {
76 self.classes.contains(class)
77 }
78
79 pub fn get_classes(&self) -> Vec<String> {
81 self.classes.iter().cloned().collect()
82 }
83
84 pub fn get_responsive_classes(&self, breakpoint: Breakpoint) -> Vec<String> {
86 self.responsive
87 .get(&breakpoint)
88 .map(|classes| classes.iter().cloned().collect())
89 .unwrap_or_default()
90 }
91
92 pub fn get_all_responsive_classes(&self) -> HashMap<Breakpoint, Vec<String>> {
94 self.responsive
95 .iter()
96 .map(|(breakpoint, classes)| (*breakpoint, classes.iter().cloned().collect()))
97 .collect()
98 }
99
100 pub fn get_conditional_classes(&self, condition: &str) -> Vec<String> {
102 self.conditional
103 .get(condition)
104 .map(|classes| classes.iter().cloned().collect())
105 .unwrap_or_default()
106 }
107
108 pub fn get_all_conditional_classes(&self) -> HashMap<String, Vec<String>> {
110 self.conditional
111 .iter()
112 .map(|(condition, classes)| (condition.clone(), classes.iter().cloned().collect()))
113 .collect()
114 }
115
116 pub fn get_custom_properties(&self) -> HashMap<String, String> {
118 self.custom.clone()
119 }
120
121 pub fn to_css_classes(&self) -> String {
123 let mut result = Vec::new();
124
125 let mut base_classes: Vec<String> = self.classes.iter().cloned().collect();
127 base_classes.sort();
128 result.extend(base_classes);
129
130 let mut responsive_classes: Vec<(Breakpoint, String)> = self
132 .responsive
133 .iter()
134 .flat_map(|(breakpoint, classes)| {
135 classes
136 .iter()
137 .map(|class| (*breakpoint, format!("{}{}", breakpoint.prefix(), class)))
138 })
139 .collect();
140 responsive_classes.sort_by(|a, b| a.0.min_width().cmp(&b.0.min_width()));
141 result.extend(responsive_classes.into_iter().map(|(_, class)| class));
142
143 let mut custom_variant_classes: Vec<String> = self
145 .conditional
146 .iter()
147 .flat_map(|(variant, classes)| {
148 let variant = variant.clone();
149 classes
150 .iter()
151 .map(move |class| format!("{}:{}", variant, class))
152 })
153 .collect();
154 custom_variant_classes.sort();
155 result.extend(custom_variant_classes);
156
157 result.join(" ")
158 }
159
160 pub fn to_css_custom_properties(&self) -> String {
162 if self.custom.is_empty() {
163 return String::new();
164 }
165
166 let properties: Vec<String> = self
167 .custom
168 .iter()
169 .map(|(property, value)| format!("--{}: {}", property, value))
170 .collect();
171
172 format!("style=\"{}\"", properties.join("; "))
173 }
174
175 pub fn merge(&mut self, other: ClassSet) {
177 self.classes.extend(other.classes);
178
179 for (breakpoint, classes) in other.responsive {
180 self.responsive
181 .entry(breakpoint)
182 .or_default()
183 .extend(classes);
184 }
185
186 for (condition, classes) in other.conditional {
187 self.conditional
188 .entry(condition)
189 .or_default()
190 .extend(classes);
191 }
192
193 self.custom.extend(other.custom);
194 }
195
196 pub fn is_empty(&self) -> bool {
198 self.classes.is_empty()
199 && self.responsive.is_empty()
200 && self.conditional.is_empty()
201 && self.custom.is_empty()
202 }
203
204 pub fn len(&self) -> usize {
206 self.classes.len()
207 + self.responsive.values().map(|classes| classes.len()).sum::<usize>()
208 + self.conditional.values().map(|classes| classes.len()).sum::<usize>()
209 }
210}
211
212impl Default for ClassSet {
213 fn default() -> Self {
214 Self::new()
215 }
216}