use dioxus::prelude::*;
#[derive(Clone, PartialEq)]
pub struct TabDef {
pub label: String,
pub icon: Option<String>,
pub content: Element,
}
#[derive(Clone, PartialEq, Props)]
pub struct TabListProps {
pub active: Signal<usize>,
pub tabs: Vec<TabDef>,
#[props(default)]
pub pills: bool,
#[props(default)]
pub fill: bool,
#[props(default)]
pub justified: bool,
#[props(default)]
pub class: String,
#[props(default)]
pub content_class: String,
}
#[component]
pub fn TabList(props: TabListProps) -> Element {
let current = *props.active.read();
let mut active_signal = props.active;
let style = if props.pills { "nav-pills" } else { "nav-tabs" };
let mut nav_classes = vec![format!("nav {style}")];
if props.fill {
nav_classes.push("nav-fill".to_string());
}
if props.justified {
nav_classes.push("nav-justified".to_string());
}
if !props.class.is_empty() {
nav_classes.push(props.class.clone());
}
let nav_class = nav_classes.join(" ");
let content_class = if props.content_class.is_empty() {
"tab-content".to_string()
} else {
format!("tab-content {}", props.content_class)
};
rsx! {
ul { class: "{nav_class}", role: "tablist",
for (i, tab) in props.tabs.iter().enumerate() {
li { class: "nav-item", role: "presentation",
button {
class: if current == i { "nav-link active" } else { "nav-link" },
r#type: "button",
role: "tab",
"aria-selected": if current == i { "true" } else { "false" },
onclick: move |_| active_signal.set(i),
if let Some(ref icon) = tab.icon {
i { class: "bi bi-{icon} me-1" }
}
"{tab.label}"
}
}
}
}
div { class: "{content_class}",
for (i, tab) in props.tabs.iter().enumerate() {
div {
class: if current == i { "tab-pane fade show active" } else { "tab-pane fade" },
role: "tabpanel",
if current == i {
{tab.content.clone()}
}
}
}
}
}
}
#[component]
pub fn Tabs(props: TabListProps) -> Element {
TabList(props)
}
pub type TabsProps = TabListProps;