reinhardt_admin/pages/components/
common.rs1use reinhardt_pages::Signal;
16use reinhardt_pages::component::{IntoPage, Page, PageElement};
17
18#[cfg(target_arch = "wasm32")]
19use reinhardt_pages::dom::EventType;
20
21#[cfg(target_arch = "wasm32")]
22use std::sync::Arc;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum ButtonVariant {
27 Primary,
29 Secondary,
31 Success,
33 Danger,
35 Warning,
37}
38
39impl ButtonVariant {
40 pub fn class(&self) -> &'static str {
42 match self {
43 ButtonVariant::Primary => "btn-primary",
44 ButtonVariant::Secondary => "btn-secondary",
45 ButtonVariant::Success => "btn-success",
46 ButtonVariant::Danger => "btn-danger",
47 ButtonVariant::Warning => "btn-warning",
48 }
49 }
50}
51
52pub fn button(text: &str, variant: ButtonVariant, disabled: bool, _on_click: Signal<bool>) -> Page {
67 let classes = format!("btn {}", variant.class());
68
69 #[cfg(target_arch = "wasm32")]
70 let button_view = {
71 PageElement::new("button")
72 .attr("class", classes.clone())
73 .attr("type", "button")
74 .attr("disabled", if disabled { "true" } else { "false" })
75 .on(
76 EventType::Click,
77 Arc::new(move |_event: web_sys::Event| {
78 _on_click.set(true);
79 }),
80 )
81 .child(text.to_string())
82 };
83
84 #[cfg(not(target_arch = "wasm32"))]
85 let button_view = {
86 PageElement::new("button")
88 .attr("class", classes)
89 .attr("type", "button")
90 .attr("disabled", if disabled { "true" } else { "false" })
91 .attr("data-reactive", "true") .child(text.to_string())
93 };
94
95 button_view.into_page()
96}
97
98pub fn loading_spinner() -> Page {
110 PageElement::new("div")
111 .attr("class", "loading-spinner")
112 .child(
113 PageElement::new("div")
114 .attr("class", "spinner-border")
115 .attr("role", "status")
116 .child(
117 PageElement::new("span")
118 .attr("class", "visually-hidden")
119 .child("Loading..."),
120 ),
121 )
122 .into_page()
123}
124
125pub fn error_display(message: &str, dismissible: bool) -> Page {
137 let mut container = PageElement::new("div")
138 .attr("class", "alert alert-danger")
139 .attr("role", "alert");
140
141 if dismissible {
142 container = container.child(
143 PageElement::new("button")
144 .attr("class", "btn-close")
145 .attr("type", "button")
146 .attr("data-bs-dismiss", "alert")
147 .attr("aria-label", "Close"),
148 );
149 }
150
151 container.child(message.to_string()).into_page()
152}
153
154pub fn pagination(current_page: Signal<u64>, total_pages: u64) -> Page {
169 let current_val = current_page.get();
170 let mut nav_items = Vec::new();
171
172 let prev_disabled = current_val <= 1;
174 nav_items.push(create_page_item(
175 "Previous",
176 prev_disabled,
177 false,
178 current_page.clone(),
179 move |page: Signal<u64>| {
180 let current = page.get();
181 if current > 1 {
182 page.set(current - 1);
183 }
184 },
185 ));
186
187 let start = current_val.saturating_sub(2).max(1);
189 let end = (current_val + 2).min(total_pages);
190
191 for page_num in start..=end {
192 let is_current = page_num == current_val;
193 let page_num_str = page_num.to_string();
194 nav_items.push(create_page_item(
195 &page_num_str,
196 false,
197 is_current,
198 current_page.clone(),
199 move |page: Signal<u64>| {
200 page.set(page_num);
201 },
202 ));
203 }
204
205 let next_disabled = current_val >= total_pages;
207 nav_items.push(create_page_item(
208 "Next",
209 next_disabled,
210 false,
211 current_page,
212 move |page: Signal<u64>| {
213 let current = page.get();
214 if current < total_pages {
215 page.set(current + 1);
216 }
217 },
218 ));
219
220 PageElement::new("div")
221 .attr("class", "d-flex justify-content-center")
222 .child(
223 PageElement::new("ul")
224 .attr("class", "pagination")
225 .children(nav_items),
226 )
227 .into_page()
228}
229
230fn create_page_item<F>(
232 text: &str,
233 disabled: bool,
234 active: bool,
235 _signal: Signal<u64>,
236 _handler: F,
237) -> Page
238where
239 F: Fn(Signal<u64>) + 'static,
240{
241 let class_name = if active {
242 "page-item active"
243 } else if disabled {
244 "page-item disabled"
245 } else {
246 "page-item"
247 };
248
249 #[cfg(target_arch = "wasm32")]
250 let link = {
251 PageElement::new("a")
252 .attr("class", "page-link")
253 .attr("href", "#")
254 .child(text.to_string())
255 .on(
256 EventType::Click,
257 Arc::new(move |_event: web_sys::Event| {
258 _handler(_signal.clone());
259 }),
260 )
261 };
262
263 #[cfg(not(target_arch = "wasm32"))]
264 let link = {
265 PageElement::new("a")
267 .attr("class", "page-link")
268 .attr("href", "#")
269 .attr("data-reactive", "true") .child(text.to_string())
271 };
272
273 PageElement::new("li")
274 .attr("class", class_name)
275 .child(link)
276 .into_page()
277}
278
279pub fn search_bar(value: Signal<String>, placeholder: &str) -> Page {
297 let current_value = value.get();
298
299 PageElement::new("div")
300 .attr("class", "input-group")
301 .child(
302 PageElement::new("span")
303 .attr("class", "input-group-text")
304 .child(PageElement::new("i").attr("class", "bi bi-search")),
305 )
306 .child(
307 PageElement::new("input")
308 .attr("class", "form-control")
309 .attr("type", "text")
310 .attr("placeholder", placeholder.to_string())
311 .attr("value", current_value),
312 )
313 .into_page()
314}