Skip to main content

encre_css/plugins/layout/container/
mod.rs

1#![doc = include_str!("README.md")]
2#![doc(alias = "layout")]
3use crate::{config::BUILTIN_SCREENS, prelude::build_plugin::*};
4
5use std::{borrow::Cow, cmp::Ordering};
6
7pub(crate) const PLUGIN: StaticPlugin = Plugin::Functional(Functional {
8    namespace: "container",
9    can_handle: |context| matches!(context.modifier, Modifier::Builtin { value: "", .. }),
10    handle: |context| {
11        if let Modifier::Builtin { .. } = context.modifier {
12            generate_wrapper(context, |context| {
13                context.buffer.line("width: 100%;");
14            });
15
16            context.buffer.raw("\n\n");
17
18            let mut first_child = true;
19
20            generate_at_rules(context, |context| {
21                let mut screens = context
22                    .config
23                    .theme
24                    .screens
25                    .iter()
26                    .map(|(a, b)| (a.clone(), b.clone()))
27                    .chain(
28                        BUILTIN_SCREENS
29                            .iter()
30                            .map(|(a, b)| (Cow::from(*a), Cow::from(*b))),
31                    )
32                    .collect::<Vec<(Cow<str>, Cow<str>)>>();
33
34                // Deduplicate screens
35                screens.sort_by(|a, b| a.0.cmp(&b.0));
36                screens.dedup_by(|a, b| a.0.eq(&b.0));
37
38                // Emulate Tailwind sorting (based on the JS `parseInt` function)
39                screens.sort_by(|a, b| {
40                    let a = if let Some(first_char_a) = a.1.chars().position(char::is_alphabetic) {
41                        a.1[..first_char_a].parse::<usize>().ok()
42                    } else {
43                        a.1.parse::<usize>().ok()
44                    };
45
46                    let b = if let Some(first_char_b) = b.1.chars().position(char::is_alphabetic) {
47                        b.1[..first_char_b].parse::<usize>().ok()
48                    } else {
49                        b.1.parse::<usize>().ok()
50                    };
51
52                    if let Some(a) = a {
53                        if let Some(b) = b {
54                            a.cmp(&b)
55                        } else {
56                            Ordering::Less
57                        }
58                    } else {
59                        Ordering::Greater
60                    }
61                });
62
63                for (_, screen) in &screens {
64                    if first_child {
65                        first_child = false;
66                    } else if context.buffer.is_unindented() {
67                        context.buffer.raw("\n\n");
68                    } else {
69                        context.buffer.raw("\n");
70                    }
71
72                    context
73                        .buffer
74                        .line(format_args!("@media (width >= {screen}) {{"));
75                    context.buffer.indent();
76
77                    generate_class(
78                        context,
79                        |context| {
80                            context.buffer.line(format_args!("max-width: {screen};"));
81                        },
82                        "",
83                    );
84
85                    context.buffer.unindent();
86                    if context.buffer.is_unindented() {
87                        context.buffer.raw("}");
88                    } else {
89                        context.buffer.line("}");
90                    }
91                }
92
93                // After rule
94                while !context.buffer.is_unindented() {
95                    context.buffer.unindent();
96
97                    if context.buffer.is_unindented() {
98                        context.buffer.raw("}");
99                    } else {
100                        context.buffer.line("}");
101                    }
102                }
103            });
104        }
105    },
106    ..Functional::default()
107});