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.classes.contains("custom-spacing"));
333 assert!(result.classes.contains("container-small"));
334 assert!(result.classes.contains("container-medium"));
335 assert!(result.classes.contains("container-large"));
336 }
337
338 #[test]
339 fn test_modern_css_features_css_values() {
340 assert_eq!(CascadeLayer::Base.to_css_value(), "base");
341 assert_eq!(CascadeLayer::Components.to_css_value(), "components");
342 assert_eq!(CascadeLayer::Utilities.to_css_value(), "utilities");
343 assert_eq!(CustomProperty::Color("red".to_string()).to_css_value(), "--color: red");
344 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_css_value(), "--spacing: 1rem");
345 assert_eq!(ModernContainerQuery::Small.to_css_value(), "small");
346 assert_eq!(ModernContainerQuery::Medium.to_css_value(), "medium");
347 assert_eq!(ModernContainerQuery::Large.to_css_value(), "large");
348 }
349
350 #[test]
351 fn test_modern_css_features_serialization() {
352 let layer = CascadeLayer::Base;
353 let serialized = serde_json::to_string(&layer).unwrap();
354 let deserialized: CascadeLayer = serde_json::from_str(&serialized).unwrap();
355 assert_eq!(layer, deserialized);
356
357 let property = CustomProperty::Color("red".to_string());
358 let serialized = serde_json::to_string(&property).unwrap();
359 let deserialized: CustomProperty = serde_json::from_str(&serialized).unwrap();
360 assert_eq!(property, deserialized);
361
362 let query = ModernContainerQuery::Small;
363 let serialized = serde_json::to_string(&query).unwrap();
364 let deserialized: ModernContainerQuery = serde_json::from_str(&serialized).unwrap();
365 assert_eq!(query, deserialized);
366 }
367
368 #[test]
369 fn test_modern_css_features_comprehensive_usage() {
370 let classes = ClassBuilder::new()
371 .layer_custom("theme")
372 .custom_property("primary-color", "#3b82f6")
373 .custom_property("secondary-color", "#64748b")
374 .container_custom("sidebar")
375 .container_custom("main");
376
377 let result = classes.build();
378 assert!(result.classes.contains("layer-theme"));
379 assert!(result.custom.contains_key("--primary-color"));
380 assert_eq!(result.custom.get("--primary-color"), Some(&"blue".to_string()));
381 assert!(result.classes.contains("custom-secondary-color"));
382 assert!(result.classes.contains("container-sidebar"));
383 assert!(result.classes.contains("container-main"));
384 }
385
386 #[test]
387 fn test_modern_css_features_custom_values() {
388 let classes = ClassBuilder::new()
389 .layer_custom_value(CascadeLayer::Custom("theme".to_string()))
390 .custom_property_value(CustomProperty::Color("blue".to_string()))
391 .custom_property_value(CustomProperty::Spacing("2rem".to_string()))
392 .container_custom_value(ModernContainerQuery::Custom("sidebar".to_string()));
393
394 let result = classes.build();
395 assert!(result.classes.contains("layer-theme"));
396 assert!(result.custom.contains_key("--color"));
397 assert_eq!(result.custom.get("--color"), Some(&"red".to_string()));
398 assert!(result.classes.contains("custom-spacing"));
399 assert!(result.classes.contains("container-sidebar"));
400 }
401
402 #[test]
403 fn test_modern_css_features_all_variants() {
404 let classes = ClassBuilder::new()
405 .layer_base()
406 .layer_components()
407 .layer_utilities()
408 .layer_custom("theme")
409 .custom_property("color", "red")
410 .custom_property("spacing", "1rem")
411 .custom_property("font-size", "16px")
412 .custom_property("font-weight", "bold")
413 .custom_property("line-height", "1.5")
414 .custom_property("border-radius", "8px")
415 .custom_property("box-shadow", "0 4px 6px -1px rgb(0 0 0 / 0.1)")
416 .custom_property("z-index", "10")
417 .custom_property("opacity", "0.8")
418 .custom_property("transform", "rotate(45deg)")
419 .custom_property("animation", "fadeIn 0.5s ease-in-out")
420 .custom_property("transition", "all 0.3s ease")
421 .container_small()
422 .container_medium()
423 .container_large()
424 .container_extra_large()
425 .container_custom("sidebar");
426
427 let result = classes.build();
428 assert!(result.classes.contains("layer-base"));
429 assert!(result.classes.contains("layer-components"));
430 assert!(result.classes.contains("layer-utilities"));
431 assert!(result.classes.contains("layer-theme"));
432 assert!(result.custom.contains_key("--color"));
434 assert_eq!(result.custom.get("--color"), Some(&"red".to_string()));
435 assert!(result.custom.contains_key("--spacing"));
436 assert_eq!(result.custom.get("--spacing"), Some(&"1rem".to_string()));
437 assert!(result.custom.contains_key("--font-size"));
438 assert_eq!(result.custom.get("--font-size"), Some(&"16px".to_string()));
439 assert!(result.custom.contains_key("--font-weight"));
440 assert_eq!(result.custom.get("--font-weight"), Some(&"bold".to_string()));
441 assert!(result.custom.contains_key("--line-height"));
442 assert_eq!(result.custom.get("--line-height"), Some(&"1.5".to_string()));
443 assert!(result.custom.contains_key("--border-radius"));
444 assert_eq!(result.custom.get("--border-radius"), Some(&"8px".to_string()));
445 assert!(result.custom.contains_key("--box-shadow"));
446 assert_eq!(result.custom.get("--box-shadow"), Some(&"0 4px 6px -1px rgb(0 0 0 / 0.1)".to_string()));
447 assert!(result.custom.contains_key("--z-index"));
448 assert_eq!(result.custom.get("--z-index"), Some(&"10".to_string()));
449 assert!(result.custom.contains_key("--opacity"));
450 assert_eq!(result.custom.get("--opacity"), Some(&"0.8".to_string()));
451 assert!(result.custom.contains_key("--transform"));
452 assert_eq!(result.custom.get("--transform"), Some(&"rotate(45deg)".to_string()));
453 assert!(result.custom.contains_key("--animation"));
454 assert_eq!(result.custom.get("--animation"), Some(&"fadeIn 0.5s ease-in-out".to_string()));
455 assert!(result.custom.contains_key("--transition"));
456 assert_eq!(result.custom.get("--transition"), Some(&"all 0.3s ease".to_string()));
457 assert!(result.classes.contains("container-small"));
458 assert!(result.classes.contains("container-medium"));
459 assert!(result.classes.contains("container-large"));
460 assert!(result.classes.contains("container-extra-large"));
461 assert!(result.classes.contains("container-sidebar"));
462 }
463}