@perspective-dev/viewer 5.3.0 → 5.4.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 (265) hide show
  1. package/dist/cdn/perspective-viewer.js +8 -8
  2. package/dist/cdn/perspective-viewer.js.map +3 -3
  3. package/dist/css/blueprint.css +1 -0
  4. package/dist/css/botanical.css +1 -1
  5. package/dist/css/dracula.css +1 -1
  6. package/dist/css/eggplant.css +1 -0
  7. package/dist/css/gruvbox-dark.css +1 -1
  8. package/dist/css/gruvbox.css +1 -1
  9. package/dist/css/icons.css +1 -1
  10. package/dist/css/intl/ar.css +1 -0
  11. package/dist/css/intl/de.css +1 -1
  12. package/dist/css/intl/es.css +1 -1
  13. package/dist/css/intl/fr.css +1 -1
  14. package/dist/css/intl/ja.css +1 -1
  15. package/dist/css/intl/pt.css +1 -1
  16. package/dist/css/intl/zh.css +1 -1
  17. package/dist/css/intl.css +1 -1
  18. package/dist/css/ledger.css +1 -0
  19. package/dist/css/monokai.css +1 -1
  20. package/dist/css/nord.css +1 -0
  21. package/dist/css/phosphor.css +1 -1
  22. package/dist/css/pro-dark.css +1 -1
  23. package/dist/css/pro.css +1 -1
  24. package/dist/css/solarized-dark.css +1 -1
  25. package/dist/css/solarized.css +1 -1
  26. package/dist/css/themes.css +1 -1
  27. package/dist/css/vaporwave.css +1 -1
  28. package/dist/css/velvet.css +1 -0
  29. package/dist/docs/perspective-docs.json +1 -1
  30. package/dist/esm/column-format.d.ts +16 -3
  31. package/dist/esm/extensions.d.ts +0 -5
  32. package/dist/esm/perspective-viewer.inline.js +7 -7
  33. package/dist/esm/perspective-viewer.inline.js.map +3 -3
  34. package/dist/esm/perspective-viewer.js +8 -8
  35. package/dist/esm/perspective-viewer.js.map +3 -3
  36. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
  37. package/dist/wasm/perspective-viewer.d.ts +62 -73
  38. package/dist/wasm/perspective-viewer.js +230 -238
  39. package/dist/wasm/perspective-viewer.wasm +0 -0
  40. package/dist/wasm/perspective-viewer.wasm.d.ts +18 -28
  41. package/package.json +7 -7
  42. package/src/css/_context_menu.css +1 -1
  43. package/src/css/_dropdown_menu.css +1 -1
  44. package/src/css/_viewer_bundle.css +7 -8
  45. package/src/css/column-dropdown.css +0 -3
  46. package/src/css/column-selector.css +8 -5
  47. package/src/css/column-settings-panel.css +0 -206
  48. package/src/css/column-style.css +95 -0
  49. package/src/css/config-selector.css +18 -10
  50. package/src/css/filter-dropdown.css +5 -5
  51. package/src/css/form/debug.css +28 -4
  52. package/src/css/function-dropdown.css +5 -5
  53. package/src/css/panel-tab.css +23 -0
  54. package/src/css/plugin-selector.css +6 -5
  55. package/src/css/render-warning.css +1 -1
  56. package/src/css/status-bar.css +52 -21
  57. package/src/css/type-icon.css +1 -3
  58. package/src/css/{containers → ui/containers}/context-menu.css +22 -16
  59. package/src/css/ui/containers/dropdown-menu-items.css +66 -0
  60. package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
  61. package/src/css/{containers → ui/containers}/tabs.css +13 -3
  62. package/src/css/{dom → ui/dom}/select.css +19 -8
  63. package/src/css/viewer.css +32 -31
  64. package/src/rust/agent/tools.rs +5 -2
  65. package/src/rust/components/chat_panel.rs +1 -1
  66. package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
  67. package/src/rust/components/column_dropdown.rs +2 -2
  68. package/src/rust/components/column_selector/active_column.rs +15 -9
  69. package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
  70. package/src/rust/components/column_selector/config_selector.rs +2 -2
  71. package/src/rust/components/column_selector/filter_column.rs +8 -5
  72. package/src/rust/components/column_selector/inactive_column.rs +4 -2
  73. package/src/rust/components/column_selector/pivot_column.rs +4 -3
  74. package/src/rust/components/column_selector/sort_column.rs +7 -4
  75. package/src/rust/components/column_selector.rs +3 -3
  76. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
  77. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +15 -17
  78. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
  79. package/src/rust/components/column_settings_sidebar/style_tab.rs +296 -238
  80. package/src/rust/components/column_settings_sidebar.rs +3 -4
  81. package/src/rust/components/copy_dropdown.rs +1 -1
  82. package/src/rust/components/datetime_column_style/custom.rs +1 -3
  83. package/src/rust/components/datetime_column_style/simple.rs +16 -6
  84. package/src/rust/components/datetime_column_style.rs +51 -12
  85. package/src/rust/components/{form/debug.rs → debug_panel.rs} +5 -5
  86. package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
  87. package/src/rust/components/export_dropdown.rs +1 -1
  88. package/src/rust/components/expression_editor.rs +1 -1
  89. package/src/rust/components/filter_dropdown.rs +2 -2
  90. package/src/rust/components/function_dropdown.rs +2 -2
  91. package/src/rust/components/main_panel/frame_theme.rs +3 -59
  92. package/src/rust/components/main_panel/reconcile.rs +9 -4
  93. package/src/rust/components/main_panel/render.rs +62 -11
  94. package/src/rust/components/main_panel/update.rs +39 -10
  95. package/src/rust/components/main_panel.rs +26 -2
  96. package/src/rust/components/mod.rs +5 -7
  97. package/src/rust/components/new_panel_menu.rs +97 -0
  98. package/src/rust/components/number_series_style.rs +2 -3
  99. package/src/rust/components/panel_menu.rs +61 -87
  100. package/src/rust/components/panel_tab.rs +100 -43
  101. package/src/rust/components/plugin_tab.rs +125 -87
  102. package/src/rust/components/render_warning.rs +2 -2
  103. package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
  104. package/src/rust/components/settings_panel.rs +3 -2
  105. package/src/rust/components/status_bar.rs +113 -20
  106. package/src/rust/components/status_indicator.rs +5 -14
  107. package/src/rust/components/string_column_style.rs +1 -2
  108. package/src/rust/components/{style/mod.rs → style.rs} +32 -15
  109. package/src/rust/components/style_controls/number_string_format/digits_section.rs +25 -15
  110. package/src/rust/components/style_controls/number_string_format/misc_section.rs +7 -3
  111. package/src/rust/components/style_controls/number_string_format/style_section.rs +2 -1
  112. package/src/rust/components/style_controls/number_string_format/types.rs +24 -0
  113. package/src/rust/components/style_controls/number_string_format.rs +37 -14
  114. package/src/rust/components/viewer/msg.rs +43 -30
  115. package/src/rust/components/viewer/panels.rs +50 -19
  116. package/src/rust/components/viewer/render.rs +15 -13
  117. package/src/rust/components/viewer/settings.rs +137 -68
  118. package/src/rust/components/viewer/snapshots.rs +5 -10
  119. package/src/rust/components/viewer/wiring.rs +1 -21
  120. package/src/rust/components/viewer.rs +15 -10
  121. package/src/rust/components/window_editor.rs +8 -9
  122. package/src/rust/config/column_config_schema.rs +358 -52
  123. package/src/rust/config/datetime_column_style.rs +4 -21
  124. package/src/rust/config/mod.rs +0 -2
  125. package/src/rust/config/number_string_format.rs +211 -18
  126. package/src/rust/config/workspace_config.rs +2 -1
  127. package/src/rust/custom_elements/debug_plugin.rs +1 -10
  128. package/src/rust/custom_elements/mod.rs +0 -3
  129. package/src/rust/custom_elements/viewer.rs +0 -18
  130. package/src/rust/js/plugin.rs +0 -86
  131. package/src/rust/lib.rs +12 -15
  132. package/src/rust/presentation/column_locator.rs +3 -7
  133. package/src/rust/presentation.rs +85 -38
  134. package/src/rust/queries/hosted_tables.rs +38 -0
  135. package/src/rust/queries/mod.rs +2 -0
  136. package/src/rust/queries/palette_set.rs +1 -1
  137. package/src/rust/queries/plugin_column_styles.rs +2 -2
  138. package/src/rust/renderer/activate.rs +1 -3
  139. package/src/rust/renderer/dispatch.rs +117 -30
  140. package/src/rust/renderer/limits.rs +24 -5
  141. package/src/rust/renderer/plugin_config.rs +56 -8
  142. package/src/rust/renderer/props.rs +2 -1
  143. package/src/rust/renderer.rs +14 -27
  144. package/src/rust/session/props.rs +4 -3
  145. package/src/rust/session/view_subscription.rs +19 -6
  146. package/src/rust/session.rs +38 -70
  147. package/src/rust/tasks/create_panel.rs +1 -1
  148. package/src/rust/tasks/dismiss_render_warning.rs +1 -1
  149. package/src/rust/tasks/eject.rs +1 -0
  150. package/src/rust/tasks/pipeline.rs +0 -60
  151. package/src/rust/tasks/presize_panels.rs +24 -1
  152. package/src/rust/tasks/restore_and_render.rs +0 -17
  153. package/src/rust/tasks/send_column_config.rs +1 -1
  154. package/src/rust/tasks/send_plugin_config.rs +1 -1
  155. package/src/rust/tasks/set_edit_mode.rs +1 -1
  156. package/src/rust/ui/containers/control_group.rs +113 -0
  157. package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
  158. package/src/rust/{components → ui}/containers/mod.rs +1 -4
  159. package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
  160. package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
  161. package/src/rust/{components → ui}/containers/select.rs +8 -8
  162. package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
  163. package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
  164. package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
  165. package/src/rust/{components → ui}/context_menu.rs +8 -13
  166. package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
  167. package/src/rust/{components → ui}/font_loader.rs +0 -26
  168. package/src/rust/{components → ui}/form/color_selector.rs +4 -2
  169. package/src/rust/ui/form/intl_label.rs +81 -0
  170. package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
  171. package/src/rust/{components → ui}/form/mod.rs +1 -10
  172. package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
  173. package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
  174. package/src/rust/{components → ui}/form/number_field.rs +1 -1
  175. package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
  176. package/src/rust/{components → ui}/form/optional_field.rs +3 -1
  177. package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
  178. package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
  179. package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
  180. package/src/rust/ui/mod.rs +56 -0
  181. package/src/rust/ui/modal.rs +61 -0
  182. package/src/rust/{components → ui}/portal.rs +174 -79
  183. package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
  184. package/src/rust/utils/completion.rs +0 -6
  185. package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
  186. package/src/rust/utils/mod.rs +2 -0
  187. package/src/rust/workspace.rs +35 -0
  188. package/src/svg/boolean-type.svg +3 -2
  189. package/src/svg/checkbox-checked-icon.svg +4 -7
  190. package/src/svg/checkbox-hover.svg +3 -3
  191. package/src/svg/checkbox-off.svg +2 -3
  192. package/src/svg/checkbox-on.svg +3 -3
  193. package/src/svg/checkbox-unchecked-icon.svg +3 -3
  194. package/src/svg/datagrid-select-column.svg +8 -15
  195. package/src/svg/datagrid-select-region.svg +8 -15
  196. package/src/svg/datagrid-select-row-tree.svg +7 -12
  197. package/src/svg/datagrid-select-row.svg +8 -13
  198. package/src/svg/date-type.svg +4 -23
  199. package/src/svg/drawer-tab-hover.svg +4 -6
  200. package/src/svg/drawer-tab-invert-hover.svg +5 -5
  201. package/src/svg/drawer-tab-invert.svg +3 -5
  202. package/src/svg/drawer-tab.svg +5 -5
  203. package/src/svg/mega-menu-icons-candlestick.svg +5 -37
  204. package/src/svg/mega-menu-icons-ohlc.svg +4 -39
  205. package/src/svg/mega-menu-icons-sunburst.svg +4 -38
  206. package/src/svg/mega-menu-icons-treemap.svg +3 -43
  207. package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
  208. package/src/svg/new-panel-icon.svg +4 -0
  209. package/src/svg/number-type.svg +3 -3
  210. package/src/svg/radio-hover.svg +3 -3
  211. package/src/svg/radio-off.svg +3 -3
  212. package/src/svg/radio-on.svg +3 -3
  213. package/src/svg/status_ok.svg +3 -26
  214. package/src/svg/string-type.svg +4 -8
  215. package/src/svg/theme-icon.svg +5 -5
  216. package/src/themes/blueprint.css +133 -0
  217. package/src/themes/botanical.css +3 -52
  218. package/src/themes/defaults.css +3 -144
  219. package/src/themes/dracula.css +1 -71
  220. package/src/themes/eggplant.css +131 -0
  221. package/src/themes/gruvbox-dark.css +2 -79
  222. package/src/themes/gruvbox.css +2 -40
  223. package/src/themes/icons.css +2 -10
  224. package/src/themes/intl/ar.css +185 -0
  225. package/src/themes/intl/de.css +65 -33
  226. package/src/themes/intl/es.css +60 -28
  227. package/src/themes/intl/fr.css +60 -28
  228. package/src/themes/intl/ja.css +62 -30
  229. package/src/themes/intl/pt.css +58 -26
  230. package/src/themes/intl/zh.css +68 -36
  231. package/src/themes/intl.css +42 -7
  232. package/src/themes/ledger.css +137 -0
  233. package/src/themes/monokai.css +1 -64
  234. package/src/themes/nord.css +131 -0
  235. package/src/themes/phosphor.css +1 -61
  236. package/src/themes/pro-dark.css +1 -52
  237. package/src/themes/pro.css +98 -0
  238. package/src/themes/solarized-dark.css +1 -46
  239. package/src/themes/solarized.css +1 -24
  240. package/src/themes/themes.css +10 -5
  241. package/src/themes/vaporwave.css +1 -57
  242. package/src/themes/velvet.css +132 -0
  243. package/src/ts/column-format.ts +52 -2
  244. package/src/ts/extensions.ts +0 -13
  245. package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
  246. package/src/css/plugin-settings-panel.css +0 -99
  247. package/src/rust/components/modal.rs +0 -177
  248. package/src/rust/custom_elements/copy_dropdown.rs +0 -219
  249. package/src/rust/custom_elements/export_dropdown.rs +0 -216
  250. package/src/rust/custom_elements/modal.rs +0 -299
  251. package/src/svg/dropdown-selector-light.svg +0 -3
  252. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline0.js +0 -0
  253. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline1.js +0 -0
  254. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline2.js +0 -0
  255. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline3.js +0 -0
  256. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline4.js +0 -0
  257. /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline5.js +0 -0
  258. /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
  259. /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
  260. /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
  261. /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
  262. /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
  263. /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
  264. /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
  265. /package/src/rust/{components → ui}/form/number_input.rs +0 -0
