dioxus_tw_components/components/molecules/sidepanel/
props.rs

1use crate::{attributes::*, components::atoms::icon::*};
2use dioxus::prelude::*;
3use dioxus_core::AttributeValue;
4use dioxus_tw_components_macro::UiComp;
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, UiComp)]
39pub struct SidePanelProps {
40    #[props(default = false)]
41    is_active: bool,
42
43    children: Element,
44}
45
46impl std::default::Default for SidePanelProps {
47    fn default() -> Self {
48        Self {
49            is_active: false,
50            children: rsx! {},
51        }
52    }
53}
54
55#[component]
56pub fn SidePanel(props: SidePanelProps) -> Element {
57    use_context_provider(|| Signal::new(SidePanelState::new(props.is_active)));
58
59    rsx! {
60        {props.children}
61    }
62}
63
64#[derive(Clone, PartialEq, Props, UiComp)]
65pub struct SidePanelTriggerProps {
66    #[props(extends = div, extends = GlobalAttributes)]
67    attributes: Vec<Attribute>,
68
69    #[props(optional, default)]
70    onclick: EventHandler<MouseEvent>,
71
72    children: Element,
73}
74
75impl std::default::Default for SidePanelTriggerProps {
76    fn default() -> Self {
77        Self {
78            attributes: Vec::<Attribute>::default(),
79            onclick: EventHandler::<MouseEvent>::default(),
80            children: rsx! {},
81        }
82    }
83}
84
85#[component]
86pub fn SidePanelTrigger(mut props: SidePanelTriggerProps) -> Element {
87    let mut state = use_context::<Signal<SidePanelState>>();
88
89    props.update_class_attribute();
90
91    let onclick = move |event: Event<MouseData>| {
92        state.write().open();
93        props.onclick.call(event)
94    };
95
96    rsx! {
97        div { onclick, ..props.attributes, {props.children} }
98    }
99}
100
101#[derive(Clone, PartialEq, Props, UiComp)]
102pub struct SidePanelCloseProps {
103    #[props(extends = div, extends = GlobalAttributes)]
104    attributes: Vec<Attribute>,
105
106    #[props(optional)]
107    children: Element,
108}
109
110impl std::default::Default for SidePanelCloseProps {
111    fn default() -> Self {
112        Self {
113            attributes: Vec::<Attribute>::default(),
114            children: Ok(VNode::default()), // Default this way to be able to check the children in SidePanelClose
115        }
116    }
117}
118
119/// Div to close the content side panel, by default it is a cross located at the top left corner of the side panel
120/// If you provide a children, it will be used instead of the default cross and no internal styling will be provided
121#[component]
122pub fn SidePanelClose(mut props: SidePanelCloseProps) -> Element {
123    let mut state = use_context::<Signal<SidePanelState>>();
124
125    let has_children = props.children != Ok(VNode::default());
126
127    if !has_children {
128        props.update_class_attribute();
129    }
130
131    let onclick = move |event: Event<MouseData>| {
132        event.stop_propagation();
133        state.write().close();
134    };
135
136    rsx! {
137        div { onclick, ..props.attributes,
138            if !has_children {
139                Icon { icon: Icons::Close }
140            } else {
141                {props.children}
142            }
143        }
144    }
145}
146
147#[derive(Clone, PartialEq, Props, UiComp)]
148pub struct SidePanelContentProps {
149    #[props(extends = div, extends = GlobalAttributes)]
150    attributes: Vec<Attribute>,
151
152    #[props(optional, default)]
153    pub animation: ReadOnlySignal<Animation>,
154    #[props(optional, default)]
155    pub side: ReadOnlySignal<Side>,
156
157    children: Element,
158}
159
160impl std::default::Default for SidePanelContentProps {
161    fn default() -> Self {
162        Self {
163            attributes: Vec::<Attribute>::default(),
164            animation: ReadOnlySignal::<Animation>::default(),
165            side: ReadOnlySignal::<Side>::default(),
166            children: rsx! {},
167        }
168    }
169}
170
171#[component]
172pub fn SidePanelContent(mut props: SidePanelContentProps) -> Element {
173    let state = use_context::<Signal<SidePanelState>>();
174
175    props.update_class_attribute();
176
177    rsx! {
178        div { "data-state": state.read().into_value(), ..props.attributes, {props.children} }
179    }
180}
181
182#[derive(Clone, PartialEq, Props, UiComp)]
183pub struct SidePanelBackgroundProps {
184    #[props(optional, default = true)]
185    interactive: bool,
186
187    #[props(extends = div, extends = GlobalAttributes)]
188    attributes: Vec<Attribute>,
189
190    #[props(optional, default)]
191    pub color: ReadOnlySignal<Color>,
192    #[props(optional, default)]
193    pub animation: ReadOnlySignal<Animation>,
194    #[props(optional, default)]
195    onclick: EventHandler<MouseEvent>,
196
197    children: Element,
198}
199
200impl std::default::Default for SidePanelBackgroundProps {
201    fn default() -> Self {
202        Self {
203            interactive: true,
204            attributes: Vec::<Attribute>::default(),
205            color: ReadOnlySignal::<Color>::default(),
206            animation: ReadOnlySignal::<Animation>::default(),
207            onclick: EventHandler::<MouseEvent>::default(),
208            children: rsx! {},
209        }
210    }
211}
212
213#[component]
214pub fn SidePanelBackground(mut props: SidePanelBackgroundProps) -> Element {
215    let mut state = use_context::<Signal<SidePanelState>>();
216
217    props.update_class_attribute();
218
219    let onclick = move |event: Event<MouseData>| {
220        event.stop_propagation();
221        if props.interactive {
222            state.write().close();
223            props.onclick.call(event)
224        }
225    };
226
227    rsx! {
228        div {
229            "data-state": state.read().into_value(),
230            onclick,
231            ..props.attributes,
232            {props.children}
233        }
234    }
235}