@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
@@ -11,6 +11,8 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  mod agg_depth_selector;
14
+ pub(crate) mod alignment_field;
15
+ pub(crate) mod font_field;
14
16
  pub(crate) mod primitive_field;
15
17
  mod symbol;
16
18
 
@@ -22,26 +24,26 @@ use perspective_client::config::ColumnType;
22
24
  use yew::{Callback, Html, Properties, function_component, html};
23
25
 
24
26
  use self::agg_depth_selector::*;
27
+ use self::alignment_field::AlignmentField;
28
+ use self::font_field::FontField;
25
29
  use self::primitive_field::{
26
30
  BoolField, ColorField, EnumField, GradientStopsField, NumberFieldPrimitive, PaletteField,
27
31
  };
28
32
  use crate::components::column_settings_sidebar::style_tab::symbol::SymbolStyle;
29
- use crate::components::containers::control_group::ControlGroup;
30
33
  use crate::components::datetime_column_style::DatetimeColumnStyle;
31
34
  use crate::components::number_series_style::NumberSeriesStyle;
32
- use crate::components::string_column_style::StringColumnStyle;
33
35
  use crate::components::style_controls::CustomNumberFormat;
34
36
  use crate::config::{
35
- ColumnConfigFieldUpdate, ControlSpec, CssKind, CustomNumberFormatConfig,
36
- DatetimeColumnStyleConfig, NamedValue, NumberFormatDefaults, NumberSeriesStyleConfig,
37
- StringColumnStyleConfig,
37
+ Alignment, ColumnConfigFieldUpdate, ControlSpec, CustomNumberFormatConfig,
38
+ DatetimeColumnStyleConfig, FontToggle, NumberFormatDefaults, NumberSeriesStyleConfig,
38
39
  };
39
40
  use crate::presentation::Presentation;
40
41
  use crate::queries::{fetch_column_abs_max, get_column_config_schema, named_values};
41
42
  use crate::renderer::Renderer;
42
43
  use crate::session::Session;
43
44
  use crate::tasks::send_column_config;
44
- use crate::utils::PtrEqRc;
45
+ use crate::ui::ControlGroup;
46
+ use crate::utils::{CssKind, NamedValue, PtrEqRc};
45
47
  use crate::workspace::Workspace;
46
48
 
47
49
  #[derive(Clone, PartialEq, Properties)]
@@ -271,12 +273,6 @@ fn render_leaf(spec: ControlSpec, keys: &[String], ctx: &FieldRenderCtx) -> Opti
271
273
  />
272
274
  }
273
275
  },
274
- ControlSpec::StringFormat => {
275
- let config: Option<StringColumnStyleConfig> = deser_sub(raw_config);
276
- html! {
277
- <StringColumnStyle {config} on_change={on_change.clone()} keys={keys.to_vec()} />
278
- }
279
- },
280
276
  ControlSpec::Symbols {
281
277
  default: default_config,
282
278
  } => {
@@ -342,6 +338,68 @@ fn render_leaf(spec: ControlSpec, keys: &[String], ctx: &FieldRenderCtx) -> Opti
342
338
  />
343
339
  }
344
340
  },
