layoutcss_parser/components/
area.rs1use crate::harmonic::get_harmonic;
2use indoc::formatdoc;
3use std::collections::HashSet;
4
5pub const AREA_STYLE: &str = r#"
6area-l{
7 display: grid;
8 }
9"#;
10
11fn area_gap_style(value: &str, harmonic: String) -> String {
12 formatdoc!(
13 r#"
14 area-l[layout~="gap:{value}"]{{
15 gap: {harmonic};
16 }}
17 "#,
18 )
19}
20
21fn area_gap_x_style(value: &str, harmonic: String) -> String {
22 formatdoc!(
23 r#"
24 area-l[layout~="gap-x:{value}"]{{
25 column-gap: {harmonic};
26 }}
27 "#,
28 )
29}
30
31fn area_gap_y_style(value: &str, harmonic: String) -> String {
32 formatdoc!(
33 r#"
34 area-l[layout~="gap-y:{value}"]{{
35 row-gap: {harmonic};
36 }}
37 "#,
38 )
39}
40
41fn area_grid_template_areas_style(value: &str, template: String) -> String {
42 formatdoc!(
43 r#"
44 area-l[layout~="template:{value}"] {{
45 grid-template-areas: {template};
46 }}
47 "#,
48 )
49}
50
51fn area_grid_area_unit_style(value: &str, unit: char, index: usize) -> String {
52 formatdoc!(
53 r#"
54 area-l[layout~="template:{value}"] > :nth-child({index}) {{
55 grid-area: {unit};
56 }}
57 "#,
58 )
59}
60
61fn area_rows_style(selector: &str, value: &str, template_selector:&str) -> String {
62 formatdoc!(
63 r#"
64 area-l[layout~="template:{template_selector}"]{selector}{{
65 grid-template-rows: {value};
66 }}
67 "#,
68 )
69}
70
71fn area_cols_style(selector: &str, value: &str, template_selector:&str ) -> String {
72 formatdoc!(
73 r#"
74 area-l[layout~="template:{template_selector}"]{selector}{{
75 grid-template-columns: {value};
76 }}
77 "#,
78 )
79}
80
81fn count_rows_and_cols(text: &str) -> (usize, usize) {
84 let rows = text.chars().filter(|c| *c == '|').count() + 1;
85 let cols = text
86 .chars()
87 .take_while(|c| *c != '|')
88 .filter(|c| *c == '-')
89 .count()
90 + 1;
91 (rows, cols)
92}
93
94fn grid_template_areas_value(text: &str) -> String {
97 let mut areas = Vec::new();
98 let text_without_parentheses = text.replace("(", "").replace(")", "");
99 for part in text_without_parentheses.split('|') {
100 let area = part
101 .chars()
102 .filter(|c| *c != '-')
103 .map(|c| c.to_string())
104 .collect::<Vec<String>>()
105 .join(" ");
106 areas.push(formatdoc!("\"{}\"", area));
107 }
108 areas.join(" ")
109}
110
111fn grid_template_rows_or_cols_rule(items: &Vec<&str>, pattern: &str, number: usize) -> String {
114 let mut rules: Vec<String> = vec![];
115 for i in 1..=number {
120 let pattern = formatdoc!("{}{}", pattern, i.to_string());
121 if let Some(item) = items.iter().find(|&s| s.starts_with(&pattern)) {
122 let parts: Vec<&str> = item.split(':').collect();
123 if let Some(value) = parts.get(1) {
124 rules.push(value.to_string());
125 }
126 } else {
127 rules.push("1fr".to_string());
128 }
129 }
130 rules.join(" ")
131}
132
133fn grid_template_rows_or_cols_selector(items: &Vec<&str>) -> String {
134 let formatted_items: Vec<String> = items
135 .iter()
136 .map(|item| {
137 formatdoc!(
138 r#"[layout~="{}"]"#,
139 item
140 )
141 })
142 .collect();
143 formatted_items.join("")
144}
145
146fn unique_letters(input: &str) -> Vec<char> {
147 let mut unique_chars = Vec::new();
148 for c in input.chars() {
149 if c.is_alphabetic() && !unique_chars.contains(&c) {
151 unique_chars.push(c);
152 }
153 }
154 unique_chars.sort();
155 unique_chars
156}
157
158pub fn area_css(
159 template: Option<&str>,
160 rows: Vec<&str>,
161 cols: Vec<&str>,
162 gap: Option<&str>,
163 gap_x: Option<&str>,
164 gap_y: Option<&str>,
165 harmonic_ratio: f64,
166 set: &mut HashSet<String>,
167) {
168 set.insert(AREA_STYLE.to_string());
169 if let Some(template) = template {
170 let template_areas = grid_template_areas_value(template);
171 set.insert(area_grid_template_areas_style(template, template_areas));
172 for (index, letter) in unique_letters(template).into_iter().enumerate() {
173 set.insert(area_grid_area_unit_style(template, letter, index + 1));
174 }
175
176 let (rows_nb, cols_nb) = count_rows_and_cols(template);
177 if !rows.is_empty() {
178 let selector = grid_template_rows_or_cols_selector(&rows);
179 let value = grid_template_rows_or_cols_rule(&rows, "row-", rows_nb);
180 set.insert(area_rows_style(&selector, &value, template));
181 }
182 if !cols.is_empty() {
183 let selector = grid_template_rows_or_cols_selector(&cols );
184 let value = grid_template_rows_or_cols_rule(&cols, "col-", cols_nb);
185 set.insert(area_cols_style(&selector, &value,template));
186 }
187 }
188
189 if let Some(value) = gap {
190 let harmonic_value = get_harmonic(value, harmonic_ratio);
191 set.insert(area_gap_style(value, harmonic_value));
192 }
193 if let Some(value) = gap_x {
194 let harmonic_value = get_harmonic(value, harmonic_ratio);
195 set.insert(area_gap_x_style(value, harmonic_value));
196 }
197 if let Some(value) = gap_y {
198 let harmonic_value = get_harmonic(value, harmonic_ratio);
199 set.insert(area_gap_y_style(value, harmonic_value));
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn demo_css_area() {
209 let mut css_set: HashSet<String> = HashSet::new();
210 area_css(
211 Some("(a-a-b-b-b|a-a-b-b-b)"),
212 vec!["row-1:800px"],
213 vec!["col-2:300px", "col-4:80px"],
214 Some("1"),
215 None,
216 None,
217 1.618,
218 &mut css_set,
219 );
220 println!("{:?}", css_set);
221 }
222}