dioxus_bulma/components/
tabs.rs1use crate::theme::BulmaSize;
2use crate::utils::build_class;
3use dioxus::prelude::*;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum TabsStyle {
7 Default,
8 Boxed,
9 Toggle,
10 ToggleRounded,
11}
12
13impl TabsStyle {
14 pub fn as_str(&self) -> &'static str {
15 match self {
16 TabsStyle::Default => "",
17 TabsStyle::Boxed => "is-boxed",
18 TabsStyle::Toggle => "is-toggle",
19 TabsStyle::ToggleRounded => "is-toggle is-toggle-rounded",
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum TabsAlignment {
26 Left,
27 Centered,
28 Right,
29}
30
31impl TabsAlignment {
32 pub fn as_str(&self) -> &'static str {
33 match self {
34 TabsAlignment::Left => "",
35 TabsAlignment::Centered => "is-centered",
36 TabsAlignment::Right => "is-right",
37 }
38 }
39}
40
41#[derive(Props, Clone, PartialEq)]
42pub struct TabsProps {
43 #[props(default)]
44 pub size: Option<BulmaSize>,
45 #[props(default)]
46 pub style: Option<TabsStyle>,
47 #[props(default)]
48 pub alignment: Option<TabsAlignment>,
49 #[props(default)]
50 pub fullwidth: Option<bool>,
51 #[props(default)]
52 pub id: Option<String>,
53 #[props(default)]
54 pub class: Option<String>,
55 #[props(default)]
56 pub css_style: Option<String>,
57 pub children: Element,
58}
59
60#[component]
61pub fn Tabs(props: TabsProps) -> Element {
62 let size = props.size.unwrap_or_default();
63 let style = props.style.unwrap_or(TabsStyle::Default);
64 let alignment = props.alignment.unwrap_or(TabsAlignment::Left);
65 let fullwidth = props.fullwidth.unwrap_or(false);
66
67 let base_classes = vec!["tabs"];
68 let optional_classes = vec![
69 if size != BulmaSize::Normal { Some(size.as_class().to_string()) } else { None },
70 if style != TabsStyle::Default {
71 if style == TabsStyle::ToggleRounded {
72 Some("is-toggle is-toggle-rounded".to_string())
73 } else {
74 Some(style.as_str().to_string())
75 }
76 } else { None },
77 if alignment != TabsAlignment::Left { Some(alignment.as_str().to_string()) } else { None },
78 if fullwidth { Some("is-fullwidth".to_string()) } else { None },
79 props.class.clone(),
80 ];
81
82 let final_class = build_class(&base_classes, &optional_classes);
83 let tabs_style = props.css_style.as_deref().unwrap_or("");
84
85 rsx! {
86 div {
87 class: "{final_class}",
88 style: "{tabs_style}",
89 id: props.id.clone(),
90 ul {
91 {props.children}
92 }
93 }
94 }
95}
96
97#[derive(Props, Clone, PartialEq)]
98pub struct TabProps {
99 #[props(default)]
100 pub active: Option<bool>,
101 #[props(default)]
102 pub disabled: Option<bool>,
103 #[props(default)]
104 pub href: Option<String>,
105 #[props(default)]
106 pub onclick: Option<EventHandler<MouseEvent>>,
107 #[cfg(feature = "router")]
109 #[props(default, into)]
110 pub to: crate::router_helpers::MaybeNav,
111 #[props(default)]
112 pub id: Option<String>,
113 #[props(default)]
114 pub class: Option<String>,
115 #[props(default)]
116 pub style: Option<String>,
117 pub children: Element,
118}
119
120#[component]
121pub fn Tab(props: TabProps) -> Element {
122 let active = props.active.unwrap_or(false);
123 let disabled = props.disabled.unwrap_or(false);
124
125 let base_classes = vec![];
126 let optional_classes = vec![
127 if active { Some("is-active".to_string()) } else { None },
128 props.class.clone(),
129 ];
130
131 let final_class = build_class(&base_classes, &optional_classes);
132 let tab_style = props.style.as_deref().unwrap_or("");
133
134 rsx! {
135 li {
136 class: "{final_class}",
137 style: "{tab_style}",
138 id: props.id.clone(),
139
140 if let Some(href) = props.href {
141 if !disabled {
142 a {
143 href: "{href}",
144 onclick: move |evt| {
145 if let Some(handler) = &props.onclick {
146 handler.call(evt);
147 }
148 },
149 {props.children}
150 }
151 } else {
152 span { {props.children} }
153 }
154 } else {
155 a {
156 onclick: move |evt| {
157 if !disabled {
158 if let Some(handler) = &props.onclick {
159 handler.call(evt);
160 }
161 }
162 },
163 {props.children}
164 }
165 }
166 }
167 }
168}