341
+ ControlSpec::Font {
342
+ key,
343
+ default,
344
+ size,
345
+ bold,
346
+ italic,
347
+ } => {
348
+ let current = raw_config
349
+ .as_ref()
350
+ .and_then(|m| m.get(&key))
351
+ .and_then(|v| v.as_str().map(|s| s.to_string()));
352
+
353
+ let toggle = |t: &FontToggle| {
354
+ raw_config
355
+ .as_ref()
356
+ .and_then(|m| m.get(&t.key))
357
+ .and_then(|v| v.as_bool())
358
+ };
359
+
360
+ let size_current = size
361
+ .as_ref()
362
+ .and_then(|s| raw_config.as_ref().and_then(|m| m.get(&s.key)))
363
+ .and_then(|v| v.as_f64());
364
+
365
+ let bold_current = bold.as_ref().and_then(toggle);
366
+ let italic_current = italic.as_ref().and_then(toggle);
367
+ html! {
368
+ <FontField
369
+ field_key={key}
370
+ {default}
371
+ {current}
372
+ {size}
373
+ {size_current}
374
+ {bold}
375
+ {bold_current}
376
+ {italic}
377
+ {italic_current}
378
+ on_change={on_change.clone()}
379
+ />
380
+ }
381
+ },
382
+ ControlSpec::Alignment {
383
+ key,
384
+ default,
385
+ corners,
386
+ } => {
387
+ let current = raw_config
388
+ .as_ref()
389
+ .and_then(|m| m.get(&key))
390
+ .and_then(|v| v.as_str())
391
+ .and_then(Alignment::parse);
392
+
393
+ html! {
394
+ <AlignmentField
395
+ field_key={key}
396
+ {default}
397
+ {corners}
398
+ {current}
399
+ on_change={on_change.clone()}
400
+ />
401
+ }
402
+ },
345
403
  ControlSpec::Bool { key, default } => {
346
404
  let current = raw_config
347
405
  .as_ref()
@@ -29,9 +29,7 @@ use crate::components::column_settings_sidebar::attributes_tab::AttributesTab;
29
29
  use crate::components::column_settings_sidebar::save_settings::SaveSettingsProps;
30
30
  use crate::components::column_settings_sidebar::style_tab::StyleTab;
31
31
  use crate::components::column_settings_sidebar::window_tab::{WindowTab, WindowTabProps};
32
- use crate::components::containers::sidebar::Sidebar;
33
- use crate::components::containers::tab_list::TabList;
34
- use crate::components::editable_header::EditableHeaderProps;
32
+ use crate::components::editable_header::{EditableHeader, EditableHeaderProps};
35
33
  use crate::components::expression_editor::ExpressionEditorProps;
36
34
  use crate::components::type_icon::TypeIconType;
37
35
  use crate::components::window_editor::WindowEditorProps;
@@ -41,6 +39,7 @@ use crate::session::{Session, SessionMetadataRc};
41
39
  use crate::tasks::{
42
40
  delete_expr, delete_window, save_expr, save_window, update_expr, update_window,
43
41
  };
42
+ use crate::ui::{Sidebar, TabList};
44
43
  use crate::utils::PtrEqRc;
45
44
  use crate::workspace::Workspace;
46
45
 
@@ -519,7 +518,7 @@ impl Component for ColumnSettingsPanel {
519
518
  is_pinned={ctx.props().is_pinned}
520
519
  on_toggle_pin={Some(ctx.props().on_toggle_pin.clone())}
521
520
  selected_tab={selected_tab_idx}
522
- {header_props}
521
+ header={html! { <EditableHeader ..header_props /> }}
523
522
  >
524
523
  <TabList<ColumnSettingsTab>
525
524
  tabs={self.tabs.clone()}
@@ -14,9 +14,9 @@ use std::rc::Rc;
14
14
 
15
15
  use yew::prelude::*;
16
16
 
17
- use super::containers::dropdown_menu::*;
18
17
  use crate::config::*;
19
18
  use crate::renderer::*;
19
+ use crate::ui::{DropDownMenu, DropDownMenuItem};
20
20
 
21
21
  type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
22
22
 
@@ -14,10 +14,8 @@ use std::rc::Rc;
14
14
 
15
15
  use yew::prelude::*;
16
16
 
17
- use crate::components::form::number_field::NumberField;
18
- use crate::components::form::select_value_field::SelectValueField;
19
- use crate::components::modal::{ModalLink, SetModalLink};
20
17
  use crate::config::*;
18
+ use crate::ui::{ModalLink, NumberField, SelectValueField, SetModalLink};
21
19
  use crate::utils::WeakScope;
22
20
 
23
21
  #[derive(Properties)]
@@ -12,9 +12,8 @@
12
12
 
13
13
  use yew::prelude::*;
14
14
 
15
- use crate::components::form::select_enum_field::SelectEnumField;
16
- use crate::components::modal::{ModalLink, SetModalLink};
17
15
  use crate::config::*;
16
+ use crate::ui::{ModalLink, SelectEnumField, SetModalLink};
18
17
  use crate::utils::WeakScope;
19
18
 
20
19
  #[derive(Properties)]
@@ -22,11 +22,10 @@ use perspective_js::utils::global::navigator;
22
22
  use wasm_bindgen::prelude::*;
23
23
  use yew::prelude::*;
24
24
 
25
- use super::modal::{ModalLink, SetModalLink};
26
25
  use crate::components::datetime_column_style::custom::DatetimeStyleCustom;
27
26
  use crate::components::datetime_column_style::simple::DatetimeStyleSimple;
28
- use crate::components::form::select_value_field::SelectValueField;
29
27
  use crate::config::*;
28
+ use crate::ui::{ModalLink, SelectValueField, SetModalLink};
30
29
  use crate::utils::WeakScope;
31
30
 
32
31
  /// Format-only widget for `datetime` columns. Renders the `date_format`
@@ -10,6 +10,7 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use std::cell::Cell;
13
14
  use std::rc::Rc;
14
15
 
15
16
  use perspective_client::ExprValidationError;
@@ -17,9 +18,11 @@ use perspective_js::utils::{ApiFuture, JsValueSerdeExt};
17
18
  use wasm_bindgen::prelude::*;
18
19
  use yew::prelude::*;
19
20
 
20
- use crate::components::form::code_editor::CodeEditor;
21
+ use crate::components::code_editor::CodeEditor;
22
+ use crate::config::*;
21
23
  use crate::js::{MimeType, copy_to_clipboard, paste_from_clipboard};
22
24
  use crate::presentation::*;
25
+ use crate::queries::fetch_hosted_tables;
23
26
  use crate::renderer::*;
24
27
  use crate::session::*;
25
28
  use crate::utils::*;
@@ -44,12 +47,18 @@ pub struct DebugPanelProps {
44
47
  pub on_auto_width: Callback<f64>,
45
48
  }
46
49
 
50
+ /// Whether the editor holds text the panel has not successfully applied, read
51
+ /// synchronously by the config-change listeners so a failed apply's own
52
+ /// `view_config_changed` never discards it.
53
+ type Dirty = Rc<Cell<bool>>;
54
+
47
55
  #[function_component(DebugPanel)]
48
56
  pub fn debug_panel(props: &DebugPanelProps) -> Html {
49
57
  let expr = use_state_eq(|| Rc::new("".to_string()));
50
58
  let error = use_state_eq(|| Option::<ExprValidationError>::None);
51
59
  let select_all = use_memo((), |()| PubSub::default());
52
60
  let modified = use_state_eq(|| false);
61
+ let dirty: Dirty = use_memo((), |()| Cell::new(false));
53
62
 
54
63
  // Measure natural width on mount and route up to `SettingsPanel`.
55
64
  let sizer = use_node_ref();
@@ -64,7 +73,7 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
64
73
  });
65
74
 
66
75
  use_effect_with((expr.setter(), props.clone()), {
67
- clone!(error, modified);
76
+ clone!(error, modified, dirty);
68
77
  move |(text, state)| {
69
78
  state.set_text(text.clone());
70
79
  error.set(None);
@@ -75,6 +84,7 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
75
84
  text.clone(),
76
85
  error.setter(),
77
86
  modified.setter(),
87
+ dirty.clone(),
78
88
  ));
79
89
 
80
90
  let sub2 = state
@@ -84,6 +94,7 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
84
94
  text.clone(),
85
95
  error.setter(),
86
96
  modified.setter(),
97
+ dirty.clone(),
87
98
  ));
