@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
@@ -27,6 +27,7 @@ mod settings;
27
27
  mod snapshots;
28
28
  mod wiring;
29
29
 
30
+ use std::collections::HashMap;
30
31
  use std::rc::Rc;
31
32
 
32
33
  use futures::channel::oneshot::Sender;
@@ -40,13 +41,13 @@ pub use self::msg::PerspectiveViewerMsg;
40
41
  use self::msg::PerspectiveViewerMsg::*;
41
42
  use self::settings::SettingsGeometry;
42
43
  use self::wiring::*;
43
- use super::font_loader::{FontLoaderProps, FontLoaderStatus};
44
44
  use crate::presentation::{Presentation, PresentationProps};
45
45
  use crate::renderer::{RendererProps, *};
46
46
  use crate::session::{SessionProps, *};
47
47
  use crate::tasks::*;
48
+ use crate::ui::{FontLoaderProps, FontLoaderStatus};
48
49
  use crate::utils::*;
49
- use crate::workspace::Workspace;
50
+ use crate::workspace::{PanelId, Workspace};
50
51
 
51
52
  #[derive(Clone, Properties)]
52
53
  pub struct PerspectiveViewerProps {
@@ -99,6 +100,10 @@ pub struct PerspectiveViewer {
99
100
  on_settings_panel_dimensions_reset: Rc<PubSub<()>>,
100
101
  settings_open: bool,
101
102
 
103
+ /// `removePanel()` completions awaiting their panel's `PanelClosed`
104
+ /// (the deferred eject — see `ClosePanel`).
105
+ pending_closes: HashMap<PanelId, Completion>,
106
+
102
107
  /// Render snapshot of the `Workspace`-owned global filter set (see
103
108
  /// `Workspace::global_filters`); refreshed by `UpdateGlobalFilters` via
104
109
  /// the `filters_changed` subscription. The status-bar chips render from
@@ -210,6 +215,7 @@ impl Component for PerspectiveViewer {
210
215
  on_resize: Default::default(),
211
216
  on_settings_panel_dimensions_reset: Default::default(),
212
217
  settings_open: false,
218
+ pending_closes: HashMap::new(),
213
219
  global_filters: Vec::new(),
214
220
  settings_geometry: Default::default(),
215
221
  session_props,
@@ -305,6 +311,7 @@ impl Component for PerspectiveViewer {
305
311
  LayoutChanged => self.on_layout_changed(ctx),
306
312
  SetActivePanel(id, completion) => self.on_set_active_panel(ctx, id, completion),
307
313
  ClosePanel(id, completion) => self.on_close_panel(ctx, id, completion),
314
+ PanelClosed(id) => self.on_panel_closed(ctx, id),
308
315
  CommitWorkspaceRestore(id) => self.on_commit_workspace_restore(ctx, id),
309
316
  DuplicatePanel(id) => self.on_duplicate_panel(ctx, id),
310
317
  NewPanel(id) => self.on_new_panel(ctx, id),
@@ -325,20 +332,18 @@ impl Component for PerspectiveViewer {
325
332
  ToggleSettingsComplete(update, resolve) => {
326
333
  self.on_toggle_settings_complete(ctx, update, resolve)
327
334
  },
328
- SettingsPanelSizeUpdate(x) => self.on_settings_panel_size_update(x),
329
- SettingsDividerMove(w) => self.on_settings_divider_move(ctx, w),
330
- SettingsDividerPump => self.on_settings_divider_pump(ctx),
331
- SettingsDividerCommit(w) => self.on_settings_divider_commit(ctx, w),
332
- SettingsDividerFinish => self.on_settings_divider_finish(ctx),
335
+ DividerMove(divider, target) => self.on_divider_move(ctx, divider, target),
336
+ DividerPump(divider) => self.on_divider_pump(ctx, divider),
337
+ DividerCommit(divider, target) => self.on_divider_commit(ctx, divider, target),
338
+ DividerFinish(divider) => self.on_divider_finish(ctx, divider),
333
339
  SettingsPanelTabChanged(tab) => self.on_settings_panel_tab_changed(tab),
334
- SettingsPanelAutoWidth(w) => self.on_settings_panel_auto_width(w),
340
+ SettingsPanelAutoWidth(w) => self.on_settings_panel_auto_width(ctx, w),
335
341
  OpenColumnSettings {
336
342
  target,
337
343
  sender,
338
344
  toggle,
339
345
  } => self.on_open_column_settings(ctx, target, sender, toggle),
340
- ColumnSettingsPanelSizeUpdate(x) => self.on_column_settings_panel_size_update(x),
341
- ColumnSettingsPanelAutoWidth(w) => self.on_column_settings_panel_auto_width(w),
346
+ ColumnSettingsPanelAutoWidth(w) => self.on_column_settings_panel_auto_width(ctx, w),
342
347
  ToggleColumnSettingsPin => self.on_toggle_column_settings_pin(ctx),
343
348
  ToggleColumnSettingsPinComplete(resolve) => {
344
349
  self.on_toggle_column_settings_pin_complete(resolve)
@@ -20,12 +20,10 @@ use yew::prelude::*;
20
20
 
21
21
  use crate::components::column_dropdown::{ColumnDropDownElement, ColumnDropDownPortal};
22
22
  use crate::components::column_selector::{ColumnSelectorColumnRow, InPlaceColumn, PivotColumn};
23
- use crate::components::containers::dragdrop_list::{
24
- DragContext, DragDropList, DragDropListItemProps,
25
- };
26
- use crate::components::containers::select::{Select, SelectItem};
23
+ use crate::components::dragdrop_list::{DragContext, DragDropList, DragDropListItemProps};
27
24
  use crate::presentation::Presentation;
28
25
  use crate::session::{Session, SessionMetadataRc};
26
+ use crate::ui::{IntlLabel, Select, SelectItem};
29
27
  use crate::utils::{AddListener, DragEffect, DragTarget, Subscription};
30
28
 
31
29
  /// The declared capabilities of one window aggregate, for a `source` column
@@ -479,8 +477,9 @@ impl Component for WindowSlotColumn {
479
477
  let presentation = ctx.props().presentation.clone();
480
478
  let action = ctx.props().action;
481
479
  move |event: DragEvent| {
482
- presentation.set_drag_image(&event).unwrap();
483
- presentation.notify_drag_start(column.clone(), DragEffect::Move(action))
480
+ if presentation.set_drag_image(&event) {
481
+ presentation.notify_drag_start(column.clone(), DragEffect::Move(action))
482
+ }
484
483
  }
485
484
  });
486
485
 
@@ -909,7 +908,7 @@ impl Component for WindowEditor {
909
908
  <div id="window-editor-slots">{ source_list }{ order_list }{ partition_list }</div>
910
909
  <div id="window-editor-container">
911
910
  if show_frame {
912
- <div class="column-style-label"><label id="window-frame-label" /></div>
911
+ <div class="column-style-label"><IntlLabel name="window-frame" /></div>
913
912
  <div class="row">
914
913
  <Select<String>
915
914
  id="window-frame-type"
@@ -943,7 +942,7 @@ impl Component for WindowEditor {
943
942
  }
944
943
  if show_offset {
945
944
  <div class="column-style-label">
946
- <label id="window-offset-label" class="indent" />
945
+ <IntlLabel name="window-offset" class="indent" />
947
946
  </div>
948
947
  <div class="row">
949
948
  <input
@@ -957,7 +956,7 @@ impl Component for WindowEditor {
957
956
  }
958
957
  if show_alpha {
959
958
  <div class="column-style-label">
960
- <label id="window-alpha-label" class="indent" />
959
+ <IntlLabel name="window-alpha" class="indent" />
961
960
  </div>
962
961
  <div class="row">
963
962
  <input
@@ -16,9 +16,9 @@ use serde::{Deserialize, Serialize};
16
16
  use serde_json::Value;
17
17
 
18
18
  use super::{
19
- CssKind, CustomNumberFormatConfig, DatetimeFormatType, KeyValueOpts,
20
- NumberSeriesStyleDefaultConfig,
19
+ CustomNumberFormatConfig, DatetimeFormatType, KeyValueOpts, NumberSeriesStyleDefaultConfig,
21
20
  };
21
+ use crate::utils::{CssKind, GradientStopSpec, canonicalize_gradient_stops};
22
22
 
23
23
  /// The full schema for one column at one point in time. Plugins may return
24
24
  /// different schemas for the same column based on the column's current
@@ -74,6 +74,38 @@ pub enum ControlSpec {
74
74
  variants: Vec<EnumVariant>,
75
75
  default: String,
76
76
  },
77
+
78
+ /// A CSS `font-family` picked from the generic keywords plus the families
79
+ /// the host can enumerate, with optional size, bold and italic inputs
80
+ /// each bound to its own key.
81
+ Font {
82
+ key: String,
83
+ default: String,
84
+
85
+ #[serde(default, skip_serializing_if = "Option::is_none")]
86
+ size: Option<FontSize>,
87
+
88
+ #[serde(default, skip_serializing_if = "Option::is_none")]
89
+ bold: Option<FontToggle>,
90
+
91
+ #[serde(default, skip_serializing_if = "Option::is_none")]
92
+ italic: Option<FontToggle>,
93
+ },
94
+ /// A 3×3 anchor picker whose value is one of the nine [`Alignment`]
95
+ /// tokens.
96
+ Alignment {
97
+ key: String,
98
+
99
+ /// The cell shown as the unmodified value and elided from serialized
100
+ /// configs, or `None` when an unset key stands for something no cell
101
+ /// can show.
102
+ #[serde(default, skip_serializing_if = "Option::is_none")]
103
+ default: Option<Alignment>,
104
+
105
+ /// Only the four corner cells are selectable.
106
+ #[serde(default, skip_serializing_if = "std::ops::Not::not")]
107
+ corners: bool,
108
+ },
77
109
  Bool {
78
110
  key: String,
79
111
  default: bool,
@@ -125,7 +157,6 @@ pub enum ControlSpec {
125
157
  #[serde(default, skip_serializing_if = "Option::is_none")]
126
158
  default: Option<DatetimeFormatType>,
127
159
  },
128
- StringFormat,
129
160
  NumberSeriesStyle {
130
161
  default: NumberSeriesStyleDefaultConfig,
131
162
  },
@@ -147,36 +178,113 @@ pub enum ControlSpec {
147
178
  },
148
179
  }
149
180
 
150
- #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
151
- pub struct EnumVariant {
152
- pub value: String,
153
- #[serde(default, skip_serializing_if = "Option::is_none")]
154
- pub label: Option<String>,
181
+ /// One cell of a 3×3 anchor grid: a corner `top-left`/`top-right`/
182
+ /// `bottom-left`/`bottom-right`, an edge `top`/`left`/`right`/`bottom`, or
183
+ /// `center`.
184
+ #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
185
+ #[serde(rename_all = "kebab-case")]
186
+ pub enum Alignment {
187
+ TopLeft,
188
+ Top,
189
+ TopRight,
190
+ Left,
191
+ Center,
192
+ Right,
193
+ BottomLeft,
194
+ Bottom,
195
+ BottomRight,
155
196
  }
156
197
 
157
- /// One stop of a [`ControlSpec::GradientStops`] value in its in-memory
158
- /// form: a `#rrggbb` color at `offset` ∈ `[0, 1]`.
198
+ impl Alignment {
199
+ /// Every cell in row-major order.
200
+ pub const ALL: [Alignment; 9] = [
201
+ Alignment::TopLeft,
202
+ Alignment::Top,
203
+ Alignment::TopRight,
204
+ Alignment::Left,
205
+ Alignment::Center,
206
+ Alignment::Right,
207
+ Alignment::BottomLeft,
208
+ Alignment::Bottom,
209
+ Alignment::BottomRight,
210
+ ];
211
+ pub const CORNERS: [Alignment; 4] = [
212
+ Alignment::TopLeft,
213
+ Alignment::TopRight,
214
+ Alignment::BottomLeft,
215
+ Alignment::BottomRight,
216
+ ];
217
+
218
+ pub fn is_corner(self) -> bool {
219
+ Self::CORNERS.contains(&self)
220
+ }
221
+
222
+ /// The serialized token.
223
+ pub fn as_str(self) -> &'static str {
224
+ match self {
225
+ Alignment::TopLeft => "top-left",
226
+ Alignment::Top => "top",
227
+ Alignment::TopRight => "top-right",
228
+ Alignment::Left => "left",
229
+ Alignment::Center => "center",
230
+ Alignment::Right => "right",
231
+ Alignment::BottomLeft => "bottom-left",
232
+ Alignment::Bottom => "bottom",
233
+ Alignment::BottomRight => "bottom-right",
234
+ }
235
+ }
236
+
237
+ pub fn parse(src: &str) -> Option<Self> {
238
+ Self::ALL.into_iter().find(|x| x.as_str() == src)
239
+ }
240
+
241
+ /// `Top Left`-style text for accessible names.
242
+ pub fn humanized(self) -> String {
243
+ self.as_str()
244
+ .split('-')
245
+ .map(|word| {
246
+ let mut chars = word.chars();
247
+ match chars.next() {
248
+ Some(first) => first.to_uppercase().chain(chars).collect::<String>(),
249
+ None => String::new(),
250
+ }
251
+ })
252
+ .collect::<Vec<_>>()
253
+ .join(" ")
254
+ }
255
+ }
256
+
257
+ /// One boolean style toggle of a [`ControlSpec::Font`] control: the
258
+ /// config key it writes and the value that counts as "not set".
159
259
  #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
160
- pub struct GradientStopSpec {
161
- pub color: String,
162
- pub offset: f64,
260
+ pub struct FontToggle {
261
+ pub key: String,
262
+ #[serde(default)]
263
+ pub default: bool,
163
264
  }
164
265
 
165
- /// The [`ControlSpec::GradientStops`] canonical stop order: offsets
166
- /// clamped to `[0, 1]` and rounded to 3 decimals, stops sorted stably
167
- /// by offset.
168
- pub fn canonicalize_gradient_stops(mut stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
169
- for stop in &mut stops {
170
- stop.offset = (stop.offset.clamp(0.0, 1.0) * 1000.0).round() / 1000.0;
171
- }
266
+ /// The size input of a [`ControlSpec::Font`] control: a number (CSS px)
267
+ /// under its own key, elided from serialized configs at `default`.
268
+ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
269
+ pub struct FontSize {
270
+ pub key: String,
271
+ pub default: f64,
272
+
273
+ #[serde(default, skip_serializing_if = "Option::is_none")]
274
+ pub min: Option<f64>,
172
275
 
173
- stops.sort_by(|a, b| {
174
- a.offset
175
- .partial_cmp(&b.offset)
176
- .unwrap_or(std::cmp::Ordering::Equal)
177
- });
276
+ #[serde(default, skip_serializing_if = "Option::is_none")]
277
+ pub max: Option<f64>,
178
278
 
179
- stops
279
+ #[serde(default, skip_serializing_if = "Option::is_none")]
280
+ pub step: Option<f64>,
281
+ }
282
+
283
+ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
284
+ pub struct EnumVariant {
285
+ pub value: String,
286
+ #[serde(default, skip_serializing_if = "Option::is_none")]
287
+ pub label: Option<String>,
180
288
  }
181
289
 
182
290
  /// Fit `stops` to a `discrete` field's fixed pair: an over-length value
@@ -207,9 +315,7 @@ impl ColumnConfigSchema {
207
315
  fn is_format(spec: &ControlSpec) -> bool {
208
316
  matches!(
209
317
  spec,
210
- ControlSpec::NumberFormat { .. }
211
- | ControlSpec::DatetimeFormat { .. }
212
- | ControlSpec::StringFormat
318
+ ControlSpec::NumberFormat { .. } | ControlSpec::DatetimeFormat { .. }
213
319
  )
214
320
  }
215
321
 
@@ -255,6 +361,18 @@ impl ColumnConfigSchema {
255
361
  canonicalize_specs(fields);
256
362
  return !fields.is_empty();
257
363
  },
364
+ ControlSpec::Alignment {
365
+ key,
366
+ default: Some(default),
367
+ corners: true,
368
+ } if !default.is_corner() => {
369
+ tracing::error!(
370
+ "Dropping `{key}` — default `{}` is not a corner",
371
+ default.as_str()
372
+ );
373
+
374
+ return false;
375
+ },
258
376
  ControlSpec::Color { key, default } => (CssKind::Color, key, default),
259
377
  ControlSpec::Palette { key, default, .. } => (CssKind::Palette, key, default),
260
378
  ControlSpec::GradientStops { key, default, .. } => {
@@ -280,6 +398,16 @@ impl ColumnConfigSchema {
280
398
  self
281
399
  }
282
400
 
401
+ /// The variants of the [`ControlSpec::Enum`] owning `key`, if any.
402
+ pub fn enum_variants_of(&self, key: &str) -> Option<&[EnumVariant]> {
403
+ self.leaf_fields().into_iter().find_map(|spec| match spec {
404
+ ControlSpec::Enum {
405
+ key: k, variants, ..
406
+ } if k == key => Some(variants.as_slice()),
407
+ _ => None,
408
+ })
409
+ }
410
+
283
411
  /// The CSS kind of the control owning `key`, if it is CSS-valued.
284
412
  pub fn css_kind_of(&self, key: &str) -> Option<CssKind> {
285
413
  self.leaf_fields().into_iter().find_map(|spec| match spec {
@@ -300,12 +428,27 @@ impl ControlSpec {
300
428
  pub fn serialized_keys(&self) -> Vec<&str> {
301
429
  match self {
302
430
  ControlSpec::DatetimeFormat { .. } => vec!["date_format"],
303
- ControlSpec::StringFormat => vec!["format"],
431
+ ControlSpec::Font {
432
+ key,
433
+ size,
434
+ bold,
435
+ italic,
436
+ ..
437
+ } => [
438
+ Some(key.as_str()),
439
+ size.as_ref().map(|s| s.key.as_str()),
440
+ bold.as_ref().map(|t| t.key.as_str()),
441
+ italic.as_ref().map(|t| t.key.as_str()),
442
+ ]
443
+ .into_iter()
444
+ .flatten()
445
+ .collect(),
304
446
  ControlSpec::NumberSeriesStyle { .. } => vec!["chart_type", "stack"],
305
447
  ControlSpec::Symbols { .. } => vec!["symbols"],
306
448
  ControlSpec::NumberFormat { .. } => vec!["number_format"],
307
449
  ControlSpec::AggregateDepth => vec!["aggregate_depth"],
308
450
  ControlSpec::Enum { key, .. }
451
+ | ControlSpec::Alignment { key, .. }
309
452
  | ControlSpec::Bool { key, .. }
310
453
  | ControlSpec::Number { key, .. }
311
454
  | ControlSpec::String { key, .. }
@@ -421,13 +564,46 @@ mod tests {
421
564
  assert_eq!(flat.css_kind_of("stack"), grouped.css_kind_of("stack"));
422
565
  }
423
566
 
567
+ #[test]
568
+ fn enum_variants_see_through_groups() {
569
+ let mode = ControlSpec::Enum {
570
+ key: "fg_mode".to_owned(),
571
+ default: "color".to_owned(),
572
+ variants: vec![
573
+ EnumVariant {
574
+ value: "disabled".to_owned(),
575
+ label: None,
576
+ },
577
+ EnumVariant {
578
+ value: "color".to_owned(),
579
+ label: None,
580
+ },
581
+ ],
582
+ };
583
+
584
+ let grouped = ColumnConfigSchema {
585
+ fields: vec![group("color", vec![mode, flag("flag")])],
586
+ };
587
+
588
+ let values: Vec<&str> = grouped
589
+ .enum_variants_of("fg_mode")
590
+ .unwrap()
591
+ .iter()
592
+ .map(|v| v.value.as_str())
593
+ .collect();
594
+
595
+ assert_eq!(values, vec!["disabled", "color"]);
596
+ assert!(grouped.enum_variants_of("flag").is_none());
597
+ assert!(grouped.enum_variants_of("missing").is_none());
598
+ }
599
+
424
600
  #[test]
425
601
  fn format_controls_group_and_merge() {
426
602
  let schema = ColumnConfigSchema {
427
603
  fields: vec![
428
604
  ControlSpec::NumberFormat { default: None },
429
605
  flag("flag"),
430
- ControlSpec::StringFormat,
606
+ ControlSpec::DatetimeFormat { default: None },
431
607
  ],
432
608
  }
433
609
  .group_format_controls();
@@ -439,19 +615,45 @@ mod tests {
439
615
 
440
616
  assert_eq!(key, "format");
441
617
  assert!(matches!(fields[0], ControlSpec::NumberFormat { .. }));
442
- assert!(matches!(fields[1], ControlSpec::StringFormat));
618
+ assert!(matches!(fields[1], ControlSpec::DatetimeFormat { .. }));
443
619
  assert!(matches!(&schema.fields[1], ControlSpec::Bool { .. }));
444
620
 
445
621
  assert_eq!(
446
622
  schema.active_keys(),
447
623
  HashSet::from([
448
624
  "number_format".to_owned(),
449
- "format".to_owned(),
625
+ "date_format".to_owned(),
450
626
  "flag".to_owned()
451
627
  ])
452
628
  );
453
629
  }
454
630
 
631
+ #[test]
632
+ fn font_owns_its_size_and_toggle_keys() {
633
+ let spec = ControlSpec::Font {
634
+ key: "font_family".to_owned(),
635
+ default: "inherit".to_owned(),
636
+ size: Some(FontSize {
637
+ key: "font_size".to_owned(),
638
+ default: 12.0,
639
+ min: None,
640
+ max: None,
641
+ step: None,
642
+ }),
643
+ bold: Some(FontToggle {
644
+ key: "bold".to_owned(),
645
+ default: false,
646
+ }),
647
+ italic: None,
648
+ };
649
+
650
+ assert_eq!(spec.serialized_keys(), vec![
651
+ "font_family",
652
+ "font_size",
653
+ "bold"
654
+ ]);
655
+ }
656
+
455
657
  #[test]
456
658
  fn format_grouping_recurses_but_never_double_wraps() {
457
659
  let schema = ColumnConfigSchema {
@@ -561,6 +763,83 @@ mod tests {
561
763
  );
562
764
  }
563
765
 
766
+ #[test]
767
+ fn alignment_tokens_round_trip() {
768
+ for align in Alignment::ALL {
769
+ assert_eq!(Alignment::parse(align.as_str()), Some(align));
770
+ assert_eq!(serde_json::to_value(align).unwrap(), json!(align.as_str()));
771
+ }
772
+
773
+ for bad in ["middle", "left-top", "top-center", "middle-left", ""] {
774
+ assert_eq!(Alignment::parse(bad), None, "{bad}");
775
+ }
776
+
777
+ assert_eq!(Alignment::ALL.iter().filter(|x| x.is_corner()).count(), 4);
778
+ assert_eq!(Alignment::TopLeft.humanized(), "Top Left");
779
+ assert_eq!(Alignment::Center.humanized(), "Center");
780
+ }
781
+
782
+ #[test]
783
+ fn alignment_deserializes_with_optional_default_and_corners() {
784
+ let schema: ColumnConfigSchema = serde_json::from_value(json!({
785
+ "fields": [
786
+ { "kind": "Alignment", "key": "align" },
787
+ { "kind": "Alignment", "key": "legend_anchor", "default": "top-right", "corners": true }
788
+ ]
789
+ }))
790
+ .unwrap();
791
+
792
+ assert!(matches!(&schema.fields[0], ControlSpec::Alignment {
793
+ default: None,
794
+ corners: false,
795
+ ..
796
+ }));
797
+ assert!(matches!(&schema.fields[1], ControlSpec::Alignment {
798
+ default: Some(Alignment::TopRight),
799
+ corners: true,
800
+ ..
801
+ }));
802
+ assert_eq!(
803
+ schema.active_keys(),
804
+ HashSet::from(["align".to_owned(), "legend_anchor".to_owned()])
805
+ );
806
+ assert!(
807
+ serde_json::from_value::<ColumnConfigSchema>(json!({
808
+ "fields": [{ "kind": "Alignment", "key": "x", "default": "middle" }]
809
+ }))
810
+ .is_err()
811
+ );
812
+ }
813
+
814
+ #[test]
815
+ fn canonicalize_defaults_drops_non_corner_default_of_corners_field() {
816
+ let schema = ColumnConfigSchema {
817
+ fields: vec![
818
+ ControlSpec::Alignment {
819
+ key: "bad".to_owned(),
820
+ default: Some(Alignment::Top),
821
+ corners: true,
822
+ },
823
+ ControlSpec::Alignment {
824
+ key: "ok".to_owned(),
825
+ default: Some(Alignment::Top),
826
+ corners: false,
827
+ },
828
+ ControlSpec::Alignment {
829
+ key: "unset".to_owned(),
830
+ default: None,
831
+ corners: true,
832
+ },
833
+ ],
834
+ }
835
+ .canonicalize_defaults();
836
+
837
+ assert_eq!(
838
+ schema.active_keys(),
839
+ HashSet::from(["ok".to_owned(), "unset".to_owned()])
840
+ );
841
+ }
842
+
564
843
  #[test]
565
844
  fn canonicalize_defaults_recurses_and_drops_empty_groups() {
566
845
  let schema = ColumnConfigSchema {
@@ -10,13 +10,11 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- mod color_mode;
14
13
  mod custom;
15
14
  mod custom_format;
16
15
  mod simple;
17
16
  mod simple_format;
18
17
 
19
- pub use color_mode::*;
20
18
  pub use custom::*;
21
19
  pub use custom_format::*;
22
20
  use serde::{Deserialize, Serialize};
@@ -65,13 +63,4 @@ pub struct DatetimeColumnStyleConfig {
65
63
  #[serde(skip_serializing_if = "Option::is_none")]
66
64
  #[ts(optional, as = "Option<_>")]
67
65
  pub date_format: Option<DatetimeFormatType>,
68
-
69
- #[serde(default)]
70
- #[serde(skip_serializing_if = "DatetimeColorMode::is_none")]
71
- pub datetime_color_mode: DatetimeColorMode,
72
-
73
- #[serde(default)]
74
- #[serde(skip_serializing_if = "Option::is_none")]
75
- #[ts(skip)]
76
- pub color: Option<String>,
77
66
  }
@@ -15,7 +15,6 @@
15
15
 
16
16
  mod column_config_schema;
17
17
  mod columns_config;
18
- mod css_scale;
19
18
  mod datetime_column_style;
20
19
  mod export_method;
21
20
  mod kvpair;
@@ -23,21 +22,18 @@ mod number_series_style;
23
22
  mod number_string_format;
24
23
  mod options;
25
24
  mod plugin_static_config;
26
- mod string_column_style;
27
25
  pub mod view_config;
28
26
  mod viewer_config;
29
27
  mod workspace_config;
30
28
 
31
29
  pub use column_config_schema::*;
32
30
  pub use columns_config::*;
33
- pub use css_scale::*;
34
31
  pub use datetime_column_style::*;
35
32
  pub use export_method::*;
36
33
  pub use number_series_style::*;
37
34
  pub use number_string_format::*;
38
35
  pub use options::*;
39
36
  pub use plugin_static_config::*;
40
- pub use string_column_style::*;
41
37
  pub use view_config::*;
42
38
  pub use viewer_config::*;
43
39
  pub use workspace_config::*;