1#![allow(non_snake_case)]
2use dioxus::prelude::*;
3
4#[derive(Debug, Clone, Copy, Default)]
5pub struct PopupService {
6 pub id: Signal<Option<String>>,
7 pub title: Signal<Option<String>>,
8 pub data: Signal<Option<Element>>,
9 pub close: Signal<bool>,
10}
11
12impl PopupService {
13 pub fn init() {
14 let srv = Self {
15 data: Signal::new(None),
16 id: Signal::new(None),
17 title: Signal::new(None),
18 close: Signal::new(true),
19 };
20 use_context_provider(|| srv);
21 }
22
23 pub fn render(&self) -> Element {
24 (self.data)().clone().unwrap_or(default())
25 }
26
27 pub fn is_opened(&self) -> bool {
28 (self.data)().is_some()
29 }
30
31 pub fn get_id(&self) -> String {
32 (self.id)().clone().unwrap_or("popup-zone".to_string())
33 }
34
35 pub fn get_title(&self) -> Option<String> {
36 (self.title)().clone()
37 }
38
39 pub fn open(&mut self, popup: Element) -> &mut Self {
40 (self.data).set(Some(popup));
41
42 self
43 }
44
45 pub fn with_id(&mut self, id: &str) -> &mut Self {
46 (self.id).set(Some(id.to_string()));
47
48 self
49 }
50
51 pub fn with_title(&mut self, title: &str) -> &mut Self {
52 (self.title).set(Some(title.to_string()));
53
54 self
55 }
56
57 pub fn without_close(&mut self) -> &mut Self {
58 (self.close).set(false);
59
60 self
61 }
62
63 pub fn close(&mut self) {
64 (self.data).set(None);
65 (self.id).set(None);
66 (self.title).set(None);
67 (self.close).set(true);
68 }
69
70 pub fn use_popup_service() -> PopupService {
71 use_context()
72 }
73}
74
75#[component]
76pub fn default() -> Element {
77 rsx! {}
78}
79
80#[component]
81pub fn PopupZone() -> Element {
82 let mut popup: PopupService = use_context();
83 let mut hover_close = use_signal(|| false);
84
85 rsx! {
86 div {
87 class: format!(
88 "{}",
89 match popup.is_opened() {
90 true => {
91 "fixed top-0 left-0 w-screen h-screen bg-black bg-opacity-50 flex justify-center items-center backdrop-blur-[4px] bg-black/25 z-[101]"
92 }
93 false => "hidden",
94 },
95 ),
96 onclick: move |_| {
97 popup.close();
98 },
99 if popup.is_opened() {
100 div {
101 class: "relative bg-[#424563] rounded-[12px] border-[#292B3C] border-[1px] p-[25px] min-w-[350px]",
102 onclick: move |e| {
103 e.stop_propagation();
104 },
105 if (popup.close)() {
106 div {
107 class: format!(
108 "absolute top-[25px] right-[25px] rounded-[4px] cursor-pointer {}",
109 if hover_close() { "bg-[{theme.background}]" } else { "" },
110 ),
111 onclick: move |_| {
112 popup.close();
113 },
114 onmouseenter: move |_| {
115 hover_close.set(true);
116 },
117 onmouseleave: move |_| {
118 hover_close.set(false);
119 },
120 Close { color: if hover_close() { "#74789E" } else { "white" } }
121 }
122 }
123 div {
124 id: popup.get_id(),
125 class: "flex flex-col items-center justify-center gap-[25px]",
126 match popup.get_title() {
127 Some(title) => {
128 rsx! {
129 div { class: "text-[20px] font-bold text-white", "{title}" }
130 }
131 }
132 None => rsx! {},
133 }
134 {popup.render()}
135 }
136 }
137 }
138 }
139 }
140}
141
142#[component]
143pub fn Close(#[props(default = "white".to_string())] color: String) -> Element {
144 rsx! {
145 svg {
146 width: "24",
147 height: "24",
148 view_box: "0 0 24 24",
149 fill: "none",
150 xmlns: "http://www.w3.org/2000/svg",
151 path {
152 d: "M16.9498 7.05029L7.05029 16.9498M7.05029 7.05029L16.9498 16.9498",
153 stroke: "{color}",
154 stroke_width: "2",
155 stroke_linecap: "round",
156 stroke_linejoin: "round",
157 }
158 }
159 }
160}