@perspective-dev/viewer 4.0.0 → 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 (184) 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 +1251 -762
  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/export_app.rs +3 -2
  126. package/src/rust/model/get_viewer_config.rs +4 -28
  127. package/src/rust/model/intersection_observer.rs +20 -8
  128. package/src/rust/model/mod.rs +11 -4
  129. package/src/rust/model/plugin_column_styles.rs +0 -31
  130. package/src/rust/model/reset_all.rs +38 -0
  131. package/src/rust/model/resize_observer.rs +34 -7
  132. package/src/rust/model/restore_and_render.rs +12 -7
  133. package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
  134. package/src/rust/model/structural.rs +194 -23
  135. package/src/rust/model/update_and_render.rs +14 -4
  136. package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
  137. package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
  138. package/src/rust/presentation.rs +60 -119
  139. package/src/rust/renderer/activate.rs +20 -5
  140. package/src/rust/renderer/limits.rs +0 -149
  141. package/src/rust/renderer/render_timer.rs +1 -1
  142. package/src/rust/renderer.rs +34 -18
  143. package/src/rust/root.rs +50 -0
  144. package/src/rust/session/column_defaults_update.rs +4 -4
  145. package/src/rust/session/drag_drop_update.rs +1 -1
  146. package/src/rust/session/metadata.rs +3 -17
  147. package/src/rust/session/replace_expression_update.rs +1 -2
  148. package/src/rust/session.rs +162 -82
  149. package/src/rust/utils/browser/blob.rs +16 -2
  150. package/src/rust/utils/browser/download.rs +1 -0
  151. package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
  152. package/src/rust/utils/browser/mod.rs +8 -4
  153. package/src/rust/utils/browser/selection.rs +5 -0
  154. package/src/rust/utils/custom_element.rs +28 -13
  155. package/src/rust/utils/datetime.rs +5 -0
  156. package/src/rust/utils/debounce.rs +7 -1
  157. package/src/rust/utils/hooks/use_async_callback.rs +7 -17
  158. package/src/rust/utils/mod.rs +28 -40
  159. package/src/rust/utils/number_format.rs +6 -5
  160. package/src/rust/utils/pubsub.rs +15 -10
  161. package/src/rust/utils/weak_scope.rs +11 -1
  162. package/src/svg/bookmark-icon.svg +4 -0
  163. package/src/svg/drag-handle copy.svg +10 -0
  164. package/src/svg/drawer-tab-hover.svg +5 -7
  165. package/src/svg/drawer-tab-invert-hover.svg +4 -8
  166. package/src/svg/drawer-tab-invert.svg +4 -7
  167. package/src/svg/drawer-tab.svg +4 -6
  168. package/src/svg/status_ok.svg +24 -24
  169. package/src/ts/extensions.ts +51 -3
  170. package/src/ts/perspective-viewer.ts +2 -14
  171. package/src/ts/plugin.ts +1 -1
  172. package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
  173. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
  174. package/src/rust/components/containers/tests/mod.rs +0 -11
  175. package/src/rust/components/containers/tests/split_panel.rs +0 -91
  176. package/src/rust/js/testing.rs +0 -149
  177. package/src/rust/utils/tee.rs +0 -88
  178. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline0.js +0 -0
  179. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline1.js +0 -0
  180. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline2.js +0 -0
  181. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline3.js +0 -0
  182. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline4.js +0 -0
  183. /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
  184. /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
@@ -12,7 +12,6 @@
12
12
 
13
13
  use std::rc::Rc;
14
14
 
15
- use wasm_bindgen::JsCast;
16
15
  use web_sys::*;
17
16
  use yew::prelude::*;
18
17
 
@@ -22,29 +21,33 @@ use crate::components::containers::select::*;
22
21
  use crate::components::status_bar_counter::StatusBarRowsCounter;
23
22
  use crate::custom_elements::copy_dropdown::*;
24
23
  use crate::custom_elements::export_dropdown::*;
24
+ use crate::custom_events::CustomEvents;
25
+ use crate::model::*;
25
26
  use crate::presentation::Presentation;
26
27
  use crate::renderer::*;
27
28
  use crate::session::*;
28
- #[cfg(test)]
29
- use crate::utils::WeakScope;
30
29
  use crate::utils::*;
31
30
  use crate::*;
32
31
 
