Skip to main content

dioxus_bulma/components/
tabs.rs

1use 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    /// If present, use router navigation instead of href
108    #[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}