@perspective-dev/viewer 5.3.1 → 5.5.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 (291) hide show
  1. package/dist/cdn/perspective-viewer.js +6 -6
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/blueprint.css +1 -1
  4. package/dist/css/botanical.css +1 -1
  5. package/dist/css/dracula.css +1 -1
  6. package/dist/css/eggplant.css +1 -1
  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 -1
  19. package/dist/css/monokai.css +1 -1
  20. package/dist/css/nord.css +1 -1
  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 -1
  29. package/dist/docs/perspective-docs.json +1 -1
  30. package/dist/esm/extensions.d.ts +0 -5
  31. package/dist/esm/perspective-viewer.d.ts +2 -0
  32. package/dist/esm/perspective-viewer.inline.js +8 -8
  33. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  34. package/dist/esm/perspective-viewer.js +5 -5
  35. package/dist/esm/perspective-viewer.js.map +2 -2
  36. package/dist/esm/plugin.d.ts +9 -0
  37. package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
  38. package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
  39. package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
  40. package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
  41. package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
  42. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
  43. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
  44. package/dist/wasm/perspective-viewer.d.ts +137 -104
  45. package/dist/wasm/perspective-viewer.js +337 -269
  46. package/dist/wasm/perspective-viewer.wasm +0 -0
  47. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
  48. package/package.json +7 -7
  49. package/src/css/_context_menu.css +1 -1
  50. package/src/css/_dropdown_menu.css +1 -1
  51. package/src/css/_viewer_bundle.css +7 -8
  52. package/src/css/column-dropdown.css +0 -3
  53. package/src/css/column-selector.css +18 -7
  54. package/src/css/column-settings-panel.css +0 -225
  55. package/src/css/column-style.css +175 -0
  56. package/src/css/config-selector.css +19 -11
  57. package/src/css/filter-dropdown.css +5 -5
  58. package/src/css/form/debug.css +37 -4
  59. package/src/css/function-dropdown.css +5 -5
  60. package/src/css/panel-tab.css +23 -0
  61. package/src/css/plugin-selector.css +5 -5
  62. package/src/css/render-warning.css +1 -1
  63. package/src/css/status-bar.css +63 -21
  64. package/src/css/type-icon.css +1 -3
  65. package/src/css/{containers → ui/containers}/context-menu.css +22 -16
  66. package/src/css/ui/containers/dropdown-menu-items.css +66 -0
  67. package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
  68. package/src/css/{containers → ui/containers}/tabs.css +2 -1
  69. package/src/css/viewer.css +32 -26
  70. package/src/rust/agent/tools.rs +80 -7
  71. package/src/rust/components/chat_panel.rs +1 -1
  72. package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
  73. package/src/rust/components/column_dropdown.rs +2 -2
  74. package/src/rust/components/column_selector/active_column.rs +24 -9
  75. package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
  76. package/src/rust/components/column_selector/config_selector.rs +2 -2
  77. package/src/rust/components/column_selector/filter_column.rs +8 -5
  78. package/src/rust/components/column_selector/inactive_column.rs +4 -2
  79. package/src/rust/components/column_selector/pivot_column.rs +4 -3
  80. package/src/rust/components/column_selector/sort_column.rs +7 -4
  81. package/src/rust/components/column_selector.rs +14 -3
  82. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
  83. package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
  84. package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
  85. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
  86. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
  87. package/src/rust/components/column_settings_sidebar/style_tab.rs +70 -12
  88. package/src/rust/components/column_settings_sidebar.rs +3 -4
  89. package/src/rust/components/copy_dropdown.rs +1 -1
  90. package/src/rust/components/datetime_column_style/custom.rs +1 -3
  91. package/src/rust/components/datetime_column_style/simple.rs +1 -2
  92. package/src/rust/components/datetime_column_style.rs +1 -2
  93. package/src/rust/components/{form/debug.rs → debug_panel.rs} +112 -55
  94. package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
  95. package/src/rust/components/export_dropdown.rs +1 -1
  96. package/src/rust/components/expression_editor.rs +1 -1
  97. package/src/rust/components/filter_dropdown.rs +2 -2
  98. package/src/rust/components/function_dropdown.rs +2 -2
  99. package/src/rust/components/main_panel/frame_theme.rs +3 -59
  100. package/src/rust/components/main_panel/msg.rs +4 -0
  101. package/src/rust/components/main_panel/reconcile.rs +9 -4
  102. package/src/rust/components/main_panel/render.rs +62 -11
  103. package/src/rust/components/main_panel/update.rs +39 -10
  104. package/src/rust/components/main_panel.rs +40 -2
  105. package/src/rust/components/mod.rs +5 -8
  106. package/src/rust/components/new_panel_menu.rs +97 -0
  107. package/src/rust/components/number_series_style.rs +2 -3
  108. package/src/rust/components/panel_menu.rs +61 -87
  109. package/src/rust/components/panel_tab.rs +100 -43
  110. package/src/rust/components/plugin_tab.rs +58 -4
  111. package/src/rust/components/render_warning.rs +2 -2
  112. package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
  113. package/src/rust/components/settings_panel.rs +8 -2
  114. package/src/rust/components/status_bar.rs +113 -19
  115. package/src/rust/components/status_indicator.rs +9 -14
  116. package/src/rust/components/{style/mod.rs → style.rs} +32 -15
  117. package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
  118. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  119. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  120. package/src/rust/components/viewer/msg.rs +43 -30
  121. package/src/rust/components/viewer/panels.rs +53 -19
  122. package/src/rust/components/viewer/render.rs +16 -13
  123. package/src/rust/components/viewer/settings.rs +137 -68
  124. package/src/rust/components/viewer/snapshots.rs +5 -10
  125. package/src/rust/components/viewer/wiring.rs +11 -22
  126. package/src/rust/components/viewer.rs +15 -10
  127. package/src/rust/components/window_editor.rs +8 -9
  128. package/src/rust/config/column_config_schema.rs +312 -33
  129. package/src/rust/config/datetime_column_style.rs +0 -11
  130. package/src/rust/config/mod.rs +0 -4
  131. package/src/rust/config/options.rs +50 -0
  132. package/src/rust/config/workspace_config.rs +70 -24
  133. package/src/rust/custom_elements/debug_plugin.rs +1 -10
  134. package/src/rust/custom_elements/mod.rs +0 -3
  135. package/src/rust/custom_elements/viewer.rs +194 -115
  136. package/src/rust/js/plugin.rs +3 -87
  137. package/src/rust/lib.rs +19 -18
  138. package/src/rust/presentation/column_locator.rs +3 -7
  139. package/src/rust/presentation.rs +68 -38
  140. package/src/rust/queries/hosted_tables.rs +38 -0
  141. package/src/rust/queries/mod.rs +2 -0
  142. package/src/rust/queries/palette_set.rs +1 -1
  143. package/src/rust/queries/plugin_column_styles.rs +6 -5
  144. package/src/rust/renderer/activate.rs +1 -3
  145. package/src/rust/renderer/dispatch.rs +83 -22
  146. package/src/rust/renderer/limits.rs +24 -5
  147. package/src/rust/renderer/plugin_config.rs +201 -41
  148. package/src/rust/renderer/plugin_store.rs +2 -1
  149. package/src/rust/renderer/props.rs +7 -1
  150. package/src/rust/renderer.rs +23 -27
  151. package/src/rust/session/props.rs +8 -3
  152. package/src/rust/session/view_subscription.rs +19 -6
  153. package/src/rust/session.rs +189 -150
  154. package/src/rust/tasks/create_panel.rs +5 -2
  155. package/src/rust/tasks/dismiss_render_warning.rs +1 -1
  156. package/src/rust/tasks/eject.rs +1 -0
  157. package/src/rust/tasks/pipeline.rs +18 -67
  158. package/src/rust/tasks/presize_panels.rs +24 -1
  159. package/src/rust/tasks/restore_and_render.rs +0 -17
  160. package/src/rust/tasks/restore_panel.rs +129 -30
  161. package/src/rust/tasks/send_column_config.rs +4 -1
  162. package/src/rust/tasks/send_plugin_config.rs +1 -1
  163. package/src/rust/tasks/set_edit_mode.rs +1 -1
  164. package/src/rust/tasks/sync_update_panels.rs +147 -67
  165. package/src/rust/tasks/table_lifecycle.rs +7 -1
  166. package/src/rust/{components → ui}/containers/control_group.rs +25 -3
  167. package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
  168. package/src/rust/{components → ui}/containers/mod.rs +0 -4
  169. package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
  170. package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
  171. package/src/rust/{components → ui}/containers/select.rs +0 -6
  172. package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
  173. package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
  174. package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
  175. package/src/rust/{components → ui}/context_menu.rs +8 -13
  176. package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
  177. package/src/rust/{components → ui}/font_loader.rs +0 -26
  178. package/src/rust/{components → ui}/form/color_selector.rs +4 -2
  179. package/src/rust/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
  180. package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
  181. package/src/rust/{components → ui}/form/mod.rs +1 -10
  182. package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
  183. package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
  184. package/src/rust/{components → ui}/form/number_field.rs +1 -1
  185. package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
  186. package/src/rust/{components → ui}/form/optional_field.rs +3 -1
  187. package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
  188. package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
  189. package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
  190. package/src/rust/ui/mod.rs +56 -0
  191. package/src/rust/ui/modal.rs +61 -0
  192. package/src/rust/{components → ui}/portal.rs +175 -79
  193. package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
  194. package/src/rust/utils/completion.rs +0 -6
  195. package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
  196. package/src/rust/utils/css_vars.rs +13 -0
  197. package/src/rust/utils/local_fonts.rs +395 -0
  198. package/src/rust/utils/mod.rs +4 -0
  199. package/src/rust/workspace.rs +47 -0
  200. package/src/svg/boolean-type.svg +3 -2
  201. package/src/svg/checkbox-checked-icon.svg +4 -7
  202. package/src/svg/checkbox-hover.svg +3 -3
  203. package/src/svg/checkbox-off.svg +2 -3
  204. package/src/svg/checkbox-on.svg +3 -3
  205. package/src/svg/checkbox-unchecked-icon.svg +3 -3
  206. package/src/svg/datagrid-select-column.svg +8 -15
  207. package/src/svg/datagrid-select-region.svg +8 -15
  208. package/src/svg/datagrid-select-row-tree.svg +7 -12
  209. package/src/svg/datagrid-select-row.svg +8 -13
  210. package/src/svg/date-type.svg +4 -23
  211. package/src/svg/drawer-tab-hover.svg +4 -6
  212. package/src/svg/drawer-tab-invert-hover.svg +5 -5
  213. package/src/svg/drawer-tab-invert.svg +3 -5
  214. package/src/svg/drawer-tab.svg +5 -5
  215. package/src/svg/mega-menu-icons-candlestick.svg +5 -37
  216. package/src/svg/mega-menu-icons-ohlc.svg +4 -39
  217. package/src/svg/mega-menu-icons-sunburst.svg +4 -38
  218. package/src/svg/mega-menu-icons-treemap.svg +3 -43
  219. package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
  220. package/src/svg/new-panel-icon.svg +4 -0
  221. package/src/svg/number-type.svg +3 -3
  222. package/src/svg/radio-hover.svg +3 -3
  223. package/src/svg/radio-off.svg +3 -3
  224. package/src/svg/radio-on.svg +3 -3
  225. package/src/svg/status_ok.svg +3 -26
  226. package/src/svg/status_pending.svg +3 -0
  227. package/src/svg/string-type.svg +4 -8
  228. package/src/svg/theme-icon.svg +5 -5
  229. package/src/themes/blueprint.css +0 -57
  230. package/src/themes/botanical.css +0 -36
  231. package/src/themes/defaults.css +1 -24
  232. package/src/themes/dracula.css +0 -58
  233. package/src/themes/eggplant.css +0 -58
  234. package/src/themes/gruvbox-dark.css +0 -65
  235. package/src/themes/gruvbox.css +1 -39
  236. package/src/themes/icons.css +2 -8
  237. package/src/themes/intl/ar.css +198 -0
  238. package/src/themes/intl/de.css +63 -40
  239. package/src/themes/intl/es.css +58 -35
  240. package/src/themes/intl/fr.css +57 -34
  241. package/src/themes/intl/ja.css +59 -36
  242. package/src/themes/intl/pt.css +54 -31
  243. package/src/themes/intl/zh.css +65 -42
  244. package/src/themes/intl.css +34 -8
  245. package/src/themes/ledger.css +1 -59
  246. package/src/themes/monokai.css +0 -51
  247. package/src/themes/nord.css +0 -55
  248. package/src/themes/phosphor.css +0 -48
  249. package/src/themes/pro-dark.css +0 -37
  250. package/src/themes/pro.css +3 -23
  251. package/src/themes/solarized-dark.css +0 -31
  252. package/src/themes/solarized.css +1 -24
  253. package/src/themes/vaporwave.css +0 -44
  254. package/src/themes/velvet.css +0 -60
  255. package/src/ts/extensions.ts +0 -13
  256. package/src/ts/perspective-viewer.ts +2 -0
  257. package/src/ts/plugin.ts +9 -0
  258. package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
  259. package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
  260. package/src/ts/ts-rs/GetViewOptions.ts +7 -0
  261. package/src/ts/ts-rs/RestoreOptions.ts +7 -1
  262. package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
  263. package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
  264. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
  265. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  266. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  267. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  268. package/src/css/plugin-settings-panel.css +0 -171
  269. package/src/rust/components/modal.rs +0 -177
  270. package/src/rust/components/string_column_style.rs +0 -121
  271. package/src/rust/config/string_column_style.rs +0 -135
  272. package/src/rust/custom_elements/copy_dropdown.rs +0 -219
  273. package/src/rust/custom_elements/export_dropdown.rs +0 -216
  274. package/src/rust/custom_elements/modal.rs +0 -299
  275. package/src/ts/ts-rs/FormatMode.ts +0 -3
  276. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  277. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
  278. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
  279. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
  280. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
  281. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
  282. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline5.js +0 -0
  283. /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
  284. /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
  285. /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
  286. /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
  287. /package/src/css/{dom → ui/dom}/select.css +0 -0
  288. /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
  289. /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
  290. /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
  291. /package/src/rust/{components → ui}/form/number_input.rs +0 -0
