gioser

tawasuyu/brahman

Rust 326TOML 123JSON 17Markdown 144606 Definiciones

⎇ 58e72c3d0837a5f1515b274ccbd87be7755ee7e7 crates/modules/barra/barra-web/src/lib.rs Rust · 72 líneas · 2.7 KB Historia Quién tocó qué crudo

refactor(monorepo): reorganización lógica + renames + SDDs + split CHANGELOG 550c98f27 · sergio ·

1//! Barra-web — binding DOM de la taskbar. Re-exporta `Task` desde
2//! `barra-core` y delega el render-to-html al core. Aquí sólo viven el
3//! mount sobre un `<ul>`, el listener de click delegado y los lookups
4//! de posición (bounding rects) que son intrínsecos al DOM.
5//!
6//! Contrato HTML mínimo:
7//! ```html
8//! <ul id="my-tasks" class="taskbar-list" role="presentation"></ul>
9//! ```
10//!
11//! Convenciones de clase generadas:
12//! - `.taskbar-item` — cada cajita
13//! - `.taskbar-item.active` — la cajita visible/foreground
14//! - `.taskbar-item-dot` — punto decorativo
15//! - `data-task="<id>"` — identificador único usable por CSS para theming
16
17use std::cell::RefCell;
18use std::rc::Rc;
19
20pub use barra_core::Task;
21use barra_core::{render_html, sanitize_attr};
22use wasm_bindgen::prelude::*;
23use wasm_bindgen::JsCast;
24use web_sys::{Element, HtmlElement, MouseEvent};
25
26#[derive(Clone)]
27pub struct TaskList {
28    list: HtmlElement,
29    on_click: Rc<RefCell<Option<Box<dyn FnMut(&str, f64, f64)>>>>,
30}
31
32impl TaskList {
33    pub fn mount(list: HtmlElement) -> Result<Self, JsValue> {
34        let on_click: Rc<RefCell<Option<Box<dyn FnMut(&str, f64, f64)>>>> =
35            Rc::new(RefCell::new(None));
36        let on_click2 = on_click.clone();
37        let cb = Closure::<dyn FnMut(MouseEvent)>::new(move |e: MouseEvent| {
38            let Some(target) = e.target() else { return };
39            let Ok(target_el): Result<Element, _> = target.dyn_into() else { return };
40            let Ok(Some(item)) = target_el.closest(".taskbar-item") else { return };
41            let Some(id) = item.get_attribute("data-task") else { return };
42            let rect = item.get_bounding_client_rect();
43            let cx = rect.left() + rect.width() / 2.0;
44            let cy = rect.top() + rect.height() / 2.0;
45            if let Some(cb) = on_click2.borrow_mut().as_mut() {
46                cb(&id, cx, cy);
47            }
48        });
49        list.add_event_listener_with_callback("click", cb.as_ref().unchecked_ref())?;
50        cb.forget();
51        Ok(Self { list, on_click })
52    }
53
54    pub fn set_tasks(&self, tasks: &[Task]) {
55        self.list.set_inner_html(&render_html(tasks));
56    }
57
58    pub fn on_click<F: FnMut(&str, f64, f64) + 'static>(&self, cb: F) {
59        *self.on_click.borrow_mut() = Some(Box::new(cb));
60    }
61
62    pub fn task_center(&self, id: &str) -> Option<(f64, f64)> {
63        let sel = format!(".taskbar-item[data-task=\"{}\"]", sanitize_attr(id));
64        let el = self.list.query_selector(&sel).ok().flatten()?;
65        let rect = el.get_bounding_client_rect();
66        Some((rect.left() + rect.width() / 2.0, rect.top() + rect.height() / 2.0))
67    }
68
69    pub fn list_el(&self) -> &HtmlElement {
70        &self.list
71    }
72}