33
- #[derive(Properties, Clone)]
32
+ #[derive(Properties, PerspectiveProperties!)]
34
33
  pub struct StatusBarProps {
34
+ // DOM Attribute
35
35
  pub id: String,
36
+
37
+ /// Fired when the reset button is clicked.
36
38
  pub on_reset: Callback<bool>,
39
+
40
+ /// Fires when the settings button is clicked
41
+ #[prop_or_default]
42
+ pub on_settings: Option<Callback<()>>,
43
+
44
+ // State
45
+ pub custom_events: CustomEvents,
37
46
  pub session: Session,
38
47
  pub renderer: Renderer,
39
48
  pub presentation: Presentation,
40
-
41
- #[cfg(test)]
42
- #[prop_or_default]
43
- pub weak_link: WeakScope<StatusBar>,
44
49
  }
45
50
 
46
- derive_model!(Renderer, Session, Presentation for StatusBarProps);
47
-
48
51
  impl PartialEq for StatusBarProps {
49
52
  fn eq(&self, other: &Self) -> bool {
50
53
  self.id == other.id
@@ -52,27 +55,29 @@ impl PartialEq for StatusBarProps {
52
55
  }
53
56
 
54
57
  pub enum StatusBarMsg {
55
- Reset(bool),
58
+ Reset(MouseEvent),
56
59
  Export,
57
60
  Copy,
58
61
  Noop,
62
+ Eject,
59
63
  SetThemeConfig((Rc<Vec<String>>, Option<usize>)),
60
64
  SetTheme(String),
61
65
  ResetTheme,
62
- // SetError(Option<String>),
63
- // TableStatsChanged,
64
- // SetIsUpdating(bool),
65
- SetTitle(Option<String>),
66
+ PointerEvent(web_sys::PointerEvent),
67
+ TitleInputEvent,
68
+ TitleChangeEvent,
66
69
  }
67
70
 
68
71
  /// A toolbar with buttons, and `Table` & `View` status information.
69
72
  pub struct StatusBar {
70
- is_updating: i32,
73
+ _subscriptions: [Subscription; 5],
74
+ copy_ref: NodeRef,
75
+ export_ref: NodeRef,
76
+ input_ref: NodeRef,
77
+ statusbar_ref: NodeRef,
71
78
  theme: Option<String>,
72
79
  themes: Rc<Vec<String>>,
73
- export_ref: NodeRef,
74
- copy_ref: NodeRef,
75
- _sub: [Subscription; 2],
80
+ title: Option<String>,
76
81
  }
77
82
 
78
83
  impl Component for StatusBar {
@@ -80,52 +85,37 @@ impl Component for StatusBar {
80
85
  type Properties = StatusBarProps;
81
86
 
82
87
  fn create(ctx: &Context<Self>) -> Self {
83
- let _sub = [
84
- ctx.props()
85
- .presentation
86
- .theme_config_updated
87
- .add_listener(ctx.link().callback(StatusBarMsg::SetThemeConfig)),
88
- ctx.props()
89
- .presentation
90
- .title_changed
91
- .add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
92
- ];
93
-
94
- // Fetch initial theme
95
- let presentation = ctx.props().presentation.clone();
96
- let on_theme = ctx.link().callback(StatusBarMsg::SetThemeConfig);
97
- ApiFuture::spawn(async move {
98
- on_theme.emit(presentation.get_selected_theme_config().await?);
99
- Ok(())
100
- });
101
-
88
+ fetch_initial_theme(ctx);
102
89
  Self {
103
- _sub,
104
- theme: None,
105
- themes: vec![].into(),
90
+ _subscriptions: register_listeners(ctx),
106
91
  copy_ref: NodeRef::default(),
107
92
  export_ref: NodeRef::default(),
108
- is_updating: 0,
93
+ input_ref: NodeRef::default(),
94
+ statusbar_ref: NodeRef::default(),
95
+ theme: None,
96
+ themes: vec![].into(),
97
+ title: ctx.props().session().get_title().clone(),
109
98
  }
110
99
  }
111
100
 
101
+ fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
102
+ self._subscriptions = register_listeners(ctx);
103
+ true
104
+ }
105
+
112
106
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
113
- match msg {
114
- StatusBarMsg::Reset(all) => {
107
+ maybe_log_or_default!(Ok(match msg {
108
+ StatusBarMsg::Reset(event) => {
109
+ let all = event.shift_key();
115
110
  ctx.props().on_reset.emit(all);
116
111
  false
117
112
  },
118
113
  StatusBarMsg::ResetTheme => {
119
- clone!(
120
- ctx.props().renderer,
121
- ctx.props().session,
122
- ctx.props().presentation
123
- );
124
-
114
+ let state = ctx.props().clone_state();
125
115
  ApiFuture::spawn(async move {
126
- presentation.reset_theme().await?;
127
- let view = session.get_view().into_apierror()?;
128
- renderer.restyle_all(&view).await
116
+ state.presentation.reset_theme().await?;
117
+ let view = state.session.get_view().into_apierror()?;
118
+ state.renderer.restyle_all(&view).await
129
119
  });
130
120
  true
131
121
  },
@@ -137,133 +127,247 @@ impl Component for StatusBar {
137
127
  should_render
138
128
  },
139
129
  StatusBarMsg::SetTheme(theme_name) => {
140
- clone!(
141
- ctx.props().renderer,
142
- ctx.props().session,
143
- ctx.props().presentation
144
- );
130
+ let state = ctx.props().clone_state();
145
131
  ApiFuture::spawn(async move {
146
- presentation.set_theme_name(Some(&theme_name)).await?;
147
- let view = session.get_view().into_apierror()?;
148
- renderer.restyle_all(&view).await
132
+ state.presentation.set_theme_name(Some(&theme_name)).await?;
133
+ let view = state.session.get_view().into_apierror()?;
134
+ state.renderer.restyle_all(&view).await
149
135
  });
150
136
 
151
137
  false
152
138
  },
153
139
  StatusBarMsg::Export => {
154
- let target = self.export_ref.cast::<HtmlElement>().unwrap();
140
+ let target = self.export_ref.cast::<HtmlElement>().into_apierror()?;
155
141
  ExportDropDownMenuElement::new_from_model(ctx.props()).open(target);
156
142
  false
157
143
  },
158
144
  StatusBarMsg::Copy => {
159
- let target = self.copy_ref.cast::<HtmlElement>().unwrap();
145
+ let target = self.copy_ref.cast::<HtmlElement>().into_apierror()?;
160
146
  CopyDropDownMenuElement::new_from_model(ctx.props()).open(target);
161
147
  false
162
148
  },
163
- StatusBarMsg::Noop => true,
164
- StatusBarMsg::SetTitle(title) => {
165
- ctx.props().presentation.set_title(title);
149
+ StatusBarMsg::Eject => {
150
+ ctx.props().presentation().on_eject.emit(());
166
151
  false
167
152
  },
168
- }
153
+ StatusBarMsg::Noop => {
154
+ self.title = ctx.props().session().get_title();
155
+ true
156
+ },
157
+ StatusBarMsg::TitleInputEvent => {
158
+ let elem = self.input_ref.cast::<HtmlInputElement>().into_apierror()?;
159
+ let title = elem.value();
160
+ let title = if title.trim().is_empty() {
161
+ None
162
+ } else {
163
+ Some(title)
164
+ };
165
+
166
+ self.title = title;
167
+ true
168
+ },
169
+ StatusBarMsg::TitleChangeEvent => {
170
+ let elem = self.input_ref.cast::<HtmlInputElement>().into_apierror()?;
171
+ let title = elem.value();
172
+ let title = if title.trim().is_empty() {
173
+ None
174
+ } else {
175
+ Some(title)
176
+ };
177
+
178
+ ctx.props().session().set_title(title);
179
+ false
180
+ },
181
+ StatusBarMsg::PointerEvent(event) => {
182
+ if event.target().map(JsValue::from)
183
+ == self.statusbar_ref.cast::<HtmlElement>().map(JsValue::from)
184
+ {
185
+ ctx.props()
186
+ .custom_events()
187
+ .dispatch_event(format!("statusbar-{}", event.type_()).as_str(), &event)?;
188
+ }
189
+
190
+ false
191
+ },
192
+ }))
169
193
  }
170
194
 
171
195
  fn view(&self, ctx: &Context<Self>) -> Html {
196
+ let Self::Properties {
197
+ custom_events,
198
+ presentation,
199
+ renderer,
200
+ session,
201
+ ..
202
+ } = ctx.props();
203
+
172
204
  let mut is_updating_class_name = classes!();
173
- if self.is_updating > 0 {
174
- is_updating_class_name.push("updating")
205
+ if session.get_title().is_some() {
206
+ is_updating_class_name.push("titled");
175
207
  };
176
208
 
177
- if ctx.props().presentation.get_title().is_some() {
178
- is_updating_class_name.push("titled")
209
+ if !presentation.is_settings_open() {
210
+ is_updating_class_name.push(["settings-closed", "titled"]);
179
211
  };
180
212
 
181
- let reset = ctx
182
- .link()
183
- .callback(|event: MouseEvent| StatusBarMsg::Reset(event.shift_key()));
184
-
185
- let export = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Export);
186
- let copy = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Copy);
187
-
188
- let onchange = ctx.link().callback({
189
- move |input: Event| {
190
- let title = input
191
- .target()
192
- .unwrap()
193
- .unchecked_into::<HtmlInputElement>()
194
- .value();
213
+ if !session.has_table() {
214
+ is_updating_class_name.push("updating");
215
+ }
195
216
 
196
- let title = if title.trim().is_empty() {
197
- None
198
- } else {
199
- Some(title)
200
- };
217
+ // TODO Memoizing these would reduce some vdom diffing later on
218
+ let onblur = ctx.link().callback(|_| StatusBarMsg::Noop);
219
+ let onclose = ctx.link().callback(|_| StatusBarMsg::Eject);
220
+ let onpointerdown = ctx.link().callback(StatusBarMsg::PointerEvent);
221
+ let onexport = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Export);
222
+ let oncopy = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Copy);
223
+ let onreset = ctx.link().callback(StatusBarMsg::Reset);
224
+ let onchange = ctx
225
+ .link()
226
+ .callback(|_: Event| StatusBarMsg::TitleChangeEvent);
201
227
 
202
- StatusBarMsg::SetTitle(title)
203
- }
204
- });
228
+ let oninput = ctx
229
+ .link()
230
+ .callback(|_: InputEvent| StatusBarMsg::TitleInputEvent);
205
231
 
206
- let is_menu = ctx.props().session.has_table()
207
- && (ctx.props().presentation.is_settings_open()
208
- || (ctx.props().presentation.get_title().is_some()
209
- && !ctx.props().session.is_errored()));
232
+ let is_menu = session.has_table() && ctx.props().on_settings.as_ref().is_none();
233
+ let is_title = is_menu
234
+ || presentation.get_is_workspace()
235
+ || session.get_title().is_some()
236
+ || session.is_errored()
237
+ || presentation.is_active(&self.input_ref.cast::<Element>());
210
238
 
211
- if !ctx.props().session.has_table() {
212
- is_updating_class_name.push("updating");
213
- }
239
+ let is_settings = session.get_title().is_some()
240
+ || presentation.get_is_workspace()
241
+ || !session.has_table()
242
+ || session.is_errored()
243
+ || presentation.is_settings_open()
244
+ || presentation.is_active(&self.input_ref.cast::<Element>());
214
245
 
215
- html! {
216
- <>
217
- <LocalStyle href={css!("status-bar")} />
218
- <div id={ctx.props().id.clone()} class={is_updating_class_name}>
219
- <StatusIndicator
220
- session={&ctx.props().session}
221
- renderer={&ctx.props().renderer}
222
- />
223
- if is_menu {
224
- <label
225
- class="input-sizer"
226
- data-value={ctx.props().presentation.get_title().unwrap_or_default()}
227
- >
228
- <input
229
- placeholder=" "
230
- value={ctx.props().presentation.get_title()}
231
- size="10"
232
- {onchange}
233
- />
234
- <span id="status-bar-placeholder" />
235
- </label>
236
- }
237
- <StatusBarRowsCounter session={&ctx.props().session} />
238
- <div id="spacer" />
239
- if is_menu {
240
- <div id="menu-bar" class="section">
241
- <ThemeSelector
242
- theme={self.theme.clone()}
243
- themes={self.themes.clone()}
244
- on_change={ctx.link().callback(StatusBarMsg::SetTheme)}
245
- on_reset={ctx.link().callback(|_| StatusBarMsg::ResetTheme)}
246
+ if is_settings {
247
+ html! {
248
+ <>
249
+ <LocalStyle href={css!("status-bar")} />
250
+ <div
251
+ ref={&self.statusbar_ref}
252
+ id={ctx.props().id.clone()}
253
+ class={is_updating_class_name}
254
+ {onpointerdown}
255
+ >
256
+ <StatusIndicator {custom_events} {renderer} {session} />
257
+ if is_title {
258
+ <label
259
+ class="input-sizer"
260
+ data-value={self.title.clone().unwrap_or_default()}
261
+ >
262
+ <input
263
+ ref={&self.input_ref}
264
+ placeholder=""
265
+ value={self.title.clone().unwrap_or_default()}
266
+ size="10"
267
+ {onblur}
268
+ {onchange}
269
+ {oninput}
270
+ />
271
+ <span id="status-bar-placeholder" />
272
+ </label>
273
+ }
274
+ if is_title {
275
+ <StatusBarRowsCounter {session} />
276
+ }
277
+ <div id="spacer" />
278
+ if is_menu {
279
+ <div id="menu-bar" class="section">
280
+ <ThemeSelector
281
+ theme={self.theme.clone()}
282
+ themes={self.themes.clone()}
283
+ on_change={ctx.link().callback(StatusBarMsg::SetTheme)}
284
+ on_reset={ctx.link().callback(|_| StatusBarMsg::ResetTheme)}
285
+ />
286
+ <div id="plugin-settings"><slot name="statusbar-extra" /></div>
287
+ <span class="hover-target">
288
+ <span id="reset" class="button" onmousedown={&onreset}>
289
+ <span />
290
+ </span>
291
+ </span>
292
+ <span
293
+ ref={&self.export_ref}
294
+ class="hover-target"
295
+ onmousedown={onexport}
296
+ >
297
+ <span id="export" class="button"><span /></span>
298
+ </span>
299
+ <span
300
+ ref={&self.copy_ref}
301
+ class="hover-target"
302
+ onmousedown={oncopy}
303
+ >
304
+ <span id="copy" class="button"><span /></span>
305
+ </span>
306
+ </div>
307
+ }
308
+ if let Some(x) = ctx.props().on_settings.as_ref() {
309
+ <div
310
+ id="settings_button"
311
+ class="noselect"
312
+ onmousedown={x.reform(|_| ())}
246
313
  />
247
- <div id="plugin-settings"><slot name="plugin-settings" /></div>
248
- <span class="hover-target">
249
- <span id="reset" class="button" onmousedown={reset}><span /></span>
250
- </span>
251
- <span class="hover-target" ref={&self.export_ref} onmousedown={export}>
252
- <span id="export" class="button"><span /></span>
253
- </span>
254
- <span class="hover-target" ref={&self.copy_ref} onmousedown={copy}>
255
- <span id="copy" class="button"><span /></span>
256
- </span>
257
- </div>
258
- }
314
+ <div id="close_button" class="noselect" onmousedown={onclose} />
315
+ }
316
+ </div>
317
+ </>
318
+ }
319
+ } else if let Some(x) = ctx.props().on_settings.as_ref() {
320
+ let class = classes!(is_updating_class_name, "floating");
321
+ html! {
322
+ <div id={ctx.props().id.clone()} {class}>
323
+ <div id="settings_button" class="noselect" onmousedown={x.reform(|_| ())} />
324
+ <div id="close_button" class="noselect" onmousedown={&onclose} />
259
325
  </div>
260
- </>
326
+ }
327
+ } else {
328
+ html! {}
261
329
  }
262
330
  }
263
331
  }
264
332
 
333
+ fn register_listeners(ctx: &Context<StatusBar>) -> [Subscription; 5] {
334
+ [
335
+ ctx.props()
336
+ .presentation()
337
+ .theme_config_updated
338
+ .add_listener(ctx.link().callback(StatusBarMsg::SetThemeConfig)),
339
+ ctx.props()
340
+ .presentation()
341
+ .visibility_changed
342
+ .add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
343
+ ctx.props()
344
+ .session()
345
+ .title_changed
346
+ .add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
347
+ ctx.props()
348
+ .session()
349
+ .table_loaded
350
+ .add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
351
+ ctx.props()
352
+ .session()
353
+ .table_errored
354
+ .add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
355
+ ]
356
+ }
357
+
358
+ fn fetch_initial_theme(ctx: &Context<StatusBar>) {
359
+ ApiFuture::spawn({
360
+ let on_theme = ctx.link().callback(StatusBarMsg::SetThemeConfig);
361
+ clone!(ctx.props().presentation());
362
+ async move {
363
+ on_theme.emit(presentation.get_selected_theme_config().await?);
364
+ Ok(())
365
+ }
366
+ });
367
+ }
368
+
265
369
  #[derive(Properties, PartialEq)]
266
- pub struct ThemeSelectorProps {
370
+ struct ThemeSelectorProps {
267
371
  pub theme: Option<String>,
268
372
  pub themes: Rc<Vec<String>>,
269
373
  pub on_reset: Callback<()>,
@@ -271,7 +375,7 @@ pub struct ThemeSelectorProps {
271
375
  }
272
376
 
273
377
  #[function_component]
274
- pub fn ThemeSelector(props: &ThemeSelectorProps) -> Html {
378
+ fn ThemeSelector(props: &ThemeSelectorProps) -> Html {
275
379
  let is_first = props
276
380
  .theme
277
381
  .as_ref()