@perspective-dev/viewer 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/dracula.css +1 -1
  4. package/dist/css/gruvbox-dark.css +1 -1
  5. package/dist/css/gruvbox.css +1 -1
  6. package/dist/css/icons.css +1 -1
  7. package/dist/css/monokai.css +1 -1
  8. package/dist/css/pro-dark.css +1 -1
  9. package/dist/css/pro.css +1 -1
  10. package/dist/css/solarized-dark.css +1 -1
  11. package/dist/css/solarized.css +1 -1
  12. package/dist/css/themes.css +1 -1
  13. package/dist/css/vaporwave.css +1 -1
  14. package/dist/esm/extensions.d.ts +23 -2
  15. package/dist/esm/perspective-viewer.d.ts +2 -7
  16. package/dist/esm/perspective-viewer.inline.js +2 -2
  17. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  18. package/dist/esm/perspective-viewer.js +2 -2
  19. package/dist/esm/perspective-viewer.js.map +4 -4
  20. package/dist/esm/plugin.d.ts +1 -1
  21. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +1 -0
  22. package/dist/wasm/perspective-viewer.d.ts +218 -46
  23. package/dist/wasm/perspective-viewer.js +1250 -761
  24. package/dist/wasm/perspective-viewer.wasm +0 -0
  25. package/dist/wasm/perspective-viewer.wasm.d.ts +38 -19
  26. package/package.json +1 -1
  27. package/src/less/containers/scroll-panel.less +0 -1
  28. package/src/less/plugin-selector.less +15 -5
  29. package/src/less/status-bar.less +75 -27
  30. package/src/less/viewer.less +140 -58
  31. package/src/rust/components/column_dropdown.rs +21 -21
  32. package/src/rust/components/column_selector/active_column.rs +131 -120
  33. package/src/rust/components/column_selector/add_expression_button.rs +5 -0
  34. package/src/rust/components/column_selector/aggregate_selector.rs +8 -4
  35. package/src/rust/components/column_selector/config_selector.rs +170 -161
  36. package/src/rust/components/column_selector/empty_column.rs +16 -11
  37. package/src/rust/components/column_selector/{expression_toolbar.rs → expr_edit_button.rs} +7 -0
  38. package/src/rust/components/column_selector/filter_column.rs +195 -194
  39. package/src/rust/components/column_selector/inactive_column.rs +82 -67
  40. package/src/rust/components/column_selector/pivot_column.rs +16 -11
  41. package/src/rust/components/column_selector/sort_column.rs +9 -7
  42. package/src/rust/components/column_selector.rs +42 -37
  43. package/src/rust/components/column_settings_sidebar/save_settings.rs +3 -1
  44. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +58 -0
  45. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +6 -6
  46. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +2 -94
  47. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +111 -0
  48. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +3 -3
  49. package/src/rust/components/column_settings_sidebar/style_tab.rs +23 -83
  50. package/src/rust/components/{column_settings_sidebar/sidebar.rs → column_settings_sidebar.rs} +198 -171
  51. package/src/rust/components/containers/dragdrop_list.rs +20 -20
  52. package/src/rust/components/containers/dropdown_menu.rs +4 -6
  53. package/src/rust/components/containers/mod.rs +1 -4
  54. package/src/rust/components/containers/scroll_panel.rs +80 -80
  55. package/src/rust/components/containers/scroll_panel_item.rs +36 -36
  56. package/src/rust/components/containers/select.rs +46 -44
  57. package/src/rust/components/containers/sidebar.rs +3 -19
  58. package/src/rust/components/{column_settings_sidebar/style_tab/symbol/symbol_config.rs → containers/sidebar_close_button.rs} +15 -9
  59. package/src/rust/components/containers/split_panel.rs +212 -200
  60. package/src/rust/components/containers/tab_list.rs +11 -11
  61. package/src/rust/components/copy_dropdown.rs +22 -25
  62. package/src/rust/components/datetime_column_style/custom.rs +19 -19
  63. package/src/rust/components/datetime_column_style/simple.rs +13 -14
  64. package/src/rust/components/datetime_column_style.rs +75 -76
  65. package/src/rust/components/editable_header.rs +18 -14
  66. package/src/rust/components/empty_row.rs +5 -5
  67. package/src/rust/components/export_dropdown.rs +42 -42
  68. package/src/rust/components/expression_editor.rs +25 -19
  69. package/src/rust/components/filter_dropdown.rs +22 -22
  70. package/src/rust/components/font_loader.rs +11 -9
  71. package/src/rust/components/form/code_editor.rs +106 -105
  72. package/src/rust/components/form/color_range_selector.rs +14 -12
  73. package/src/rust/components/form/color_selector.rs +3 -1
  74. package/src/rust/components/form/debug.rs +95 -94
  75. package/src/rust/components/form/highlight.rs +5 -3
  76. package/src/rust/components/form/mod.rs +3 -2
  77. package/src/rust/components/form/optional_field.rs +2 -2
  78. package/src/rust/components/form/{select_field.rs → select_enum_field.rs} +1 -46
  79. package/src/rust/components/form/select_value_field.rs +64 -0
  80. package/src/rust/components/function_dropdown.rs +21 -21
  81. package/src/rust/components/main_panel.rs +219 -0
  82. package/src/rust/components/mod.rs +6 -6
  83. package/src/rust/components/modal.rs +42 -42
  84. package/src/rust/components/number_column_style.rs +34 -88
  85. package/src/rust/components/plugin_selector.rs +22 -25
  86. package/src/rust/components/render_warning.rs +9 -6
  87. package/src/rust/components/settings_panel.rs +82 -0
  88. package/src/rust/components/status_bar.rs +250 -146
  89. package/src/rust/components/status_bar_counter.rs +26 -119
  90. package/src/rust/components/status_indicator.rs +95 -79
  91. package/src/rust/components/string_column_style.rs +45 -45
  92. package/src/rust/components/style/style_provider.rs +1 -15
  93. package/src/rust/components/style_controls/number_string_format/digits_section.rs +1 -1
  94. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  95. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  96. package/src/rust/components/style_controls/number_string_format.rs +45 -46
  97. package/src/rust/components/type_icon.rs +14 -11
  98. package/src/rust/components/viewer.rs +241 -384
  99. package/src/rust/config/columns_config.rs +2 -2
  100. package/src/rust/config/datetime_column_style.rs +1 -6
  101. package/src/rust/config/mod.rs +1 -0
  102. package/src/rust/config/number_column_style.rs +0 -6
  103. package/src/rust/config/number_string_format.rs +27 -4
  104. package/src/rust/config/viewer_config.rs +27 -167
  105. package/src/rust/custom_elements/copy_dropdown.rs +14 -6
  106. package/src/rust/custom_elements/export_dropdown.rs +15 -7
  107. package/src/rust/custom_elements/filter_dropdown.rs +4 -4
  108. package/src/rust/custom_elements/mod.rs +3 -0
  109. package/src/rust/custom_elements/viewer.rs +353 -161
  110. package/src/rust/custom_events.rs +55 -32
  111. package/src/rust/dragdrop.rs +4 -24
  112. package/src/rust/exprtk/cursor.rs +10 -1
  113. package/src/rust/exprtk/mod.rs +2 -0
  114. package/src/rust/exprtk/tokenize.rs +20 -3
  115. package/src/rust/js/clipboard.rs +2 -2
  116. package/src/rust/js/mimetype.rs +2 -7
  117. package/src/rust/js/mod.rs +0 -1
  118. package/src/rust/js/plugin.rs +7 -0
  119. package/src/rust/lib.rs +18 -5
  120. package/src/rust/model/column_locator.rs +82 -0
  121. package/src/rust/model/columns_iter_set.rs +1 -0
  122. package/src/rust/model/copy_export.rs +50 -14
  123. package/src/rust/model/edit_expression.rs +2 -5
  124. package/src/rust/model/eject.rs +41 -0
  125. package/src/rust/model/get_viewer_config.rs +4 -28
  126. package/src/rust/model/intersection_observer.rs +20 -8
  127. package/src/rust/model/mod.rs +11 -4
  128. package/src/rust/model/plugin_column_styles.rs +0 -31
  129. package/src/rust/model/reset_all.rs +38 -0
  130. package/src/rust/model/resize_observer.rs +34 -7
  131. package/src/rust/model/restore_and_render.rs +12 -7
  132. package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
  133. package/src/rust/model/structural.rs +194 -23
  134. package/src/rust/model/update_and_render.rs +14 -4
  135. package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
  136. package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
  137. package/src/rust/presentation.rs +60 -119
  138. package/src/rust/renderer/activate.rs +20 -5
  139. package/src/rust/renderer/limits.rs +0 -149
  140. package/src/rust/renderer/render_timer.rs +1 -1
  141. package/src/rust/renderer.rs +34 -18
  142. package/src/rust/root.rs +50 -0
  143. package/src/rust/session/column_defaults_update.rs +4 -4
  144. package/src/rust/session/drag_drop_update.rs +1 -1
  145. package/src/rust/session/metadata.rs +3 -17
  146. package/src/rust/session/replace_expression_update.rs +1 -2
  147. package/src/rust/session.rs +162 -82
  148. package/src/rust/utils/browser/blob.rs +16 -2
  149. package/src/rust/utils/browser/download.rs +1 -0
  150. package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
  151. package/src/rust/utils/browser/mod.rs +8 -4
  152. package/src/rust/utils/browser/selection.rs +5 -0
  153. package/src/rust/utils/custom_element.rs +28 -13
  154. package/src/rust/utils/datetime.rs +5 -0
  155. package/src/rust/utils/debounce.rs +7 -1
  156. package/src/rust/utils/hooks/use_async_callback.rs +7 -17
  157. package/src/rust/utils/mod.rs +28 -40
  158. package/src/rust/utils/number_format.rs +6 -5
  159. package/src/rust/utils/pubsub.rs +15 -10
  160. package/src/rust/utils/weak_scope.rs +11 -1
  161. package/src/svg/bookmark-icon.svg +4 -0
  162. package/src/svg/drag-handle copy.svg +10 -0
  163. package/src/svg/drawer-tab-hover.svg +5 -7
  164. package/src/svg/drawer-tab-invert-hover.svg +4 -8
  165. package/src/svg/drawer-tab-invert.svg +4 -7
  166. package/src/svg/drawer-tab.svg +4 -6
  167. package/src/svg/status_ok.svg +24 -24
  168. package/src/ts/extensions.ts +51 -3
  169. package/src/ts/perspective-viewer.ts +2 -14
  170. package/src/ts/plugin.ts +1 -1
  171. package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
  172. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
  173. package/src/rust/components/containers/tests/mod.rs +0 -11
  174. package/src/rust/components/containers/tests/split_panel.rs +0 -91
  175. package/src/rust/js/testing.rs +0 -149
  176. package/src/rust/utils/tee.rs +0 -88
  177. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline0.js +0 -0
  178. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline1.js +0 -0
  179. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline2.js +0 -0
  180. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline3.js +0 -0
  181. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline4.js +0 -0
  182. /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
  183. /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
