@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
@@ -166,13 +166,14 @@ pub async fn presize_visible_panels_open(
166
166
  pub async fn presize_visible_panels_pane_width(
167
167
  workspace: &Workspace,
168
168
  elem: &web_sys::HtmlElement,
169
+ pane_sel: &str,
169
170
  pane_width: f64,
170
171
  ) -> StagedPresents {
171
172
  let Some(mpc) = shadow_rect(elem, "#main_panel_container") else {
172
173
  return StagedPresents::default();
173
174
  };
174
175
 
175
- let Some(pane) = shadow_rect(elem, "#app_panel > .split-panel-child") else {
176
+ let Some(pane) = shadow_rect(elem, pane_sel) else {
176
177
  return StagedPresents::default();
177
178
  };
178
179
 
@@ -308,6 +309,28 @@ fn shadow_rect(elem: &web_sys::HtmlElement, sel: &str) -> Option<web_sys::DomRec
308
309
  )
309
310
  }
310
311
 
312
+ /// The width the `SplitPanel` pane at `pane_sel` takes with no divider
313
+ /// override, measured under one forced synchronous layout that no paint or
314
+ /// `ResizeObserver` can observe, or `None` when the pane isn't in the DOM.
315
+ pub fn measure_pane_natural_width(elem: &web_sys::HtmlElement, pane_sel: &str) -> Option<f64> {
316
+ let pane = elem
317
+ .shadow_root()?
318
+ .query_selector(pane_sel)
319
+ .ok()??
320
+ .dyn_into::<web_sys::HtmlElement>()
321
+ .ok()?;
322
+
323
+ let style = pane.style();
324
+ let css_text = style.css_text();
325
+ let class_name = pane.class_name();
326
+ style.set_css_text("");
327
+ let _ = pane.class_list().remove_1("is-width-override");
328
+ let width = pane.get_bounding_client_rect().width();
329
+ style.set_css_text(&css_text);
330
+ pane.set_class_name(&class_name);
331
+ Some(width)
332
+ }
333
+
311
334
  /// The open-state geometry deltas cached for the next settings *open* (P2):
312
335
  /// `(layout_area.w − mpc.w, main_column.h − mpc.h)`. Only meaningful while the
313
336
  /// settings pane is open (i.e. measured at close time).
@@ -10,10 +10,6 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! Apply a full [`ViewerConfigUpdate`] (settings, theme, title, plugin,
14
- //! plugin_config, columns_config, view_config) and re-draw, on the snapshot
15
- //! pipeline (see `tasks/pipeline.rs` / `SESSION_CONFIG_COHERENCE_PLAN.md`).
16
-
17
13
  use futures::Future;
18
14
  use perspective_client::clone;
19
15
 
@@ -28,19 +24,6 @@ use crate::*;
28
24
  /// Apply a full [`ViewerConfigUpdate`] (theme, title, plugin selection,
29
25
  /// plugin config, columns config, view config) to the engines and re-draw.
30
26
  /// Returns an [`ApiFuture<()>`] which resolves when the draw completes.
31
- ///
32
- /// `origin` says who initiated the restore ([`RunOrigin`]): a `Public`
33
- /// element-API call keeps the no-op-restore refresh affordance (an
34
- /// `Unchanged` reconcile still repaints via `update`); an `Internal`
35
- /// restore that reconciles `Unchanged` and changes no plugin state
36
- /// dispatches nothing.
37
- ///
38
- /// This function owns the PRE-LOCK prologue only (element-level settings /
39
- /// title / host-theme mirror, plugin resolution, the synchronous config
40
- /// commit and spinner token); the run itself is
41
- /// [`locked_run`] with the caller's `task` awaited inside the lock. The
42
- /// `update`'s `table` field is NOT applied here — table binding is the
43
- /// `task`'s job (see `restore_panel` / `table_lifecycle`).
44
27
  pub fn restore_and_render(
45
28
  session: &Session,
46
29
  renderer: &Renderer,
@@ -49,7 +49,7 @@ pub fn send_column_config(
49
49
 
50
50
  clone!(session);
51
51
  renderer
52
- .update_lazy(async move { Ok(session.get_view()) })
52
+ .update_lazy(async move { Ok(session.get_view_with_dimensions()) })
53
53
  .await?;
54
54
  renderer.column_style_changed.emit(columns_configs);
55
55
  Ok(())
@@ -44,7 +44,7 @@ pub fn send_plugin_config(session: &Session, renderer: &Renderer, update: Column
44
44
  .restore(&plugin_token, Some(&columns_configs))?;
45
45
  clone!(session);
46
46
  renderer
47
- .update_lazy(async move { Ok(session.get_view()) })
47
+ .update_lazy(async move { Ok(session.get_view_with_dimensions()) })
48
48
  .await?;
49
49
  renderer.plugin_config_changed.emit(plugin_config);
50
50
  }
@@ -51,7 +51,7 @@ pub fn set_edit_mode(session: &Session, renderer: &Renderer, mode: &str) {
51
51
  .restore(&plugin_token, Some(&columns_configs))?;
52
52
  clone!(session);
53
53
  renderer
54
- .update_lazy(async move { Ok(session.get_view()) })
54
+ .update_lazy(async move { Ok(session.get_view_with_dimensions()) })
55
55
  .await?;
56
56
  renderer.plugin_config_changed.emit(plugin_config);
57
57
  }
@@ -0,0 +1,113 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ use wasm_bindgen::JsCast;
14
+ use web_sys::{Event, MouseEvent};
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;
21
+
22
+ #[derive(Properties, PartialEq)]
23
+ pub struct ControlGroupProps {
24
+ pub group_key: String,
25
+
26
+ pub open: bool,
27
+
28
+ pub on_toggle: Callback<(String, bool)>,
29
+
30
+ pub children: Html,
31
+ }
32
+
33
+ #[function_component]
34
+ pub fn ControlGroup(props: &ControlGroupProps) -> Html {
35
+ let ontoggle = use_callback(
36
+ (props.group_key.clone(), props.on_toggle.clone()),
37
+ |event: Event, (group_key, on_toggle)| {
38
+ if let Some(el) = event
39
+ .target()
40
+ .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
41
+ {
42
+ on_toggle.emit((group_key.clone(), el.has_attribute("open")));
43
+ }
44
+ },
45
+ );
46
+
47
+ let onclick = use_callback((), |event: MouseEvent, _| {
48
+ if !event.shift_key() {
49
+ return;
50
+ }
51
+
52
+ event.prevent_default();
53
+ let Some(details) = event
54
+ .target()
55
+ .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
56
+ .and_then(|x| x.closest("details.control-group").ok().flatten())
57
+ else {
58
+ return;
59
+ };
60
+
61
+ let Some(scope) = details.closest(".tab-section").ok().flatten() else {
62
+ return;
63
+ };
64
+
65
+ let expand = !details.has_attribute("open");
66
+ let Ok(groups) = scope.query_selector_all("details.control-group") else {
67
+ return;
68
+ };
69
+
70
+ for i in 0..groups.length() {
71
+ let Some(el) = groups
72
+ .item(i)
73
+ .and_then(|x| x.dyn_into::<web_sys::Element>().ok())
74
+ else {
75
+ continue;
76
+ };
77
+
78
+ if expand {
79
+ let _ = el.set_attribute("open", "");
80
+ } else {
81
+ let _ = el.remove_attribute("open");
82
+ }
83
+ }
84
+ });
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
+
103
+ let open = *use_mut_ref(|| props.open).borrow();
104
+ html! {
105
+ <details ref={details_ref} class="control-group" {open} {ontoggle}>
106
+ <summary {onclick}>
107
+ <span class="control-group-chevron shift-alt-icon" />
108
+ <IntlLabel name={props.group_key.clone()} group=true />
109
+ </summary>
110
+ { props.children.clone() }
111
+ </details>
112
+ }
113
+ }
@@ -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,10 +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
- pub mod dragdrop_list;
13
+ pub mod control_group;
17
14
  pub mod dropdown_menu;
18
15
  pub mod scroll_panel;
19
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))
@@ -230,9 +224,15 @@ where
230
224
  <label>
231
225
  { ctx.props().label.as_ref().map(|x| x.to_string()).unwrap_or_default() }
232
226
  </label>
233
- <div class={wrapper_class} data-value={value}>{ select }</div>
227
+ <div class={wrapper_class} data-value={value}>
228
+ { select }
229
+ <span class="select-arrow" />
230
+ </div>
234
231
  } else {
235
- <div class={wrapper_class} data-value={value}>{ select }</div>
232
+ <div class={wrapper_class} data-value={value}>
233
+ { select }
234
+ <span class="select-arrow" />
235
+ </div>
236
236
  }
237
237
  }
238
238
  }
@@ -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} />