dioxus_bootstrap_css/
nav.rs1use dioxus::prelude::*;
2
3use crate::types::{Color, NavbarExpand};
4
5#[derive(Clone, PartialEq, Props)]
43pub struct NavProps {
44 #[props(default)]
46 pub pills: bool,
47 #[props(default)]
49 pub tabs: bool,
50 #[props(default)]
52 pub underline: bool,
53 #[props(default)]
55 pub fill: bool,
56 #[props(default)]
58 pub justified: bool,
59 #[props(default)]
61 pub vertical: bool,
62 #[props(default)]
64 pub class: String,
65 #[props(extends = GlobalAttributes)]
67 attributes: Vec<Attribute>,
68 pub children: Element,
70}
71
72#[component]
73pub fn Nav(props: NavProps) -> Element {
74 let mut classes = vec!["nav".to_string()];
75 if props.pills {
76 classes.push("nav-pills".to_string());
77 }
78 if props.tabs {
79 classes.push("nav-tabs".to_string());
80 }
81 if props.underline {
82 classes.push("nav-underline".to_string());
83 }
84 if props.fill {
85 classes.push("nav-fill".to_string());
86 }
87 if props.justified {
88 classes.push("nav-justified".to_string());
89 }
90 if props.vertical {
91 classes.push("flex-column".to_string());
92 }
93 if !props.class.is_empty() {
94 classes.push(props.class.clone());
95 }
96 let full_class = classes.join(" ");
97
98 rsx! {
99 ul { class: "{full_class}",
100 ..props.attributes,
101 {props.children}
102 }
103 }
104}
105
106#[derive(Clone, PartialEq, Props)]
140pub struct NavbarProps {
141 #[props(default)]
143 pub color: Option<Color>,
144 #[props(default)]
146 pub expand: NavbarExpand,
147 #[props(default)]
149 pub brand: Option<Element>,
150 #[props(default)]
152 pub class: String,
153 #[props(extends = GlobalAttributes)]
155 attributes: Vec<Attribute>,
156 pub children: Element,
158}
159
160#[component]
161pub fn Navbar(props: NavbarProps) -> Element {
162 let mut classes = vec!["navbar".to_string(), props.expand.to_string()];
163
164 if let Some(ref color) = props.color {
165 match color {
166 Color::Dark => {
167 classes.push("bg-dark".to_string());
168 classes.push("[data-bs-theme=dark]".to_string());
169 }
170 Color::Light => {
171 classes.push("bg-light".to_string());
172 }
173 c => {
174 classes.push(format!("bg-{c}"));
175 }
176 }
177 }
178
179 if !props.class.is_empty() {
180 classes.push(props.class.clone());
181 }
182
183 let full_class = classes.join(" ");
184
185 rsx! {
186 nav { class: "{full_class}",
187 ..props.attributes,
188 div { class: "container-fluid",
189 if let Some(brand) = props.brand {
190 {brand}
191 }
192 {props.children}
193 }
194 }
195 }
196}
197
198#[derive(Clone, PartialEq, Props)]
206pub struct NavbarTogglerProps {
207 pub collapsed: Signal<bool>,
209 #[props(default)]
211 pub class: String,
212 #[props(extends = GlobalAttributes)]
214 attributes: Vec<Attribute>,
215}
216
217#[component]
218pub fn NavbarToggler(props: NavbarTogglerProps) -> Element {
219 let is_collapsed = *props.collapsed.read();
220 let mut signal = props.collapsed;
221
222 let full_class = if props.class.is_empty() {
223 "navbar-toggler".to_string()
224 } else {
225 format!("navbar-toggler {}", props.class)
226 };
227
228 rsx! {
229 button {
230 class: "{full_class}",
231 r#type: "button",
232 "aria-expanded": if !is_collapsed { "true" } else { "false" },
233 "aria-label": "Toggle navigation",
234 onclick: move |_| signal.set(!is_collapsed),
235 ..props.attributes,
236 span { class: "navbar-toggler-icon" }
237 }
238 }
239}
240
241#[derive(Clone, PartialEq, Props)]
253pub struct NavbarCollapseProps {
254 pub collapsed: Signal<bool>,
256 #[props(default)]
258 pub class: String,
259 #[props(extends = GlobalAttributes)]
261 attributes: Vec<Attribute>,
262 pub children: Element,
264}
265
266#[component]
267pub fn NavbarCollapse(props: NavbarCollapseProps) -> Element {
268 let is_collapsed = *props.collapsed.read();
269 let show = if !is_collapsed { " show" } else { "" };
270
271 let full_class = if props.class.is_empty() {
272 format!("collapse navbar-collapse{show}")
273 } else {
274 format!("collapse navbar-collapse{show} {}", props.class)
275 };
276
277 rsx! {
278 div { class: "{full_class}",
279 ..props.attributes,
280 ul { class: "navbar-nav me-auto mb-2 mb-lg-0",
281 {props.children}
282 }
283 }
284 }
285}
286
287#[derive(Clone, PartialEq, Props)]
289pub struct NavItemProps {
290 #[props(default)]
292 pub class: String,
293 #[props(extends = GlobalAttributes)]
295 attributes: Vec<Attribute>,
296 pub children: Element,
298}
299
300#[component]
301pub fn NavItem(props: NavItemProps) -> Element {
302 let full_class = if props.class.is_empty() {
303 "nav-item".to_string()
304 } else {
305 format!("nav-item {}", props.class)
306 };
307
308 rsx! {
309 li { class: "{full_class}", ..props.attributes, {props.children} }
310 }
311}
312
313#[derive(Clone, PartialEq, Props)]
321pub struct NavLinkProps {
322 #[props(default = "#".to_string())]
324 pub href: String,
325 #[props(default)]
327 pub active: bool,
328 #[props(default)]
330 pub disabled: bool,
331 #[props(default)]
333 pub onclick: Option<EventHandler<MouseEvent>>,
334 #[props(default)]
336 pub class: String,
337 #[props(extends = GlobalAttributes)]
339 attributes: Vec<Attribute>,
340 pub children: Element,
342}
343
344#[component]
345pub fn NavLink(props: NavLinkProps) -> Element {
346 let mut classes = vec!["nav-link".to_string()];
347 if props.active {
348 classes.push("active".to_string());
349 }
350 if props.disabled {
351 classes.push("disabled".to_string());
352 }
353 if !props.class.is_empty() {
354 classes.push(props.class.clone());
355 }
356 let full_class = classes.join(" ");
357
358 rsx! {
359 a {
360 class: "{full_class}",
361 href: "{props.href}",
362 "aria-current": if props.active { "page" } else { "" },
363 onclick: move |evt| {
364 if let Some(handler) = &props.onclick {
365 handler.call(evt);
366 }
367 },
368 ..props.attributes,
369 {props.children}
370 }
371 }
372}