1use crate::classes::ClassBuilder;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
25pub enum SizingValue {
26 Zero,
28 Px,
30 Fractional(f32),
32 Integer(u32),
34 Auto,
36 Full,
38 Screen,
40 Min,
42 Max,
44 Fit,
46 Fraction(Fraction),
48 GridFraction(GridFraction),
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
54pub enum Fraction {
55 Half,
57 Third,
59 TwoThirds,
61 Quarter,
63 TwoQuarters,
65 ThreeQuarters,
67 Fifth,
69 TwoFifths,
71 ThreeFifths,
73 FourFifths,
75 Sixth,
77 TwoSixths,
79 ThreeSixths,
81 FourSixths,
83 FiveSixths,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
89pub enum GridFraction {
90 OneTwelfth,
92 TwoTwelfths,
94 ThreeTwelfths,
96 FourTwelfths,
98 FiveTwelfths,
100 SixTwelfths,
102 SevenTwelfths,
104 EightTwelfths,
106 NineTwelfths,
108 TenTwelfths,
110 ElevenTwelfths,
112}
113
114impl std::hash::Hash for SizingValue {
115 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
116 match self {
117 SizingValue::Zero => 0u8.hash(state),
118 SizingValue::Px => 1u8.hash(state),
119 SizingValue::Fractional(f) => {
120 2u8.hash(state);
121 ((f * 1000.0) as u32).hash(state);
122 }
123 SizingValue::Integer(i) => {
124 3u8.hash(state);
125 i.hash(state);
126 }
127 SizingValue::Auto => 4u8.hash(state),
128 SizingValue::Full => 5u8.hash(state),
129 SizingValue::Screen => 6u8.hash(state),
130 SizingValue::Min => 7u8.hash(state),
131 SizingValue::Max => 8u8.hash(state),
132 SizingValue::Fit => 9u8.hash(state),
133 SizingValue::Fraction(f) => {
134 10u8.hash(state);
135 std::mem::discriminant(f).hash(state);
136 }
137 SizingValue::GridFraction(gf) => {
138 11u8.hash(state);
139 std::mem::discriminant(gf).hash(state);
140 }
141 }
142 }
143}
144
145impl std::hash::Hash for Fraction {
146 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
147 std::mem::discriminant(self).hash(state);
148 }
149}
150
151impl std::hash::Hash for GridFraction {
152 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
153 std::mem::discriminant(self).hash(state);
154 }
155}
156
157impl std::cmp::Eq for SizingValue {}
158impl std::cmp::Eq for Fraction {}
159impl std::cmp::Eq for GridFraction {}
160
161impl SizingValue {
162 pub fn to_css_value(&self) -> String {
164 match self {
165 SizingValue::Zero => "0".to_string(),
166 SizingValue::Px => "1px".to_string(),
167 SizingValue::Fractional(f) => format!("{}rem", f * 0.25),
168 SizingValue::Integer(i) => format!("{}rem", *i as f32 * 0.25),
169 SizingValue::Auto => "auto".to_string(),
170 SizingValue::Full => "100%".to_string(),
171 SizingValue::Screen => "100vh".to_string(), SizingValue::Min => "min-content".to_string(),
173 SizingValue::Max => "max-content".to_string(),
174 SizingValue::Fit => "fit-content".to_string(),
175 SizingValue::Fraction(f) => f.to_css_value(),
176 SizingValue::GridFraction(gf) => gf.to_css_value(),
177 }
178 }
179
180 pub fn to_css_value_width(&self) -> String {
182 match self {
183 SizingValue::Screen => "100vw".to_string(),
184 _ => self.to_css_value(),
185 }
186 }
187
188 pub fn to_css_value_height(&self) -> String {
190 match self {
191 SizingValue::Screen => "100vh".to_string(),
192 _ => self.to_css_value(),
193 }
194 }
195
196 pub fn to_class_name(&self) -> String {
198 match self {
199 SizingValue::Zero => "0".to_string(),
200 SizingValue::Px => "px".to_string(),
201 SizingValue::Fractional(f) => format!("{}", f),
202 SizingValue::Integer(i) => i.to_string(),
203 SizingValue::Auto => "auto".to_string(),
204 SizingValue::Full => "full".to_string(),
205 SizingValue::Screen => "screen".to_string(),
206 SizingValue::Min => "min".to_string(),
207 SizingValue::Max => "max".to_string(),
208 SizingValue::Fit => "fit".to_string(),
209 SizingValue::Fraction(f) => f.to_class_name(),
210 SizingValue::GridFraction(gf) => gf.to_class_name(),
211 }
212 }
213
214 pub fn all_values() -> Vec<SizingValue> {
216 let mut values = vec![
217 SizingValue::Zero,
218 SizingValue::Px,
219 SizingValue::Fractional(0.5),
220 SizingValue::Fractional(1.5),
221 SizingValue::Fractional(2.5),
222 SizingValue::Fractional(3.5),
223 SizingValue::Integer(1),
224 SizingValue::Integer(2),
225 SizingValue::Integer(3),
226 SizingValue::Integer(4),
227 SizingValue::Integer(5),
228 SizingValue::Integer(6),
229 SizingValue::Integer(8),
230 SizingValue::Integer(10),
231 SizingValue::Integer(12),
232 SizingValue::Integer(16),
233 SizingValue::Integer(20),
234 SizingValue::Integer(24),
235 SizingValue::Integer(32),
236 SizingValue::Integer(40),
237 SizingValue::Integer(48),
238 SizingValue::Integer(56),
239 SizingValue::Integer(64),
240 SizingValue::Integer(72),
241 SizingValue::Integer(80),
242 SizingValue::Integer(96),
243 SizingValue::Auto,
244 SizingValue::Full,
245 SizingValue::Screen,
246 SizingValue::Min,
247 SizingValue::Max,
248 SizingValue::Fit,
249 ];
250
251 values.extend([
253 SizingValue::Fraction(Fraction::Half),
254 SizingValue::Fraction(Fraction::Third),
255 SizingValue::Fraction(Fraction::TwoThirds),
256 SizingValue::Fraction(Fraction::Quarter),
257 SizingValue::Fraction(Fraction::TwoQuarters),
258 SizingValue::Fraction(Fraction::ThreeQuarters),
259 SizingValue::Fraction(Fraction::Fifth),
260 SizingValue::Fraction(Fraction::TwoFifths),
261 SizingValue::Fraction(Fraction::ThreeFifths),
262 SizingValue::Fraction(Fraction::FourFifths),
263 SizingValue::Fraction(Fraction::Sixth),
264 SizingValue::Fraction(Fraction::TwoSixths),
265 SizingValue::Fraction(Fraction::ThreeSixths),
266 SizingValue::Fraction(Fraction::FourSixths),
267 SizingValue::Fraction(Fraction::FiveSixths),
268 ]);
269
270 values.extend([
272 SizingValue::GridFraction(GridFraction::OneTwelfth),
273 SizingValue::GridFraction(GridFraction::TwoTwelfths),
274 SizingValue::GridFraction(GridFraction::ThreeTwelfths),
275 SizingValue::GridFraction(GridFraction::FourTwelfths),
276 SizingValue::GridFraction(GridFraction::FiveTwelfths),
277 SizingValue::GridFraction(GridFraction::SixTwelfths),
278 SizingValue::GridFraction(GridFraction::SevenTwelfths),
279 SizingValue::GridFraction(GridFraction::EightTwelfths),
280 SizingValue::GridFraction(GridFraction::NineTwelfths),
281 SizingValue::GridFraction(GridFraction::TenTwelfths),
282 SizingValue::GridFraction(GridFraction::ElevenTwelfths),
283 ]);
284
285 values
286 }
287}
288
289impl Fraction {
290 pub fn to_css_value(&self) -> String {
291 match self {
292 Fraction::Half => "50%".to_string(),
293 Fraction::Third => "33.333333%".to_string(),
294 Fraction::TwoThirds => "66.666667%".to_string(),
295 Fraction::Quarter => "25%".to_string(),
296 Fraction::TwoQuarters => "50%".to_string(),
297 Fraction::ThreeQuarters => "75%".to_string(),
298 Fraction::Fifth => "20%".to_string(),
299 Fraction::TwoFifths => "40%".to_string(),
300 Fraction::ThreeFifths => "60%".to_string(),
301 Fraction::FourFifths => "80%".to_string(),
302 Fraction::Sixth => "16.666667%".to_string(),
303 Fraction::TwoSixths => "33.333333%".to_string(),
304 Fraction::ThreeSixths => "50%".to_string(),
305 Fraction::FourSixths => "66.666667%".to_string(),
306 Fraction::FiveSixths => "83.333333%".to_string(),
307 }
308 }
309
310 pub fn to_class_name(&self) -> String {
311 match self {
312 Fraction::Half => "1/2".to_string(),
313 Fraction::Third => "1/3".to_string(),
314 Fraction::TwoThirds => "2/3".to_string(),
315 Fraction::Quarter => "1/4".to_string(),
316 Fraction::TwoQuarters => "2/4".to_string(),
317 Fraction::ThreeQuarters => "3/4".to_string(),
318 Fraction::Fifth => "1/5".to_string(),
319 Fraction::TwoFifths => "2/5".to_string(),
320 Fraction::ThreeFifths => "3/5".to_string(),
321 Fraction::FourFifths => "4/5".to_string(),
322 Fraction::Sixth => "1/6".to_string(),
323 Fraction::TwoSixths => "2/6".to_string(),
324 Fraction::ThreeSixths => "3/6".to_string(),
325 Fraction::FourSixths => "4/6".to_string(),
326 Fraction::FiveSixths => "5/6".to_string(),
327 }
328 }
329}
330
331impl GridFraction {
332 pub fn to_css_value(&self) -> String {
333 match self {
334 GridFraction::OneTwelfth => "8.333333%".to_string(),
335 GridFraction::TwoTwelfths => "16.666667%".to_string(),
336 GridFraction::ThreeTwelfths => "25%".to_string(),
337 GridFraction::FourTwelfths => "33.333333%".to_string(),
338 GridFraction::FiveTwelfths => "41.666667%".to_string(),
339 GridFraction::SixTwelfths => "50%".to_string(),
340 GridFraction::SevenTwelfths => "58.333333%".to_string(),
341 GridFraction::EightTwelfths => "66.666667%".to_string(),
342 GridFraction::NineTwelfths => "75%".to_string(),
343 GridFraction::TenTwelfths => "83.333333%".to_string(),
344 GridFraction::ElevenTwelfths => "91.666667%".to_string(),
345 }
346 }
347
348 pub fn to_class_name(&self) -> String {
349 match self {
350 GridFraction::OneTwelfth => "1/12".to_string(),
351 GridFraction::TwoTwelfths => "2/12".to_string(),
352 GridFraction::ThreeTwelfths => "3/12".to_string(),
353 GridFraction::FourTwelfths => "4/12".to_string(),
354 GridFraction::FiveTwelfths => "5/12".to_string(),
355 GridFraction::SixTwelfths => "6/12".to_string(),
356 GridFraction::SevenTwelfths => "7/12".to_string(),
357 GridFraction::EightTwelfths => "8/12".to_string(),
358 GridFraction::NineTwelfths => "9/12".to_string(),
359 GridFraction::TenTwelfths => "10/12".to_string(),
360 GridFraction::ElevenTwelfths => "11/12".to_string(),
361 }
362 }
363}
364
365impl fmt::Display for SizingValue {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 write!(f, "{}", self.to_class_name())
368 }
369}
370
371pub trait WidthUtilities {
373 fn width(self, value: SizingValue) -> Self;
375
376 fn min_width(self, value: SizingValue) -> Self;
378
379 fn max_width(self, value: SizingValue) -> Self;
381}
382
383impl WidthUtilities for ClassBuilder {
384 fn width(self, value: SizingValue) -> Self {
385 self.class(format!("w-{}", value.to_class_name()))
386 }
387
388 fn min_width(self, value: SizingValue) -> Self {
389 self.class(format!("min-w-{}", value.to_class_name()))
390 }
391
392 fn max_width(self, value: SizingValue) -> Self {
393 self.class(format!("max-w-{}", value.to_class_name()))
394 }
395}
396
397pub trait HeightUtilities {
399 fn height(self, value: SizingValue) -> Self;
401
402 fn min_height(self, value: SizingValue) -> Self;
404
405 fn max_height(self, value: SizingValue) -> Self;
407}
408
409impl HeightUtilities for ClassBuilder {
410 fn height(self, value: SizingValue) -> Self {
411 self.class(format!("h-{}", value.to_class_name()))
412 }
413
414 fn min_height(self, value: SizingValue) -> Self {
415 self.class(format!("min-h-{}", value.to_class_name()))
416 }
417
418 fn max_height(self, value: SizingValue) -> Self {
419 self.class(format!("max-h-{}", value.to_class_name()))
420 }
421}
422
423pub trait AspectRatioUtilities {
425 fn aspect_ratio(self, ratio: &str) -> Self;
427}
428
429impl AspectRatioUtilities for ClassBuilder {
430 fn aspect_ratio(self, ratio: &str) -> Self {
431 match ratio {
432 "1/1" => self.class("aspect-square"),
433 "16/9" => self.class("aspect-video"),
434 "4/3" => self.class("aspect-[4/3]"),
435 "3/2" => self.class("aspect-[3/2]"),
436 "2/3" => self.class("aspect-[2/3]"),
437 "9/16" => self.class("aspect-[9/16]"),
438 _ => self.class(format!("aspect-[{}]", ratio)),
439 }
440 }
441}
442
443impl ClassBuilder {
445 pub fn aspect_square(self) -> Self {
447 self.aspect_ratio("1/1")
448 }
449
450 pub fn aspect_video(self) -> Self {
452 self.aspect_ratio("16/9")
453 }
454
455 pub fn aspect_4_3(self) -> Self {
457 self.aspect_ratio("4/3")
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464
465 #[test]
466 fn test_sizing_value_to_css_value() {
467 assert_eq!(SizingValue::Zero.to_css_value(), "0");
468 assert_eq!(SizingValue::Px.to_css_value(), "1px");
469 assert_eq!(SizingValue::Fractional(0.5).to_css_value(), "0.125rem");
470 assert_eq!(SizingValue::Integer(4).to_css_value(), "1rem");
471 assert_eq!(SizingValue::Auto.to_css_value(), "auto");
472 assert_eq!(SizingValue::Full.to_css_value(), "100%");
473 assert_eq!(SizingValue::Screen.to_css_value(), "100vh");
474 }
475
476 #[test]
477 fn test_sizing_value_to_css_value_width() {
478 assert_eq!(SizingValue::Screen.to_css_value_width(), "100vw");
479 assert_eq!(SizingValue::Full.to_css_value_width(), "100%");
480 }
481
482 #[test]
483 fn test_sizing_value_to_css_value_height() {
484 assert_eq!(SizingValue::Screen.to_css_value_height(), "100vh");
485 assert_eq!(SizingValue::Full.to_css_value_height(), "100%");
486 }
487
488 #[test]
489 fn test_fraction_to_css_value() {
490 assert_eq!(Fraction::Half.to_css_value(), "50%");
491 assert_eq!(Fraction::Third.to_css_value(), "33.333333%");
492 assert_eq!(Fraction::TwoThirds.to_css_value(), "66.666667%");
493 }
494
495 #[test]
496 fn test_fraction_to_class_name() {
497 assert_eq!(Fraction::Half.to_class_name(), "1/2");
498 assert_eq!(Fraction::Third.to_class_name(), "1/3");
499 assert_eq!(Fraction::TwoThirds.to_class_name(), "2/3");
500 }
501
502 #[test]
503 fn test_grid_fraction_to_css_value() {
504 assert_eq!(GridFraction::OneTwelfth.to_css_value(), "8.333333%");
505 assert_eq!(GridFraction::SixTwelfths.to_css_value(), "50%");
506 assert_eq!(GridFraction::ElevenTwelfths.to_css_value(), "91.666667%");
507 }
508
509 #[test]
510 fn test_grid_fraction_to_class_name() {
511 assert_eq!(GridFraction::OneTwelfth.to_class_name(), "1/12");
512 assert_eq!(GridFraction::SixTwelfths.to_class_name(), "6/12");
513 assert_eq!(GridFraction::ElevenTwelfths.to_class_name(), "11/12");
514 }
515
516 #[test]
517 fn test_width_utilities() {
518 let classes = ClassBuilder::new()
519 .width(SizingValue::Full)
520 .min_width(SizingValue::Integer(4))
521 .max_width(SizingValue::Integer(8))
522 .build();
523
524 let css_classes = classes.to_css_classes();
525 assert!(css_classes.contains("w-full"));
526 assert!(css_classes.contains("min-w-4"));
527 assert!(css_classes.contains("max-w-8"));
528 }
529
530 #[test]
531 fn test_height_utilities() {
532 let classes = ClassBuilder::new()
533 .height(SizingValue::Screen)
534 .min_height(SizingValue::Integer(4))
535 .max_height(SizingValue::Integer(8))
536 .build();
537
538 let css_classes = classes.to_css_classes();
539 assert!(css_classes.contains("h-screen"));
540 assert!(css_classes.contains("min-h-4"));
541 assert!(css_classes.contains("max-h-8"));
542 }
543
544 #[test]
545 fn test_fractional_sizing() {
546 let classes = ClassBuilder::new()
547 .width(SizingValue::Fraction(Fraction::Half))
548 .height(SizingValue::Fraction(Fraction::Third))
549 .build();
550
551 let css_classes = classes.to_css_classes();
552 assert!(css_classes.contains("w-1/2"));
553 assert!(css_classes.contains("h-1/3"));
554 }
555
556 #[test]
557 fn test_grid_fractional_sizing() {
558 let classes = ClassBuilder::new()
559 .width(SizingValue::GridFraction(GridFraction::SixTwelfths))
560 .height(SizingValue::GridFraction(GridFraction::FourTwelfths))
561 .build();
562
563 let css_classes = classes.to_css_classes();
564 assert!(css_classes.contains("w-6/12"));
565 assert!(css_classes.contains("h-4/12"));
566 }
567
568 #[test]
569 fn test_special_sizing_values() {
570 let classes = ClassBuilder::new()
571 .width(SizingValue::Auto)
572 .height(SizingValue::Fit)
573 .min_width(SizingValue::Min)
574 .max_width(SizingValue::Max)
575 .build();
576
577 let css_classes = classes.to_css_classes();
578 assert!(css_classes.contains("w-auto"));
579 assert!(css_classes.contains("h-fit"));
580 assert!(css_classes.contains("min-w-min"));
581 assert!(css_classes.contains("max-w-max"));
582 }
583
584 #[test]
586 fn test_all_tailwind_sizing_values() {
587 let test_values = vec![
589 (SizingValue::Zero, "w-0"),
591 (SizingValue::Integer(1), "w-1"),
592 (SizingValue::Integer(2), "w-2"),
593 (SizingValue::Integer(3), "w-3"),
594 (SizingValue::Integer(4), "w-4"),
595 (SizingValue::Integer(5), "w-5"),
596 (SizingValue::Integer(6), "w-6"),
597 (SizingValue::Integer(8), "w-8"),
598 (SizingValue::Integer(10), "w-10"),
599 (SizingValue::Integer(12), "w-12"),
600 (SizingValue::Integer(16), "w-16"),
601 (SizingValue::Integer(20), "w-20"),
602 (SizingValue::Integer(24), "w-24"),
603 (SizingValue::Integer(32), "w-32"),
604 (SizingValue::Integer(40), "w-40"),
605 (SizingValue::Integer(48), "w-48"),
606 (SizingValue::Integer(56), "w-56"),
607 (SizingValue::Integer(64), "w-64"),
608 (SizingValue::Integer(72), "w-72"),
609 (SizingValue::Integer(80), "w-80"),
610 (SizingValue::Integer(96), "w-96"),
611 (SizingValue::Fractional(0.5), "w-0.5"),
613 (SizingValue::Fractional(1.5), "w-1.5"),
614 (SizingValue::Fractional(2.5), "w-2.5"),
615 (SizingValue::Fractional(3.5), "w-3.5"),
616 (SizingValue::Px, "w-px"),
618 (SizingValue::Auto, "w-auto"),
619 (SizingValue::Full, "w-full"),
620 (SizingValue::Screen, "w-screen"),
621 (SizingValue::Min, "w-min"),
622 (SizingValue::Max, "w-max"),
623 (SizingValue::Fit, "w-fit"),
624 ];
625
626 for (value, expected_class) in test_values {
627 let classes = ClassBuilder::new().width(value).build();
628 let css_classes = classes.to_css_classes();
629 assert!(css_classes.contains(expected_class),
630 "Missing sizing value: {} (expected class: {})",
631 format!("{:?}", value), expected_class);
632 }
633 }
634
635 #[test]
637 fn test_aspect_ratio_utilities() {
638 let classes = ClassBuilder::new()
640 .aspect_ratio("1/1") .aspect_ratio("16/9") .aspect_ratio("4/3") .build();
644
645 let css_classes = classes.to_css_classes();
646 assert!(css_classes.contains("aspect-square"));
647 assert!(css_classes.contains("aspect-video"));
648 assert!(css_classes.contains("aspect-[4/3]"));
649 }
650
651 #[test]
652 fn test_sizing_value_display() {
653 assert_eq!(format!("{}", SizingValue::Zero), "0");
655 assert_eq!(format!("{}", SizingValue::Px), "px");
656 assert_eq!(format!("{}", SizingValue::Fractional(0.5)), "0.5");
657 assert_eq!(format!("{}", SizingValue::Integer(4)), "4");
658 assert_eq!(format!("{}", SizingValue::Auto), "auto");
659 assert_eq!(format!("{}", SizingValue::Full), "full");
660 assert_eq!(format!("{}", SizingValue::Screen), "screen");
661 assert_eq!(format!("{}", SizingValue::Min), "min");
662 assert_eq!(format!("{}", SizingValue::Max), "max");
663 assert_eq!(format!("{}", SizingValue::Fit), "fit");
664 assert_eq!(format!("{}", SizingValue::Fraction(Fraction::Half)), "1/2");
665 assert_eq!(format!("{}", SizingValue::GridFraction(GridFraction::SixTwelfths)), "6/12");
666 }
667
668 #[test]
669 fn test_fraction_display() {
670 assert_eq!(Fraction::Half.to_class_name(), "1/2");
672 assert_eq!(Fraction::Third.to_class_name(), "1/3");
673 assert_eq!(Fraction::TwoThirds.to_class_name(), "2/3");
674 assert_eq!(Fraction::Quarter.to_class_name(), "1/4");
675 assert_eq!(Fraction::TwoQuarters.to_class_name(), "2/4");
676 assert_eq!(Fraction::ThreeQuarters.to_class_name(), "3/4");
677 assert_eq!(Fraction::Fifth.to_class_name(), "1/5");
678 assert_eq!(Fraction::TwoFifths.to_class_name(), "2/5");
679 assert_eq!(Fraction::ThreeFifths.to_class_name(), "3/5");
680 assert_eq!(Fraction::FourFifths.to_class_name(), "4/5");
681 assert_eq!(Fraction::Sixth.to_class_name(), "1/6");
682 assert_eq!(Fraction::TwoSixths.to_class_name(), "2/6");
683 assert_eq!(Fraction::ThreeSixths.to_class_name(), "3/6");
684 assert_eq!(Fraction::FourSixths.to_class_name(), "4/6");
685 assert_eq!(Fraction::FiveSixths.to_class_name(), "5/6");
686 }
687
688 #[test]
689 fn test_grid_fraction_display() {
690 assert_eq!(GridFraction::OneTwelfth.to_class_name(), "1/12");
692 assert_eq!(GridFraction::TwoTwelfths.to_class_name(), "2/12");
693 assert_eq!(GridFraction::ThreeTwelfths.to_class_name(), "3/12");
694 assert_eq!(GridFraction::FourTwelfths.to_class_name(), "4/12");
695 assert_eq!(GridFraction::FiveTwelfths.to_class_name(), "5/12");
696 assert_eq!(GridFraction::SixTwelfths.to_class_name(), "6/12");
697 assert_eq!(GridFraction::SevenTwelfths.to_class_name(), "7/12");
698 assert_eq!(GridFraction::EightTwelfths.to_class_name(), "8/12");
699 assert_eq!(GridFraction::NineTwelfths.to_class_name(), "9/12");
700 assert_eq!(GridFraction::TenTwelfths.to_class_name(), "10/12");
701 assert_eq!(GridFraction::ElevenTwelfths.to_class_name(), "11/12");
702 }
703
704 #[test]
705 fn test_sizing_value_class_names() {
706 assert_eq!(SizingValue::Zero.to_class_name(), "0");
708 assert_eq!(SizingValue::Px.to_class_name(), "px");
709 assert_eq!(SizingValue::Fractional(0.5).to_class_name(), "0.5");
710 assert_eq!(SizingValue::Integer(4).to_class_name(), "4");
711 assert_eq!(SizingValue::Auto.to_class_name(), "auto");
712 assert_eq!(SizingValue::Full.to_class_name(), "full");
713 assert_eq!(SizingValue::Screen.to_class_name(), "screen");
714 assert_eq!(SizingValue::Min.to_class_name(), "min");
715 assert_eq!(SizingValue::Max.to_class_name(), "max");
716 assert_eq!(SizingValue::Fit.to_class_name(), "fit");
717 assert_eq!(SizingValue::Fraction(Fraction::Half).to_class_name(), "1/2");
718 assert_eq!(SizingValue::GridFraction(GridFraction::SixTwelfths).to_class_name(), "6/12");
719 }
720
721 #[test]
722 fn test_all_fraction_css_values() {
723 assert_eq!(Fraction::Half.to_css_value(), "50%");
725 assert_eq!(Fraction::Third.to_css_value(), "33.333333%");
726 assert_eq!(Fraction::TwoThirds.to_css_value(), "66.666667%");
727 assert_eq!(Fraction::Quarter.to_css_value(), "25%");
728 assert_eq!(Fraction::TwoQuarters.to_css_value(), "50%");
729 assert_eq!(Fraction::ThreeQuarters.to_css_value(), "75%");
730 assert_eq!(Fraction::Fifth.to_css_value(), "20%");
731 assert_eq!(Fraction::TwoFifths.to_css_value(), "40%");
732 assert_eq!(Fraction::ThreeFifths.to_css_value(), "60%");
733 assert_eq!(Fraction::FourFifths.to_css_value(), "80%");
734 assert_eq!(Fraction::Sixth.to_css_value(), "16.666667%");
735 assert_eq!(Fraction::TwoSixths.to_css_value(), "33.333333%");
736 assert_eq!(Fraction::ThreeSixths.to_css_value(), "50%");
737 assert_eq!(Fraction::FourSixths.to_css_value(), "66.666667%");
738 assert_eq!(Fraction::FiveSixths.to_css_value(), "83.333333%");
739 }
740
741 #[test]
742 fn test_all_fraction_class_names() {
743 assert_eq!(Fraction::Half.to_class_name(), "1/2");
745 assert_eq!(Fraction::Third.to_class_name(), "1/3");
746 assert_eq!(Fraction::TwoThirds.to_class_name(), "2/3");
747 assert_eq!(Fraction::Quarter.to_class_name(), "1/4");
748 assert_eq!(Fraction::TwoQuarters.to_class_name(), "2/4");
749 assert_eq!(Fraction::ThreeQuarters.to_class_name(), "3/4");
750 assert_eq!(Fraction::Fifth.to_class_name(), "1/5");
751 assert_eq!(Fraction::TwoFifths.to_class_name(), "2/5");
752 assert_eq!(Fraction::ThreeFifths.to_class_name(), "3/5");
753 assert_eq!(Fraction::FourFifths.to_class_name(), "4/5");
754 assert_eq!(Fraction::Sixth.to_class_name(), "1/6");
755 assert_eq!(Fraction::TwoSixths.to_class_name(), "2/6");
756 assert_eq!(Fraction::ThreeSixths.to_class_name(), "3/6");
757 assert_eq!(Fraction::FourSixths.to_class_name(), "4/6");
758 assert_eq!(Fraction::FiveSixths.to_class_name(), "5/6");
759 }
760
761 #[test]
762 fn test_all_grid_fraction_css_values() {
763 assert_eq!(GridFraction::OneTwelfth.to_css_value(), "8.333333%");
765 assert_eq!(GridFraction::TwoTwelfths.to_css_value(), "16.666667%");
766 assert_eq!(GridFraction::ThreeTwelfths.to_css_value(), "25%");
767 assert_eq!(GridFraction::FourTwelfths.to_css_value(), "33.333333%");
768 assert_eq!(GridFraction::FiveTwelfths.to_css_value(), "41.666667%");
769 assert_eq!(GridFraction::SixTwelfths.to_css_value(), "50%");
770 assert_eq!(GridFraction::SevenTwelfths.to_css_value(), "58.333333%");
771 assert_eq!(GridFraction::EightTwelfths.to_css_value(), "66.666667%");
772 assert_eq!(GridFraction::NineTwelfths.to_css_value(), "75%");
773 assert_eq!(GridFraction::TenTwelfths.to_css_value(), "83.333333%");
774 assert_eq!(GridFraction::ElevenTwelfths.to_css_value(), "91.666667%");
775 }
776
777 #[test]
778 fn test_all_grid_fraction_class_names() {
779 assert_eq!(GridFraction::OneTwelfth.to_class_name(), "1/12");
781 assert_eq!(GridFraction::TwoTwelfths.to_class_name(), "2/12");
782 assert_eq!(GridFraction::ThreeTwelfths.to_class_name(), "3/12");
783 assert_eq!(GridFraction::FourTwelfths.to_class_name(), "4/12");
784 assert_eq!(GridFraction::FiveTwelfths.to_class_name(), "5/12");
785 assert_eq!(GridFraction::SixTwelfths.to_class_name(), "6/12");
786 assert_eq!(GridFraction::SevenTwelfths.to_class_name(), "7/12");
787 assert_eq!(GridFraction::EightTwelfths.to_class_name(), "8/12");
788 assert_eq!(GridFraction::NineTwelfths.to_class_name(), "9/12");
789 assert_eq!(GridFraction::TenTwelfths.to_class_name(), "10/12");
790 assert_eq!(GridFraction::ElevenTwelfths.to_class_name(), "11/12");
791 }
792
793 #[test]
794 fn test_sizing_serialization() {
795 let sizing_value = SizingValue::Full;
797 let serialized = serde_json::to_string(&sizing_value).unwrap();
798 let deserialized: SizingValue = serde_json::from_str(&serialized).unwrap();
799 assert_eq!(sizing_value, deserialized);
800
801 let fraction = Fraction::Half;
802 let serialized = serde_json::to_string(&fraction).unwrap();
803 let deserialized: Fraction = serde_json::from_str(&serialized).unwrap();
804 assert_eq!(fraction, deserialized);
805
806 let grid_fraction = GridFraction::SixTwelfths;
807 let serialized = serde_json::to_string(&grid_fraction).unwrap();
808 let deserialized: GridFraction = serde_json::from_str(&serialized).unwrap();
809 assert_eq!(grid_fraction, deserialized);
810 }
811
812 #[test]
813 fn test_sizing_equality_and_hash() {
814 let sizing_value1 = SizingValue::Full;
816 let sizing_value2 = SizingValue::Full;
817 let sizing_value3 = SizingValue::Auto;
818
819 assert_eq!(sizing_value1, sizing_value2);
820 assert_ne!(sizing_value1, sizing_value3);
821
822 use std::collections::hash_map::DefaultHasher;
824 use std::hash::{Hash, Hasher};
825
826 let mut hasher1 = DefaultHasher::new();
827 let mut hasher2 = DefaultHasher::new();
828 sizing_value1.hash(&mut hasher1);
829 sizing_value2.hash(&mut hasher2);
830 assert_eq!(hasher1.finish(), hasher2.finish());
831 }
832
833 #[test]
834 fn test_aspect_ratio_convenience_methods() {
835 let classes = ClassBuilder::new()
837 .aspect_square()
838 .aspect_video()
839 .aspect_4_3()
840 .build();
841
842 let css_classes = classes.to_css_classes();
843 assert!(css_classes.contains("aspect-square"));
844 assert!(css_classes.contains("aspect-video"));
845 assert!(css_classes.contains("aspect-[4/3]"));
846 }
847
848 #[test]
849 fn test_comprehensive_sizing_utilities() {
850 let classes = ClassBuilder::new()
852 .width(SizingValue::Zero)
854 .width(SizingValue::Px)
855 .width(SizingValue::Fractional(0.5))
856 .width(SizingValue::Integer(4))
857 .width(SizingValue::Auto)
858 .width(SizingValue::Full)
859 .width(SizingValue::Screen)
860 .width(SizingValue::Min)
861 .width(SizingValue::Max)
862 .width(SizingValue::Fit)
863 .width(SizingValue::Fraction(Fraction::Half))
864 .width(SizingValue::GridFraction(GridFraction::SixTwelfths))
865
866 .min_width(SizingValue::Integer(2))
868 .min_width(SizingValue::Fraction(Fraction::Third))
869
870 .max_width(SizingValue::Integer(8))
872 .max_width(SizingValue::Fraction(Fraction::TwoThirds))
873
874 .height(SizingValue::Zero)
876 .height(SizingValue::Px)
877 .height(SizingValue::Fractional(1.5))
878 .height(SizingValue::Integer(6))
879 .height(SizingValue::Auto)
880 .height(SizingValue::Full)
881 .height(SizingValue::Screen)
882 .height(SizingValue::Min)
883 .height(SizingValue::Max)
884 .height(SizingValue::Fit)
885 .height(SizingValue::Fraction(Fraction::Quarter))
886 .height(SizingValue::GridFraction(GridFraction::FourTwelfths))
887
888 .min_height(SizingValue::Integer(3))
890 .min_height(SizingValue::Fraction(Fraction::Fifth))
891
892 .max_height(SizingValue::Integer(10))
894 .max_height(SizingValue::Fraction(Fraction::ThreeQuarters))
895
896 .aspect_ratio("1/1")
898 .aspect_ratio("16/9")
899 .aspect_ratio("4/3")
900 .aspect_ratio("3/2")
901 .aspect_ratio("2/3")
902 .aspect_ratio("9/16")
903 .aspect_ratio("21/9")
904 .build();
905
906 let css_classes = classes.to_css_classes();
907
908 assert!(css_classes.contains("w-0"));
910 assert!(css_classes.contains("w-px"));
911 assert!(css_classes.contains("w-0.5"));
912 assert!(css_classes.contains("w-4"));
913 assert!(css_classes.contains("w-auto"));
914 assert!(css_classes.contains("w-full"));
915 assert!(css_classes.contains("w-screen"));
916 assert!(css_classes.contains("w-min"));
917 assert!(css_classes.contains("w-max"));
918 assert!(css_classes.contains("w-fit"));
919 assert!(css_classes.contains("w-1/2"));
920 assert!(css_classes.contains("w-6/12"));
921
922 assert!(css_classes.contains("min-w-2"));
924 assert!(css_classes.contains("min-w-1/3"));
925
926 assert!(css_classes.contains("max-w-8"));
928 assert!(css_classes.contains("max-w-2/3"));
929
930 assert!(css_classes.contains("h-0"));
932 assert!(css_classes.contains("h-px"));
933 assert!(css_classes.contains("h-1.5"));
934 assert!(css_classes.contains("h-6"));
935 assert!(css_classes.contains("h-auto"));
936 assert!(css_classes.contains("h-full"));
937 assert!(css_classes.contains("h-screen"));
938 assert!(css_classes.contains("h-min"));
939 assert!(css_classes.contains("h-max"));
940 assert!(css_classes.contains("h-fit"));
941 assert!(css_classes.contains("h-1/4"));
942 assert!(css_classes.contains("h-4/12"));
943
944 assert!(css_classes.contains("min-h-3"));
946 assert!(css_classes.contains("min-h-1/5"));
947
948 assert!(css_classes.contains("max-h-10"));
950 assert!(css_classes.contains("max-h-3/4"));
951
952 assert!(css_classes.contains("aspect-square"));
954 assert!(css_classes.contains("aspect-video"));
955 assert!(css_classes.contains("aspect-[4/3]"));
956 assert!(css_classes.contains("aspect-[3/2]"));
957 assert!(css_classes.contains("aspect-[2/3]"));
958 assert!(css_classes.contains("aspect-[9/16]"));
959 assert!(css_classes.contains("aspect-[21/9]"));
960 }
961}