Skip to main content

dioxus_bootstrap_css/
grid.rs

1use dioxus::prelude::*;
2
3use crate::types::ColumnSize;
4
5/// Bootstrap Container component.
6///
7/// ```rust
8/// rsx! {
9///     Container { "Fixed width content" }
10///     Container { fluid: true, "Full width content" }
11/// }
12/// ```
13#[derive(Clone, PartialEq, Props)]
14pub struct ContainerProps {
15    /// Use container-fluid for full width.
16    #[props(default)]
17    pub fluid: bool,
18    /// Additional CSS classes.
19    #[props(default)]
20    pub class: String,
21    /// Child elements.
22    pub children: Element,
23}
24
25#[component]
26pub fn Container(props: ContainerProps) -> Element {
27    let base = if props.fluid {
28        "container-fluid"
29    } else {
30        "container"
31    };
32    let full_class = if props.class.is_empty() {
33        base.to_string()
34    } else {
35        format!("{base} {}", props.class)
36    };
37
38    rsx! {
39        div { class: "{full_class}", {props.children} }
40    }
41}
42
43/// Bootstrap Row component.
44///
45/// ```rust
46/// rsx! {
47///     Row { class: "g-3",
48///         Col { lg: ColumnSize::Span(6), "Left" }
49///         Col { lg: ColumnSize::Span(6), "Right" }
50///     }
51/// }
52/// ```
53#[derive(Clone, PartialEq, Props)]
54pub struct RowProps {
55    /// Additional CSS classes (e.g., "g-3", "align-items-center").
56    #[props(default)]
57    pub class: String,
58    /// Child elements.
59    pub children: Element,
60}
61
62#[component]
63pub fn Row(props: RowProps) -> Element {
64    let full_class = if props.class.is_empty() {
65        "row".to_string()
66    } else {
67        format!("row {}", props.class)
68    };
69
70    rsx! {
71        div { class: "{full_class}", {props.children} }
72    }
73}
74
75/// Bootstrap Col (column) component with responsive breakpoint props.
76///
77/// ```rust
78/// rsx! {
79///     Col { xs: ColumnSize::Span(12), md: ColumnSize::Span(6), lg: ColumnSize::Span(4),
80///         "Responsive column"
81///     }
82///     Col { lg: ColumnSize::Auto, "Auto-width column" }
83/// }
84/// ```
85#[derive(Clone, PartialEq, Props)]
86pub struct ColProps {
87    /// Column size at xs breakpoint (default, no breakpoint prefix).
88    #[props(default)]
89    pub xs: Option<ColumnSize>,
90    /// Column size at sm breakpoint.
91    #[props(default)]
92    pub sm: Option<ColumnSize>,
93    /// Column size at md breakpoint.
94    #[props(default)]
95    pub md: Option<ColumnSize>,
96    /// Column size at lg breakpoint.
97    #[props(default)]
98    pub lg: Option<ColumnSize>,
99    /// Column size at xl breakpoint.
100    #[props(default)]
101    pub xl: Option<ColumnSize>,
102    /// Column size at xxl breakpoint.
103    #[props(default)]
104    pub xxl: Option<ColumnSize>,
105    /// Offset at xs breakpoint.
106    #[props(default)]
107    pub offset: Option<u8>,
108    /// Offset at sm breakpoint.
109    #[props(default)]
110    pub offset_sm: Option<u8>,
111    /// Offset at md breakpoint.
112    #[props(default)]
113    pub offset_md: Option<u8>,
114    /// Offset at lg breakpoint.
115    #[props(default)]
116    pub offset_lg: Option<u8>,
117    /// Offset at xl breakpoint.
118    #[props(default)]
119    pub offset_xl: Option<u8>,
120    /// Offset at xxl breakpoint.
121    #[props(default)]
122    pub offset_xxl: Option<u8>,
123    /// Column order (0-5 or "first"/"last" via class prop).
124    #[props(default)]
125    pub order: Option<u8>,
126    /// Column order at sm breakpoint.
127    #[props(default)]
128    pub order_sm: Option<u8>,
129    /// Column order at md breakpoint.
130    #[props(default)]
131    pub order_md: Option<u8>,
132    /// Column order at lg breakpoint.
133    #[props(default)]
134    pub order_lg: Option<u8>,
135    /// Additional CSS classes.
136    #[props(default)]
137    pub class: String,
138    /// Child elements.
139    pub children: Element,
140}
141
142#[component]
143pub fn Col(props: ColProps) -> Element {
144    let mut classes = Vec::new();
145
146    if let Some(size) = &props.xs {
147        classes.push(format!("col-{size}"));
148    }
149    if let Some(size) = &props.sm {
150        classes.push(format!("col-sm-{size}"));
151    }
152    if let Some(size) = &props.md {
153        classes.push(format!("col-md-{size}"));
154    }
155    if let Some(size) = &props.lg {
156        classes.push(format!("col-lg-{size}"));
157    }
158    if let Some(size) = &props.xl {
159        classes.push(format!("col-xl-{size}"));
160    }
161    if let Some(size) = &props.xxl {
162        classes.push(format!("col-xxl-{size}"));
163    }
164
165    // Default to "col" if no breakpoints specified
166    if classes.is_empty() {
167        classes.push("col".to_string());
168    }
169
170    // Offsets
171    if let Some(n) = props.offset {
172        classes.push(format!("offset-{n}"));
173    }
174    if let Some(n) = props.offset_sm {
175        classes.push(format!("offset-sm-{n}"));
176    }
177    if let Some(n) = props.offset_md {
178        classes.push(format!("offset-md-{n}"));
179    }
180    if let Some(n) = props.offset_lg {
181        classes.push(format!("offset-lg-{n}"));
182    }
183    if let Some(n) = props.offset_xl {
184        classes.push(format!("offset-xl-{n}"));
185    }
186    if let Some(n) = props.offset_xxl {
187        classes.push(format!("offset-xxl-{n}"));
188    }
189
190    // Order
191    if let Some(n) = props.order {
192        classes.push(format!("order-{n}"));
193    }
194    if let Some(n) = props.order_sm {
195        classes.push(format!("order-sm-{n}"));
196    }
197    if let Some(n) = props.order_md {
198        classes.push(format!("order-md-{n}"));
199    }
200    if let Some(n) = props.order_lg {
201        classes.push(format!("order-lg-{n}"));
202    }
203
204    if !props.class.is_empty() {
205        classes.push(props.class.clone());
206    }
207
208    let full_class = classes.join(" ");
209
210    rsx! {
211        div { class: "{full_class}", {props.children} }
212    }
213}