88
99
 
89
100
  let sub3 = state
@@ -93,6 +104,7 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
93
104
  text.clone(),
94
105
  error.setter(),
95
106
  modified.setter(),
107
+ dirty.clone(),
96
108
  ));
97
109
 
98
110
  || {
@@ -104,16 +116,17 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
104
116
  });
105
117
 
106
118
  let oninput = use_callback(expr.setter(), {
107
- clone!(modified);
119
+ clone!(modified, dirty);
108
120
  move |x, expr| {
121
+ dirty.set(true);
109
122
  modified.set(true);
110
123
  expr.set(x)
111
124
  }
112
125
  });
113
126
 
114
127
  let onsave = use_callback((expr.clone(), error.clone(), props.clone()), {
115
- clone!(modified);
116
- move |_, (text, error, props)| props.on_save(text, error, &modified)
128
+ clone!(modified, dirty);
129
+ move |_, (text, error, props)| props.on_save(text, text.setter(), error, &modified, &dirty)
117
130
  });
118
131
 
119
132
  let oncopy = use_callback(
@@ -133,13 +146,14 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
133
146
  );
134
147
 
135
148
  let onapply = use_callback((expr.clone(), error.clone(), props.clone()), {
136
- clone!(modified);
137
- move |_, (text, error, props)| props.on_save(text, error, &modified)
149
+ clone!(modified, dirty);
150
+ move |_, (text, error, props)| props.on_save(text, text.setter(), error, &modified, &dirty)
138
151
  });
