use rdom_core::{ListenerOptions, NodeId};
use crate::{TuiDom, TuiEvent};
pub fn install(dom: &mut TuiDom) {
let root = dom.root();
dom.add_event_listener(root, "click", ListenerOptions::default(), move |ctx| {
if ctx.event.default_prevented() {
return;
}
let Some(target) = ctx.event.target else {
return;
};
let Some(summary) = closest_summary(ctx.dom, target) else {
return;
};
let Some(details) = parent_details(ctx.dom, summary) else {
return;
};
toggle(ctx.dom, details);
})
.expect("details click listener install");
dom.add_event_listener(root, "keydown", ListenerOptions::default(), move |ctx| {
if ctx.event.default_prevented() {
return;
}
let Some(focused) = ctx.dom.focused() else {
return;
};
if ctx.dom.node(focused).tag_name() != Some("summary") {
return;
}
let Some(key) = ctx.event.detail.as_keyboard() else {
return;
};
if key.modifiers.ctrl || key.modifiers.alt || key.modifiers.meta {
return;
}
if !matches!(key.key.as_str(), "Enter" | " ") {
return;
}
let Some(details) = parent_details(ctx.dom, focused) else {
return;
};
toggle(ctx.dom, details);
})
.expect("details keydown listener install");
}
fn toggle(dom: &mut TuiDom, details: NodeId) {
let was_open = dom.node(details).has_attribute("open");
if was_open {
let _ = dom.remove_attribute(details, "open");
} else {
let _ = dom.set_attribute(details, "open", "");
}
let (old_state, new_state) = if was_open {
(rdom_core::ToggleState::Open, rdom_core::ToggleState::Closed)
} else {
(rdom_core::ToggleState::Closed, rdom_core::ToggleState::Open)
};
let mut ev = TuiEvent::new("toggle");
ev.event = ev.event.clone().with_bubbles(false);
ev.event.detail = rdom_core::EventDetail::Toggle(Box::new(rdom_core::ToggleDetail::new(
old_state, new_state,
)));
crate::tui_event::dispatch_to_live(dom, details, &mut ev);
}
fn closest_summary(dom: &TuiDom, id: NodeId) -> Option<NodeId> {
let mut cur = Some(id);
while let Some(n) = cur {
if dom.node(n).tag_name() == Some("summary") {
return Some(n);
}
cur = dom.node(n).parent_node().map(|p| p.id());
}
None
}
fn parent_details(dom: &TuiDom, summary: NodeId) -> Option<NodeId> {
let parent = dom.node(summary).parent_node()?;
if parent.tag_name() == Some("details") {
Some(parent.id())
} else {
None
}
}
#[cfg(test)]
mod tests;