use std::cell::{Cell, RefCell};
use web_sys::Element;
pub(super) trait Panel {
fn id(&self) -> &'static str;
fn label(&self) -> &'static str;
fn mount(&self, host: &Element);
fn fingerprint(&self) -> String;
fn render(&self, host: &Element);
fn handle_action(&self, action: &str, el: &Element) -> bool;
}
thread_local! {
static PANELS: RefCell<Vec<Box<dyn Panel>>> = RefCell::new(Vec::new());
static LAST_FPS: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) };
static MOUNTED: RefCell<Vec<bool>> = const { RefCell::new(Vec::new()) };
static ACTIVE: Cell<usize> = const { Cell::new(0) };
}
pub(super) fn register(panel: Box<dyn Panel>) {
PANELS.with(|p| p.borrow_mut().push(panel));
LAST_FPS.with(|f| f.borrow_mut().push(String::new()));
MOUNTED.with(|m| m.borrow_mut().push(false));
}
pub(super) fn count() -> usize {
PANELS.with(|p| p.borrow().len())
}
pub(super) fn active_index() -> usize {
ACTIVE.with(|c| c.get())
}
pub(super) fn set_active(idx: usize) {
let n = count();
if idx < n {
ACTIVE.with(|c| c.set(idx));
}
}
pub(super) fn summary() -> Vec<(&'static str, &'static str, bool)> {
let active = active_index();
PANELS.with(|p| {
p.borrow()
.iter()
.enumerate()
.map(|(i, panel)| (panel.id(), panel.label(), i == active))
.collect()
})
}
pub(super) fn ensure_mounted(host_for: impl Fn(usize, &str) -> Option<Element>) {
let n = count();
for i in 0..n {
let already = MOUNTED.with(|m| m.borrow()[i]);
if already {
continue;
}
let id = PANELS.with(|p| p.borrow()[i].id());
let Some(host) = host_for(i, id) else {
continue;
};
PANELS.with(|p| p.borrow()[i].mount(&host));
MOUNTED.with(|m| m.borrow_mut()[i] = true);
}
}
pub(super) fn render_active(host: &Element) {
let idx = active_index();
if idx >= count() {
return;
}
let fp = PANELS.with(|p| p.borrow()[idx].fingerprint());
let changed = LAST_FPS.with(|fps| {
let mut f = fps.borrow_mut();
if f[idx] != fp {
f[idx] = fp;
true
} else {
false
}
});
if !changed {
return;
}
PANELS.with(|p| p.borrow()[idx].render(host));
}
pub(super) fn invalidate_active() {
let idx = active_index();
if idx < count() {
LAST_FPS.with(|fps| fps.borrow_mut()[idx] = String::new());
}
}
pub(super) fn dispatch_action_to_active(action: &str, el: &Element) -> bool {
let idx = active_index();
if idx >= count() {
return false;
}
PANELS.with(|p| p.borrow()[idx].handle_action(action, el))
}