@@ -10,25 +10,27 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use std::rc::Rc;
14
+
13
15
  use perspective_client::config::Filter;
14
16
  use wasm_bindgen_futures::spawn_local;
15
17
  use web_sys::*;
16
18
  use yew::prelude::*;
17
19
 
18
20
  use super::status_indicator::StatusIndicator;
19
- use crate::components::containers::select::*;
20
21
  use crate::components::copy_dropdown::CopyDropDownMenu;
21
22
  use crate::components::export_dropdown::ExportDropDownMenu;
22
23
  use crate::components::global_filter_bar::GlobalFilterBar;
23
- use crate::components::portal::PortalModal;
24
- use crate::components::status_bar_counter::StatusBarRowsCounter;
24
+ use crate::components::new_panel_menu::{HostedTables, NewPanelMenu, NewPanelPick, PanelLabels};
25
25
  use crate::components::style::StyleSurface;
26
26
  use crate::config::*;
27
27
  use crate::js::*;
28
28
  use crate::presentation::{Presentation, PresentationProps};
29
+ use crate::queries::fetch_hosted_tables;
29
30
  use crate::renderer::*;
30
31
  use crate::session::*;
31
32
  use crate::tasks::*;
33
+ use crate::ui::{PortalModal, Select, SelectItem};
32
34
  use crate::utils::*;
