perspective_viewer/components/
settings_panel.rs1use std::rc::Rc;
14
15use perspective_client::config::{ViewConfig, ViewConfigUpdate};
16use perspective_js::utils::ApiFuture;
17use yew::prelude::*;
18
19use super::column_selector::ColumnSelector;
20use super::plugin_selector::PluginSelector;
21use super::plugin_tab::PluginTab;
22use crate::components::containers::sidebar_close_button::SidebarCloseButton;
23use crate::components::form::debug::DebugPanel;
24use crate::config::PluginUpdate;
25use crate::presentation::{ColumnLocator, OpenColumnSettings, Presentation};
26use crate::renderer::*;
27use crate::session::column_defaults_update::*;
28use crate::session::*;
29use crate::tasks::update_and_render;
30use crate::utils::*;
31
32#[derive(Clone, Properties)]
33pub struct SettingsPanelProps {
34 pub on_close: Callback<()>,
35 pub on_resize: Rc<PubSub<()>>,
36 pub on_select_column: Callback<Option<ColumnLocator>>,
37 pub on_debug: Callback<()>,
38 pub is_debug: bool,
39
40 pub plugin_name: Option<String>,
42 pub available_plugins: PtrEqRc<Vec<String>>,
43 pub has_table: Option<TableLoadState>,
44 pub named_column_count: usize,
45 pub view_config: PtrEqRc<ViewConfig>,
46
47 pub plugin_config: PtrEqRc<serde_json::Map<String, serde_json::Value>>,
51
52 pub drag_column: Option<String>,
55
56 pub metadata: SessionMetadataRc,
59
60 pub open_column_settings: OpenColumnSettings,
63
64 pub selected_theme: Option<String>,
66
67 pub selected_tab: SelectedTab,
71
72 pub auto_width: f64,
75
76 pub on_select_tab: Callback<SelectedTab>,
78
79 pub on_auto_width: Callback<f64>,
81
82 pub on_dimensions_reset: Rc<PubSub<()>>,
88
89 pub session: Session,
91 pub renderer: Renderer,
92 pub presentation: Presentation,
93}
94
95impl PartialEq for SettingsPanelProps {
96 fn eq(&self, rhs: &Self) -> bool {
97 self.is_debug == rhs.is_debug
98 && self.plugin_name == rhs.plugin_name
99 && self.available_plugins == rhs.available_plugins
100 && self.has_table == rhs.has_table
101 && self.named_column_count == rhs.named_column_count
102 && self.view_config == rhs.view_config
103 && self.plugin_config == rhs.plugin_config
104 && self.drag_column == rhs.drag_column
105 && self.metadata == rhs.metadata
106 && self.open_column_settings == rhs.open_column_settings
107 && self.selected_theme == rhs.selected_theme
108 && self.selected_tab == rhs.selected_tab
109 && self.auto_width == rhs.auto_width
110 }
111}
112
113#[derive(Debug, PartialEq, Clone, Copy, Default)]
114pub enum SelectedTab {
115 #[default]
116 Query,
117 Plugin,
118 Debug,
119}
120
121#[function_component]
122pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
123 let SettingsPanelProps {
124 presentation,
125 renderer,
126 session,
127 ..
128 } = &props;
129
130 let selected_column = {
131 let locator = props.open_column_settings.locator.clone();
132 let config = &props.view_config;
133 locator.filter(|locator| match locator {
134 ColumnLocator::Table(_name) => {
135 locator
136 .name()
137 .map(|n| {
138 config.columns.iter().any(|maybe_col| {
139 maybe_col.as_ref().map(|col| col == n).unwrap_or_default()
140 }) || config.group_by.iter().any(|col| col == n)
141 || config.split_by.iter().any(|col| col == n)
142 || config.filter.iter().any(|col| col.column() == n)
143 || config.sort.iter().any(|col| &col.0 == n)
144 })
145 .unwrap_or_default()
146 && props.renderer.can_render_column_styles()
147 },
148 _ => true,
149 })
150 };
151
152 let plugin_name = props.plugin_name.clone();
153 let available_plugins = props.available_plugins.clone();
154 let selected = props.selected_tab;
155
156 let width = props.auto_width;
161 let on_auto_width = props.on_auto_width.clone();
162
163 let on_select_plugin = {
166 clone!(renderer, session, presentation);
167 let session_metadata = props.metadata.clone();
168 let view_config = props.view_config.clone();
169 Callback::from(move |plugin_name: String| {
170 if session.is_errored() {
171 return;
172 }
173 let metadata = renderer.get_next_plugin_metadata(&PluginUpdate::Update(plugin_name));
174 let prev_metadata = renderer.metadata();
175 let plugin_config = metadata.as_deref().unwrap_or(&*prev_metadata);
176 let rollup_features = session_metadata
177 .get_features()
178 .map(|x| x.get_group_rollup_modes())
179 .unwrap();
180
181 let group_rollups = plugin_config.get_group_rollups(&rollup_features);
182 let mut update = ViewConfigUpdate {
183 group_rollup_mode: group_rollups.first().cloned(),
184 ..ViewConfigUpdate::default()
185 };
186
187 update.set_update_column_defaults(
188 &session_metadata,
189 &view_config.columns,
190 plugin_config,
191 );
192
193 if let Ok(task) = update_and_render(&session, &renderer, update) {
194 ApiFuture::spawn(task);
195 }
196
197 presentation.set_open_column_settings(None);
198 })
199 };
200
201 let cb1 = props.on_select_column.clone();
202 let set_debug = use_callback(
203 props.on_select_tab.clone(),
204 move |_: PointerEvent, on_select_tab| {
205 on_select_tab.emit(SelectedTab::Debug);
206 cb1.emit(None)
207 },
208 );
209
210 let cb2 = props.on_select_column.clone();
211 let set_plugin = use_callback(
212 props.on_select_tab.clone(),
213 move |_: PointerEvent, on_select_tab| {
214 on_select_tab.emit(SelectedTab::Plugin);
215 cb2.emit(None)
216 },
217 );
218
219 let set_query = use_callback(
220 props.on_select_tab.clone(),
221 |_: PointerEvent, on_select_tab| on_select_tab.emit(SelectedTab::Query),
222 );
223
224 let tab_class = |l_tab: SelectedTab, r_tab: SelectedTab| {
225 if l_tab == r_tab {
226 "settings_tab selected_tab"
227 } else {
228 "settings_tab"
229 }
230 };
231
232 let on_open_expr_panel = use_callback(props.on_select_column.clone(), |c, on_select| {
233 on_select.emit(Some(c))
234 });
235
236 html! {
237 <div id="settings_panel" class="sidebar_column noselect split-panel orient-vertical">
238 if selected_column.is_none() {
239 <SidebarCloseButton
240 id="settings_close_button"
241 on_close_sidebar={&props.on_close.clone()}
242 />
243 }
244 <PluginSelector
245 {plugin_name}
246 {available_plugins}
247 {on_select_plugin}
248 />
249 <div id="settings_tab_bar" class="settings_tab_bar_scroll_offset">
250 <div
251 id="query_tabbar_tab"
252 class={tab_class(selected, SelectedTab::Query)}
253 onpointerdown={set_query}
254 />
255 <div
256 id="plugin_tabbar_tab"
257 class={tab_class(selected, SelectedTab::Plugin)}
258 onpointerdown={set_plugin}
259 />
260 <div
261 id="debug_tabbar_tab"
262 class={tab_class(selected, SelectedTab::Debug)}
263 onpointerdown={set_debug}
264 />
265 </div>
266 if selected == SelectedTab::Query {
267 <ColumnSelector
268 on_resize={&props.on_resize}
269 {on_open_expr_panel}
270 {selected_column}
271 has_table={props.has_table.clone()}
272 named_column_count={props.named_column_count}
273 view_config={props.view_config.clone()}
274 drag_column={props.drag_column.clone()}
275 metadata={props.metadata.clone()}
276 selected_theme={props.selected_theme.clone()}
277 presentation={presentation.clone()}
278 renderer={renderer.clone()}
279 session={session.clone()}
280 initial_width={width}
281 on_auto_width={on_auto_width.clone()}
282 on_dimensions_reset={&props.on_dimensions_reset}
283 />
284 } else if selected == SelectedTab::Plugin {
285 <PluginTab
286 view_config={props.view_config.clone()}
287 plugin_config={props.plugin_config.clone()}
288 renderer={renderer.clone()}
289 session={session.clone()}
290 />
293 } else {
294 <DebugPanel
295 {presentation}
296 {renderer}
297 {session}
298 initial_width={width}
299 on_auto_width={on_auto_width.clone()}
300 />
301 }
302 <div class="scroll-panel-auto-width" style={format!("width:{}px", width)} />
306 </div>
307 }
308}