1use crate::classes::ClassBuilder;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum CascadeLayer {
13 Base,
15 Components,
17 Utilities,
19 Custom(String),
21}
22
23impl fmt::Display for CascadeLayer {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 match self {
26 CascadeLayer::Base => write!(f, "base"),
27 CascadeLayer::Components => write!(f, "components"),
28 CascadeLayer::Utilities => write!(f, "utilities"),
29 CascadeLayer::Custom(name) => write!(f, "{}", name),
30 }
31 }
32}
33
34impl CascadeLayer {
35 pub fn to_class_name(&self) -> String {
37 match self {
38 CascadeLayer::Base => "layer-base".to_string(),
39 CascadeLayer::Components => "layer-components".to_string(),
40 CascadeLayer::Utilities => "layer-utilities".to_string(),
41 CascadeLayer::Custom(name) => format!("layer-{}", name),
42 }
43 }
44
45 pub fn to_css_value(&self) -> String {
47 self.to_string()
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub enum CustomProperty {
54 Color(String),
56 Spacing(String),
58 FontSize(String),
60 FontWeight(String),
62 LineHeight(String),
64 BorderRadius(String),
66 BoxShadow(String),
68 ZIndex(String),
70 Opacity(String),
72 Transform(String),
74 Animation(String),
76 Transition(String),
78 Generic(String, String),
80}
81
82impl fmt::Display for CustomProperty {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 match self {
85 CustomProperty::Color(value) => write!(f, "--color: {}", value),
86 CustomProperty::Spacing(value) => write!(f, "--spacing: {}", value),
87 CustomProperty::FontSize(value) => write!(f, "--font-size: {}", value),
88 CustomProperty::FontWeight(value) => write!(f, "--font-weight: {}", value),
89 CustomProperty::LineHeight(value) => write!(f, "--line-height: {}", value),
90 CustomProperty::BorderRadius(value) => write!(f, "--border-radius: {}", value),
91 CustomProperty::BoxShadow(value) => write!(f, "--box-shadow: {}", value),
92 CustomProperty::ZIndex(value) => write!(f, "--z-index: {}", value),
93 CustomProperty::Opacity(value) => write!(f, "--opacity: {}", value),
94 CustomProperty::Transform(value) => write!(f, "--transform: {}", value),
95 CustomProperty::Animation(value) => write!(f, "--animation: {}", value),
96 CustomProperty::Transition(value) => write!(f, "--transition: {}", value),
97 CustomProperty::Generic(name, value) => write!(f, "--{}: {}", name, value),
98 }
99 }
100}
101
102impl CustomProperty {
103 pub fn to_class_name(&self) -> String {
105 match self {
106 CustomProperty::Color(_) => "custom-color".to_string(),
107 CustomProperty::Spacing(_) => "custom-spacing".to_string(),
108 CustomProperty::FontSize(_) => "custom-font-size".to_string(),
109 CustomProperty::FontWeight(_) => "custom-font-weight".to_string(),
110 CustomProperty::LineHeight(_) => "custom-line-height".to_string(),
111 CustomProperty::BorderRadius(_) => "custom-border-radius".to_string(),
112 CustomProperty::BoxShadow(_) => "custom-box-shadow".to_string(),
113 CustomProperty::ZIndex(_) => "custom-z-index".to_string(),
114 CustomProperty::Opacity(_) => "custom-opacity".to_string(),
115 CustomProperty::Transform(_) => "custom-transform".to_string(),
116 CustomProperty::Animation(_) => "custom-animation".to_string(),
117 CustomProperty::Transition(_) => "custom-transition".to_string(),
118 CustomProperty::Generic(name, _) => format!("custom-{}", name),
119 }
120 }
121
122 pub fn to_css_value(&self) -> String {
124 self.to_string()
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
130pub enum ModernContainerQuery {
131 Small,
133 Medium,
135 Large,
137 ExtraLarge,
139 Custom(String),
141}
142
143impl fmt::Display for ModernContainerQuery {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 match self {
146 ModernContainerQuery::Small => write!(f, "small"),
147 ModernContainerQuery::Medium => write!(f, "medium"),
148 ModernContainerQuery::Large => write!(f, "large"),
149 ModernContainerQuery::ExtraLarge => write!(f, "extra-large"),
150 ModernContainerQuery::Custom(size) => write!(f, "{}", size),
151 }
152 }
153}
154
155impl ModernContainerQuery {
156 pub fn to_class_name(&self) -> String {
158 match self {
159 ModernContainerQuery::Small => "container-small".to_string(),
160 ModernContainerQuery::Medium => "container-medium".to_string(),
161 ModernContainerQuery::Large => "container-large".to_string(),
162 ModernContainerQuery::ExtraLarge => "container-extra-large".to_string(),
163 ModernContainerQuery::Custom(size) => format!("container-{}", size),
164 }
165 }
166
167 pub fn to_css_value(&self) -> String {
169 self.to_string()
170 }
171}
172
173pub trait ModernCssFeaturesUtilities {
175 fn layer_base(self) -> Self;
177 fn layer_components(self) -> Self;
179 fn layer_utilities(self) -> Self;
181 fn layer_custom(self, name: &str) -> Self;
183 fn layer_custom_value(self, layer: CascadeLayer) -> Self;
185 fn custom_property(self, name: &str, _value: &str) -> Self;
187 fn custom_property_value(self, property: CustomProperty) -> Self;
189 fn container_small(self) -> Self;
191 fn container_medium(self) -> Self;
193 fn container_large(self) -> Self;
195 fn container_extra_large(self) -> Self;
197 fn container_custom(self, size: &str) -> Self;
199 fn container_custom_value(self, query: ModernContainerQuery) -> Self;
201}
202
203impl ModernCssFeaturesUtilities for ClassBuilder {
204 fn layer_base(self) -> Self {
205 self.class("layer-base")
206 }
207
208 fn layer_components(self) -> Self {
209 self.class("layer-components")
210 }
211
212 fn layer_utilities(self) -> Self {
213 self.class("layer-utilities")
214 }
215
216 fn layer_custom(self, name: &str) -> Self {
217 let class_name = format!("layer-{}", name);
218 self.class(class_name)
219 }
220
221 fn layer_custom_value(self, layer: CascadeLayer) -> Self {
222 self.class(&layer.to_class_name())
223 }
224
225 fn custom_property(self, name: &str, value: &str) -> Self {
226 self.custom(name, value)
227 }
228
229 fn custom_property_value(self, property: CustomProperty) -> Self {
230 self.class(&property.to_class_name())
231 }
232
233 fn container_small(self) -> Self {
234 self.class("container-small")
235 }
236
237 fn container_medium(self) -> Self {
238 self.class("container-medium")
239 }
240
241 fn container_large(self) -> Self {
242 self.class("container-large")
243 }
244
245 fn container_extra_large(self) -> Self {
246 self.class("container-extra-large")
247 }
248
249 fn container_custom(self, size: &str) -> Self {
250 let class_name = format!("container-{}", size);
251 self.class(class_name)
252 }
253
254 fn container_custom_value(self, query: ModernContainerQuery) -> Self {
255 self.class(&query.to_class_name())
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::classes::ClassBuilder;
263
264 #[test]
265 fn test_cascade_layer_enum_values() {
266 assert_eq!(CascadeLayer::Base.to_string(), "base");
267 assert_eq!(CascadeLayer::Components.to_string(), "components");
268 assert_eq!(CascadeLayer::Utilities.to_string(), "utilities");
269 assert_eq!(CascadeLayer::Custom("custom".to_string()).to_string(), "custom");
270 }
271
272 #[test]
273 fn test_cascade_layer_class_names() {
274 assert_eq!(CascadeLayer::Base.to_class_name(), "layer-base");
275 assert_eq!(CascadeLayer::Components.to_class_name(), "layer-components");
276 assert_eq!(CascadeLayer::Utilities.to_class_name(), "layer-utilities");
277 assert_eq!(CascadeLayer::Custom("custom".to_string()).to_class_name(), "layer-custom");
278 }
279
280 #[test]
281 fn test_custom_property_enum_values() {
282 assert_eq!(CustomProperty::Color("red".to_string()).to_string(), "--color: red");
283 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_string(), "--spacing: 1rem");
284 assert_eq!(CustomProperty::FontSize("16px".to_string()).to_string(), "--font-size: 16px");
285 assert_eq!(CustomProperty::Generic("custom".to_string(), "value".to_string()).to_string(), "--custom: value");
286 }
287
288 #[test]
289 fn test_custom_property_class_names() {
290 assert_eq!(CustomProperty::Color("red".to_string()).to_class_name(), "custom-color");
291 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_class_name(), "custom-spacing");
292 assert_eq!(CustomProperty::FontSize("16px".to_string()).to_class_name(), "custom-font-size");
293 assert_eq!(CustomProperty::Generic("custom".to_string(), "value".to_string()).to_class_name(), "custom-custom");
294 }
295
296 #[test]
297 fn test_container_query_enum_values() {
298 assert_eq!(ModernContainerQuery::Small.to_string(), "small");
299 assert_eq!(ModernContainerQuery::Medium.to_string(), "medium");
300 assert_eq!(ModernContainerQuery::Large.to_string(), "large");
301 assert_eq!(ModernContainerQuery::ExtraLarge.to_string(), "extra-large");
302 assert_eq!(ModernContainerQuery::Custom("custom".to_string()).to_string(), "custom");
303 }
304
305 #[test]
306 fn test_container_query_class_names() {
307 assert_eq!(ModernContainerQuery::Small.to_class_name(), "container-small");
308 assert_eq!(ModernContainerQuery::Medium.to_class_name(), "container-medium");
309 assert_eq!(ModernContainerQuery::Large.to_class_name(), "container-large");
310 assert_eq!(ModernContainerQuery::ExtraLarge.to_class_name(), "container-extra-large");
311 assert_eq!(ModernContainerQuery::Custom("custom".to_string()).to_class_name(), "container-custom");
312 }
313
314 #[test]
315 fn test_modern_css_features_utilities() {
316 let classes = ClassBuilder::new()
317 .layer_base()
318 .layer_components()
319 .layer_utilities()
320 .custom_property("color", "red")
321 .custom_property("spacing", "1rem")
322 .container_small()
323 .container_medium()
324 .container_large();
325
326 let result = classes.build();
327 assert!(result.classes.contains("layer-base"));
328 assert!(result.classes.contains("layer-components"));
329 assert!(result.classes.contains("layer-utilities"));
330 assert!(result.custom.contains_key("color"));
331 assert_eq!(result.custom.get("color"), Some(&"red".to_string()));
332 assert!(result.custom.contains_key("spacing"));
333 assert_eq!(result.custom.get("spacing"), Some(&"1rem".to_string()));
334 assert!(result.classes.contains("container-small"));
335 assert!(result.classes.contains("container-medium"));
336 assert!(result.classes.contains("container-large"));
337 }
338
339 #[test]
340 fn test_modern_css_features_css_values() {
341 assert_eq!(CascadeLayer::Base.to_css_value(), "base");
342 assert_eq!(CascadeLayer::Components.to_css_value(), "components");
343 assert_eq!(CascadeLayer::Utilities.to_css_value(), "utilities");
344 assert_eq!(CustomProperty::Color("red".to_string()).to_css_value(), "--color: red");
345 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_css_value(), "--spacing: 1rem");
346 assert_eq!(ModernContainerQuery::Small.to_css_value(), "small");
347 assert_eq!(ModernContainerQuery::Medium.to_css_value(), "medium");
348 assert_eq!(ModernContainerQuery::Large.to_css_value(), "large");
349 }
350
351 #[test]
352 fn test_modern_css_features_serialization() {
353 let layer = CascadeLayer::Base;
354 let serialized = serde_json::to_string(&layer).unwrap();
355 let deserialized: CascadeLayer = serde_json::from_str(&serialized).unwrap();
356 assert_eq!(layer, deserialized);
357
358 let property = CustomProperty::Color("red".to_string());
359 let serialized = serde_json::to_string(&property).unwrap();
360 let deserialized: CustomProperty = serde_json::from_str(&serialized).unwrap();
361 assert_eq!(property, deserialized);
362
363 let query = ModernContainerQuery::Small;
364 let serialized = serde_json::to_string(&query).unwrap();
365 let deserialized: ModernContainerQuery = serde_json::from_str(&serialized).unwrap();
366 assert_eq!(query, deserialized);
367 }
368
369 #[test]
370 fn test_modern_css_features_comprehensive_usage() {
371 let classes = ClassBuilder::new()
372 .layer_custom("theme")
373 .custom_property("primary-color", "#3b82f6")
374 .custom_property("secondary-color", "#64748b")
375 .container_custom("sidebar")
376 .container_custom("main");
377
378 let result = classes.build();
379 assert!(result.classes.contains("layer-theme"));
380 assert!(result.custom.contains_key("primary-color"));
381 assert_eq!(result.custom.get("primary-color"), Some(&"#3b82f6".to_string()));
382 assert!(result.custom.contains_key("secondary-color"));
383 assert_eq!(result.custom.get("secondary-color"), Some(&"#64748b".to_string()));
384 assert!(result.classes.contains("container-sidebar"));
385 assert!(result.classes.contains("container-main"));
386 }
387
388 #[test]
389 fn test_modern_css_features_custom_values() {
390 let classes = ClassBuilder::new()
391 .layer_custom_value(CascadeLayer::Custom("theme".to_string()))
392 .custom_property_value(CustomProperty::Color("blue".to_string()))
393 .custom_property_value(CustomProperty::Spacing("2rem".to_string()))
394 .container_custom_value(ModernContainerQuery::Custom("sidebar".to_string()));
395
396 let result = classes.build();
397 assert!(result.classes.contains("layer-theme"));
398 assert!(result.classes.contains("custom-color"));
399 assert!(result.classes.contains("custom-spacing"));
400 assert!(result.classes.contains("container-sidebar"));
401 }
402
403 #[test]
404 fn test_modern_css_features_all_variants() {
405 let classes = ClassBuilder::new()
406 .layer_base()
407 .layer_components()
408 .layer_utilities()
409 .layer_custom("theme")
410 .custom_property("color", "red")
411 .custom_property("spacing", "1rem")
412 .custom_property("font-size", "16px")
413 .custom_property("font-weight", "bold")
414 .custom_property("line-height", "1.5")
415 .custom_property("border-radius", "8px")
416 .custom_property("box-shadow", "0 4px 6px -1px rgb(0 0 0 / 0.1)")
417 .custom_property("z-index", "10")
418 .custom_property("opacity", "0.8")
419 .custom_property("transform", "rotate(45deg)")
420 .custom_property("animation", "fadeIn 0.5s ease-in-out")
421 .custom_property("transition", "all 0.3s ease")
422 .container_small()
423 .container_medium()
424 .container_large()
425 .container_extra_large()
426 .container_custom("sidebar");
427
428 let result = classes.build();
429 assert!(result.classes.contains("layer-base"));
430 assert!(result.classes.contains("layer-components"));
431 assert!(result.classes.contains("layer-utilities"));
432 assert!(result.classes.contains("layer-theme"));
433 assert!(result.custom.contains_key("color"));
435 assert_eq!(result.custom.get("color"), Some(&"red".to_string()));
436 assert!(result.custom.contains_key("spacing"));
437 assert_eq!(result.custom.get("spacing"), Some(&"1rem".to_string()));
438 assert!(result.custom.contains_key("font-size"));
439 assert_eq!(result.custom.get("font-size"), Some(&"16px".to_string()));
440 assert!(result.custom.contains_key("font-weight"));
441 assert_eq!(result.custom.get("font-weight"), Some(&"bold".to_string()));
442 assert!(result.custom.contains_key("line-height"));
443 assert_eq!(result.custom.get("line-height"), Some(&"1.5".to_string()));
444 assert!(result.custom.contains_key("border-radius"));
445 assert_eq!(result.custom.get("border-radius"), Some(&"8px".to_string()));
446 assert!(result.custom.contains_key("box-shadow"));
447 assert_eq!(result.custom.get("box-shadow"), Some(&"0 4px 6px -1px rgb(0 0 0 / 0.1)".to_string()));
448 assert!(result.custom.contains_key("z-index"));
449 assert_eq!(result.custom.get("z-index"), Some(&"10".to_string()));
450 assert!(result.custom.contains_key("opacity"));
451 assert_eq!(result.custom.get("opacity"), Some(&"0.8".to_string()));
452 assert!(result.custom.contains_key("transform"));
453 assert_eq!(result.custom.get("transform"), Some(&"rotate(45deg)".to_string()));
454 assert!(result.custom.contains_key("animation"));
455 assert_eq!(result.custom.get("animation"), Some(&"fadeIn 0.5s ease-in-out".to_string()));
456 assert!(result.custom.contains_key("transition"));
457 assert_eq!(result.custom.get("transition"), Some(&"all 0.3s ease".to_string()));
458 assert!(result.classes.contains("container-small"));
459 assert!(result.classes.contains("container-medium"));
460 assert!(result.classes.contains("container-large"));
461 assert!(result.classes.contains("container-extra-large"));
462 assert!(result.classes.contains("container-sidebar"));
463 }
464}