perspective_viewer/components/
main_panel.rs1use futures::channel::oneshot::*;
14use perspective_js::utils::*;
15use wasm_bindgen::prelude::*;
16use yew::prelude::*;
17
18use super::render_warning::RenderWarning;
19use super::status_bar::StatusBar;
20use crate::PerspectiveProperties;
21use crate::custom_events::CustomEvents;
22use crate::presentation::Presentation;
23use crate::renderer::*;
24use crate::session::*;
25use crate::utils::*;
26
27#[derive(Clone, Properties, PerspectiveProperties!)]
28pub struct MainPanelProps {
29 pub on_settings: Callback<()>,
30
31 pub custom_events: CustomEvents,
33 pub session: Session,
34 pub renderer: Renderer,
35 pub presentation: Presentation,
36}
37
38impl PartialEq for MainPanelProps {
39 fn eq(&self, _rhs: &Self) -> bool {
40 false
41 }
42}
43
44impl MainPanelProps {
45 fn is_title(&self) -> bool {
46 self.session.get_title().is_some()
47 }
48}
49
50#[derive(Debug)]
51pub enum MainPanelMsg {
52 Reset(bool, Option<Sender<()>>),
53 RenderLimits(Option<(usize, usize, Option<usize>, Option<usize>)>),
54 PointerEvent(web_sys::PointerEvent),
55 Error,
56}
57
58pub struct MainPanel {
59 _subscriptions: [Subscription; 2],
60 dimensions: Option<(usize, usize, Option<usize>, Option<usize>)>,
61 main_panel_ref: NodeRef,
62}
63
64impl Component for MainPanel {
65 type Message = MainPanelMsg;
66 type Properties = MainPanelProps;
67
68 fn create(ctx: &Context<Self>) -> Self {
69 let session_sub = {
70 let callback = ctx.link().callback(move |(_, render_limits)| {
71 MainPanelMsg::RenderLimits(Some(render_limits))
72 });
73
74 ctx.props()
75 .renderer
76 .render_limits_changed
77 .add_listener(callback)
78 };
79
80 let error_sub = ctx
81 .props()
82 .session
83 .table_errored
84 .add_listener(ctx.link().callback(|_| MainPanelMsg::Error));
85
86 Self {
87 _subscriptions: [session_sub, error_sub],
88 dimensions: None,
89 main_panel_ref: NodeRef::default(),
90 }
91 }
92
93 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
94 match msg {
95 MainPanelMsg::Error => true,
96 MainPanelMsg::Reset(all, sender) => {
97 ctx.props().presentation.set_open_column_settings(None);
98
99 clone!(
100 ctx.props().renderer,
101 ctx.props().session,
102 ctx.props().presentation
103 );
104
105 ApiFuture::spawn(async move {
106 session
107 .reset(ResetOptions {
108 config: true,
109 expressions: all,
110 ..ResetOptions::default()
111 })
112 .await?;
113 let columns_config = if all {
114 presentation.reset_columns_configs();
115 None
116 } else {
117 Some(presentation.all_columns_configs())
118 };
119
120 renderer.reset(columns_config.as_ref()).await?;
121 presentation.reset_available_themes(None).await;
122 if all {
123 presentation.reset_theme().await?;
124 }
125
126 let result = renderer.draw(session.validate().await?.create_view()).await;
127 if let Some(sender) = sender {
128 sender.send(()).unwrap();
129 }
130
131 renderer.reset_changed.emit(());
132 result
133 });
134
135 false
136 },
137
138 MainPanelMsg::RenderLimits(dimensions) => {
139 if self.dimensions != dimensions {
140 self.dimensions = dimensions;
141 true
142 } else {
143 false
144 }
145 },
146
147 MainPanelMsg::PointerEvent(event) => {
148 if event.target().map(JsValue::from)
149 == self
150 .main_panel_ref
151 .cast::<web_sys::HtmlElement>()
152 .map(JsValue::from)
153 {
154 ctx.props()
155 .custom_events
156 .dispatch_event(format!("statusbar-{}", event.type_()).as_str(), &event)
157 .unwrap();
158 }
159
160 false
161 },
162 }
163 }
164
165 fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
166 true
167 }
168
169 fn view(&self, ctx: &Context<Self>) -> Html {
170 let Self::Properties {
171 custom_events,
172 presentation,
173 renderer,
174 session,
175 ..
176 } = ctx.props();
177
178 let is_settings_open =
179 ctx.props().presentation.is_settings_open() && ctx.props().session.has_table();
180
181 let on_settings = (!is_settings_open).then(|| ctx.props().on_settings.clone());
182
183 let mut class = classes!();
184 if !is_settings_open {
185 class.push("settings-closed");
186 }
187
188 if ctx.props().is_title() {
189 class.push("titled");
190 }
191
192 let on_reset = ctx.link().callback(|all| MainPanelMsg::Reset(all, None));
193 let pointerdown = ctx.link().callback(MainPanelMsg::PointerEvent);
194 html! {
195 <div id="main_column">
196 <StatusBar
197 id="status_bar"
198 {on_settings}
199 on_reset={on_reset.clone()}
200 {custom_events}
201 {presentation}
202 {renderer}
203 {session}
204 />
205 <div
206 id="main_panel_container"
207 ref={self.main_panel_ref.clone()}
208 {class}
209 onpointerdown={pointerdown}
210 >
211 <RenderWarning {renderer} {session} dimensions={self.dimensions} />
212 <slot />
213 </div>
214 </div>
215 }
216 }
217
218 fn destroy(&mut self, _ctx: &Context<Self>) {}
219}