ironhtml_bootstrap/grid.rs
1//! Bootstrap grid system components.
2//!
3//! The grid system uses containers, rows, and columns to layout content.
4//!
5//! ## Example
6//!
7//! ```rust
8//! use ironhtml_bootstrap::grid::*;
9//! use ironhtml::typed::Element;
10//! use ironhtml_elements::Div;
11//!
12//! let layout = container(|c| {
13//! c.child::<Div, _>(|_| {
14//! row(|r| {
15//! r.child::<Div, _>(|_| col(4, |c| c.text("Column 1")))
16//! .child::<Div, _>(|_| col(4, |c| c.text("Column 2")))
17//! .child::<Div, _>(|_| col(4, |c| c.text("Column 3")))
18//! })
19//! })
20//! });
21//!
22//! let html = layout.render();
23//! assert!(html.contains(r#"class="container"#));
24//! assert!(html.contains(r#"class="row"#));
25//! assert!(html.contains(r#"class="col-4"#));
26//! ```
27
28use crate::Breakpoint;
29use ironhtml::typed::Element;
30use ironhtml_elements::Div;
31
32/// Create a Bootstrap container.
33///
34/// Generates: `<div class="container">...</div>`
35///
36/// ## Example
37///
38/// ```rust
39/// use ironhtml_bootstrap::grid::container;
40///
41/// let c = container(|c| c.text("Content"));
42/// assert!(c.render().contains(r#"<div class="container">"#));
43/// ```
44#[must_use]
45pub fn container<F>(f: F) -> Element<Div>
46where
47 F: FnOnce(Element<Div>) -> Element<Div>,
48{
49 f(Element::<Div>::new().class("container"))
50}
51
52/// Create a fluid container (100% width).
53///
54/// Generates: `<div class="container-fluid">...</div>`
55#[must_use]
56pub fn container_fluid<F>(f: F) -> Element<Div>
57where
58 F: FnOnce(Element<Div>) -> Element<Div>,
59{
60 f(Element::<Div>::new().class("container-fluid"))
61}
62
63/// Create a responsive container.
64///
65/// Generates: `<div class="container-{breakpoint}">...</div>`
66///
67/// ## Example
68///
69/// ```rust
70/// use ironhtml_bootstrap::{grid::container_bp, Breakpoint};
71///
72/// let c = container_bp(Breakpoint::Md, |c| c.text("Content"));
73/// assert!(c.render().contains(r#"class="container-md"#));
74/// ```
75#[must_use]
76pub fn container_bp<F>(bp: Breakpoint, f: F) -> Element<Div>
77where
78 F: FnOnce(Element<Div>) -> Element<Div>,
79{
80 let class = alloc::format!("container-{}", bp.as_str());
81 f(Element::<Div>::new().class(&class))
82}
83
84/// Create a row.
85///
86/// Generates: `<div class="row">...</div>`
87#[must_use]
88pub fn row<F>(f: F) -> Element<Div>
89where
90 F: FnOnce(Element<Div>) -> Element<Div>,
91{
92 f(Element::<Div>::new().class("row"))
93}
94
95/// Create a row with custom gutter.
96///
97/// Generates: `<div class="row g-{gutter}">...</div>`
98#[must_use]
99pub fn row_gutter<F>(gutter: u8, f: F) -> Element<Div>
100where
101 F: FnOnce(Element<Div>) -> Element<Div>,
102{
103 let class = alloc::format!("row g-{gutter}");
104 f(Element::<Div>::new().class(&class))
105}
106
107/// Create an auto-width column.
108///
109/// Generates: `<div class="col">...</div>`
110#[must_use]
111pub fn col_auto<F>(f: F) -> Element<Div>
112where
113 F: FnOnce(Element<Div>) -> Element<Div>,
114{
115 f(Element::<Div>::new().class("col"))
116}
117
118/// Create a column with specific size (1-12).
119///
120/// Generates: `<div class="col-{size}">...</div>`
121///
122/// ## Example
123///
124/// ```rust
125/// use ironhtml_bootstrap::grid::col;
126///
127/// let c = col(6, |c| c.text("Half width"));
128/// assert!(c.render().contains(r#"class="col-6"#));
129/// ```
130#[must_use]
131pub fn col<F>(size: u8, f: F) -> Element<Div>
132where
133 F: FnOnce(Element<Div>) -> Element<Div>,
134{
135 let class = alloc::format!("col-{size}");
136 f(Element::<Div>::new().class(&class))
137}
138
139/// Create a responsive column.
140///
141/// Generates: `<div class="col-{breakpoint}-{size}">...</div>`
142///
143/// ## Example
144///
145/// ```rust
146/// use ironhtml_bootstrap::{grid::col_bp, Breakpoint};
147///
148/// let c = col_bp(Breakpoint::Md, 6, |c| c.text("Half on medium+"));
149/// assert!(c.render().contains(r#"class="col-md-6"#));
150/// ```
151#[must_use]
152pub fn col_bp<F>(bp: Breakpoint, size: u8, f: F) -> Element<Div>
153where
154 F: FnOnce(Element<Div>) -> Element<Div>,
155{
156 let class = alloc::format!("col-{}-{size}", bp.as_str());
157 f(Element::<Div>::new().class(&class))
158}
159
160extern crate alloc;
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_container() {
168 let c = container(|c| c.text("Hello"));
169 assert!(c.render().contains(r#"<div class="container">"#));
170 }
171
172 #[test]
173 fn test_row_and_columns() {
174 let layout = row(|r| {
175 r.child::<Div, _>(|_| col(4, |c| c.text("A")))
176 .child::<Div, _>(|_| col(8, |c| c.text("B")))
177 });
178 let html = layout.render();
179 assert!(html.contains(r#"class="row"#));
180 assert!(html.contains(r#"class="col-4"#));
181 assert!(html.contains(r#"class="col-8"#));
182 }
183}