layoutcss_parser/components/
grid.rs

1use indoc::formatdoc;
2use std::collections::HashSet;
3
4use crate::harmonic::get_harmonic;
5
6const GRID_STYLE: &str = r#"
7  grid-l{
8    display: grid;
9  }
10"#;
11
12fn grid_gap_style(value: &str, harmonic: String) -> String {
13    formatdoc!(
14        r#"
15        grid-l[layout~="gap:{value}"]{{
16            gap: {harmonic};
17        }}
18        "#,
19    )
20}
21
22fn grid_gap_x_style(value: &str, harmonic: String) -> String {
23    formatdoc!(
24        r#"
25        grid-l[layout~="gap-x:{value}"]{{
26            column-gap: {harmonic};
27        }}
28        "#,
29    )
30}
31
32fn grid_gap_y_style(value: &str, harmonic: String) -> String {
33    formatdoc!(
34        r#"
35        grid-l[layout~="gap-y:{value}"]{{
36            row-gap: {harmonic};
37        }}
38        "#,
39    )
40}
41
42fn grid_group_empty(min_cell_width: &str) -> String {
43    formatdoc!(
44        r#"
45        grid-l[layout*="min-cell-width:{min_cell_width}"] {{
46            grid-template-columns: repeat(auto-fit, minmax(min({min_cell_width}, 100%),1fr));
47        }}
48        "#,
49    )
50}
51
52fn grid_group_max_cols(min_cell_width: &str, max_cols: &str, gap_delta_max: &str) -> String {
53    formatdoc!(
54        r#"
55        grid-l[layout*="min-cell-width:{min_cell_width}"][layout*="max-cols:{max_cols}"]{{
56            grid-template-columns: repeat(auto-fit, minmax(min(100%, max({min_cell_width}, (100% / {max_cols} - {gap_delta_max}))),1fr));
57        }}
58        "#,
59    )
60}
61
62fn grid_group_min_cols(min_cell_width: &str, min_cols: &str, gap_delta_min: &str) -> String {
63    formatdoc!(
64        r#"
65        grid-l[layout*="min-cell-width:{min_cell_width}"][layout*="min-cols:{min_cols}"]:has(:nth-child({min_cols})){{
66            grid-template-columns: repeat(auto-fit, minmax(min((100% / {min_cols} - {gap_delta_min}), {min_cell_width}), 1fr));
67        }}
68        grid-l[layout*="min-cell-width:{min_cell_width}"][layout*="min-cols:{min_cols}"]{{
69            grid-template-columns: repeat({min_cols}, 1fr);
70        }}
71        "#,
72    )
73}
74
75fn grid_group_min_cols_max_cols(
76    min_cell_width: &str,
77    min_cols: &str,
78    max_cols: &str,
79    gap_delta_min: &str,
80    gap_delta_max: &str,
81    fr: f64,
82) -> String {
83    formatdoc!(
84        r#"
85        grid-l[layout*="min-cell-width:{min_cell_width}"][layout*="min-cols:{min_cols}"][layout*="max-cols:{max_cols}"]:has(:nth-child({min_cols})){{
86            grid-template-columns:
87                repeat(auto-fit,
88                    minmax(
89                        min(
90                            (100% / {min_cols} - {gap_delta_min}),
91                                max({min_cell_width}, (100% / {max_cols} - {gap_delta_max}))
92                            ),
93                            {fr}fr
94                            )
95                        )
96                    }}
97        grid-l[layout*="min-cell-width:{min_cell_width}"][layout*="min-cols:{min_cols}"][layout*="max-cols:{max_cols}"]{{
98            grid-template-columns: repeat({min_cols}, 1fr);
99        }}
100        "#,
101    )
102}
103
104fn gap_delta(cols: &str, gap: Option<&str>, harmonic_ratio: f64) -> String {
105    if let Some(value) = gap {
106        match cols.parse::<f64>() {
107            Ok(cols_number) => {
108                let hr =  get_harmonic(&value, harmonic_ratio);
109                format!("{hr} * ({cols_number} - 0.98) / {cols_number}").to_string()
110                //get_harmonic(&value, harmonic_ratio) * (val - 0.98) / val,
111            },
112            Err(_) => "0px".to_string(),
113        }
114    } else {
115        "0px".to_string()
116    }
117}
118
119pub fn grid_css(
120    min_cell_width: Option<&str>,
121    min_cols: Option<&str>,
122    max_cols: Option<&str>,
123    gap: Option<&str>,
124    gap_x: Option<&str>,
125    gap_y: Option<&str>,
126    harmonic_ratio: f64,
127    set: &mut HashSet<String>,
128) {
129    set.insert(GRID_STYLE.to_string());
130    if let Some(ref value) = gap {
131        let harmonic_value = get_harmonic(value, harmonic_ratio);
132        set.insert(grid_gap_style(value, harmonic_value));
133    }
134    if let Some(ref value) = gap_x {
135        let harmonic_value = get_harmonic(value, harmonic_ratio);
136        set.insert(grid_gap_x_style(value, harmonic_value));
137    }
138    if let Some(ref value) = gap_y {
139        let harmonic_value = get_harmonic(value, harmonic_ratio);
140        set.insert(grid_gap_y_style(value, harmonic_value));
141    }
142    if let Some(min_cell_width) = min_cell_width {
143        match (min_cols, max_cols) {
144            (Some(min_cols), Some(max_cols)) => {
145
146                let gap_delta_min = gap_delta(min_cols, gap, harmonic_ratio);
147                let gap_delta_max = gap_delta(max_cols, gap, harmonic_ratio);
148                let fr = 1.0 / min_cols.parse::<f64>().unwrap_or(-1.0);
149                set.insert(grid_group_min_cols_max_cols(
150                    min_cell_width,
151                    min_cols,
152                    max_cols,
153                    &gap_delta_min,
154                    &gap_delta_max,
155                    fr,
156                ));
157            }
158            (Some(min_cols), None) => {
159                let gap_delta_min = gap_delta(min_cols, gap, harmonic_ratio);
160                set.insert(grid_group_min_cols(min_cell_width, min_cols, &gap_delta_min));
161            }
162            (None, Some(max_cols)) => {
163                let gap_delta_max = gap_delta(max_cols, gap, harmonic_ratio);
164                set.insert(grid_group_max_cols(min_cell_width, max_cols, &gap_delta_max));
165            }
166            _ => {
167                set.insert(grid_group_empty(min_cell_width));
168            }
169        }
170    }
171}
172
173