@@ -26,6 +26,9 @@ use crate::*;
26
26
  extern "C" {
27
27
  #[wasm_bindgen(typescript_type = "ViewerConfigUpdate")]
28
28
  pub type JsViewerConfigUpdate;
29
+
30
+ #[wasm_bindgen(typescript_type = "WorkspaceConfigUpdate")]
31
+ pub type JsWorkspaceConfigUpdate;
29
32
  }
30
33
 
31
34
  pub type SyncUpdatePanelsResult = (
@@ -33,12 +36,12 @@ pub type SyncUpdatePanelsResult = (
33
36
  std::vec::Vec<perspective_js::utils::ApiFuture<()>>,
34
37
  );
35
38
 
36
- /// Update the panel state to match a `WorkspaceConfigUpdate`.
37
- ///
38
- /// This method must be synchronous.
39
+ /// Synchronously apply a `WorkspaceConfigUpdate` to the element's models
40
+ /// field-wise, replacing the panel set only when `panels` is present, and
41
+ /// return the fresh panels to restore plus the eject tasks.
39
42
  pub fn sync_update_panels(
40
43
  this: &PerspectiveViewerElement,
41
- update: JsViewerConfigUpdate,
44
+ update: JsWorkspaceConfigUpdate,
42
45
  ) -> ApiResult<SyncUpdatePanelsResult> {
43
46
  let WorkspaceConfigUpdate {
44
47
  active,
@@ -49,98 +52,175 @@ pub fn sync_update_panels(
49
52
  palette,
50
53
  } = update.into_serde_ext()?;
51
54
 
52
- let palette = validate_palette(palette.unwrap_or_default())
53
- .map_err(|e| ApiError::from(JsValue::from_str(&e)))?;
55
+ let palette = match palette {
56
+ OptionalUpdate::Missing => None,
57
+ OptionalUpdate::SetDefault => Some(Default::default()),
58
+ OptionalUpdate::Update(palette) => {
59
+ Some(validate_palette(palette).map_err(|e| ApiError::from(JsValue::from_str(&e)))?)
60
+ },
61
+ };
62
+
54
63
  let old_ids = this.workspace.panel_ids();
64
+ let replace = panels.is_some();
55
65
 
56
66
  // Phase 1 — models only, NO renders and NO draws
57
67
  let mut id_map: HashMap<String, String> = Default::default();
58
- let mut fallback_fresh: Option<String> = None;
59
- let mut active_fresh: Option<String> = None;
60
68
  let mut contents = Vec::new();
61
- // `panels` entries are `ViewerConfigInitial`s — `table` required by
62
- // TYPE (a placed panel without a binding would be permanently blank),
63
- // and no per-panel `settings` exists (element-level; carried by the
64
- // top-level `active` field). An EMPTY `panels` map restores to the
65
- // zero-panel empty stage — the former table-less fallback panel was
66
- // exactly the blank-panel state this type exists to preclude.
67
- for (saved_id, config) in panels {
68
- let (fresh, session, renderer, config) = create_panel_model(
69
- &this.elem,
70
- &this.presentation,
71
- &this.workspace,
72
- None,
73
- config.into(),
74
- None,
75
- Placement::Placed,
76
- );
77
-
78
- if active.as_deref() == Some(saved_id.as_str()) {
79
- active_fresh = Some(fresh.as_str().to_owned());
80
- }
81
-
82
- fallback_fresh.get_or_insert_with(|| fresh.as_str().to_owned());
83
- id_map.insert(saved_id, fresh.as_str().to_owned());
84
- contents.push((fresh, session, renderer, config));
85
- }
86
-
87
69
  let mut eject_tasks = Vec::new();
88
- if let Some(panel) = this.workspace.take_reserved() {
89
- eject_tasks.push(eject_panel(panel));
70
+ match panels {
71
+ // `panels` entries are `ViewerConfigInitial`s — `table` required by
72
+ // TYPE (a placed panel without a binding would be permanently blank),
73
+ // and no per-panel `settings` exists (element-level; carried by the
74
+ // top-level `active` field). An EMPTY `panels` map restores to the
75
+ // zero-panel empty stage — the former table-less fallback panel was
76
+ // exactly the blank-panel state this type exists to preclude.
77
+ Some(panels) => {
78
+ for (saved_id, config) in panels {
79
+ let (fresh, session, renderer, config) = create_panel_model(
80
+ &this.elem,
81
+ &this.presentation,
82
+ &this.workspace,
83
+ None,
84
+ config.into(),
85
+ None,
86
+ Placement::Placed,
87
+ );
88
+
89
+ id_map.insert(saved_id, fresh.as_str().to_owned());
90
+ contents.push((fresh, session, renderer, config));
91
+ }
92
+
93
+ if let Some(panel) = this.workspace.take_reserved() {
94
+ eject_tasks.push(eject_panel(panel));
95
+ }
96
+
97
+ for old in &old_ids {
98
+ if let Some(panel) = this.workspace.remove_panel(old) {
99
+ eject_tasks.push(eject_panel(panel));
100
+ }
101
+ }
102
+ },
103
+ None => {
104
+ for id in &old_ids {
105
+ id_map.insert(id.as_str().to_owned(), id.as_str().to_owned());
106
+ }
107
+ },
90
108
  }
91
109
 
92
- for old in old_ids {
93
- if let Some(panel) = this.workspace.remove_panel(&old) {
94
- eject_tasks.push(eject_panel(panel));
95
- }
110
+ if let Some(palette) = palette {
111
+ this.presentation.set_palette(palette)?;
96
112
  }
97
113
 
98
- this.presentation.set_palette(palette)?;
99
-
100
114
  // Phase 2 — stage the remapped layout tree on the Workspace
115
+ let layout_staged = layout.is_some();
101
116
  if let Some(layout) = layout {
102
117
  this.workspace
103
118
  .set_pending_layout(layout.remap(&|name| id_map.get(name).cloned()));
119
+ if !replace {
120
+ this.workspace.layout_staged().emit(());
121
+ }
104
122
  }
105
123
 
106
124
  // Phase 3 — the single visible commit
107
- if let Some(saved) = &active
108
- && active_fresh.is_none()
109
- {
110
- tracing::warn!("`active` names unknown panel \"{saved}\"");
111
- }
112
-
113
- let sidebar_open = active_fresh.is_some();
114
- if let Some(target) = active_fresh.or(fallback_fresh)
125
+ let active_target = match &active {
126
+ OptionalUpdate::Update(saved) => {
127
+ let target = id_map.get(saved).cloned();
128
+ if target.is_none() {
129
+ tracing::warn!("`active` names unknown panel \"{saved}\"");
130
+ }
131
+
132
+ target
133
+ },
134
+ _ => None,
135
+ };
136
+
137
+ let commit_target = if replace {
138
+ active_target
139
+ .clone()
140
+ .or_else(|| contents.first().map(|(id, ..)| id.as_str().to_owned()))
141
+ } else if layout_staged || active_target.is_some() {
142
+ active_target
143
+ .clone()
144
+ .or_else(|| this.workspace.active_id().map(|id| id.as_str().to_owned()))
145
+ } else {
146
+ None
147
+ };
148
+
149
+ if let Some(target) = commit_target
115
150
  && let Some(app) = this.root.borrow().as_ref()
116
151
  {
117
152
  app.send_message(PerspectiveViewerMsg::CommitWorkspaceRestore(target));
118
153
  }
119
154
 
120
- if let Some(app) = this.root.borrow().as_ref() {
155
+ let settings = if replace {
156
+ Some(active_target.is_some())
157
+ } else {
158
+ match active {
159
+ OptionalUpdate::Update(_) => active_target.as_ref().map(|_| true),
160
+ OptionalUpdate::SetDefault => Some(false),
161
+ OptionalUpdate::Missing => None,
162
+ }
163
+ };
164
+
165
+ if let Some(open) = settings
166
+ && let Some(app) = this.root.borrow().as_ref()
167
+ {
121
168
  // Silent (`announce: false`): each restored panel's own view-config
122
169
  // commit dispatch announces the config, settings field included.
123
170
  app.send_message(PerspectiveViewerMsg::ToggleSettingsInit(
124
- Some(SettingsUpdate::Update(sidebar_open)),
171
+ Some(SettingsUpdate::Update(open)),
125
172
  false,
126
173
  None,
127
174
  ));
128
175
  }
129
176
 
130
- let masters = masters
131
- .unwrap_or_default()
132
- .into_iter()
133
- .filter_map(|saved| match id_map.get(&saved) {
134
- Some(fresh) => Some(PanelId::from(fresh.as_str())),
135
- None => {
136
- tracing::warn!("`masters` names unknown panel \"{saved}\"");
137
- None
138
- },
139
- })
140
- .collect::<Vec<_>>();
141
-
142
- this.workspace.set_masters(masters);
143
- this.workspace
144
- .set_global_filters(global_filters.unwrap_or_default());
177
+ let masters = match masters {
178
+ OptionalUpdate::Missing => None,
179
+ OptionalUpdate::SetDefault => Some(Vec::new()),
180
+ OptionalUpdate::Update(masters) => Some(
181
+ masters
182
+ .into_iter()
183
+ .filter_map(|saved| match id_map.get(&saved) {
184
+ Some(fresh) => Some(PanelId::from(fresh.as_str())),
185
+ None => {
186
+ tracing::warn!("`masters` names unknown panel \"{saved}\"");
187
+ None
188
+ },
189
+ })
190
+ .collect::<Vec<_>>(),
191
+ ),
192
+ };
193
+
194
+ if let Some(masters) = masters {
195
+ let before = this.workspace.masters();
196
+ this.workspace.set_masters(masters);
197
+ if !replace {
198
+ let after = this.workspace.masters();
199
+ for id in &old_ids {
200
+ let is_master = after.contains(id);
201
+ if before.contains(id) != is_master
202
+ && let Some(panel) = this.workspace.panel(id)
203
+ {
204
+ let mode = if is_master {
205
+ "SELECT_ROW_TREE"
206
+ } else {
207
+ "READ_ONLY"
208
+ };
209
+
210
+ set_edit_mode(&panel.session, &panel.renderer, mode);
211
+ if !is_master {
212
+ this.workspace.clear_contribution(id);
213
+ }
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ match global_filters {
220
+ OptionalUpdate::Missing => {},
221
+ OptionalUpdate::SetDefault => this.workspace.set_global_filters(Vec::new()),
222
+ OptionalUpdate::Update(filters) => this.workspace.set_global_filters(filters),
223
+ }
224
+
145
225
  Ok((contents, eject_tasks))
146
226
  }
@@ -150,7 +150,13 @@ async fn bind_pending(
150
150
  return Ok(());
151
151
  };
152
152
 
153
- super::restore_panel::bind_table_task(&session, &workspace, name).await
153
+ super::restore_panel::bind_table_task(
154
+ &session,
155
+ &workspace,
156
+ name,
157
+ crate::session::MissingTable::Pend,
158
+ )
159
+ .await
154
160
  }
155
161
  },
156
162
  )
@@ -12,7 +12,12 @@
12
12
 
13
13
  use wasm_bindgen::JsCast;
14
14
  use web_sys::{Event, MouseEvent};
15
- use yew::{Callback, Html, Properties, function_component, html, use_callback, use_mut_ref};
15
+ use yew::{
16
+ Callback, Html, Properties, function_component, html, use_callback, use_effect_with,
17
+ use_mut_ref, use_node_ref,
18
+ };
19
+
20
+ use crate::ui::form::intl_label::IntlLabel;
16
21
 
17
22
  #[derive(Properties, PartialEq)]
18
23
  pub struct ControlGroupProps {
@@ -78,12 +83,29 @@ pub fn ControlGroup(props: &ControlGroupProps) -> Html {
78
83
  }
79
84
  });
80
85
 
86
+ let details_ref = use_node_ref();
87
+ {
88
+ let details_ref = details_ref.clone();
89
+ use_effect_with(
90
+ (props.group_key.clone(), props.on_toggle.clone()),
91
+ move |(group_key, on_toggle)| {
92
+ let group_key = group_key.clone();
93
+ let on_toggle = on_toggle.clone();
94
+ move || {
95
+ if let Some(el) = details_ref.cast::<web_sys::Element>() {
96
+ on_toggle.emit((group_key, el.has_attribute("open")));
97
+ }
98
+ }
99
+ },
100
+ );
101
+ }
102
+
81
103
  let open = *use_mut_ref(|| props.open).borrow();
82
104
  html! {
83
- <details class="control-group" {open} {ontoggle}>
105
+ <details ref={details_ref} class="control-group" {open} {ontoggle}>
84
106
  <summary {onclick}>
85
107
  <span class="control-group-chevron shift-alt-icon" />
86
- <label id={format!("{}-group-label", props.group_key)} />
108
+ <IntlLabel name={props.group_key.clone()} group=true />
87
109
  </summary>
88
110
  { props.children.clone() }
89
111
  </details>
@@ -27,6 +27,7 @@ where
27
27
  pub callback: Callback<T>,
28
28
  }
29
29
 
30
+ /// A clickable item list, optionally grouped (`DropDownMenuItem::OptGroup`).
30
31
  pub struct DropDownMenu<T>
31
32
  where
32
33
  T: Into<Html> + Clone + PartialEq + 'static,
@@ -64,7 +65,9 @@ where
64
65
  });
65
66
 
66
67
  html! {
67
- <span onmousedown={click} class="selected">{ x.clone().into() }</span>
68
+ <span onmousedown={click} class="dropdown-menu-item">
69
+ { x.clone().into() }
70
+ </span>
68
71
  }
69
72
  },
70
73
  DropDownMenuItem::OptGroup(name, xs) => {
@@ -78,7 +81,7 @@ where
78
81
  move |_: MouseEvent| value.clone()
79
82
  });
80
83
  html! {
81
- <span onmousedown={ click }>
84
+ <span onmousedown={ click } class="dropdown-menu-item">
82
85
  { x.clone().into() }
83
86
  </span>
84
87
  }
@@ -10,11 +10,7 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! `containers` are generic container-like components for collections of other
14
- //! `Component` types.
15
-
16
13
  pub mod control_group;
17
- pub mod dragdrop_list;
18
14
  pub mod dropdown_menu;
19
15
  pub mod scroll_panel;
20
16
  pub mod scroll_panel_item;
@@ -10,9 +10,6 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- // Forked from https://github.com/AircastDev/yew-virtual-scroller (Apache 2.0)
14
- // Adds support for Yew 0.19, auto-width and a simplified message structure.
15
-
16
13
  use std::ops::Range;
17
14
  use std::rc::Rc;
18
15
 
@@ -67,8 +64,6 @@ pub struct ScrollPanelProps {
67
64
  }
68
65
 
69
66
  impl ScrollPanelProps {
70
- /// Calculate the total virtual height of this scroll panel from the `size`
71
- /// prop of its children.
72
67
  fn total_height(&self) -> f64 {
73
68
  self.children
74
69
  .iter()
@@ -148,8 +143,6 @@ impl Component for ScrollPanel {
148
143
  self.calculate_window_content(ctx)
149
144
  },
150
145
 
151
- // TODO don't hardcode pixel offsets - fix the CSS container to not
152
- // require pixel offsets to work.
153
146
  ScrollPanelMsg::UpdateViewportDimensions => {
154
147
  let viewport = self.viewport_elem(ctx);
155
148
  let rect = viewport.get_bounding_client_rect();
@@ -174,9 +167,6 @@ impl Component for ScrollPanel {
174
167
  }
175
168
  }
176
169
 
177
- /// If the new total row height is different than last time this component
178
- /// was rendered, we need to double-render to read the container's
179
- /// potentially updated height.
180
170
  fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
181
171
  let total_height = ctx.props().total_height();
182
172
  self.needs_rerender =
@@ -213,8 +203,6 @@ impl Component for ScrollPanel {
213
203
  ]
214
204
  });
215
205
 
216
- // TODO This glitches - we should use the `sticky` positioning strategy that
217
- // `regular-table` uses.
218
206
  html! {
219
207
  <div
220
208
  ref={self.viewport(ctx)}
@@ -306,16 +294,6 @@ impl ScrollPanel {
306
294
  .map(|x| x.0)
307
295
  .unwrap_or_default();
308
296
 
309
- // Why is this `end_node + 2`, I can see you asking yourself? `end_node` is the
310
- // index of the last visible child, but [`Range`] is an open interval so we must
311
- // increment by 1. The next rendered element is always occluded by the parent
312
- // container, it may seem unnecessary to render it, however not doing so causing
313
- // scroll glitching in Chrome:
314
- // * When the first pixel of the `end_node + 1` child is scrolled into view, the
315
- // container element it is embedded in will expand past the end of the scroll
316
- // container.
317
- // * Chrome detects this and helpfully scrolls this new element into view,
318
- // re-triggering the on scroll callback.
319
297
  let visible_range = start_node..ctx.props().children.len().min(end_node + 2);
320
298
  let content_window = Some(ContentWindow {
321
299
  scroll_top,
@@ -10,9 +10,6 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- // Forked from https://github.com/AircastDev/yew-virtual-scroller (Apache 2.0)
14
- // Adds support for Yew 0.19, auto-width and a simplified message structure.
15
-
16
13
  use std::cell::Cell;
17
14
  use std::rc::Rc;
18
15
 
@@ -22,21 +19,11 @@ use yew::prelude::*;
22
19
  pub struct ScrollPanelItemProps {
23
20
  pub children: Children,
24
21
 
25
- /// The expected size of this component in pixels. Calculating this value
26
- /// ahead of time makes it easier to implement a high-performance virtual
27
- /// renderer without resorting to weird tricks, _but_ checking that this
28
- /// hint is correct is nearly as expensive. So, we only generate the
29
- /// validation code in debug builds.
30
22
  pub size_hint: f64,
31
23
 
32
- /// Should we measure the child sizes or just rely on the hint? This
33
- /// option may be enabled during debugging to validation render optimization
34
- /// via [`Self::size_hint`], but will faile tests due to the extra DOM
35
- /// structure it emits.
36
24
  #[prop_or_default]
37
25
  pub is_debug_mode: bool,
38
26
 
39
- /// Communicate the panel size to virtual parent
40
27
  #[prop_or_default]
41
28
  measured_size: Rc<Cell<Option<f64>>>,
42
29
  }
@@ -88,10 +88,6 @@ where
88
88
  true
89
89
  },
90
90
 
91
- // All the up and down arrow keys to instantly select, which is
92
- // useful for keyboard warrios. This is not ada without the shift
93
- // key. I'm not sure if it is _with_ the shift key either, but its
94
- // cool.
95
91
  SelectMsg::KeyboardInput(is_shift, idx, code) => {
96
92
  if is_shift {
97
93
  if code.as_str() == "ArrowUp" {
@@ -114,13 +110,11 @@ where
114
110
  }
115
111
  }
116
112
 
117
- // The `<select>` has its own state not refelcted by `SelectProps`.
118
113
  fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
119
114
  self.selected = ctx.props().selected.clone();
120
115
  true
121
116
  }
122
117
 
123
- // Annoyingly, `<select>` cannot be updated from its HTML alone.
124
118
  fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
125
119
  if let Some(elem) = self.select_ref.cast::<web_sys::HtmlSelectElement>() {
126
120
  elem.set_value(&format!("{}", self.selected))
@@ -19,38 +19,25 @@ use yew::{
19
19
  use_node_ref,
20
20
  };
21
21
 
22
- use crate::components::containers::sidebar_close_button::SidebarCloseButton;
23
- use crate::components::editable_header::{EditableHeader, EditableHeaderProps};
24
22
  use crate::js::{ResizeObserver, ResizeObserverEntry};
23
+ use crate::ui::containers::sidebar_close_button::SidebarCloseButton;
25
24
 
26
25
  #[derive(PartialEq, Clone, Properties)]
27
26
  pub struct SidebarProps {
28
- /// The component's children.
29
27
  pub children: Children,
30
28
 
31
- /// When this callback is called, the sidebar will close
32
29
  pub on_close: Callback<()>,
33
30
  pub id_prefix: String,
34
31
  pub width_override: Option<i32>,
35
32
  pub selected_tab: Option<usize>,
36
- pub header_props: EditableHeaderProps,
37
-
38
- /// Trap-door width shared across this sidebar's tabs: the lifted
39
- /// running max of the widths this component reports through
40
- /// `on_auto_width`. Held by the parent (ultimately
41
- /// `PerspectiveViewer`'s geometry state, like the settings panel's
42
- /// Query/Plugin/Debug trap-door) so it survives tab switches AND
43
- /// sidebar re-mounts, and clears on divider reset.
33
+ pub header: Html,
34
+
44
35
  #[prop_or_default]
45
36
  pub auto_width: f64,
46
37
 
47
- /// Fires with the sidebar's rendered width after each render; the
48
- /// owner keeps the running max threaded back as `auto_width`.
49
38
  #[prop_or_default]
50
39
  pub on_auto_width: Callback<f64>,
51
40
 
52
- /// Pinned state for the header's pin toggle; the button renders only
53
- /// when `on_toggle_pin` is provided.
54
41
  #[prop_or_default]
55
42
  pub is_pinned: bool,
56
43
 
@@ -58,23 +45,12 @@ pub struct SidebarProps {
58
45
  pub on_toggle_pin: Option<Callback<()>>,
59
46
  }
60
47
 
61
- /// Sidebars are designed to live in a
62
- /// [`super::split_panel::SplitPanel`]
48
+ /// Sidebars are designed to live in a `SplitPanel`.
63
49
  #[function_component]
64
50
  pub fn Sidebar(p: &SidebarProps) -> Html {
65
51
  let id = &p.id_prefix;
66
52
  let noderef = use_node_ref();
67
53
 
68
- // The trap-door reports the sidebar's rendered width to its owner via
69
- // a `ResizeObserver` on the sidebar element, NOT a render effect:
70
- // width changes are driven by DOM mutations anywhere in the tab
71
- // subtree (e.g. the window editor staging a long column name into a
72
- // slot), which need not re-render this component at all. The observer
73
- // sees every one. `contentRect` (not the border box) is load-bearing:
74
- // the sizer below is a content child, so ratcheting the border box
75
- // would feed any sidebar padding back into unbounded growth. A manual
76
- // divider drag (`width_override`) disables the ratchet until the
77
- // divider resets.
78
54
  let live_props = use_mut_ref(|| (Callback::<f64>::default(), None::<i32>));
79
55
  *live_props.borrow_mut() = (p.on_auto_width.clone(), p.width_override);
80
56
  use_effect_with((), {
@@ -139,10 +115,7 @@ pub fn Sidebar(p: &SidebarProps) -> Html {
139
115
  <>
140
116
  <SidebarCloseButton id={format!("{id}_close_button")} on_close_sidebar={&p.on_close} />
141
117
  <div class="sidebar_column" id={format!("{id}_sidebar")} ref={noderef}>
142
- <div class="sidebar_header">
143
- <EditableHeader ..p.header_props.clone() />
144
- { pin_button }
145
- </div>
118
+ <div class="sidebar_header">{ p.header.clone() }{ pin_button }</div>
146
119
  <div class="sidebar_border" id={format!("{id}_border")} />
147
120
  { p.children.iter().collect::<Html>() }
148
121
  <div class="sidebar-auto-width" style={width_style} />