Skip to main content

dioxus_tw_components/components/
sidepanel.rs

1use crate::components::icon::*;
2use crate::dioxus_core::IntoAttributeValue;
3use dioxus::prelude::*;
4use dioxus_core::AttributeValue;
5
6#[derive(Clone, Copy)]
7pub struct SidePanelState {
8    is_active: bool,
9}
10
11impl SidePanelState {
12    fn new(is_active: bool) -> Self {
13        Self { is_active }
14    }
15
16    pub fn toggle(&mut self) {
17        self.is_active = !self.is_active;
18    }
19
20    pub fn open(&mut self) {
21        self.is_active = true;
22    }
23
24    pub fn close(&mut self) {
25        self.is_active = false;
26    }
27}
28
29impl IntoAttributeValue for SidePanelState {
30    fn into_value(self) -> AttributeValue {
31        match self.is_active {
32            true => AttributeValue::Text("active".to_string()),
33            false => AttributeValue::Text("inactive".to_string()),
34        }
35    }
36}
37
38#[derive(Clone, PartialEq, Props)]
39pub struct SidePanelProps {
40    #[props(default = false)]
41    is_active: bool,
42
43    children: Element,
44}
45
46#[component]
47pub fn SidePanel(props: SidePanelProps) -> Element {
48    use_context_provider(|| Signal::new(SidePanelState::new(props.is_active)));
49
50    rsx! {
51        {props.children}
52    }
53}
54
55#[derive(Clone, PartialEq, Props)]
56pub struct SidePanelTriggerProps {
57    #[props(extends = div, extends = GlobalAttributes)]
58    attributes: Vec<Attribute>,
59
60    #[props(optional, default)]
61    onclick: EventHandler<MouseEvent>,
62
63    children: Element,
64}
65
66#[component]
67pub fn SidePanelTrigger(mut props: SidePanelTriggerProps) -> Element {
68    let mut state = use_context::<Signal<SidePanelState>>();
69
70    let default_classes = "button";
71    crate::setup_class_attribute(&mut props.attributes, default_classes);
72
73    let onclick = move |event: Event<MouseData>| {
74        state.write().open();
75        props.onclick.call(event)
76    };
77
78    rsx! {
79        button { onclick, ..props.attributes, {props.children} }
80    }
81}
82
83#[derive(Clone, PartialEq, Props)]
84pub struct SidePanelCloseProps {
85    #[props(extends = div, extends = GlobalAttributes)]
86    attributes: Vec<Attribute>,
87
88    #[props(default)]
89    children: Element,
90}
91
92impl std::default::Default for SidePanelCloseProps {
93    fn default() -> Self {
94        Self {
95            attributes: Vec::<Attribute>::default(),
96            children: Ok(VNode::default()), // Default this way to be able to check the children in SidePanelClose
97        }
98    }
99}
100
101/// Div to close the content side panel, by default it is a cross located at the top left corner of the side panel
102/// If you provide a children, it will be used instead of the default cross and no internal styling will be provided
103#[component]
104pub fn SidePanelClose(mut props: SidePanelCloseProps) -> Element {
105    let mut state = use_context::<Signal<SidePanelState>>();
106
107    let has_children = props.children != Ok(VNode::default());
108
109    if !has_children {
110        let default_classes = "sidepanel-close";
111        crate::setup_class_attribute(&mut props.attributes, default_classes);
112    }
113
114    let onclick = move |event: Event<MouseData>| {
115        event.stop_propagation();
116        state.write().close();
117    };
118
119    rsx! {
120        div { onclick, ..props.attributes,
121            if !has_children {
122                Icon { icon: Icons::Close }
123            } else {
124                {props.children}
125            }
126        }
127    }
128}
129
130#[derive(Clone, PartialEq, Props)]
131pub struct SidePanelContentProps {
132    #[props(extends = div, extends = GlobalAttributes)]
133    attributes: Vec<Attribute>,
134
135    children: Element,
136}
137
138#[component]
139pub fn SidePanelContent(mut props: SidePanelContentProps) -> Element {
140    let state = use_context::<Signal<SidePanelState>>();
141
142    let default_classes = "sidepanel-content";
143    crate::setup_class_attribute(&mut props.attributes, default_classes);
144
145    rsx! {
146        div {
147            "data-state": state.read().into_value(),
148            ..props.attributes,
149            {props.children}
150        }
151    }
152}
153
154#[derive(Clone, PartialEq, Props)]
155pub struct SidePanelBackgroundProps {
156    #[props(optional, default = true)]
157    interactive: bool,
158
159    #[props(extends = div, extends = GlobalAttributes)]
160    attributes: Vec<Attribute>,
161
162    #[props(optional, default)]
163    onclick: EventHandler<MouseEvent>,
164
165    children: Element,
166}
167
168#[component]
169pub fn SidePanelBackground(mut props: SidePanelBackgroundProps) -> Element {
170    let mut state = use_context::<Signal<SidePanelState>>();
171
172    let default_classes = "sidepanel-background";
173    crate::setup_class_attribute(&mut props.attributes, default_classes);
174
175    let onclick = move |event: Event<MouseData>| {
176        event.stop_propagation();
177        if props.interactive {
178            state.write().close();
179            props.onclick.call(event)
180        }
181    };
182
183    rsx! {
184        div {
185            "data-state": state.read().into_value(),
186            onclick,
187            ..props.attributes,
188            {props.children}
189        }
190    }
191}