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!(
270 CascadeLayer::Custom("custom".to_string()).to_string(),
271 "custom"
272 );
273 }
274
275 #[test]
276 fn test_cascade_layer_class_names() {
277 assert_eq!(CascadeLayer::Base.to_class_name(), "layer-base");
278 assert_eq!(CascadeLayer::Components.to_class_name(), "layer-components");
279 assert_eq!(CascadeLayer::Utilities.to_class_name(), "layer-utilities");
280 assert_eq!(
281 CascadeLayer::Custom("custom".to_string()).to_class_name(),
282 "layer-custom"
283 );
284 }
285
286 #[test]
287 fn test_custom_property_enum_values() {
288 assert_eq!(
289 CustomProperty::Color("red".to_string()).to_string(),
290 "--color: red"
291 );
292 assert_eq!(
293 CustomProperty::Spacing("1rem".to_string()).to_string(),
294 "--spacing: 1rem"
295 );
296 assert_eq!(
297 CustomProperty::FontSize("16px".to_string()).to_string(),
298 "--font-size: 16px"
299 );
300 assert_eq!(
301 CustomProperty::Generic("custom".to_string(), "value".to_string()).to_string(),
302 "--custom: value"
303 );
304 }
305
306 #[test]
307 fn test_custom_property_class_names() {
308 assert_eq!(
309 CustomProperty::Color("red".to_string()).to_class_name(),
310 "custom-color"
311 );
312 assert_eq!(
313 CustomProperty::Spacing("1rem".to_string()).to_class_name(),
314 "custom-spacing"
315 );
316 assert_eq!(
317 CustomProperty::FontSize("16px".to_string()).to_class_name(),
318 "custom-font-size"
319 );
320 assert_eq!(
321 CustomProperty::Generic("custom".to_string(), "value".to_string()).to_class_name(),
322 "custom-custom"
323 );
324 }
325
326 #[test]
327 fn test_container_query_enum_values() {
328 assert_eq!(ModernContainerQuery::Small.to_string(), "small");
329 assert_eq!(ModernContainerQuery::Medium.to_string(), "medium");
330 assert_eq!(ModernContainerQuery::Large.to_string(), "large");
331 assert_eq!(ModernContainerQuery::ExtraLarge.to_string(), "extra-large");
332 assert_eq!(
333 ModernContainerQuery::Custom("custom".to_string()).to_string(),
334 "custom"
335 );
336 }
337
338 #[test]
339 fn test_container_query_class_names() {
340 assert_eq!(
341 ModernContainerQuery::Small.to_class_name(),
342 "container-small"
343 );
344 assert_eq!(
345 ModernContainerQuery::Medium.to_class_name(),
346 "container-medium"
347 );
348 assert_eq!(
349 ModernContainerQuery::Large.to_class_name(),
350 "container-large"
351 );
352 assert_eq!(
353 ModernContainerQuery::ExtraLarge.to_class_name(),
354 "container-extra-large"
355 );
356 assert_eq!(
357 ModernContainerQuery::Custom("custom".to_string()).to_class_name(),
358 "container-custom"
359 );
360 }
361
362 #[test]
363 fn test_modern_css_features_utilities() {
364 let classes = ClassBuilder::new()
365 .layer_base()
366 .layer_components()
367 .layer_utilities()
368 .custom_property("color", "red")
369 .custom_property("spacing", "1rem")
370 .container_small()
371 .container_medium()
372 .container_large();
373
374 let result = classes.build();
375 assert!(result.classes.contains("layer-base"));
376 assert!(result.classes.contains("layer-components"));
377 assert!(result.classes.contains("layer-utilities"));
378 assert!(result.custom.contains_key("color"));
379 assert_eq!(result.custom.get("color"), Some(&"red".to_string()));
380 assert!(result.custom.contains_key("spacing"));
381 assert_eq!(result.custom.get("spacing"), Some(&"1rem".to_string()));
382 assert!(result.classes.contains("container-small"));
383 assert!(result.classes.contains("container-medium"));
384 assert!(result.classes.contains("container-large"));
385 }
386
387 #[test]
388 fn test_modern_css_features_css_values() {
389 assert_eq!(CascadeLayer::Base.to_css_value(), "base");
390 assert_eq!(CascadeLayer::Components.to_css_value(), "components");
391 assert_eq!(CascadeLayer::Utilities.to_css_value(), "utilities");
392 assert_eq!(
393 CustomProperty::Color("red".to_string()).to_css_value(),
394 "--color: red"
395 );
396 assert_eq!(
397 CustomProperty::Spacing("1rem".to_string()).to_css_value(),
398 "--spacing: 1rem"
399 );
400 assert_eq!(ModernContainerQuery::Small.to_css_value(), "small");
401 assert_eq!(ModernContainerQuery::Medium.to_css_value(), "medium");
402 assert_eq!(ModernContainerQuery::Large.to_css_value(), "large");
403 }
404
405 #[test]
406 fn test_modern_css_features_serialization() {
407 let layer = CascadeLayer::Base;
408 let serialized = serde_json::to_string(&layer).unwrap();
409 let deserialized: CascadeLayer = serde_json::from_str(&serialized).unwrap();
410 assert_eq!(layer, deserialized);
411
412 let property = CustomProperty::Color("red".to_string());
413 let serialized = serde_json::to_string(&property).unwrap();
414 let deserialized: CustomProperty = serde_json::from_str(&serialized).unwrap();
415 assert_eq!(property, deserialized);
416
417 let query = ModernContainerQuery::Small;
418 let serialized = serde_json::to_string(&query).unwrap();
419 let deserialized: ModernContainerQuery = serde_json::from_str(&serialized).unwrap();
420 assert_eq!(query, deserialized);
421 }
422
423 #[test]
424 fn test_modern_css_features_comprehensive_usage() {
425 let classes = ClassBuilder::new()
426 .layer_custom("theme")
427 .custom_property("primary-color", "#3b82f6")
428 .custom_property("secondary-color", "#64748b")
429 .container_custom("sidebar")
430 .container_custom("main");
431
432 let result = classes.build();
433 assert!(result.classes.contains("layer-theme"));
434 assert!(result.custom.contains_key("primary-color"));
435 assert_eq!(
436 result.custom.get("primary-color"),
437 Some(&"#3b82f6".to_string())
438 );
439 assert!(result.custom.contains_key("secondary-color"));
440 assert_eq!(
441 result.custom.get("secondary-color"),
442 Some(&"#64748b".to_string())
443 );
444 assert!(result.classes.contains("container-sidebar"));
445 assert!(result.classes.contains("container-main"));
446 }
447
448 #[test]
449 fn test_modern_css_features_custom_values() {
450 let classes = ClassBuilder::new()
451 .layer_custom_value(CascadeLayer::Custom("theme".to_string()))
452 .custom_property_value(CustomProperty::Color("blue".to_string()))
453 .custom_property_value(CustomProperty::Spacing("2rem".to_string()))
454 .container_custom_value(ModernContainerQuery::Custom("sidebar".to_string()));
455
456 let result = classes.build();
457 assert!(result.classes.contains("layer-theme"));
458 assert!(result.classes.contains("custom-color"));
459 assert!(result.classes.contains("custom-spacing"));
460 assert!(result.classes.contains("container-sidebar"));
461 }
462
463 #[test]
464 fn test_modern_css_features_all_variants() {
465 let classes = ClassBuilder::new()
466 .layer_base()
467 .layer_components()
468 .layer_utilities()
469 .layer_custom("theme")
470 .custom_property("color", "red")
471 .custom_property("spacing", "1rem")
472 .custom_property("font-size", "16px")
473 .custom_property("font-weight", "bold")
474 .custom_property("line-height", "1.5")
475 .custom_property("border-radius", "8px")
476 .custom_property("box-shadow", "0 4px 6px -1px rgb(0 0 0 / 0.1)")
477 .custom_property("z-index", "10")
478 .custom_property("opacity", "0.8")
479 .custom_property("transform", "rotate(45deg)")
480 .custom_property("animation", "fadeIn 0.5s ease-in-out")
481 .custom_property("transition", "all 0.3s ease")
482 .container_small()
483 .container_medium()
484 .container_large()
485 .container_extra_large()
486 .container_custom("sidebar");
487
488 let result = classes.build();
489 assert!(result.classes.contains("layer-base"));
490 assert!(result.classes.contains("layer-components"));
491 assert!(result.classes.contains("layer-utilities"));
492 assert!(result.classes.contains("layer-theme"));
493 assert!(result.custom.contains_key("color"));
495 assert_eq!(result.custom.get("color"), Some(&"red".to_string()));
496 assert!(result.custom.contains_key("spacing"));
497 assert_eq!(result.custom.get("spacing"), Some(&"1rem".to_string()));
498 assert!(result.custom.contains_key("font-size"));
499 assert_eq!(result.custom.get("font-size"), Some(&"16px".to_string()));
500 assert!(result.custom.contains_key("font-weight"));
501 assert_eq!(result.custom.get("font-weight"), Some(&"bold".to_string()));
502 assert!(result.custom.contains_key("line-height"));
503 assert_eq!(result.custom.get("line-height"), Some(&"1.5".to_string()));
504 assert!(result.custom.contains_key("border-radius"));
505 assert_eq!(result.custom.get("border-radius"), Some(&"8px".to_string()));
506 assert!(result.custom.contains_key("box-shadow"));
507 assert_eq!(
508 result.custom.get("box-shadow"),
509 Some(&"0 4px 6px -1px rgb(0 0 0 / 0.1)".to_string())
510 );
511 assert!(result.custom.contains_key("z-index"));
512 assert_eq!(result.custom.get("z-index"), Some(&"10".to_string()));
513 assert!(result.custom.contains_key("opacity"));
514 assert_eq!(result.custom.get("opacity"), Some(&"0.8".to_string()));
515 assert!(result.custom.contains_key("transform"));
516 assert_eq!(
517 result.custom.get("transform"),
518 Some(&"rotate(45deg)".to_string())
519 );
520 assert!(result.custom.contains_key("animation"));
521 assert_eq!(
522 result.custom.get("animation"),
523 Some(&"fadeIn 0.5s ease-in-out".to_string())
524 );
525 assert!(result.custom.contains_key("transition"));
526 assert_eq!(
527 result.custom.get("transition"),
528 Some(&"all 0.3s ease".to_string())
529 );
530 assert!(result.classes.contains("container-small"));
531 assert!(result.classes.contains("container-medium"));
532 assert!(result.classes.contains("container-large"));
533 assert!(result.classes.contains("container-extra-large"));
534 assert!(result.classes.contains("container-sidebar"));
535 }
536}