use dioxus::prelude::*;
use crate::types::{Color, NavbarExpand};
#[derive(Clone, PartialEq, Props)]
pub struct NavProps {
#[props(default)]
pub pills: bool,
#[props(default)]
pub tabs: bool,
#[props(default)]
pub underline: bool,
#[props(default)]
pub fill: bool,
#[props(default)]
pub justified: bool,
#[props(default)]
pub vertical: bool,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn Nav(props: NavProps) -> Element {
let mut classes = vec!["nav".to_string()];
if props.pills {
classes.push("nav-pills".to_string());
}
if props.tabs {
classes.push("nav-tabs".to_string());
}
if props.underline {
classes.push("nav-underline".to_string());
}
if props.fill {
classes.push("nav-fill".to_string());
}
if props.justified {
classes.push("nav-justified".to_string());
}
if props.vertical {
classes.push("flex-column".to_string());
}
if !props.class.is_empty() {
classes.push(props.class.clone());
}
let full_class = classes.join(" ");
rsx! {
ul { class: "{full_class}",
..props.attributes,
{props.children}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct NavbarProps {
#[props(default)]
pub color: Option<Color>,
#[props(default)]
pub expand: NavbarExpand,
#[props(default)]
pub brand: Option<Element>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn Navbar(props: NavbarProps) -> Element {
let mut classes = vec!["navbar".to_string(), props.expand.to_string()];
let is_dark = matches!(props.color.as_ref(), Some(Color::Dark));
if let Some(ref color) = props.color {
match color {
Color::Dark => {
classes.push("bg-dark".to_string());
}
Color::Light => {
classes.push("bg-light".to_string());
}
c => {
classes.push(format!("bg-{c}"));
}
}
}
if !props.class.is_empty() {
classes.push(props.class.clone());
}
let full_class = classes.join(" ");
rsx! {
nav {
class: "{full_class}",
"data-bs-theme": if is_dark { "dark" } else { "" },
..props.attributes,
div { class: "container-fluid",
if let Some(brand) = props.brand {
{brand}
}
{props.children}
}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct NavbarTogglerProps {
pub collapsed: Signal<bool>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
}
#[component]
pub fn NavbarToggler(props: NavbarTogglerProps) -> Element {
let is_collapsed = *props.collapsed.read();
let mut signal = props.collapsed;
let full_class = if props.class.is_empty() {
"navbar-toggler".to_string()
} else {
format!("navbar-toggler {}", props.class)
};
rsx! {
button {
class: "{full_class}",
r#type: "button",
"aria-expanded": if !is_collapsed { "true" } else { "false" },
"aria-label": "Toggle navigation",
onclick: move |_| signal.set(!is_collapsed),
..props.attributes,
span { class: "navbar-toggler-icon" }
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct NavbarCollapseProps {
pub collapsed: Signal<bool>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn NavbarCollapse(props: NavbarCollapseProps) -> Element {
let is_collapsed = *props.collapsed.read();
let show = if !is_collapsed { " show" } else { "" };
let full_class = if props.class.is_empty() {
format!("collapse navbar-collapse{show}")
} else {
format!("collapse navbar-collapse{show} {}", props.class)
};
rsx! {
div { class: "{full_class}",
..props.attributes,
{props.children}
}
}
}
#[derive(Clone, PartialEq, Props)]
pub struct NavItemProps {
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn NavItem(props: NavItemProps) -> Element {
let full_class = if props.class.is_empty() {
"nav-item".to_string()
} else {
format!("nav-item {}", props.class)
};
rsx! {
li { class: "{full_class}", ..props.attributes, {props.children} }
}
}
#[derive(Clone, PartialEq, Props)]
pub struct NavLinkProps {
#[props(default = "#".to_string())]
pub href: String,
#[props(default)]
pub active: bool,
#[props(default)]
pub disabled: bool,
#[props(default)]
pub onclick: Option<EventHandler<MouseEvent>>,
#[props(default)]
pub class: String,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
#[component]
pub fn NavLink(props: NavLinkProps) -> Element {
let mut classes = vec!["nav-link".to_string()];
if props.active {
classes.push("active".to_string());
}
if props.disabled {
classes.push("disabled".to_string());
}
if !props.class.is_empty() {
classes.push(props.class.clone());
}
let full_class = classes.join(" ");
rsx! {
a {
class: "{full_class}",
href: "{props.href}",
"aria-current": if props.active { "page" } else { "" },
onclick: move |evt| {
if let Some(handler) = &props.onclick {
handler.call(evt);
}
},
..props.attributes,
{props.children}
}
}
}