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