@@ -13,102 +13,40 @@
13
13
  use std::rc::Rc;
14
14
 
15
15
  use futures::channel::oneshot::*;
16
- use perspective_client::config::ColumnType;
16
+ use perspective_js::utils::*;
17
17
  use wasm_bindgen::prelude::*;
18
18
  use yew::prelude::*;
19
19
 
20
- use super::column_selector::ColumnSelector;
21
20
  use super::containers::split_panel::SplitPanel;
22
21
  use super::font_loader::{FontLoader, FontLoaderProps, FontLoaderStatus};
23
22
  use super::form::debug::DebugPanel;
24
- use super::plugin_selector::PluginSelector;
25
- use super::render_warning::RenderWarning;
26
- use super::status_bar::StatusBar;
27
23
  use super::style::{LocalStyle, StyleProvider};
28
- use crate::components::column_settings_sidebar::ColumnSettingsSidebar;
29
- use crate::components::containers::sidebar::SidebarCloseButton;
24
+ use crate::components::column_settings_sidebar::ColumnSettingsPanel;
25
+ use crate::components::main_panel::MainPanel;
26
+ use crate::components::settings_panel::SettingsPanel;
30
27
  use crate::config::*;
31
28
  use crate::custom_events::CustomEvents;
32
29
  use crate::dragdrop::*;
