dioxus_tw_components/components/
modal.rs1use crate::components::icon::*;
2use crate::dioxus_core::IntoAttributeValue;
3use dioxus::prelude::*;
4use dioxus_core::AttributeValue;
5
6#[derive(Clone, Copy)]
7pub struct ModalState {
8 is_active: bool,
9}
10
11impl ModalState {
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
21impl IntoAttributeValue for ModalState {
22 fn into_value(self) -> AttributeValue {
23 match self.is_active {
24 true => AttributeValue::Text("active".to_string()),
25 false => AttributeValue::Text("inactive".to_string()),
26 }
27 }
28}
29
30#[derive(Clone, PartialEq, Props)]
31pub struct ModalProps {
32 #[props(default = false)]
33 is_active: bool,
34
35 children: Element,
36}
37
38#[component]
39pub fn Modal(props: ModalProps) -> Element {
40 let mut state = use_context_provider(|| Signal::new(ModalState::new(props.is_active)));
41
42 rsx! {
43 div {
44 tabindex: 0,
45 onkeydown: move |e: KeyboardEvent| {
46 if e.key() == Key::Escape {
47 state.write().toggle();
48 }
49 },
50 {props.children}
51 }
52 }
53}
54
55#[derive(Clone, PartialEq, Props)]
56pub struct ModalTriggerProps {
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 ModalTrigger(mut props: ModalTriggerProps) -> Element {
68 let mut state = use_context::<Signal<ModalState>>();
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 event.stop_propagation();
75 state.write().toggle();
76 props.onclick.call(event)
77 };
78
79 rsx! {
80 button { onclick, ..props.attributes, {props.children} }
81 }
82}
83
84#[derive(Clone, PartialEq, Props)]
85pub struct ModalCloseProps {
86 #[props(extends = div, extends = GlobalAttributes)]
87 attributes: Vec<Attribute>,
88
89 #[props(default)]
90 children: Element,
91}
92
93impl std::default::Default for ModalCloseProps {
94 fn default() -> Self {
95 Self {
96 attributes: Vec::<Attribute>::default(),
97 children: Ok(VNode::default()), }
99 }
100}
101
102#[component]
105pub fn ModalClose(mut props: ModalCloseProps) -> Element {
106 let mut state = use_context::<Signal<ModalState>>();
107
108 let has_children = props.children != Ok(VNode::default());
109
110 if !has_children {
111 let default_classes = "modal-close";
112 crate::setup_class_attribute(&mut props.attributes, default_classes);
113 }
114
115 let onclick = move |event: Event<MouseData>| {
116 event.stop_propagation();
117 state.write().toggle();
118 };
119
120 rsx! {
121 div {
122 "data-state": state.read().into_value(),
123 onclick,
124 ..props.attributes,
125 if !has_children {
126 Icon { icon: Icons::Close }
127 } else {
128 {props.children}
129 }
130 }
131 }
132}
133
134#[derive(Clone, PartialEq, Props)]
135pub struct ModalContentProps {
136 #[props(extends = div, extends = GlobalAttributes)]
137 attributes: Vec<Attribute>,
138
139 children: Element,
140}
141
142#[component]
143pub fn ModalContent(mut props: ModalContentProps) -> Element {
144 let state = use_context::<Signal<ModalState>>();
145
146 let default_classes = "modal-content";
147 crate::setup_class_attribute(&mut props.attributes, default_classes);
148
149 rsx! {
150 div {
151 "data-state": state.read().into_value(),
152 ..props.attributes,
153 {props.children}
154 }
155 }
156}
157
158#[derive(Clone, PartialEq, Props)]
159pub struct ModalBackgroundProps {
160 #[props(optional, default = true)]
161 interactive: bool,
162
163 #[props(extends = div, extends = GlobalAttributes)]
164 attributes: Vec<Attribute>,
165
166 #[props(optional, default)]
167 onclick: EventHandler<MouseEvent>,
168
169 children: Element,
170}
171
172#[component]
173pub fn ModalBackground(mut props: ModalBackgroundProps) -> Element {
174 let mut state = use_context::<Signal<ModalState>>();
175
176 let default_classes = "modal-background";
177 crate::setup_class_attribute(&mut props.attributes, default_classes);
178
179 let onclick = move |event: Event<MouseData>| {
180 event.stop_propagation();
181 if props.interactive {
182 state.write().toggle();
183 props.onclick.call(event)
184 }
185 };
186
187 rsx! {
188 div {
189 "data-state": state.read().into_value(),
190 onclick,
191 ..props.attributes,
192 {props.children}
193 }
194 }
195}