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