perspective_viewer/components/containers/
control_group.rs1use wasm_bindgen::JsCast;
14use web_sys::{Event, MouseEvent};
15use yew::{Callback, Html, Properties, function_component, html, use_callback, use_mut_ref};
16
17#[derive(Properties, PartialEq)]
18pub struct ControlGroupProps {
19 pub group_key: String,
20
21 pub open: bool,
22
23 pub on_toggle: Callback<(String, bool)>,
24
25 pub children: Html,
26}
27
28#[function_component]
29pub fn ControlGroup(props: &ControlGroupProps) -> Html {
30 let ontoggle = use_callback(
31 (props.group_key.clone(), props.on_toggle.clone()),
32 |event: Event, (group_key, on_toggle)| {
33 if let Some(el) = event
34 .target()
35 .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
36 {
37 on_toggle.emit((group_key.clone(), el.has_attribute("open")));
38 }
39 },
40 );
41
42 let onclick = use_callback((), |event: MouseEvent, _| {
43 if !event.shift_key() {
44 return;
45 }
46
47 event.prevent_default();
48 let Some(details) = event
49 .target()
50 .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
51 .and_then(|x| x.closest("details.control-group").ok().flatten())
52 else {
53 return;
54 };
55
56 let Some(scope) = details.closest(".tab-section").ok().flatten() else {
57 return;
58 };
59
60 let expand = !details.has_attribute("open");
61 let Ok(groups) = scope.query_selector_all("details.control-group") else {
62 return;
63 };
64
65 for i in 0..groups.length() {
66 let Some(el) = groups
67 .item(i)
68 .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
69 else {
70 continue;
71 };
72
73 if expand {
74 let _ = el.set_attribute("open", "");
75 } else {
76 let _ = el.remove_attribute("open");
77 }
78 }
79 });
80
81 let open = *use_mut_ref(|| props.open).borrow();
82 html! {
83 <details class="control-group" {open} {ontoggle}>
84 <summary {onclick}>
85 <span class="control-group-chevron shift-alt-icon" />
86 <label id={format!("{}-group-label", props.group_key)} />
87 </summary>
88 { props.children.clone() }
89 </details>
90 }
91}