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 { "container-fluid" } else { "container" };
28    let full_class = if props.class.is_empty() {
29        base.to_string()
30    } else {
31        format!("{base} {}", props.class)
32    };
33
34    rsx! {
35        div { class: "{full_class}", {props.children} }
36    }
37}
38
39/// Bootstrap Row component.
40///
41/// ```rust
42/// rsx! {
43///     Row { class: "g-3",
44///         Col { lg: ColumnSize::Span(6), "Left" }
45///         Col { lg: ColumnSize::Span(6), "Right" }
46///     }
47/// }
48/// ```
49#[derive(Clone, PartialEq, Props)]
50pub struct RowProps {
51    /// Additional CSS classes (e.g., "g-3", "align-items-center").
52    #[props(default)]
53    pub class: String,
54    /// Child elements.
55    pub children: Element,
56}
57
58#[component]
59pub fn Row(props: RowProps) -> Element {
60    let full_class = if props.class.is_empty() {
61        "row".to_string()
62    } else {
63        format!("row {}", props.class)
64    };
65
66    rsx! {
67        div { class: "{full_class}", {props.children} }
68    }
69}
70
71/// Bootstrap Col (column) component with responsive breakpoint props.
72///
73/// ```rust
74/// rsx! {
75///     Col { xs: ColumnSize::Span(12), md: ColumnSize::Span(6), lg: ColumnSize::Span(4),
76///         "Responsive column"
77///     }
78///     Col { lg: ColumnSize::Auto, "Auto-width column" }
79/// }
80/// ```
81#[derive(Clone, PartialEq, Props)]
82pub struct ColProps {
83    /// Column size at xs breakpoint (default, no breakpoint prefix).
84    #[props(default)]
85    pub xs: Option<ColumnSize>,
86    /// Column size at sm breakpoint.
87    #[props(default)]
88    pub sm: Option<ColumnSize>,
89    /// Column size at md breakpoint.
90    #[props(default)]
91    pub md: Option<ColumnSize>,
92    /// Column size at lg breakpoint.
93    #[props(default)]
94    pub lg: Option<ColumnSize>,
95    /// Column size at xl breakpoint.
96    #[props(default)]
97    pub xl: Option<ColumnSize>,
98    /// Column size at xxl breakpoint.
99    #[props(default)]
100    pub xxl: Option<ColumnSize>,
101    /// Additional CSS classes.
102    #[props(default)]
103    pub class: String,
104    /// Child elements.
105    pub children: Element,
106}
107
108#[component]
109pub fn Col(props: ColProps) -> Element {
110    let mut classes = Vec::new();
111
112    if let Some(size) = &props.xs {
113        classes.push(format!("col-{size}"));
114    }
115    if let Some(size) = &props.sm {
116        classes.push(format!("col-sm-{size}"));
117    }
118    if let Some(size) = &props.md {
119        classes.push(format!("col-md-{size}"));
120    }
121    if let Some(size) = &props.lg {
122        classes.push(format!("col-lg-{size}"));
123    }
124    if let Some(size) = &props.xl {
125        classes.push(format!("col-xl-{size}"));
126    }
127    if let Some(size) = &props.xxl {
128        classes.push(format!("col-xxl-{size}"));
129    }
130
131    // Default to "col" if no breakpoints specified
132    if classes.is_empty() {
133        classes.push("col".to_string());
134    }
135
136    if !props.class.is_empty() {
137        classes.push(props.class.clone());
138    }
139
140    let full_class = classes.join(" ");
141
142    rsx! {
143        div { class: "{full_class}", {props.children} }
144    }
145}