Skip to main content

dioxus_tw_components/components/
tabs.rs

1use crate::DropdownManager;
2use dioxus::prelude::*;
3
4struct TabsState(String);
5
6#[derive(Clone, PartialEq, Props)]
7pub struct TabsProps {
8    #[props(optional)]
9    default_tab: ReadSignal<String>,
10
11    #[props(extends = div, extends = GlobalAttributes)]
12    attributes: Vec<Attribute>,
13
14    children: Element,
15}
16
17#[component]
18pub fn Tabs(props: TabsProps) -> Element {
19    use_context_provider(|| Signal::new(TabsState(props.default_tab.read().clone())));
20
21    rsx! {
22        div { ..props.attributes, {props.children} }
23    }
24}
25
26#[derive(Clone, PartialEq, Props)]
27pub struct TabsListProps {
28    #[props(extends = div, extends = GlobalAttributes)]
29    attributes: Vec<Attribute>,
30
31    children: Element,
32}
33
34#[component]
35pub fn TabsList(mut props: TabsListProps) -> Element {
36    let default_classes = "tabs-list";
37    crate::setup_class_attribute(&mut props.attributes, default_classes);
38
39    rsx! {
40        div { ..props.attributes, {props.children} }
41    }
42}
43
44#[derive(Clone, PartialEq, Props)]
45pub struct TabsTriggerProps {
46    #[props(extends = button, extends = GlobalAttributes)]
47    attributes: Vec<Attribute>,
48
49    #[props(optional)]
50    id: ReadSignal<String>,
51
52    children: Element,
53}
54
55#[component]
56pub fn TabsTrigger(mut props: TabsTriggerProps) -> Element {
57    let mut state = use_context::<Signal<TabsState>>();
58    let manager = try_use_context::<Signal<DropdownManager>>();
59
60    let default_classes = "tabs-trigger";
61    crate::setup_class_attribute(&mut props.attributes, default_classes);
62
63    let is_active = state.read().0 == *props.id.read();
64
65    let onclick = move |_: MouseEvent| {
66        state.write().0 = props.id.read().clone();
67        // Close all open dropdowns
68        if let Some(mut mgr) = manager {
69            mgr.write().advance();
70        }
71    };
72
73    rsx! {
74        button {
75            "data-state": if is_active { "active" } else { "inactive" },
76            onclick,
77            ..props.attributes,
78            {props.children}
79        }
80    }
81}
82
83#[derive(Clone, PartialEq, Props)]
84pub struct TabsContentProps {
85    #[props(extends = div, extends = GlobalAttributes)]
86    attributes: Vec<Attribute>,
87
88    #[props(optional)]
89    id: ReadSignal<String>,
90
91    children: Element,
92}
93
94#[component]
95pub fn TabsContent(mut props: TabsContentProps) -> Element {
96    let state = use_context::<Signal<TabsState>>();
97
98    let default_classes = "tabs-content";
99    crate::setup_class_attribute(&mut props.attributes, default_classes);
100
101    let is_active = state.read().0 == *props.id.read();
102
103    rsx! {
104        div {
105            "data-state": if is_active { "active" } else { "inactive" },
106            ..props.attributes,
107            {props.children}
108        }
109    }
110}