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 let class_name = format!("custom-{}", name);
227 self.class(class_name)
228 }
229
230 fn custom_property_value(self, property: CustomProperty) -> Self {
231 self.class(&property.to_class_name())
232 }
233
234 fn container_small(self) -> Self {
235 self.class("container-small")
236 }
237
238 fn container_medium(self) -> Self {
239 self.class("container-medium")
240 }
241
242 fn container_large(self) -> Self {
243 self.class("container-large")
244 }
245
246 fn container_extra_large(self) -> Self {
247 self.class("container-extra-large")
248 }
249
250 fn container_custom(self, size: &str) -> Self {
251 let class_name = format!("container-{}", size);
252 self.class(class_name)
253 }
254
255 fn container_custom_value(self, query: ModernContainerQuery) -> Self {
256 self.class(&query.to_class_name())
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use crate::classes::ClassBuilder;
264
265 #[test]
266 fn test_cascade_layer_enum_values() {
267 assert_eq!(CascadeLayer::Base.to_string(), "base");
268 assert_eq!(CascadeLayer::Components.to_string(), "components");
269 assert_eq!(CascadeLayer::Utilities.to_string(), "utilities");
270 assert_eq!(CascadeLayer::Custom("custom".to_string()).to_string(), "custom");
271 }
272
273 #[test]
274 fn test_cascade_layer_class_names() {
275 assert_eq!(CascadeLayer::Base.to_class_name(), "layer-base");
276 assert_eq!(CascadeLayer::Components.to_class_name(), "layer-components");
277 assert_eq!(CascadeLayer::Utilities.to_class_name(), "layer-utilities");
278 assert_eq!(CascadeLayer::Custom("custom".to_string()).to_class_name(), "layer-custom");
279 }
280
281 #[test]
282 fn test_custom_property_enum_values() {
283 assert_eq!(CustomProperty::Color("red".to_string()).to_string(), "--color: red");
284 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_string(), "--spacing: 1rem");
285 assert_eq!(CustomProperty::FontSize("16px".to_string()).to_string(), "--font-size: 16px");
286 assert_eq!(CustomProperty::Generic("custom".to_string(), "value".to_string()).to_string(), "--custom: value");
287 }
288
289 #[test]
290 fn test_custom_property_class_names() {
291 assert_eq!(CustomProperty::Color("red".to_string()).to_class_name(), "custom-color");
292 assert_eq!(CustomProperty::Spacing("1rem".to_string()).to_class_name(), "custom-spacing");
293 assert_eq!(CustomProperty::FontSize("16px".to_string()).to_class_name(), "custom-font-size");
294 assert_eq!(CustomProperty::Generic("custom".to_string(), "value".to_string()).to_class_name(), "custom-custom");
295 }
296
297 #[test]
298 fn test_container_query_enum_values() {
299 assert_eq!(ModernContainerQuery::Small.to_string(), "small");
300 assert_eq!(ModernContainerQuery::Medium.to_string(), "medium");
301 assert_eq!(ModernContainerQuery::Large.to_string(), "large");
302 assert_eq!(ModernContainerQuery::ExtraLarge.to_string(), "extra-large");
303 assert_eq!(ModernContainerQuery::Custom("custom".to_string()).to_string(), "custom");
304 }
305
306 #[test]
307 fn test_container_query_class_names() {
308 assert_eq!(ModernContainerQuery::Small.to_class_name(), "container-small");
309 assert_eq!(ModernContainerQuery::Medium.to_class_name(), "container-medium");
310 assert_eq!(ModernContainerQuery::Large.to_class_name(), "container-large");
311 assert_eq!(ModernContainerQuery::ExtraLarge.to_class_name(), "container-extra-large");
312 assert_eq!(ModernContainerQuery::Custom("custom".to_string()).to_class_name(), "container-custom");
313 }
314
315 #[test]
316 fn test_modern_css_features_utilities() {
317 let classes = ClassBuilder::new()
318 .layer_base()
319 .layer_components()
320 .layer_utilities()
321 .custom_property("color", "red")
322 .custom_property("spacing", "1rem")
323 .container_small()
324 .container_medium()
325 .container_large();
326
327 let result = classes.build();
328 assert!(result.contains("layer-base"));
329 assert!(result.contains("layer-components"));
330 assert!(result.contains("layer-utilities"));
331 assert!(result.contains("custom-color"));
332 assert!(result.contains("custom-spacing"));
333 assert!(result.contains("container-small"));
334 assert!(result.contains("container-medium"));
335 assert!(result.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.contains("layer-theme"));
379 assert!(result.contains("custom-primary-color"));
380 assert!(result.contains("custom-secondary-color"));
381 assert!(result.contains("container-sidebar"));
382 assert!(result.contains("container-main"));
383 }
384
385 #[test]
386 fn test_modern_css_features_custom_values() {
387 let classes = ClassBuilder::new()
388 .layer_custom_value(CascadeLayer::Custom("theme".to_string()))
389 .custom_property_value(CustomProperty::Color("blue".to_string()))
390 .custom_property_value(CustomProperty::Spacing("2rem".to_string()))
391 .container_custom_value(ModernContainerQuery::Custom("sidebar".to_string()));
392
393 let result = classes.build();
394 assert!(result.contains("layer-theme"));
395 assert!(result.contains("custom-color"));
396 assert!(result.contains("custom-spacing"));
397 assert!(result.contains("container-sidebar"));
398 }
399
400 #[test]
401 fn test_modern_css_features_all_variants() {
402 let classes = ClassBuilder::new()
403 .layer_base()
404 .layer_components()
405 .layer_utilities()
406 .layer_custom("theme")
407 .custom_property("color", "red")
408 .custom_property("spacing", "1rem")
409 .custom_property("font-size", "16px")
410 .custom_property("font-weight", "bold")
411 .custom_property("line-height", "1.5")
412 .custom_property("border-radius", "8px")
413 .custom_property("box-shadow", "0 4px 6px -1px rgb(0 0 0 / 0.1)")
414 .custom_property("z-index", "10")
415 .custom_property("opacity", "0.8")
416 .custom_property("transform", "rotate(45deg)")
417 .custom_property("animation", "fadeIn 0.5s ease-in-out")
418 .custom_property("transition", "all 0.3s ease")
419 .container_small()
420 .container_medium()
421 .container_large()
422 .container_extra_large()
423 .container_custom("sidebar");
424
425 let result = classes.build();
426 assert!(result.contains("layer-base"));
427 assert!(result.contains("layer-components"));
428 assert!(result.contains("layer-utilities"));
429 assert!(result.contains("layer-theme"));
430 assert!(result.contains("custom-color"));
431 assert!(result.contains("custom-spacing"));
432 assert!(result.contains("custom-font-size"));
433 assert!(result.contains("custom-font-weight"));
434 assert!(result.contains("custom-line-height"));
435 assert!(result.contains("custom-border-radius"));
436 assert!(result.contains("custom-box-shadow"));
437 assert!(result.contains("custom-z-index"));
438 assert!(result.contains("custom-opacity"));
439 assert!(result.contains("custom-transform"));
440 assert!(result.contains("custom-animation"));
441 assert!(result.contains("custom-transition"));
442 assert!(result.contains("container-small"));
443 assert!(result.contains("container-medium"));
444 assert!(result.contains("container-large"));
445 assert!(result.contains("container-extra-large"));
446 assert!(result.contains("container-sidebar"));
447 }
448}