perspective_viewer/ui/containers/
sidebar.rs1use perspective_client::clone;
14use wasm_bindgen::JsCast;
15use wasm_bindgen::prelude::Closure;
16use web_sys::HtmlElement;
17use yew::{
18 Callback, Children, Html, Properties, function_component, html, use_effect_with, use_mut_ref,
19 use_node_ref,
20};
21
22use crate::js::{ResizeObserver, ResizeObserverEntry};
23use crate::ui::containers::sidebar_close_button::SidebarCloseButton;
24
25#[derive(PartialEq, Clone, Properties)]
26pub struct SidebarProps {
27 pub children: Children,
28
29 pub on_close: Callback<()>,
30 pub id_prefix: String,
31 pub width_override: Option<i32>,
32 pub selected_tab: Option<usize>,
33 pub header: Html,
34
35 #[prop_or_default]
36 pub auto_width: f64,
37
38 #[prop_or_default]
39 pub on_auto_width: Callback<f64>,
40
41 #[prop_or_default]
42 pub is_pinned: bool,
43
44 #[prop_or_default]
45 pub on_toggle_pin: Option<Callback<()>>,
46}
47
48#[function_component]
50pub fn Sidebar(p: &SidebarProps) -> Html {
51 let id = &p.id_prefix;
52 let noderef = use_node_ref();
53
54 let live_props = use_mut_ref(|| (Callback::<f64>::default(), None::<i32>));
55 *live_props.borrow_mut() = (p.on_auto_width.clone(), p.width_override);
56 use_effect_with((), {
57 clone!(noderef, live_props);
58 move |_| {
59 let closure =
60 Closure::<dyn FnMut(js_sys::Array)>::new(move |entries: js_sys::Array| {
61 let (on_auto_width, width_override) = live_props.borrow().clone();
62 if width_override.is_none() {
63 for entry in entries.iter() {
64 let entry: ResizeObserverEntry = entry.unchecked_into();
65 on_auto_width.emit(entry.content_rect().width());
66 }
67 }
68 });
69
70 let observer = ResizeObserver::new(closure.as_ref().unchecked_ref());
71 let elem = noderef.cast::<HtmlElement>();
72 if let Some(elem) = &elem {
73 observer.observe(elem);
74 }
75
76 move || {
77 if let Some(elem) = &elem {
78 observer.unobserve(elem);
79 }
80
81 drop(closure);
82 }
83 }
84 });
85
86 let auto_width = if p.width_override.is_none() {
87 p.auto_width
88 } else {
89 0.0
90 };
91
92 let width_style = format!("min-width: 200px; width: {}px", auto_width);
93 let pin_button = p.on_toggle_pin.as_ref().map(|cb| {
94 let onclick = {
95 let cb = cb.clone();
96 Callback::from(move |_: web_sys::MouseEvent| cb.emit(()))
97 };
98
99 let mut class = yew::classes!("sidebar_pin_button");
100 if p.is_pinned {
101 class.push("is-pinned");
102 }
103
104 html! {
105 <span
106 id={format!("{id}_pin_button")}
107 {class}
108 title={if p.is_pinned { "Unpin" } else { "Pin" }}
109 {onclick}
110 />
111 }
112 });
113
114 html! {
115 <>
116 <SidebarCloseButton id={format!("{id}_close_button")} on_close_sidebar={&p.on_close} />
117 <div class="sidebar_column" id={format!("{id}_sidebar")} ref={noderef}>
118 <div class="sidebar_header">{ p.header.clone() }{ pin_button }</div>
119 <div class="sidebar_border" id={format!("{id}_border")} />
120 { p.children.iter().collect::<Html>() }
121 <div class="sidebar-auto-width" style={width_style} />
122 </div>
123 </>
124 }
125}