Skip to main content

perspective_viewer/components/containers/
control_group.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use 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}