139
152
 
140
153
  let onreset = use_callback((expr.setter(), error.clone(), props.clone()), {
141
- clone!(modified);
154
+ clone!(modified, dirty);
142
155
  move |_, (text, error, props)| {
156
+ dirty.set(false);
143
157
  props.set_text(text.clone());
144
158
  error.set(None);
145
159
  modified.set(false);
@@ -147,16 +161,17 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
147
161
  });
148
162
 
149
163
  let onpaste = use_callback((expr.clone(), error.clone(), props.clone()), {
150
- clone!(modified);
164
+ clone!(modified, dirty);
151
165
  move |_, (text, error, props)| {
152
- clone!(text, error, props, modified);
166
+ clone!(text, error, props, modified, dirty);
153
167
  ApiFuture::spawn(async move {
154
168
  if let Some(x) = paste_from_clipboard().await {
155
169
  let x = Rc::new(x);
170
+ dirty.set(true);
156
171
  modified.set(true);
157
172
  error.set(None);
158
173
  text.set(x.clone());
159
- props.on_save(&x, &error, &modified);
174
+ props.on_save(&x, text.setter(), &error, &modified, &dirty);
160
175
  }
161
176
 
162
177
  Ok(())
@@ -169,10 +184,10 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
169
184
  <div id="debug-panel-overflow">
170
185
  <div id="debug-panel" class="sidebar_column" ref={sizer}>
171
186
  <div id="debug-panel-controls">
172
- <button disabled={!*modified} onclick={onapply}>{ "Apply" }</button>
173
- <button disabled={!*modified} onclick={onreset}>{ "Reset" }</button>
174
- <button onclick={oncopy}>{ "Copy" }</button>
175
- <button onclick={onpaste}>{ "Paste" }</button>
187
+ <button id="debug-panel-apply" disabled={!*modified} onclick={onapply} />
188
+ <button id="debug-panel-reset" disabled={!*modified} onclick={onreset} />
189
+ <button id="debug-panel-copy" onclick={oncopy} />
190
+ <button id="debug-panel-paste" onclick={onpaste} />
176
191
  </div>
177
192
  <div id="debug-panel-editor">
178
193
  <CodeEditor
@@ -184,6 +199,9 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
184
199
  select_all={select_all.subscriber()}
185
200
  error={(*error).clone()}
186
201
  />
202
+ if let Some(err) = &*error {
203
+ <div id="debug-panel-error" class="error">{ &err.error_message }</div>
204
+ }
187
205
  </div>
188
206
  <div
189
207
  class="scroll-panel-auto-width"
@@ -219,66 +237,105 @@ impl DebugPanelProps {
219
237
  text: UseStateSetter<Rc<String>>,
220
238
  error: UseStateSetter<Option<ExprValidationError>>,
221
239
  modified: UseStateSetter<bool>,
240
+ dirty: Dirty,
222
241
  ) -> impl Fn(()) + use<> {
223
242
  let props = self.clone();
224
243
  move |_| {
244
+ if dirty.get() {
245
+ return;
246
+ }
247
+
225
248
  error.set(None);
226
249
  props.set_text(text.clone());
227
250
  modified.set(false);
228
251
  }
229
252
  }
230
253
 
254
+ /// Validate and apply the editor's text as this panel's config, treating
255
+ /// an un-hosted `table` as a validation error rather than a pending bind.
231
256
  fn on_save(
232
257
  &self,
233
- text: &Rc<String>,
258
+ source: &Rc<String>,
259
+ text: UseStateSetter<Rc<String>>,
234
260
  error: &UseStateHandle<Option<ExprValidationError>>,
235
261
  modified: &UseStateHandle<bool>,
262
+ dirty: &Dirty,
236
263
  ) {
237
264
  let props = self.clone();
238
- clone!(text, error, modified);
265
+ clone!(source, error, modified, dirty);
239
266
  ApiFuture::spawn(async move {
240
- match serde_json::from_str(&text) {
241
- Ok(config) => {
242
- let active =
243
- props.workspace.active_renderer().as_ref() == Some(&props.renderer);
244
- match crate::tasks::restore_panel(
245
- &props.session,
246
- &props.renderer,
247
- &props.presentation,
248
- &props.workspace,
249
- crate::tasks::RestoreMode::Existing { active },
250
- config,
251
- crate::tasks::RestoreErrors::Suppress,
252
- )
253
- .await
254
- {
255
- Ok(_) => {
256
- modified.set(false);
257
- },
258
- Err(e) => {
259
- modified.set(true);
260
- error.set(Some(ExprValidationError {
261
- error_message: JsValue::from(e).as_string().unwrap_or_else(|| {
262
- "Failed to validate viewer config".to_owned()
263
- }),
264
- line: 0_u32,
265
- column: 0,
266
- }));
267
- },
268
- }
269
- Ok(())
270
- },
267
+ let fail = |message: String, (line, column): (u32, u32)| {
268
+ dirty.set(true);
269
+ modified.set(true);
270
+ error.set(Some(ExprValidationError {
271
+ error_message: message,
272
+ line,
273
+ column,
274
+ }));
275
+ };
276
+
277
+ let config: ViewerConfigUpdate = match serde_json::from_str(&source) {
278
+ Ok(config) => config,
271
279
  Err(err) => {
272
- modified.set(true);
273
- error.set(Some(ExprValidationError {
274
- error_message: err.to_string(),
275
- line: err.line() as u32 - 1,
276
- column: err.column() as u32 - 1,
277
- }));
280
+ let position = (err.line() as u32 - 1, err.column() as u32 - 1);
281
+ fail(err.to_string(), position);
282
+ return Ok(());
283
+ },
284
+ };
285
+
286
+ if let OptionalUpdate::Update(name) = &config.table {
287
+ let hosted = fetch_hosted_tables(&props.workspace).await;
288
+ if !hosted
289
+ .iter()
290
+ .any(|(_, names)| names.iter().any(|n| n == name))
291
+ {
292
+ fail(
293
+ format!("Unknown table \"{name}\""),
294
+ locate_key(&source, "table"),
295
+ );
296
+
297
+ return Ok(());
298
+ }
299
+ }
300
+
301
+ let active = props.workspace.active_renderer().as_ref() == Some(&props.renderer);
302
+ let result = crate::tasks::restore_panel(
303
+ &props.session,
304
+ &props.renderer,
305
+ &props.presentation,
306
+ &props.workspace,
307
+ crate::tasks::RestoreMode::Existing { active },
308
+ config,
309
+ crate::tasks::RestoreErrors::Suppress,
310
+ MissingTable::Error,
311
+ )
312
+ .await;
278
313
 
279
- Ok(())
314
+ match result {
315
+ Ok(_) => {
316
+ dirty.set(false);
317
+ error.set(None);
318
+ modified.set(false);
319
+ props.set_text(text);
280
320
  },
321
+ Err(e) => fail(format!("{e}"), (0, 0)),
281
322
  }
323
+
324
+ Ok(())
282
325
  });
283
326
  }
284
327
  }
328
+
329
+ /// The 0-based `(line, column)` of the first `"key"` in `text`, or `(0, 0)`
330
+ /// when absent.
331
+ fn locate_key(text: &str, key: &str) -> (u32, u32) {
332
+ let needle = format!("\"{key}\"");
333
+ text.lines()
334
+ .enumerate()
335
+ .find_map(|(line, content)| {
336
+ content
337
+ .find(&needle)
338
+ .map(|column| (line as u32, column as u32))
339
+ })
340
+ .unwrap_or((0, 0))
341
+ }
@@ -23,6 +23,7 @@ use crate::components::column_dropdown::ColumnDropDownElement;
23
23
  use crate::components::column_selector::{EmptyColumn, InPlaceColumn, InvalidColumn};
24
24
  use crate::components::type_icon::TypeIcon;
25
25
  use crate::presentation::{DragDropContainer, Presentation};
26
+ use crate::ui::intl_slug;
26
27
  use crate::utils::DragTarget;
27
28
 
28
29
  #[derive(Properties, Derivative)]
@@ -405,16 +406,7 @@ where
405
406
  // here while the var VALUE stays in the stylesheets - custom
406
407
  // properties inherit, so the label's `:before` picks it up.
407
408
  let role_style = ctx.props().role_label.as_ref().map(|role| {
408
- let slug = role
409
- .chars()
410
- .map(|x| {
411
- if x.is_ascii_alphanumeric() {
412
- x.to_ascii_lowercase()
413
- } else {
414
- '-'
415
- }
416
- })
417
- .collect::<String>();
409
+ let slug = intl_slug(role);
418
410
 
419
411
  // The second var is a PRESENCE FLAG: CSS cannot ask whether a
420
412
  // custom property is set, so the slot's stylesheet reads it as
@@ -14,10 +14,10 @@ use std::rc::Rc;
14
14
 
15
15
  use yew::prelude::*;
16
16
 
17
- use super::containers::dropdown_menu::*;
18
17
  use crate::config::*;
19
18
  use crate::renderer::*;
20
19
  use crate::session::Session;
20
+ use crate::ui::{DropDownMenu, DropDownMenuItem};
21
21
 
22
22
  pub type ExportDropDownMenuItem = DropDownMenuItem<ExportFile>;
23
23
 
@@ -15,7 +15,7 @@ use std::rc::Rc;
15
15
  use perspective_client::{ExprValidationError, clone};
16
16
  use yew::prelude::*;
17
17
 
18
- use super::form::code_editor::*;
18
+ use super::code_editor::CodeEditor;
19
19
  use crate::session::{Session, SessionMetadataRc};
20
20
  use crate::tasks::{ExprValidation, validate_expression};
21
21
 
@@ -19,9 +19,9 @@ use web_sys::*;
19
19
  use yew::html::ImplicitClone;
20
20
  use yew::prelude::*;
21
21
 
22
- use super::portal::PortalModal;
23
22
  use super::style::StyleSurface;
24
23
  use crate::session::Session;
24
+ use crate::ui::PortalModal;
25
25
  use crate::utils::*;
26
26
  use crate::*;
27
27
 
@@ -219,7 +219,7 @@ impl Component for FilterDropDownPortal {
219
219
  html! {
220
220
  <PortalModal
221
221
  tag_name="perspective-dropdown"
222
- surface={StyleSurface::FilterDropdown}
222
+ sheet={StyleSurface::FilterDropdown.sheet()}
223
223
  {target}
224
224
  own_focus=false
225
225
  {on_close}
@@ -19,8 +19,8 @@ use web_sys::*;
19
19
  use yew::html::ImplicitClone;
20
20
  use yew::prelude::*;
21
21
 
22
- use super::portal::PortalModal;
23
22
  use super::style::StyleSurface;
23
+ use crate::ui::PortalModal;
24
24
  use crate::utils::*;
25
25
 
26
26
  #[derive(Default)]
@@ -157,7 +157,7 @@ impl Component for FunctionDropDownPortal {
157
157
  html! {
158
158
  <PortalModal
159
159
  tag_name="perspective-dropdown"
160
- surface={StyleSurface::FunctionDropdown}
160
+ sheet={StyleSurface::FunctionDropdown.sheet()}
161
161
  {target}
162
162
  own_focus=false
163
163
  {on_close}