33
35
  use crate::workspace::Workspace;
34
36
  use crate::*;
@@ -41,9 +43,12 @@ pub struct StatusBarProps {
41
43
  /// Fired when the reset button is clicked.
42
44
  pub on_reset: Callback<bool>,
43
45
 
44
- /// Snapshots threaded from root. Component reads `has_table`, `stats`,
45
- /// `error`, `title` from session_props; `selected_theme`,
46
- /// `available_themes`, `is_workspace` from presentation_props.
46
+ /// The left-anchored "New" action, fired with the [`NewPanelMenu`] pick.
47
+ pub on_new_panel: Callback<NewPanelPick>,
48
+
49
+ /// Snapshots threaded from root. Component reads `has_table`,
50
+ /// `has_table_cells`, `error` from session_props; `selected_theme`,
51
+ /// `available_themes` from presentation_props.
47
52
  pub session_props: SessionProps,
48
53
  pub presentation_props: PresentationProps,
49
54
 
@@ -89,6 +94,18 @@ impl PartialEq for StatusBarProps {
89
94
 
90
95
  pub enum StatusBarMsg {
91
96
  Reset(MouseEvent),
97
+
98
+ /// The "New" button: fetch the hosted-table listing, then open the
99
+ /// dropdown.
100
+ NewPanel,
101
+
102
+ /// The listing resolved: open the dropdown anchored at the button, if
103
+ /// the generation is still the newest.
104
+ OpenNewPanel(u32, HostedTables),
105
+ CloseNewPanel,
106
+
107
+ /// A pick in the "New" dropdown.
108
+ NewPanelPick(NewPanelPick),
92
109
  Export,
93
110
  Copy,
94
111
  CloseExport,
@@ -103,9 +120,17 @@ pub enum StatusBarMsg {
103
120
  pub struct StatusBar {
104
121
  copy_ref: NodeRef,
105
122
  export_ref: NodeRef,
123
+ new_ref: NodeRef,
106
124
  statusbar_ref: NodeRef,
107
125
  copy_target: Option<HtmlElement>,
108
126
  export_target: Option<HtmlElement>,
127
+
128
+ /// The "New" dropdown's anchor and listings while it is open.
129
+ new_menu: Option<(HtmlElement, HostedTables, PanelLabels)>,
130
+
131
+ /// Bumped per "New" click; a resolving fetch opens only if it is still
132
+ /// the newest one.
133
+ new_generation: u32,
109
134
  }
110
135
 
111
136
  impl Component for StatusBar {
@@ -116,9 +141,12 @@ impl Component for StatusBar {
116
141
  Self {
117
142
  copy_ref: NodeRef::default(),
118
143
  export_ref: NodeRef::default(),
144
+ new_ref: NodeRef::default(),
119
145
  statusbar_ref: NodeRef::default(),
120
146
  copy_target: None,
121
147
  export_target: None,
148
+ new_menu: None,
149
+ new_generation: 0,
122
150
  }
123
151
  }
124
152
 
@@ -129,6 +157,54 @@ impl Component for StatusBar {
129
157
  ctx.props().on_reset.emit(all);
130
158
  false
131
159
  },
160
+ StatusBarMsg::NewPanel => {
161
+ self.new_generation = self.new_generation.wrapping_add(1);
162
+ let generation = self.new_generation;
163
+ let workspace = ctx.props().workspace.clone();
164
+ let link = ctx.link().clone();
165
+ ApiFuture::spawn(async move {
166
+ let tables = fetch_hosted_tables(&workspace).await;
167
+ link.send_message(StatusBarMsg::OpenNewPanel(generation, Rc::new(tables)));
168
+ Ok(())
169
+ });
170
+
171
+ false
172
+ },
173
+ StatusBarMsg::OpenNewPanel(generation, tables) => {
174
+ if generation != self.new_generation {
175
+ return false;
176
+ }
177
+
178
+ match self.new_ref.cast::<HtmlElement>() {
179
+ Some(target) => {
180
+ let workspace = &ctx.props().workspace;
181
+ let panels = Rc::new(
182
+ workspace
183
+ .panel_ids()
184
+ .into_iter()
185
+ .filter_map(|id| {
186
+ let panel = workspace.panel(&id)?;
187
+ let title = panel.session.get_title().filter(|t| !t.is_empty());
188
+ Some((id.as_str().to_owned(), title))
189
+ })
190
+ .collect::<Vec<_>>(),
191
+ );
192
+
193
+ self.new_menu = Some((target, tables, panels));
194
+ true
195
+ },
196
+ None => false,
197
+ }
198
+ },
199
+ StatusBarMsg::CloseNewPanel => {
200
+ self.new_menu = None;
201
+ true
202
+ },
203
+ StatusBarMsg::NewPanelPick(pick) => {
204
+ ctx.props().on_new_panel.emit(pick);
205
+ self.new_menu = None;
206
+ true
207
+ },
132
208
  StatusBarMsg::ResetTheme => {
133
209
  update_theme(
134
210
  &ctx.props().renderer,
@@ -187,7 +263,6 @@ impl Component for StatusBar {
187
263
  let has_table = ctx.props().session_props.has_table.clone();
188
264
  let is_errored = ctx.props().session_props.is_errored();
189
265
  let is_settings_open = ctx.props().is_settings_open;
190
- let title = &ctx.props().session_props.title;
191
266
 
192
267
  let mut is_updating_class_name = classes!();
193
268
  if !is_settings_open {
@@ -204,6 +279,9 @@ impl Component for StatusBar {
204
279
  let onexport = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Export);
205
280
  let oncopy = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Copy);
206
281
  let onreset = ctx.link().callback(StatusBarMsg::Reset);
282
+ let onnew = ctx.link().callback(|_: MouseEvent| StatusBarMsg::NewPanel);
283
+ let on_new_select = ctx.link().callback(StatusBarMsg::NewPanelPick);
284
+ let on_close_new = ctx.link().callback(|_| StatusBarMsg::CloseNewPanel);
207
285
 
208
286
  // Project only the *active* panel's plugin toolbar into the shared status
209
287
  // bar. Each panel's toolbar slots into `statusbar-extra-{its-panel-id}`
@@ -216,12 +294,7 @@ impl Component for StatusBar {
216
294
  .map(|id| format!("statusbar-extra-{id}"))
217
295
  .unwrap_or_else(|| "statusbar-extra".to_owned());
218
296
  let is_menu = matches!(has_table, Some(TableLoadState::Loaded)) && is_settings_open;
219
- // The editable title moved to the per-panel `<PanelTab>` headers; the
220
- // status bar no longer hosts an `<input>`, so its `is_active` factor is
221
- // gone. `is_title` now only gates the row counter.
222
- let is_title =
223
- is_menu || ctx.props().presentation_props.is_workspace || title.is_some() || is_errored;
224
-
297
+ let is_panel_bar = !ctx.props().workspace.clients().is_empty();
225
298
  let is_settings =
226
299
  !matches!(has_table, Some(TableLoadState::Loaded)) || is_errored || is_settings_open;
227
300
 
@@ -293,11 +366,16 @@ impl Component for StatusBar {
293
366
  update_count={ctx.props().update_count}
294
367
  session_props={ctx.props().session_props.clone()}
295
368
  />
296
- if is_title {
297
- <StatusBarRowsCounter stats={ctx.props().session_props.stats.clone()} />
369
+ if is_panel_bar {
370
+ <div id="panel-bar" class="section">
371
+ <span ref={&self.new_ref} class="hover-target" onmousedown={onnew}>
372
+ <span id="new_panel" class="button">
373
+ <span class="icon" />
374
+ <span class="icon-label" />
375
+ </span>
376
+ </span>
377
+ </div>
298
378
  }
299
- // Global-filter chips sit between the row stats and the
300
- // menu icons (rendered only when there are filters).
301
379
  if !ctx.props().global_filters.is_empty() {
302
380
  <GlobalFilterBar
303
381
  filters={ctx.props().global_filters.clone()}
@@ -349,9 +427,25 @@ impl Component for StatusBar {
349
427
  </div>
350
428
  }
351
429
  </div>
430
+ <PortalModal
431
+ tag_name="perspective-new-panel-menu"
432
+ sheet={StyleSurface::DropdownMenu.sheet()}
433
+ target={self.new_menu.as_ref().map(|(target, ..)| target.clone())}
434
+ own_focus=true
435
+ on_close={on_close_new}
436
+ theme={ctx.props().presentation_props.selected_theme.clone().unwrap_or_default()}
437
+ >
438
+ if let Some((_, tables, panels)) = &self.new_menu {
439
+ <NewPanelMenu
440
+ tables={Some(tables.clone())}
441
+ panels={panels.clone()}
442
+ callback={on_new_select}
443
+ />
444
+ }
445
+ </PortalModal>
352
446
  <PortalModal
353
447
  tag_name="perspective-copy-menu"
354
- surface={StyleSurface::DropdownMenu}
448
+ sheet={StyleSurface::DropdownMenu.sheet()}
355
449
  target={self.copy_target.clone()}
356
450
  own_focus=true
357
451
  on_close={on_close_copy}
@@ -361,7 +455,7 @@ impl Component for StatusBar {
361
455
  </PortalModal>
362
456
  <PortalModal
363
457
  tag_name="perspective-export-menu"
364
- surface={StyleSurface::DropdownMenu}
458
+ sheet={StyleSurface::DropdownMenu.sheet()}
365
459
  target={self.export_target.clone()}
366
460
  own_focus=true
367
461
  on_close={on_close_export}
@@ -429,7 +523,6 @@ fn ThemeSelector(props: &ThemeSelectorProps) -> Html {
429
523
  <span class="icon" />
430
524
  <Select<String>
431
525
  id="theme_selector"
432
- class="invert"
433
526
  {values}
434
527
  selected={selected.to_owned()}
435
528
  on_select={props.on_change.clone()}
@@ -22,18 +22,15 @@ use crate::utils::*;
22
22
 
23
23
  /// Value-prop version: no PubSub subscriptions, no reducer.
24
24
  /// The parent (`StatusBar`) re-renders this component whenever
25
- /// `session_props.error/has_table/stats` or `update_count` change (via
26
- /// root's `UpdateInFlight` / `UpdateSession` messages).
25
+ /// `session_props.error/has_table/has_table_cells` or `update_count` change
26
+ /// (via root's `UpdateInFlight` / `UpdateSession` messages).
27
27
  #[derive(PartialEq, Properties)]
28
28
  pub struct StatusIndicatorProps {
29
29
  pub renderer: Renderer,
30
30
  pub session: Session,
31
- /// Number of in-flight CONFIG-DRIVEN runs (>0 → "updating" spinner) —
32
- /// a level-triggered snapshot of `Session::in_flight_config_runs`
33
- /// (RAII-settled; see `UPDATE_COUNT_REGRESSION_PLAN.md`).
31
+
32
+ /// TODO(texodus): remove this
34
33
  pub update_count: u32,
35
- /// Snapshot of session value props — read for `error`, `has_table`,
36
- /// `stats` to derive the icon state.
37
34
  pub session_props: SessionProps,
38
35
  }
39
36
 
@@ -42,13 +39,7 @@ pub struct StatusIndicatorProps {
42
39
  /// reconnect callback when in an error state.
43
40
  #[function_component]
44
41
  pub fn StatusIndicator(props: &StatusIndicatorProps) -> Html {
45
- let has_table_cells = props
46
- .session_props
47
- .stats
48
- .as_ref()
49
- .and_then(|s| s.num_table_cells)
50
- .is_some();
51
-
42
+ let has_table_cells = props.session_props.has_table_cells;
52
43
  let state = if let Some(err) = &props.session_props.error {
53
44
  StatusIconState::Errored(
54
45
  err.message(),
@@ -12,9 +12,8 @@
12
12
 
13
13
  use yew::prelude::*;
14
14
 
15
- use super::modal::{ModalLink, SetModalLink};
16
- use crate::components::form::select_enum_field::SelectEnumField;
17
15
  use crate::config::*;
16
+ use crate::ui::{ModalLink, SelectEnumField, SetModalLink};
18
17
  use crate::utils::WeakScope;
19
18
 
20
19
  /// Format-only widget for [`String`] columns. Renders the `format` enum
@@ -25,15 +25,44 @@
25
25
  //!
26
26
  //! ```ignore
27
27
  //! html! {
28
- //! <StyleProvider root={host} surface={StyleSurface::Viewer}>
28
+ //! <StyleProvider root={host} sheet={StyleSurface::Viewer.sheet()}>
29
29
  //! <h1>{ "I am styled!" }</h1>
30
30
  //! </StyleProvider>
31
31
  //! }
32
32
  //! ```
33
33
 
34
- mod style_provider;
34
+ /// The CSS bundle a shadow tree adopts; each variant maps to one build-time
35
+ /// `src/css/_*.css` entrypoint.
36
+ #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
37
+ pub enum StyleSurface {
38
+ /// The `<perspective-viewer>` ShadowRoot.
39
+ #[default]
40
+ Viewer,
41
+ /// Column-name completion dropdown (inverted color scheme).
42
+ ColumnDropdown,
43
+ /// Filter-value completion dropdown.
44
+ FilterDropdown,
45
+ /// Expression function-completion dropdown.
46
+ FunctionDropdown,
47
+ /// Copy / Export menu (non-inverted color scheme).
48
+ DropdownMenu,
49
+ /// Panel context menu (right-click a panel).
50
+ ContextMenu,
51
+ }
35
52
 
36
- pub use style_provider::{StyleProvider, StyleSurface};
53
+ impl StyleSurface {
54
+ /// The shared, lazily constructed `CSSStyleSheet` for this surface.
55
+ pub fn sheet(self) -> web_sys::CssStyleSheet {
56
+ match self {
57
+ StyleSurface::Viewer => VIEWER_SHEET.with(|x| x.clone()),
58
+ StyleSurface::ColumnDropdown => COLUMN_DROPDOWN_SHEET.with(|x| x.clone()),
59
+ StyleSurface::FilterDropdown => FILTER_DROPDOWN_SHEET.with(|x| x.clone()),
60
+ StyleSurface::FunctionDropdown => FUNCTION_DROPDOWN_SHEET.with(|x| x.clone()),
61
+ StyleSurface::DropdownMenu => DROPDOWN_MENU_SHEET.with(|x| x.clone()),
62
+ StyleSurface::ContextMenu => CONTEXT_MENU_SHEET.with(|x| x.clone()),
63
+ }
64
+ }
65
+ }
37
66
 
38
67
  /// The viewer-surface bundle: every `src/css` file except the floating-popup
39
68
  /// sheets. Adopted into the `<perspective-viewer>` ShadowRoot.
@@ -76,15 +105,3 @@ fn make_sheet(css: &str) -> web_sys::CssStyleSheet {
76
105
  sheet.replace_sync(css).unwrap();
77
106
  sheet
78
107
  }
79
-
80
- /// The memoized, shared `CSSStyleSheet` for a given [`StyleSurface`].
81
- pub(crate) fn surface_sheet(surface: StyleSurface) -> web_sys::CssStyleSheet {
82
- match surface {
83
- StyleSurface::Viewer => VIEWER_SHEET.with(|x| x.clone()),
84
- StyleSurface::ColumnDropdown => COLUMN_DROPDOWN_SHEET.with(|x| x.clone()),
85
- StyleSurface::FilterDropdown => FILTER_DROPDOWN_SHEET.with(|x| x.clone()),
86
- StyleSurface::FunctionDropdown => FUNCTION_DROPDOWN_SHEET.with(|x| x.clone()),
87
- StyleSurface::DropdownMenu => DROPDOWN_MENU_SHEET.with(|x| x.clone()),
88
- StyleSurface::ContextMenu => CONTEXT_MENU_SHEET.with(|x| x.clone()),
89
- }
90
- }
@@ -17,15 +17,13 @@ use perspective_client::config::ColumnType;
17
17
  use yew::html;
18
18
 
19
19
  use super::CustomNumberFormat;
20
- use crate::components::containers::select::{Select, SelectItem};
21
- use crate::components::form::number_field::NumberField;
22
- use crate::components::form::number_range_field::NumberRangeField;
23
- use crate::components::form::optional_field::OptionalField;
24
- use crate::components::form::select_enum_field::SelectEnumField;
25
20
  use crate::components::style_controls::CustomNumberFormatMsg;
26
21
  use crate::config::{
27
22
  ROUNDING_INCREMENTS, RoundingIncrement, RoundingMode, RoundingPriority, TrailingZeroDisplay,
28
23
  };
24
+ use crate::ui::{
25
+ NumberField, NumberRangeField, OptionalField, Select, SelectEnumField, SelectItem,
26
+ };
29
27
 
30
28
  impl CustomNumberFormat {
31
29
  pub fn digits_section(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
@@ -37,7 +35,7 @@ impl CustomNumberFormat {
37
35
  min=1.
38
36
  max=21.
39
37
  step=1.
40
- default=1.
38
+ default={ctx.props().defaults.minimum_integer_digits}
41
39
  current_value={self.config.minimum_integer_digits}
42
40
  on_change={ctx.link().callback(CustomNumberFormatMsg::MinimumIntegerDigits)}
43
41
  />
@@ -84,14 +82,23 @@ impl CustomNumberFormat {
84
82
  }
85
83
 
86
84
  fn float_section(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
85
+ let defaults = &ctx.props().defaults;
87
86
  let fractional_value = Some((
88
- self.config.minimum_fraction_digits.unwrap_or(2.),
89
- self.config.maximum_fraction_digits.unwrap_or(2.),
87
+ self.config
88
+ .minimum_fraction_digits
89
+ .unwrap_or(defaults.fraction.0),
90
+ self.config
91
+ .maximum_fraction_digits
92
+ .unwrap_or(defaults.fraction.1),
90
93
  ));
91
94
 
92
95
  let significant_value = Some((
93
- self.config.minimum_significant_digits.unwrap_or(1.),
94
- self.config.maximum_significant_digits.unwrap_or(21.),
96
+ self.config
97
+ .minimum_significant_digits
98
+ .unwrap_or(defaults.significant.0),
99
+ self.config
100
+ .maximum_significant_digits
101
+ .unwrap_or(defaults.significant.1),
95
102
  ));
96
103
 
97
104
  html! {
@@ -102,7 +109,7 @@ impl CustomNumberFormat {
102
109
  min=0.
103
110
  max=20.
104
111
  step=1.
105
- default={(2., 2.)}
112
+ default={defaults.fraction}
106
113
  current_value={fractional_value}
107
114
  on_change={ctx.link().callback(CustomNumberFormatMsg::FracChange)}
108
115
  />
@@ -113,7 +120,7 @@ impl CustomNumberFormat {
113
120
  min=1.
114
121
  max=21.
115
122
  step=1.
116
- default={(1., 21.)}
123
+ default={defaults.significant}
117
124
  current_value={significant_value}
118
125
  on_change={ctx.link().callback(CustomNumberFormatMsg::SigChange)}
119
126
  />
@@ -121,17 +128,20 @@ impl CustomNumberFormat {
121
128
  { self.rounding_increment(ctx) }
122
129
  <SelectEnumField<RoundingPriority>
123
130
  label="rounding-priority"
124
- current_value={self.config.rounding_priority}
131
+ current_value={self.config.rounding_priority.unwrap_or(defaults.rounding_priority)}
132
+ default_value={defaults.rounding_priority}
125
133
  on_change={ctx.link().callback(CustomNumberFormatMsg::RoundingPriority)}
126
134
  />
127
135
  <SelectEnumField<RoundingMode>
128
136
  label="rounding-mode"
129
- current_value={self.config.rounding_mode}
137
+ current_value={self.config.rounding_mode.unwrap_or(defaults.rounding_mode)}
138
+ default_value={defaults.rounding_mode}
130
139
  on_change={ctx.link().callback(CustomNumberFormatMsg::RoundingMode)}
131
140
  />
132
141
  <SelectEnumField<TrailingZeroDisplay>
133
142
  label="trailing-zero-display"
134
- current_value={self.config.trailing_zero_display}
143
+ current_value={self.config.trailing_zero_display.unwrap_or(defaults.trailing_zero_display)}
144
+ default_value={defaults.trailing_zero_display}
135
145
  on_change={ctx.link().callback(CustomNumberFormatMsg::TrailingZero)}
136
146
  />
137
147
  </>
@@ -13,9 +13,9 @@
13
13
  use yew::html;
14
14
 
15
15
  use super::CustomNumberFormat;
16
- use crate::components::form::select_enum_field::SelectEnumField;
17
16
  use crate::components::style_controls::{CustomNumberFormatMsg, NotationName};
18
17
  use crate::config::*;
18
+ use crate::ui::SelectEnumField;
19
19
 
20
20
  impl CustomNumberFormat {
21
21
  pub fn misc_section(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
@@ -32,23 +32,27 @@ impl CustomNumberFormat {
32
32
  None
33
33
  };
34
34
 
35
+ let defaults = &ctx.props().defaults;
35
36
  html! {
36
37
  <>
37
38
  <SelectEnumField<NotationName>
38
39
  label="notation"
39
40
  on_change={ctx.link().callback(CustomNumberFormatMsg::NotationChanged)}
40
41
  current_value={self.notation.unwrap_or_default()}
42
+ default_value={NotationName::from(&defaults.notation)}
41
43
  />
42
44
  { compact_display_checkbox }
43
45
  <SelectEnumField<UseGrouping>
44
46
  label="use-grouping"
45
47
  on_change={ctx.link().callback(CustomNumberFormatMsg::UseGrouping)}
46
- current_value={self.config.use_grouping.unwrap_or_default()}
48
+ current_value={self.config.use_grouping.unwrap_or(defaults.use_grouping)}
49
+ default_value={defaults.use_grouping}
47
50
  />
48
51
  <SelectEnumField<SignDisplay>
49
52
  label="sign-display"
50
53
  on_change={ctx.link().callback(CustomNumberFormatMsg::SignDisplay)}
51
- current_value={self.config.sign_display.unwrap_or_default()}
54
+ current_value={self.config.sign_display.unwrap_or(defaults.sign_display)}
55
+ default_value={defaults.sign_display}
52
56
  />
53
57
  </>
54
58
  }
@@ -13,9 +13,9 @@
13
13
  use yew::html;
14
14
 
15
15
  use super::CustomNumberFormat;
16
- use crate::components::form::select_enum_field::SelectEnumField;
17
16
  use crate::components::style_controls::{CustomNumberFormatMsg, NumberStyle};
18
17
  use crate::config::*;
18
+ use crate::ui::SelectEnumField;
19
19
 
20
20
  impl CustomNumberFormat {
21
21
  pub fn style_section(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
@@ -60,6 +60,7 @@ impl CustomNumberFormat {
60
60
  <SelectEnumField<NumberStyle>
61
61
  label="style"
62
62
  current_value={self.style}
63
+ default_value={NumberStyle::from(&ctx.props().defaults.style)}
63
64
  on_change={ctx.link().callback(CustomNumberFormatMsg::StyleChanged)}
64
65
  />
65
66
  { section }
@@ -12,6 +12,8 @@
12
12
 
13
13
  use strum::{Display, EnumIter};
14
14
 
15
+ use crate::config::{Notation, NumberFormatStyle};
16
+
15
17
  #[derive(Clone, PartialEq, Debug, Copy, Default, EnumIter, Display)]
16
18
  pub enum NumberStyle {
17
19
  #[default]
@@ -21,6 +23,17 @@ pub enum NumberStyle {
21
23
  Unit,
22
24
  }
23
25
 
26
+ impl From<&NumberFormatStyle> for NumberStyle {
27
+ fn from(style: &NumberFormatStyle) -> Self {
28
+ match style {
29
+ NumberFormatStyle::Decimal => Self::Decimal,
30
+ NumberFormatStyle::Currency(_) => Self::Currency,
31
+ NumberFormatStyle::Percent => Self::Percent,
32
+ NumberFormatStyle::Unit(_) => Self::Unit,
33
+ }
34
+ }
35
+ }
36
+
24
37
  #[derive(Clone, PartialEq, Debug, Copy, Default, Display, EnumIter)]
25
38
  pub enum NotationName {
26
39
  #[default]
@@ -29,3 +42,14 @@ pub enum NotationName {
29
42
  Engineering,
30
43
  Compact,
31
44
  }
45
+
46
+ impl From<&Notation> for NotationName {
47
+ fn from(notation: &Notation) -> Self {
48
+ match notation {
49
+ Notation::Standard => Self::Standard,
50
+ Notation::Scientific => Self::Scientific,
51
+ Notation::Engineering => Self::Engineering,
52
+ Notation::Compact(_) => Self::Compact,
53
+ }
54
+ }
55
+ }
@@ -15,6 +15,8 @@ mod misc_section;
15
15
  mod style_section;
16
16
  mod types;
17
17
 
18
+ use std::rc::Rc;
19
+
18
20
  use perspective_client::config::ColumnType;
19
21
  pub use types::*;
20
22
  use yew::{Callback, Component, Properties, html};
@@ -27,6 +29,10 @@ pub struct CustomNumberFormatProps {
27
29
  pub on_change: Callback<ColumnConfigFieldUpdate>,
28
30
  pub view_type: ColumnType,
29
31
  pub column_name: String,
32
+
33
+ /// Resolved default values for this column under the active plugin.
34
+ pub defaults: Rc<NumberFormatDefaults>,
35
+
30
36
  #[prop_or_default]
31
37
  pub keys: Vec<String>,
32
38
  }
@@ -147,25 +153,39 @@ impl Component for CustomNumberFormat {
147
153
  self.config.minimum_integer_digits = val;
148
154
  },
149
155
  CustomNumberFormatMsg::FracChange(val) => {
156
+ let defaults = &ctx.props().defaults;
150
157
  self.config.rounding_increment = None;
151
158
  self.config.maximum_fraction_digits = val.map(|(_, val)| {
152
- let min = self.config.minimum_fraction_digits.unwrap_or(2.);
159
+ let min = self
160
+ .config
161
+ .minimum_fraction_digits
162
+ .unwrap_or(defaults.fraction.0);
153
163
  val.max(min)
154
164
  });
155
165
 
156
166
  self.config.minimum_fraction_digits = val.map(|(val, _)| {
157
- let max = self.config.maximum_fraction_digits.unwrap_or(2.);
167
+ let max = self
168
+ .config
169
+ .maximum_fraction_digits
170
+ .unwrap_or(defaults.fraction.1);
158
171
  val.min(max)
159
172
  });
160
173
  },
161
174
  CustomNumberFormatMsg::SigChange(val) => {
175
+ let defaults = &ctx.props().defaults;
162
176
  self.config.maximum_significant_digits = val.map(|(_, val)| {
163
- let min = self.config.minimum_significant_digits.unwrap_or(1.);
177
+ let min = self
178
+ .config
179
+ .minimum_significant_digits
180
+ .unwrap_or(defaults.significant.0);
164
181
  val.max(min)
165
182
  });
166
183
 
167
184
  self.config.minimum_significant_digits = val.map(|(val, _)| {
168
- let max = self.config.maximum_significant_digits.unwrap_or(21.);
185
+ let max = self
186
+ .config
187
+ .maximum_significant_digits
188
+ .unwrap_or(defaults.significant.1);
169
189
  val.min(max)
170
190
  });
171
191
  },
@@ -191,8 +211,7 @@ impl Component for CustomNumberFormat {
191
211
  },
192
212
  };
193
213
 
194
- let is_float = ctx.props().view_type == ColumnType::Float;
195
- let filtered_config = self.config.clone().filter_default(is_float);
214
+ let filtered_config = self.config.clone().filter_default(&ctx.props().defaults);
196
215
  let mut value = serde_json::Map::new();
197
216
  if filtered_config != CustomNumberFormatConfig::default() {
198
217
  value.insert(
@@ -216,20 +235,24 @@ impl Component for CustomNumberFormat {
216
235
 
217
236
  impl CustomNumberFormat {
218
237
  fn initialize(ctx: &yew::prelude::Context<Self>) -> Self {
219
- let config = ctx.props().restored_config.clone();
238
+ let defaults = &ctx.props().defaults;
239
+ let mut config = ctx.props().restored_config.clone();
240
+ if config._style.is_none() && defaults.style != NumberFormatStyle::Decimal {
241
+ config._style = Some(defaults.style.clone());
242
+ }
243
+
244
+ if config._notation.is_none() && defaults.notation != Notation::Standard {
245
+ config._notation = Some(defaults.notation.clone());
246
+ }
247
+
220
248
  Self {
221
249
  style: config
222
250
  ._style
223
251
  .as_ref()
224
- .map(|style| match style {
225
- NumberFormatStyle::Decimal => NumberStyle::Decimal,
226
- NumberFormatStyle::Currency(_) => NumberStyle::Currency,
227
- NumberFormatStyle::Percent => NumberStyle::Percent,
228
- NumberFormatStyle::Unit(_) => NumberStyle::Unit,
229
- })
252
+ .map(NumberStyle::from)
230
253
  .unwrap_or_default(),
254
+ notation: config._notation.as_ref().map(NotationName::from),
231
255
  config,
232
- notation: None,
233
256
  }
234
257
  }
235
258
  }