33
30
  use crate::model::*;
34
- use crate::presentation::Presentation;
31
+ use crate::presentation::{ColumnLocator, ColumnSettingsTab, Presentation};
35
32
  use crate::renderer::*;
36
33
  use crate::session::*;
37
34
  use crate::utils::*;
38
- use crate::*;
39
-
40
- /// Locates a view column.
41
- /// Table columns are those defined on the table, but their types will reflect
42
- /// the view type, not the table type.
43
- #[derive(Clone, Debug, PartialEq)]
44
- pub enum ColumnLocator {
45
- Table(String),
46
- Expression(String),
47
- NewExpression,
48
- }
49
- impl ColumnLocator {
50
- /// Pulls the column's name from the locator.
51
- /// If the column is a new expression which has yet to be saved, the
52
- /// function will return None.
53
- pub fn name(&self) -> Option<&String> {
54
- match self {
55
- Self::Table(s) | Self::Expression(s) => Some(s),
56
- Self::NewExpression => None,
57
- }
58
- }
59
-
60
- pub fn name_or_default(&self, session: &Session) -> String {
61
- match self {
62
- Self::Table(s) | Self::Expression(s) => s.clone(),
63
- Self::NewExpression => session.metadata().make_new_column_name(None),
64
- }
65
- }
66
-
67
- pub fn is_active(&self, session: &Session) -> bool {
68
- self.name()
69
- .map(|name| session.is_column_active(name))
70
- .unwrap_or_default()
71
- }
72
-
73
- #[inline(always)]
74
- pub fn is_saved_expr(&self) -> bool {
75
- matches!(self, ColumnLocator::Expression(_))
76
- }
77
-
78
- #[inline(always)]
79
- pub fn is_expr(&self) -> bool {
80
- matches!(
81
- self,
82
- ColumnLocator::Expression(_) | ColumnLocator::NewExpression
83
- )
84
- }
85
-
86
- #[inline(always)]
87
- pub fn is_new_expr(&self) -> bool {
88
- matches!(self, ColumnLocator::NewExpression)
89
- }
90
-
91
- pub fn view_type(&self, session: &Session) -> Option<ColumnType> {
92
- let name = self.name().cloned().unwrap_or_default();
93
- session.metadata().get_column_view_type(name.as_str())
94
- }
95
- }
35
+ use crate::{PerspectiveProperties, css};
96
36
 
97
- #[derive(Properties)]
37
+ #[derive(Clone, Properties, PerspectiveProperties!)]
98
38
  pub struct PerspectiveViewerProps {
39
+ /// The light DOM element this component will render to.
99
40
  pub elem: web_sys::HtmlElement,
41
+
42
+ /// State
43
+ pub custom_events: CustomEvents,
44
+ pub dragdrop: DragDrop,
100
45
  pub session: Session,
101
46
  pub renderer: Renderer,
102
47
  pub presentation: Presentation,
103
- pub dragdrop: DragDrop,
104
- pub custom_events: CustomEvents,
105
-
106
- #[prop_or_default]
107
- pub weak_link: WeakScope<PerspectiveViewer>,
108
48
  }
109
49
 
