seed/browser/dom/
css_units.rs

1//! [MDN web docs](https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Values_and_units)
2
3macro_rules! create_unit_items {
4    { $( $variant:tt => $function:tt => $literal_unit:tt ),* $(,)?} => {
5        // ---- Create macro `unit!` ----
6        #[macro_export]
7        macro_rules! unit {
8            { $value:expr } => {
9                {
10                    $value.to_string()
11                }
12             };
13             $(
14                { $value:expr, $literal_unit } => {
15                    {
16                        $value.to_string() + stringify!($literal_unit)
17                    }
18                 };
19             )*
20             { $value:expr, $unit:expr } => {
21                {
22                    let unit: Unit = $unit;
23                    format!("{}{}", $value, unit)
24                }
25             };
26        }
27
28        // ---- Create enum `Unit` ----
29        #[allow(dead_code)]
30        #[derive(Clone, Copy)]
31        pub enum Unit {
32            $($variant,)*
33        }
34        impl std::fmt::Display for Unit {
35            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
36                let text_unit = match self {
37                    $(Unit::$variant => stringify!($literal_unit),)*
38                };
39                write!(f, "{}", text_unit)
40            }
41        }
42
43        // ---- Create unit functions (e.g. `px(..)`) ----
44        $(
45            #[allow(dead_code)]
46            pub fn $function(value: impl std::fmt::Display) -> String {
47                format!("{}{}", value, stringify!($literal_unit))
48            }
49        )*
50    }
51}
52
53// https://flaviocopes.com/css-units/
54// Unit::Variant => function name => literal unit
55create_unit_items! {
56    // Is like ex, but measures the width of 0 (zero).
57    Ch => ch => ch,
58    // Centimeter (maps to 37.8 pixels).
59    Cm => cm => cm,
60    // Value assigned to that element’s font-size, measures the width of the m letter.
61    Em => em => em,
62    // Fraction units, and they are used in CSS Grid to divide space into fractions.
63    Fr => fr => fr,
64    // Is like em, but measures the height of the x letter.
65    Ex => ex => ex,
66    // Inch (maps to 96 pixels).
67    In => inch => in,
68    // Millimeter.
69    Mm => mm => mm,
70    // Pica (1 pica = 12 points).
71    Pc => pc => pc,
72    // Percent.
73    Percent => percent => %,
74    // Point (1 inch = 72 points).
75    Pt => pt => pt,
76    // Pixel.
77    Px => px => px,
78    // Quarter of a millimeter.
79    Q => q => q,
80    // Is similar to em, but uses the root element (html) font-size.
81    Rem => rem => rem,
82    // Viewport height unit represents a percentage of the viewport height.
83    Vh => vh => vh,
84    // Viewport minimum unit represents the minimum between the height or width in terms of percentage.
85    Vmin => vmin => vmin,
86    // Viewport maximum unit represents the maximum between the height or width in terms of percentage.
87    Vmax => vmax => vmax,
88    // Viewport width unit represents a percentage of the viewport width.
89    Vw => vw => vw,
90}
91
92#[cfg(test)]
93pub mod tests {
94    use super::*;
95    use wasm_bindgen_test::*;
96
97    // ----------------- Macro Unit -----------------
98    #[wasm_bindgen_test]
99    fn variable() {
100        let width = -100;
101        assert_eq!(unit!(width, px), "-100px");
102    }
103
104    #[wasm_bindgen_test]
105    fn expression() {
106        assert_eq!(unit!(100 + 350, px), "450px");
107    }
108
109    #[wasm_bindgen_test]
110    fn without_unit() {
111        assert_eq!(unit!(2.5), "2.5");
112    }
113
114    #[wasm_bindgen_test]
115    fn str_with_variant() {
116        assert_eq!(unit!("68", Unit::Mm), "68mm");
117    }
118
119    #[wasm_bindgen_test]
120    fn percent_unit() {
121        assert_eq!(unit!(15_236.56f64, %), "15236.56%");
122    }
123
124    #[wasm_bindgen_test]
125    fn in_unit_with_negative_zero() {
126        assert_eq!(unit!(-0, in), "0in");
127    }
128
129    // ----------------- Functions -----------------
130    #[wasm_bindgen_test]
131    fn px_function() {
132        assert_eq!(px(15), "15px");
133    }
134
135    #[wasm_bindgen_test]
136    fn inch_function() {
137        assert_eq!(inch(-15.63), "-15.63in");
138    }
139
140    #[wasm_bindgen_test]
141    fn percent_function() {
142        assert_eq!(percent("35"), "35%");
143    }
144}