110
- derive_model!(Renderer, Session, Presentation for PerspectiveViewerProps);
111
-
112
50
  impl PartialEq for PerspectiveViewerProps {
113
51
  fn eq(&self, _rhs: &Self) -> bool {
114
52
  false
@@ -117,45 +55,40 @@ impl PartialEq for PerspectiveViewerProps {
117
55
 
118
56
  impl PerspectiveViewerProps {
119
57
  fn is_title(&self) -> bool {
120
- !self.presentation.get_is_workspace() && self.presentation.get_title().is_some()
58
+ self.session.get_title().is_some()
121
59
  }
122
60
  }
123
61
 
124
62
  #[derive(Debug)]
125
63
  pub enum PerspectiveViewerMsg {
126
- Resize,
127
- Reset(bool, Option<Sender<()>>),
128
- ToggleSettingsInit(Option<SettingsUpdate>, Option<Sender<ApiResult<JsValue>>>),
129
- ToggleSettingsComplete(SettingsUpdate, Sender<()>),
130
- ToggleDebug,
131
- PreloadFontsUpdate,
132
- RenderLimits(Option<(usize, usize, Option<usize>, Option<usize>)>),
133
- SettingsPanelSizeUpdate(Option<i32>),
134
64
  ColumnSettingsPanelSizeUpdate(Option<i32>),
135
- Error,
65
+ ColumnSettingsTabChanged(ColumnSettingsTab),
136
66
  OpenColumnSettings {
137
67
  locator: Option<ColumnLocator>,
138
68
  sender: Option<Sender<()>>,
139
69
  toggle: bool,
140
70
  },
71
+ PreloadFontsUpdate,
72
+ Reset(bool, Option<Sender<()>>),
73
+ Resize,
74
+ SettingsPanelSizeUpdate(Option<i32>),
75
+ ToggleDebug,
76
+ ToggleSettingsComplete(SettingsUpdate, Sender<()>),
77
+ ToggleSettingsInit(Option<SettingsUpdate>, Option<Sender<ApiResult<JsValue>>>),
141
78
  }
142
79
 
80
+ use PerspectiveViewerMsg::*;
81
+
143
82
  pub struct PerspectiveViewer {
144
- dimensions: Option<(usize, usize, Option<usize>, Option<usize>)>,
145
- on_rendered: Option<Sender<()>>,
146
- fonts: FontLoaderProps,
147
- settings_open: bool,
83
+ _subscriptions: [Subscription; 1],
84
+ column_settings_panel_width_override: Option<i32>,
148
85
  debug_open: bool,
149
- /// The column which will be opened in the ColumnSettingsSidebar
150
- selected_column: Option<ColumnLocator>,
151
- selected_column_is_active: bool, // TODO: should we use a struct?
86
+ fonts: FontLoaderProps,
87
+ on_close_column_settings: Callback<()>,
88
+ on_rendered: Option<Sender<()>>,
152
89
  on_resize: Rc<PubSub<()>>,
153
- on_dimensions_reset: Rc<PubSub<()>>,
154
- _subscriptions: [Subscription; 2],
90
+ settings_open: bool,
155
91
  settings_panel_width_override: Option<i32>,
156
- column_settings_panel_width_override: Option<i32>,
157
-
158
- on_close_column_settings: Callback<()>,
159
92
  }
160
93
 
161
94
  impl Component for PerspectiveViewer {
@@ -163,94 +96,58 @@ impl Component for PerspectiveViewer {
163
96
  type Properties = PerspectiveViewerProps;
164
97
 
165
98
  fn create(ctx: &Context<Self>) -> Self {
166
- *ctx.props().weak_link.borrow_mut() = Some(ctx.link().clone());
167
99
  let elem = ctx.props().elem.clone();
168
- let callback = ctx
169
- .link()
170
- .callback(|()| PerspectiveViewerMsg::PreloadFontsUpdate);
100
+ let fonts = FontLoaderProps::new(&elem, ctx.link().callback(|()| PreloadFontsUpdate));
171
101
 
172
102
  let session_sub = {
173
- clone!(
174
- ctx.props().presentation,
175
- ctx.props().session,
176
- plugin_query = ctx.props().get_plugin_column_styles_query()
177
- );
178
- let callback = ctx.link().batch_callback(move |(update, render_limits)| {
103
+ let props = ctx.props().clone();
104
+ let callback = ctx.link().batch_callback(move |(update, _)| {
179
105
  if update {
180
- vec![PerspectiveViewerMsg::RenderLimits(Some(render_limits))]
106
+ vec![]
181
107
  } else {
182
- let locator =
183
- presentation
184
- .get_open_column_settings()
185
- .locator
186
- .filter(|locator| match &locator {
187
- ColumnLocator::Table(name) => {
188
- locator.is_active(&session)
189
- && plugin_query
190
- .can_render_column_styles(name)
191
- .unwrap_or_default()
192
- },
193
- _ => true,
194
- });
195
-
196
- vec![
197
- PerspectiveViewerMsg::RenderLimits(Some(render_limits)),
198
- PerspectiveViewerMsg::OpenColumnSettings {
199
- locator,
200
- sender: None,
201
- toggle: false,
202
- },
203
- ]
108
+ let locator = props.get_current_column_locator();
109
+ vec![OpenColumnSettings {
110
+ locator,
111
+ sender: None,
112
+ toggle: false,
113
+ }]
204
114
  }
205
115
  });
116
+
206
117
  ctx.props()
207
118
  .renderer
208
119
  .render_limits_changed
209
120
  .add_listener(callback)
210
121
  };
211
122
 
212
- let error_sub = ctx
213
- .props()
214
- .session
215
- .table_errored
216
- .add_listener(ctx.link().callback(|_| PerspectiveViewerMsg::Error));
217
-
218
- let on_close_column_settings =
219
- ctx.link()
220
- .callback(|_| PerspectiveViewerMsg::OpenColumnSettings {
221
- locator: None,
222
- sender: None,
223
- toggle: false,
224
- });
123
+ let on_close_column_settings = ctx.link().callback(|_| OpenColumnSettings {
124
+ locator: None,
125
+ sender: None,
126
+ toggle: false,
127
+ });
225
128
 
226
129
  Self {
227
- dimensions: None,
228
- on_rendered: None,
229
- fonts: FontLoaderProps::new(&elem, callback),
230
- settings_open: false,
130
+ _subscriptions: [session_sub],
131
+ column_settings_panel_width_override: None,
231
132
  debug_open: false,
232
- selected_column: None,
233
- selected_column_is_active: false,
133
+ fonts,
134
+ on_close_column_settings,
135
+ on_rendered: None,
234
136
  on_resize: Default::default(),
235
- on_dimensions_reset: Default::default(),
236
- _subscriptions: [session_sub, error_sub],
137
+ settings_open: false,
237
138
  settings_panel_width_override: None,
238
- column_settings_panel_width_override: None,
239
- on_close_column_settings,
240
139
  }
241
140
  }
242
141
 
243
142
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
244
- let needs_update = self.selected_column.is_some();
245
143
  match msg {
246
- PerspectiveViewerMsg::PreloadFontsUpdate => true,
247
- PerspectiveViewerMsg::Resize => {
144
+ PreloadFontsUpdate => true,
145
+ Resize => {
248
146
  self.on_resize.emit(());
249
147
  false
250
148
  },
251
- PerspectiveViewerMsg::Error => true,
252
- PerspectiveViewerMsg::Reset(all, sender) => {
253
- self.selected_column = None;
149
+ Reset(all, sender) => {
150
+ ctx.props().presentation.set_open_column_settings(None);
254
151
  clone!(
255
152
  ctx.props().renderer,
256
153
  ctx.props().session,
@@ -258,7 +155,13 @@ impl Component for PerspectiveViewer {
258
155
  );
259
156
 
260
157
  ApiFuture::spawn(async move {
261
- session.reset(all).await?;
158
+ session
159
+ .reset(ResetOptions {
160
+ config: true,
161
+ expressions: all,
162
+ ..ResetOptions::default()
163
+ })
164
+ .await?;
262
165
  let columns_config = if all {
263
166
  presentation.reset_columns_configs();
264
167
  None
@@ -281,111 +184,85 @@ impl Component for PerspectiveViewer {
281
184
  result
282
185
  });
283
186
 
284
- needs_update
285
- },
286
- PerspectiveViewerMsg::ToggleDebug => {
287
- self.debug_open = !self.debug_open;
288
- clone!(ctx.props().renderer, ctx.props().session);
289
- ApiFuture::spawn(async move {
290
- renderer.draw(session.validate().await?.create_view()).await
291
- });
292
-
293
- true
187
+ false
294
188
  },
295
- PerspectiveViewerMsg::ToggleSettingsInit(Some(SettingsUpdate::Missing), None) => false,
296
- PerspectiveViewerMsg::ToggleSettingsInit(
297
- Some(SettingsUpdate::Missing),
298
- Some(resolve),
299
- ) => {
189
+ ToggleSettingsInit(Some(SettingsUpdate::Missing), None) => false,
190
+ ToggleSettingsInit(Some(SettingsUpdate::Missing), Some(resolve)) => {
300
191
  resolve.send(Ok(JsValue::UNDEFINED)).unwrap();
301
192
  false
302
193
  },
303
- PerspectiveViewerMsg::ToggleSettingsInit(Some(SettingsUpdate::SetDefault), resolve) => {
194
+ ToggleSettingsInit(Some(SettingsUpdate::SetDefault), resolve) => {
304
195
  self.init_toggle_settings_task(ctx, Some(false), resolve);
305
196
  false
306
197
  },
307
- PerspectiveViewerMsg::ToggleSettingsInit(
308
- Some(SettingsUpdate::Update(force)),
309
- resolve,
310
- ) => {
198
+ ToggleSettingsInit(Some(SettingsUpdate::Update(force)), resolve) => {
311
199
  self.init_toggle_settings_task(ctx, Some(force), resolve);
312
200
  false
313
201
  },
314
- PerspectiveViewerMsg::ToggleSettingsInit(None, resolve) => {
202
+ ToggleSettingsInit(None, resolve) => {
315
203
  self.init_toggle_settings_task(ctx, None, resolve);
316
204
  false
317
205
  },
318
- PerspectiveViewerMsg::ToggleSettingsComplete(SettingsUpdate::SetDefault, resolve)
319
- if self.settings_open =>
320
- {
321
- self.selected_column = None;
206
+ ToggleSettingsComplete(SettingsUpdate::SetDefault, resolve) if self.settings_open => {
207
+ ctx.props().presentation.set_open_column_settings(None);
322
208
  self.settings_open = false;
323
209
  self.on_rendered = Some(resolve);
324
210
  true
325
211
  },
326
- PerspectiveViewerMsg::ToggleSettingsComplete(
327
- SettingsUpdate::Update(force),
328
- resolve,
329
- ) if force != self.settings_open => {
330
- self.selected_column = None;
212
+ ToggleSettingsComplete(SettingsUpdate::Update(force), resolve)
213
+ if force != self.settings_open =>
214
+ {
215
+ ctx.props().presentation.set_open_column_settings(None);
331
216
  self.settings_open = force;
332
217
  self.on_rendered = Some(resolve);
333
218
  true
334
219
  },
335
- PerspectiveViewerMsg::ToggleSettingsComplete(_, resolve)
220
+ ToggleSettingsComplete(_, resolve)
336
221
  if matches!(self.fonts.get_status(), FontLoaderStatus::Finished) =>
337
222
  {
338
- self.selected_column = None;
223
+ ctx.props().presentation.set_open_column_settings(None);
339
224
  if let Err(e) = resolve.send(()) {
340
225
  tracing::error!("toggle settings failed {:?}", e);
341
226
  }
342
227
 
343
228
  false
344
229
  },
345
- PerspectiveViewerMsg::ToggleSettingsComplete(_, resolve) => {
346
- self.selected_column = None;
230
+ ToggleSettingsComplete(_, resolve) => {
231
+ ctx.props().presentation.set_open_column_settings(None);
347
232
  self.on_rendered = Some(resolve);
348
233
  true
349
234
  },
350
- PerspectiveViewerMsg::RenderLimits(dimensions) => {
351
- if self.dimensions != dimensions {
352
- self.dimensions = dimensions;
353
- true
354
- } else {
355
- false
356
- }
357
- },
358
- PerspectiveViewerMsg::OpenColumnSettings {
235
+ OpenColumnSettings {
359
236
  locator,
360
237
  sender,
361
238
  toggle,
362
239
  } => {
363
- let is_active = locator
364
- .as_ref()
365
- .map(|l| l.is_active(&ctx.props().session))
366
- .unwrap_or_default();
367
-
368
- self.selected_column_is_active = is_active;
369
- if toggle && self.selected_column == locator {
370
- self.selected_column = None;
371
- (false, None)
372
- } else {
373
- self.selected_column.clone_from(&locator);
374
-
375
- locator
376
- .clone()
377
- .map(|c| (true, c.name().cloned()))
378
- .unwrap_or_default()
379
- };
380
-
381
240
  let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
382
- open_column_settings
383
- .locator
384
- .clone_from(&self.selected_column);
385
-
386
- ctx.props()
387
- .presentation
388
- .set_open_column_settings(Some(open_column_settings));
241
+ if locator == open_column_settings.locator {
242
+ if toggle {
243
+ ctx.props().presentation.set_open_column_settings(None);
244
+ }
245
+ } else {
246
+ open_column_settings.locator.clone_from(&locator);
247
+ open_column_settings.tab =
248
+ if matches!(locator, Some(ColumnLocator::NewExpression)) {
249
+ Some(ColumnSettingsTab::Attributes)
250
+ } else {
251
+ locator.as_ref().and_then(|x| {
252
+ x.name().map(|x| {
253
+ if ctx.props().session.is_column_active(x) {
254
+ ColumnSettingsTab::Style
255
+ } else {
256
+ ColumnSettingsTab::Attributes
257
+ }
258
+ })
259
+ })
260
+ };
261
+
262
+ ctx.props()
263
+ .presentation
264
+ .set_open_column_settings(Some(open_column_settings));
265
+ }
389
266
 
390
267
  if let Some(sender) = sender {
391
268
  sender.send(()).unwrap();
@@ -393,22 +270,39 @@ impl Component for PerspectiveViewer {
393
270
 
394
271
  true
395
272
  },
396
- PerspectiveViewerMsg::SettingsPanelSizeUpdate(Some(x)) => {
273
+ SettingsPanelSizeUpdate(Some(x)) => {
397
274
  self.settings_panel_width_override = Some(x);
398
275
  false
399
276
  },
400
- PerspectiveViewerMsg::SettingsPanelSizeUpdate(None) => {
277
+ SettingsPanelSizeUpdate(None) => {
401
278
  self.settings_panel_width_override = None;
402
279
  false
403
280
  },
404
- PerspectiveViewerMsg::ColumnSettingsPanelSizeUpdate(Some(x)) => {
281
+ ColumnSettingsPanelSizeUpdate(Some(x)) => {
405
282
  self.column_settings_panel_width_override = Some(x);
406
283
  false
407
284
  },
408
- PerspectiveViewerMsg::ColumnSettingsPanelSizeUpdate(None) => {
285
+ ColumnSettingsPanelSizeUpdate(None) => {
409
286
  self.column_settings_panel_width_override = None;
410
287
  false
411
288
  },
289
+ ColumnSettingsTabChanged(tab) => {
290
+ let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
291
+ open_column_settings.tab.clone_from(&Some(tab));
292
+ ctx.props()
293
+ .presentation
294
+ .set_open_column_settings(Some(open_column_settings));
295
+ true
296
+ },
297
+ ToggleDebug => {
298
+ self.debug_open = !self.debug_open;
299
+ clone!(ctx.props().renderer, ctx.props().session);
300
+ ApiFuture::spawn(async move {
301
+ renderer.draw(session.validate().await?.create_view()).await
302
+ });
303
+
304
+ true
305
+ },
412
306
  }
413
307
  }
414
308
 
@@ -421,12 +315,7 @@ impl Component for PerspectiveViewer {
421
315
 
422
316
  /// On rendered call notify_resize(). This also triggers any registered
423
317
  /// async callbacks to the Custom Element API.
424
- fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
425
- ctx.props()
426
- .presentation
427
- .set_settings_open(Some(self.settings_open))
428
- .unwrap();
429
-
318
+ fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
430
319
  if self.on_rendered.is_some()
431
320
  && matches!(self.fonts.get_status(), FontLoaderStatus::Finished)
432
321
  && self.on_rendered.take().unwrap().send(()).is_err()
@@ -435,175 +324,125 @@ impl Component for PerspectiveViewer {
435
324
  }
436
325
  }
437
326
 
438
- /// `PerspectiveViewer` has two basic UI modes - "open" and "closed".
439
327
  fn view(&self, ctx: &Context<Self>) -> Html {
440
- let settings = ctx
441
- .link()
442
- .callback(|_| PerspectiveViewerMsg::ToggleSettingsInit(None, None));
443
-
444
- let on_close_settings = ctx
445
- .link()
446
- .callback(|()| PerspectiveViewerMsg::ToggleSettingsInit(None, None));
328
+ let Self::Properties {
329
+ custom_events,
330
+ dragdrop,
331
+ presentation,
332
+ renderer,
333
+ session,
334
+ ..
335
+ } = ctx.props();
336
+
337
+ let is_settings_open = self.settings_open && ctx.props().session.has_table();
338
+ let mut class = classes!();
339
+ if !is_settings_open {
340
+ class.push("settings-closed");
341
+ }
447
342
 
448
- let on_toggle_debug = ctx.link().callback(|_| PerspectiveViewerMsg::ToggleDebug);
449
- let mut class = classes!("settings-closed");
450
343
  if ctx.props().is_title() {
451
344
  class.push("titled");
452
345
  }
453
346
 
454
- let on_open_expr_panel =
455
- ctx.link()
456
- .callback(|c| PerspectiveViewerMsg::OpenColumnSettings {
457
- locator: Some(c),
458
- sender: None,
459
- toggle: true,
460
- });
461
-
462
- let on_reset = ctx
463
- .link()
464
- .callback(|all| PerspectiveViewerMsg::Reset(all, None));
347
+ let on_open_expr_panel = ctx.link().callback(|c| OpenColumnSettings {
348
+ locator: Some(c),
349
+ sender: None,
350
+ toggle: true,
351
+ });
465
352
 
466
353
  let on_split_panel_resize = ctx
467
354
  .link()
468
- .callback(|(x, _)| PerspectiveViewerMsg::SettingsPanelSizeUpdate(Some(x)));
355
+ .callback(|(x, _)| SettingsPanelSizeUpdate(Some(x)));
469
356
 
470
357
  let on_column_settings_panel_resize = ctx
471
358
  .link()
472
- .callback(|(x, _)| PerspectiveViewerMsg::ColumnSettingsPanelSizeUpdate(Some(x)));
359
+ .callback(|(x, _)| ColumnSettingsPanelSizeUpdate(Some(x)));
473
360
 
361
+ let on_close_settings = ctx.link().callback(|()| ToggleSettingsInit(None, None));
362
+ let on_debug = ctx.link().callback(|_| ToggleDebug);
363
+ let selected_column = ctx.props().get_current_column_locator();
364
+ let selected_tab = ctx.props().presentation.get_open_column_settings().tab;
474
365
  let settings_panel = html! {
475
- <div id="settings_panel" class="sidebar_column noselect split-panel orient-vertical">
476
- if self.selected_column.is_none() {
477
- <SidebarCloseButton
478
- id="settings_close_button"
479
- on_close_sidebar={&on_close_settings}
480
- />
481
- }
482
- <SidebarCloseButton
483
- id={if self.debug_open { "debug_close_button" } else { "debug_open_button" }}
484
- on_close_sidebar={&on_toggle_debug}
485
- />
486
- <PluginSelector
487
- session={&ctx.props().session}
488
- renderer={&ctx.props().renderer}
489
- presentation={&ctx.props().presentation}
490
- />
491
- <ColumnSelector
492
- dragdrop={&ctx.props().dragdrop}
493
- renderer={&ctx.props().renderer}
494
- session={&ctx.props().session}
495
- presentation={&ctx.props().presentation}
366
+ if is_settings_open {
367
+ <SettingsPanel
368
+ on_close={on_close_settings}
496
369
  on_resize={&self.on_resize}
497
- on_open_expr_panel={&on_open_expr_panel}
498
- on_dimensions_reset={&self.on_dimensions_reset}
499
- selected_column={self.selected_column.clone()}
370
+ on_select_column={on_open_expr_panel}
371
+ is_debug={self.debug_open}
372
+ {on_debug}
373
+ {dragdrop}
374
+ {presentation}
375
+ {renderer}
376
+ {session}
500
377
  />
501
- </div>
378
+ }
502
379
  };
503
380
 
504
- let main_panel = html! {
505
- <div id="main_column">
506
- <StatusBar
507
- id="status_bar"
508
- session={&ctx.props().session}
509
- renderer={&ctx.props().renderer}
510
- presentation={&ctx.props().presentation}
511
- on_reset={on_reset.clone()}
512
- />
513
- <div id="main_panel_container">
514
- <RenderWarning
515
- dimensions={self.dimensions}
516
- session={&ctx.props().session}
517
- renderer={&ctx.props().renderer}
381
+ let on_settings = ctx.link().callback(|()| ToggleSettingsInit(None, None));
382
+ let on_select_tab = ctx.link().callback(ColumnSettingsTabChanged);
383
+ let column_settings_panel = html! {
384
+ if let Some(selected_column) = selected_column {
385
+ <SplitPanel
386
+ id="modal_panel"
387
+ reverse=true
388
+ initial_size={self.column_settings_panel_width_override}
389
+ on_reset={ctx.link().callback(|_| ColumnSettingsPanelSizeUpdate(None))}
390
+ on_resize={on_column_settings_panel_resize}
391
+ >
392
+ <ColumnSettingsPanel
393
+ {selected_column}
394
+ {selected_tab}
395
+ on_close={self.on_close_column_settings.clone()}
396
+ width_override={self.column_settings_panel_width_override}
397
+ {on_select_tab}
398
+ {custom_events}
399
+ {presentation}
400
+ {renderer}
401
+ {session}
518
402
  />
519
- <slot />
520
- </div>
521
- if let Some(selected_column) = self.selected_column.clone() {
522
- <SplitPanel
523
- id="modal_panel"
524
- reverse=true
525
- initial_size={self.column_settings_panel_width_override}
526
- on_reset={ctx.link().callback(|_| PerspectiveViewerMsg::ColumnSettingsPanelSizeUpdate(None))}
527
- on_resize={on_column_settings_panel_resize}
528
- >
529
- <ColumnSettingsSidebar
530
- session={&ctx.props().session}
531
- renderer={&ctx.props().renderer}
532
- custom_events={&ctx.props().custom_events}
533
- presentation={&ctx.props().presentation}
534
- {selected_column}
535
- on_close={self.on_close_column_settings.clone()}
536
- width_override={self.column_settings_panel_width_override}
537
- is_active={self.selected_column_is_active}
538
- />
539
- <></>
540
- </SplitPanel>
541
- }
542
- </div>
403
+ <></>
404
+ </SplitPanel>
405
+ }
406
+ };
407
+
408
+ let main_panel = html! {
409
+ <MainPanel {on_settings} {custom_events} {presentation} {renderer} {session} />
410
+ };
411
+
412
+ let debug_panel = html! {
413
+ if self.debug_open { <DebugPanel {presentation} {renderer} {session} /> }
543
414
  };
544
415
 
545
416
  html! {
546
- <>
547
- <StyleProvider root={ctx.props().elem.clone()}>
548
- <LocalStyle href={css!("viewer")} />
549
- if self.settings_open && ctx.props().session.has_table() {
550
- if self.debug_open {
551
- <SplitPanel
552
- id="app_panel"
553
- reverse=true
554
- initial_size={self.settings_panel_width_override}
555
- on_reset={ctx.link().callback(|_| PerspectiveViewerMsg::SettingsPanelSizeUpdate(None))}
556
- on_resize={on_split_panel_resize}
557
- on_resize_finished={ctx.props().render_callback()}
558
- >
559
- <DebugPanel
560
- session={ctx.props().session()}
561
- renderer={ctx.props().renderer()}
562
- presentation={ctx.props().presentation()}
563
- />
564
- { settings_panel }
565
- { main_panel }
566
- </SplitPanel>
567
- } else {
568
- <SplitPanel
569
- id="app_panel"
570
- reverse=true
571
- initial_size={self.settings_panel_width_override}
572
- on_reset={ctx.link().callback(|_| PerspectiveViewerMsg::SettingsPanelSizeUpdate(None))}
573
- on_resize={on_split_panel_resize}
574
- on_resize_finished={ctx.props().resize_callback()}
575
- >
576
- { settings_panel }
417
+ <StyleProvider root={ctx.props().elem.clone()}>
418
+ <LocalStyle href={css!("viewer")} />
419
+ <div id="component_container">
420
+ if is_settings_open {
421
+ <SplitPanel
422
+ id="app_panel"
423
+ reverse=true
424
+ skip_empty=true
425
+ initial_size={self.settings_panel_width_override}
426
+ on_reset={ctx.link().callback(|_| SettingsPanelSizeUpdate(None))}
427
+ on_resize={on_split_panel_resize.clone()}
428
+ on_resize_finished={ctx.props().render_callback()}
429
+ >
430
+ { debug_panel }
431
+ { settings_panel }
432
+ <div id="main_column_container">
577
433
  { main_panel }
578
- </SplitPanel>
579
- }
434
+ { column_settings_panel }
435
+ </div>
436
+ </SplitPanel>
580
437
  } else {
581
- <RenderWarning
582
- dimensions={self.dimensions}
583
- session={&ctx.props().session}
584
- renderer={&ctx.props().renderer}
585
- />
586
- if ctx.props().is_title() || !ctx.props().session.has_table() || ctx.props().session.is_errored() {
587
- <StatusBar
588
- id="status_bar"
589
- session={&ctx.props().session}
590
- renderer={&ctx.props().renderer}
591
- presentation={&ctx.props().presentation}
592
- {on_reset}
593
- />
594
- }
595
- <div id="main_panel_container" {class}><slot /></div>
596
- if !ctx.props().presentation.get_is_workspace() {
597
- <div
598
- id="settings_button"
599
- class={if ctx.props().is_title() { "noselect button closed titled" } else { "noselect button closed" }}
600
- onmousedown={settings}
601
- />
602
- }
438
+ <div id="main_column_container">
439
+ { main_panel }
440
+ { column_settings_panel }
441
+ </div>
603
442
  }
604
- </StyleProvider>
443
+ </div>
605
444
  <FontLoader ..self.fonts.clone() />
606
- </>
445
+ </StyleProvider>
607
446
  }
608
447
  }
609
448
 
@@ -631,6 +470,7 @@ impl PerspectiveViewer {
631
470
  sender: Option<Sender<ApiResult<JsValue>>>,
632
471
  ) {
633
472
  let is_open = ctx.props().presentation.is_settings_open();
473
+ ctx.props().presentation.set_settings_before_open(!is_open);
634
474
  match force {
635
475
  Some(force) if is_open == force => {
636
476
  if let Some(sender) = sender {
@@ -641,15 +481,32 @@ impl PerspectiveViewer {
641
481
  let force = !is_open;
642
482
  let callback = ctx.link().callback(move |resolve| {
643
483
  let update = SettingsUpdate::Update(force);
644
- PerspectiveViewerMsg::ToggleSettingsComplete(update, resolve)
484
+ ToggleSettingsComplete(update, resolve)
645
485
  });
646
486
 
647
- clone!(ctx.props().renderer, ctx.props().session);
487
+ clone!(
488
+ ctx.props().renderer,
489
+ ctx.props().session,
490
+ ctx.props().presentation
491
+ );
492
+
648
493
  ApiFuture::spawn(async move {
649
494
  let result = if session.js_get_table().is_some() {
650
- renderer.presize(force, callback.emit_async_safe()).await
495
+ renderer
496
+ .presize(force, {
497
+ let (sender, receiver) = channel::<()>();
498
+ async move {
499
+ callback.emit(sender);
500
+ presentation.set_settings_open(!is_open);
501
+ Ok(receiver.await?)
502
+ }
503
+ })
504
+ .await
651
505
  } else {
652
- callback.emit_async_safe().await?;
506
+ let (sender, receiver) = channel::<()>();
507
+ callback.emit(sender);
508
+ presentation.set_settings_open(!is_open);
509
+ receiver.await?;
653
510
  Ok(JsValue::UNDEFINED)
654
511
  };
655
512