@perspective-dev/viewer 4.4.1 → 4.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 (225) hide show
  1. package/dist/cdn/perspective-viewer.js +1 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/bootstrap.d.ts +2 -1
  24. package/dist/esm/column-format.d.ts +51 -0
  25. package/dist/esm/extensions.d.ts +2 -0
  26. package/dist/esm/perspective-viewer.d.ts +3 -1
  27. package/dist/esm/perspective-viewer.inline.js +1 -2
  28. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  29. package/dist/esm/perspective-viewer.js +1 -2
  30. package/dist/esm/perspective-viewer.js.map +4 -4
  31. package/dist/esm/perspective-viewer.worker.d.ts +2 -0
  32. package/dist/esm/plugin.d.ts +16 -72
  33. package/dist/esm/ts-rs/ColumnSelectMode.d.ts +1 -0
  34. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +77 -0
  35. package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -3
  36. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -4
  37. package/dist/wasm/perspective-viewer.d.ts +77 -18
  38. package/dist/wasm/perspective-viewer.js +293 -144
  39. package/dist/wasm/perspective-viewer.wasm +0 -0
  40. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -15
  41. package/package.json +24 -2
  42. package/src/css/column-selector.css +3 -2
  43. package/src/css/column-settings-panel.css +35 -6
  44. package/src/css/column-style.css +27 -2
  45. package/src/css/containers/scroll-panel.css +2 -1
  46. package/src/css/containers/tabs.css +8 -52
  47. package/src/css/dom/checkbox.css +0 -4
  48. package/src/css/form/code-editor.css +1 -0
  49. package/src/css/form/debug.css +3 -10
  50. package/src/css/plugin-selector.css +33 -0
  51. package/src/css/plugin-settings-panel.css +99 -0
  52. package/src/css/viewer.css +65 -3
  53. package/src/rust/components/column_dropdown.rs +3 -1
  54. package/src/rust/components/column_selector/active_column.rs +13 -19
  55. package/src/rust/components/column_selector/config_selector.rs +20 -20
  56. package/src/rust/components/column_selector/filter_column.rs +14 -14
  57. package/src/rust/components/column_selector/inactive_column.rs +9 -15
  58. package/src/rust/components/column_selector/pivot_column.rs +7 -7
  59. package/src/rust/components/column_selector/sort_column.rs +7 -7
  60. package/src/rust/components/column_selector.rs +55 -37
  61. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +15 -7
  62. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +394 -0
  63. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +15 -6
  64. package/src/rust/components/column_settings_sidebar/style_tab.rs +267 -136
  65. package/src/rust/components/column_settings_sidebar.rs +43 -49
  66. package/src/rust/components/containers/dragdrop_list.rs +5 -5
  67. package/src/rust/components/containers/mod.rs +0 -1
  68. package/src/rust/components/containers/scroll_panel.rs +21 -7
  69. package/src/rust/components/containers/sidebar.rs +8 -6
  70. package/src/rust/components/containers/split_panel.rs +3 -3
  71. package/src/rust/components/containers/tab_list.rs +3 -9
  72. package/src/rust/components/copy_dropdown.rs +2 -3
  73. package/src/rust/components/datetime_column_style.rs +19 -81
  74. package/src/rust/components/editable_header.rs +2 -3
  75. package/src/rust/components/export_dropdown.rs +2 -3
  76. package/src/rust/components/expression_editor.rs +29 -17
  77. package/src/rust/components/filter_dropdown.rs +2 -1
  78. package/src/rust/components/form/color_range_selector.rs +14 -7
  79. package/src/rust/components/form/debug.rs +47 -37
  80. package/src/rust/components/main_panel.rs +24 -65
  81. package/src/rust/components/mod.rs +2 -1
  82. package/src/rust/components/number_series_style.rs +161 -0
  83. package/src/rust/components/plugin_tab.rs +221 -0
  84. package/src/rust/components/settings_panel.rs +181 -59
  85. package/src/rust/components/status_bar.rs +140 -173
  86. package/src/rust/components/status_indicator.rs +15 -22
  87. package/src/rust/components/string_column_style.rs +20 -82
  88. package/src/rust/components/style_controls/number_string_format.rs +14 -30
  89. package/src/rust/components/viewer.rs +92 -131
  90. package/src/rust/config/column_config_schema.rs +195 -0
  91. package/src/rust/config/columns_config.rs +4 -97
  92. package/src/rust/config/datetime_column_style.rs +0 -5
  93. package/src/rust/config/mod.rs +8 -2
  94. package/src/rust/config/number_series_style.rs +79 -0
  95. package/src/rust/config/plugin_static_config.rs +144 -0
  96. package/src/rust/config/string_column_style.rs +0 -5
  97. package/src/rust/config/viewer_config.rs +5 -6
  98. package/src/rust/custom_elements/copy_dropdown.rs +30 -18
  99. package/src/rust/custom_elements/debug_plugin.rs +1 -3
  100. package/src/rust/custom_elements/export_dropdown.rs +26 -18
  101. package/src/rust/custom_elements/viewer.rs +62 -73
  102. package/src/rust/custom_events.rs +181 -224
  103. package/src/rust/js/plugin.rs +45 -117
  104. package/src/rust/lib.rs +34 -5
  105. package/src/rust/presentation/drag_helpers.rs +206 -0
  106. package/src/rust/presentation/props.rs +8 -0
  107. package/src/rust/presentation.rs +256 -41
  108. package/src/rust/{tasks → queries}/column_locator.rs +17 -73
  109. package/src/rust/queries/column_values.rs +59 -0
  110. package/src/rust/{tasks → queries}/columns_iter_set.rs +11 -18
  111. package/src/rust/queries/exports.rs +96 -0
  112. package/src/rust/queries/fetch_column_stats.rs +94 -0
  113. package/src/rust/queries/get_viewer_config.rs +54 -0
  114. package/src/rust/queries/mod.rs +44 -0
  115. package/src/rust/queries/plugin_column_styles.rs +101 -0
  116. package/src/rust/{engines.rs → queries/validate_expression.rs} +26 -15
  117. package/src/rust/renderer/activate.rs +1 -0
  118. package/src/rust/renderer/limits.rs +9 -4
  119. package/src/rust/renderer/plugin_store.rs +12 -0
  120. package/src/rust/renderer/props.rs +28 -3
  121. package/src/rust/renderer/registry.rs +40 -15
  122. package/src/rust/renderer.rs +640 -51
  123. package/src/rust/session/column_defaults_update.rs +20 -28
  124. package/src/rust/session/drag_drop_update.rs +10 -10
  125. package/src/rust/session/metadata.rs +31 -16
  126. package/src/rust/session/props.rs +15 -6
  127. package/src/rust/session/view_subscription.rs +10 -0
  128. package/src/rust/session.rs +109 -147
  129. package/src/rust/tasks/copy_export.rs +178 -158
  130. package/src/rust/tasks/{structural.rs → dismiss_render_warning.rs} +20 -40
  131. package/src/rust/tasks/edit_expression.rs +68 -88
  132. package/src/rust/tasks/eject.rs +25 -22
  133. package/src/rust/tasks/intersection_observer.rs +8 -21
  134. package/src/rust/tasks/mod.rs +19 -21
  135. package/src/rust/tasks/reset_all.rs +78 -0
  136. package/src/rust/tasks/resize_observer.rs +11 -33
  137. package/src/rust/tasks/restore_and_render.rs +117 -90
  138. package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +38 -35
  139. package/src/rust/tasks/send_plugin_config.rs +32 -33
  140. package/src/rust/tasks/update_and_render.rs +66 -47
  141. package/src/rust/{components/containers/trap_door_panel.rs → tasks/update_theme.rs} +34 -33
  142. package/src/rust/tasks/validate_expression.rs +61 -0
  143. package/src/rust/utils/browser/selection.rs +4 -4
  144. package/src/rust/utils/mod.rs +0 -63
  145. package/src/svg/mega-menu-icons-density.svg +23 -0
  146. package/src/svg/mega-menu-icons-map-density.svg +24 -0
  147. package/src/svg/mega-menu-icons-map-line.svg +19 -0
  148. package/src/themes/botanical.css +27 -53
  149. package/src/themes/defaults.css +24 -36
  150. package/src/themes/dracula.css +36 -54
  151. package/src/themes/gruvbox-dark.css +39 -59
  152. package/src/themes/gruvbox.css +16 -28
  153. package/src/themes/icons.css +3 -0
  154. package/src/themes/intl/de.css +42 -6
  155. package/src/themes/intl/es.css +42 -6
  156. package/src/themes/intl/fr.css +42 -6
  157. package/src/themes/intl/ja.css +42 -6
  158. package/src/themes/intl/pt.css +42 -6
  159. package/src/themes/intl/zh.css +42 -6
  160. package/src/themes/intl.css +37 -4
  161. package/src/themes/monokai.css +45 -61
  162. package/src/themes/phosphor.css +20 -29
  163. package/src/themes/pro-dark.css +25 -34
  164. package/src/themes/solarized-dark.css +21 -36
  165. package/src/themes/solarized.css +13 -23
  166. package/src/themes/vaporwave.css +40 -74
  167. package/src/ts/bootstrap.ts +14 -3
  168. package/src/ts/column-format.ts +162 -0
  169. package/src/ts/extensions.ts +4 -0
  170. package/src/ts/perspective-viewer.ts +9 -1
  171. package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
  172. package/src/ts/plugin.ts +25 -101
  173. package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
  174. package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
  175. package/src/ts/ts-rs/ViewerConfig.ts +1 -2
  176. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
  177. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
  178. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
  179. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
  180. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
  181. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  182. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
  183. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  184. package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
  185. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
  186. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
  187. package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
  188. package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
  189. package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
  190. package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
  191. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
  192. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
  193. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  194. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
  195. package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
  196. package/src/rust/components/number_column_style.rs +0 -491
  197. package/src/rust/config/number_column_style.rs +0 -136
  198. package/src/rust/dragdrop.rs +0 -481
  199. package/src/rust/tasks/plugin_column_styles.rs +0 -98
  200. package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
  201. package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
  202. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
  203. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
  204. package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
  205. package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
  206. package/src/ts/ts-rs/FormatMode.ts +0 -3
  207. package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
  208. package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
  209. package/src/ts/ts-rs/PluginConfig.ts +0 -4
  210. package/src/ts/ts-rs/RoundingMode.ts +0 -3
  211. package/src/ts/ts-rs/RoundingPriority.ts +0 -3
  212. package/src/ts/ts-rs/SignDisplay.ts +0 -3
  213. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
  214. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
  215. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  216. package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
  217. package/src/ts/ts-rs/UseGrouping.ts +0 -3
  218. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline0.js +0 -0
  219. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline1.js +0 -0
  220. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline2.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline3.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline4.js +0 -0
  223. /package/src/rust/{tasks → config}/export_method.rs +0 -0
  224. /package/src/rust/{tasks → queries}/export_app.rs +0 -0
  225. /package/src/rust/{tasks → queries}/is_invalid_drop.rs +0 -0
@@ -25,7 +25,7 @@ use yew::{Callback, Html, Properties, html};
25
25
  use crate::components::column_settings_sidebar::style_tab::symbol::symbol_pairs::PairsList;
26
26
  use crate::components::filter_dropdown::{FilterDropDownElement, FilterDropDownPortal};
27
27
  use crate::components::style::LocalStyle;
28
- use crate::config::{ColumnConfigValueUpdate, KeyValueOpts, SymbolKVPair};
28
+ use crate::config::{ColumnConfigFieldUpdate, KeyValueOpts, SymbolKVPair};
29
29
  use crate::css;
30
30
  use crate::session::Session;
31
31
 
@@ -34,10 +34,12 @@ pub struct SymbolAttrProps {
34
34
  pub session: Session,
35
35
  pub column_name: String,
36
36
  pub restored_config: Option<HashMap<String, String>>,
37
- pub on_change: Callback<ColumnConfigValueUpdate>,
37
+ pub on_change: Callback<ColumnConfigFieldUpdate>,
38
38
  pub default_config: KeyValueOpts,
39
39
  /// Selected theme name, threaded for PortalModal consumers.
40
40
  pub selected_theme: Option<String>,
41
+ #[prop_or_default]
42
+ pub keys: Vec<String>,
41
43
  }
42
44
  impl SymbolAttrProps {
43
45
  pub fn next_default_symbol(&self, pairs_len: usize) -> String {
@@ -92,10 +94,17 @@ impl yew::Component for SymbolStyle {
92
94
  .into_iter()
93
95
  .filter_map(|pair| Some((pair.key?, pair.value)))
94
96
  .collect::<HashMap<_, _>>();
95
- let update = Some(symbols).filter(|x| !x.is_empty());
96
- ctx.props()
97
- .on_change
98
- .emit(ColumnConfigValueUpdate::Symbols(update));
97
+ let mut value = serde_json::Map::new();
98
+ if !symbols.is_empty() {
99
+ value.insert(
100
+ "symbols".to_owned(),
101
+ serde_json::to_value(&symbols).unwrap_or(serde_json::Value::Null),
102
+ );
103
+ }
104
+ ctx.props().on_change.emit(ColumnConfigFieldUpdate {
105
+ keys: ctx.props().keys.clone(),
106
+ value,
107
+ });
99
108
 
100
109
  let has_last_key = new_pairs
101
110
  .last()
@@ -11,28 +11,33 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  mod agg_depth_selector;
14
- mod stub;
14
+ pub(crate) mod primitive_field;
15
15
  mod symbol;
16
16
 
17
+ use std::collections::HashMap;
18
+
17
19
  use itertools::Itertools;
18
20
  use perspective_client::config::ColumnType;
19
21
  use yew::{Html, Properties, function_component, html};
20
22
 
21
23
  use self::agg_depth_selector::*;
22
- use crate::components::column_settings_sidebar::style_tab::stub::Stub;
24
+ use self::primitive_field::{
25
+ BoolField, ColorField, ColorRangeField, EnumField, NumberFieldPrimitive,
26
+ };
23
27
  use crate::components::column_settings_sidebar::style_tab::symbol::SymbolStyle;
24
28
  use crate::components::datetime_column_style::DatetimeColumnStyle;
25
- use crate::components::number_column_style::NumberColumnStyle;
29
+ use crate::components::number_series_style::NumberSeriesStyle;
26
30
  use crate::components::string_column_style::StringColumnStyle;
27
31
  use crate::components::style_controls::CustomNumberFormat;
28
- use crate::custom_events::CustomEvents;
32
+ use crate::config::{
33
+ ControlSpec, CustomNumberFormatConfig, DatetimeColumnStyleConfig, NumberSeriesStyleConfig,
34
+ StringColumnStyleConfig,
35
+ };
29
36
  use crate::presentation::Presentation;
37
+ use crate::queries::{fetch_column_abs_max, get_column_config_schema};
30
38
  use crate::renderer::Renderer;
31
39
  use crate::session::Session;
32
- use crate::tasks::{
33
- HasCustomEvents, HasPresentation, HasRenderer, HasSession, SendPluginConfig,
34
- get_column_style_control_options,
35
- };
40
+ use crate::tasks::send_column_config;
36
41
  use crate::utils::PtrEqRc;
37
42
 
38
43
  #[derive(Clone, PartialEq, Properties)]
@@ -47,161 +52,287 @@ pub struct StyleTabProps {
47
52
  /// Session metadata snapshot — threaded from parent.
48
53
  pub metadata: PtrEqRc<crate::session::SessionMetadata>,
49
54
 
55
+ /// Per-column stats snapshot — threaded from `SessionProps`.
56
+ pub column_stats: PtrEqRc<HashMap<String, crate::session::ColumnStats>>,
57
+
50
58
  /// Selected theme name, threaded for PortalModal consumers.
51
59
  pub selected_theme: Option<String>,
52
60
 
53
61
  // State
54
- pub custom_events: CustomEvents,
55
62
  pub presentation: Presentation,
56
63
  pub renderer: Renderer,
57
64
  pub session: Session,
58
65
  }
59
66
 
60
- impl HasCustomEvents for StyleTabProps {
61
- fn custom_events(&self) -> &CustomEvents {
62
- &self.custom_events
63
- }
64
- }
67
+ #[function_component]
68
+ pub fn StyleTab(props: &StyleTabProps) -> Html {
69
+ // Bumped on every primitive field change so Yew re-renders the tab
70
+ // and re-queries `column_config_schema` with the new value. Without
71
+ // this, dynamic field gating (e.g. show `Color` only when
72
+ // `string_color_mode != none`) wouldn't surface until the user
73
+ // closed and reopened the sidebar.
74
+ let revision = yew::use_state(|| 0u32);
65
75
 
66
- impl HasPresentation for StyleTabProps {
67
- fn presentation(&self) -> &Presentation {
68
- &self.presentation
69
- }
70
- }
76
+ // `abs_max` lives in `Session`'s shared cache, propagated as a
77
+ // value-semantic snapshot through `props.column_stats`. The cache
78
+ // is cleared on `view_config_changed`; on cache miss we spawn the
79
+ // fetch and let `column_stats_changed` drive the re-render via
80
+ // `SessionProps`.
81
+ let abs_max = props
82
+ .column_stats
83
+ .get(&props.column_name)
84
+ .and_then(|s| s.abs_max);
71
85
 
72
- impl HasRenderer for StyleTabProps {
73
- fn renderer(&self) -> &Renderer {
74
- &self.renderer
75
- }
76
- }
86
+ yew::use_effect_with((props.column_name.clone(), props.view_config.clone()), {
87
+ let session = props.session.clone();
88
+ let column_name = props.column_name.clone();
89
+ move |_| {
90
+ fetch_column_abs_max(&session, column_name);
91
+ || ()
92
+ }
93
+ });
94
+
95
+ let raw_config = props.renderer.get_columns_config(&props.column_name);
96
+ let on_change = {
97
+ let state = props.clone();
98
+ let column_name = props.column_name.clone();
99
+ let revision = revision.clone();
100
+ yew::Callback::from(move |config: crate::config::ColumnConfigFieldUpdate| {
101
+ send_column_config(&state.session, &state.renderer, &column_name, config);
102
+ revision.set(*revision + 1);
103
+ })
104
+ };
77
105
 
78
- impl HasSession for StyleTabProps {
79
- fn session(&self) -> &Session {
80
- &self.session
106
+ fn deser_sub<T: serde::de::DeserializeOwned>(
107
+ raw: &Option<serde_json::Map<String, serde_json::Value>>,
108
+ ) -> Option<T> {
109
+ raw.as_ref()
110
+ .and_then(|m| serde_json::from_value::<T>(serde_json::Value::Object(m.clone())).ok())
81
111
  }
82
- }
83
112
 
84
- #[function_component]
85
- pub fn StyleTab(props: &StyleTabProps) -> Html {
86
- let config = props.presentation.get_columns_config(&props.column_name);
87
- let on_change = yew::use_callback(
88
- (props.clone(), props.column_name.clone()),
89
- |config, (state, column_name)| {
90
- state.send_plugin_config(column_name, config);
91
- },
92
- );
93
-
94
- let components = get_column_style_control_options(
113
+ let components = get_column_config_schema(
95
114
  &props.renderer,
96
115
  &props.view_config,
97
116
  &props.metadata,
98
117
  &props.column_name,
118
+ raw_config.as_ref(),
119
+ abs_max,
99
120
  )
100
- .map(|opts| {
101
- let mut components = vec![];
102
- if !props.view_config.group_by.is_empty() {
103
- let aggregate_depth = config.as_ref().map(|x| x.aggregate_depth as f64);
104
- components.push(("Aggregate Depth", html! {
105
- <AggregateDepthSelector
106
- group_by_depth={props.group_by_depth}
107
- on_change={on_change.clone()}
108
- column_name={props.column_name.to_owned()}
109
- value={aggregate_depth.unwrap_or_default() as u32}
110
- />
111
- }));
112
- }
113
-
114
- if let Some(default_config) = opts.datagrid_number_style {
115
- let config = config
116
- .as_ref()
117
- .map(|config| config.datagrid_number_style.clone());
118
-
119
- components.push(("Number Styles", html! {
120
- <NumberColumnStyle
121
- column_name={props.column_name.clone()}
122
- {config}
123
- {default_config}
124
- on_change={on_change.clone()}
125
- session={props.session.clone()}
126
- />
127
- }));
128
- }
129
- if let Some(default_config) = opts.datagrid_string_style {
130
- let config = config
131
- .as_ref()
132
- .map(|config| config.datagrid_string_style.clone());
133
-
134
- components.push(("String Styles", html! {
135
- <StringColumnStyle {config} {default_config} on_change={on_change.clone()} />
136
- }));
137
- }
121
+ .map(|schema| {
122
+ schema
123
+ .fields
124
+ .into_iter()
125
+ .filter_map(|spec| {
126
+ let keys: Vec<String> = spec
127
+ .serialized_keys()
128
+ .into_iter()
129
+ .map(|s| s.to_string())
130
+ .collect();
131
+ let component = match spec {
132
+ ControlSpec::AggregateDepth => {
133
+ let aggregate_depth = raw_config
134
+ .as_ref()
135
+ .and_then(|m| m.get("aggregate_depth"))
136
+ .and_then(|v| v.as_u64())
137
+ .unwrap_or(0) as u32;
138
+ html! {
139
+ <AggregateDepthSelector
140
+ group_by_depth={props.group_by_depth}
141
+ on_change={on_change.clone()}
142
+ column_name={props.column_name.to_owned()}
143
+ value={aggregate_depth}
144
+ keys={keys.clone()}
145
+ />
146
+ }
147
+ },
148
+ ControlSpec::NumberSeriesStyle {
149
+ default: default_config,
150
+ } => {
151
+ let config: Option<NumberSeriesStyleConfig> = deser_sub(&raw_config);
152
+ html! {
153
+ <NumberSeriesStyle
154
+ {config}
155
+ {default_config}
156
+ on_change={on_change.clone()}
157
+ keys={keys.clone()}
158
+ />
159
+ }
160
+ },
161
+ ControlSpec::DatetimeFormat => {
162
+ let config: Option<DatetimeColumnStyleConfig> = deser_sub(&raw_config);
163
+ let enable_time_config = props.ty.unwrap() == ColumnType::Datetime;
164
+ html! {
165
+ <DatetimeColumnStyle
166
+ {enable_time_config}
167
+ {config}
168
+ on_change={on_change.clone()}
169
+ keys={keys.clone()}
170
+ />
171
+ }
172
+ },
173
+ ControlSpec::StringFormat => {
174
+ let config: Option<StringColumnStyleConfig> = deser_sub(&raw_config);
175
+ html! {
176
+ <StringColumnStyle
177
+ {config}
178
+ on_change={on_change.clone()}
179
+ keys={keys.clone()}
180
+ />
181
+ }
182
+ },
183
+ ControlSpec::Symbols {
184
+ default: default_config,
185
+ } => {
186
+ let restored_config: HashMap<String, String> = raw_config
187
+ .as_ref()
188
+ .and_then(|m| m.get("symbols"))
189
+ .and_then(|v| serde_json::from_value(v.clone()).ok())
190
+ .unwrap_or_default();
138
191
 
139
- if let Some(default_config) = opts.datagrid_datetime_style {
140
- let config = config
141
- .as_ref()
142
- .map(|config| config.datagrid_datetime_style.clone());
143
-
144
- let enable_time_config = props.ty.unwrap() == ColumnType::Datetime;
145
- components.push(("Datetime Styles", html! {
146
- <DatetimeColumnStyle
147
- {enable_time_config}
148
- {config}
149
- {default_config}
150
- on_change={on_change.clone()}
151
- />
152
- }))
153
- }
192
+ html! {
193
+ <SymbolStyle
194
+ {default_config}
195
+ restored_config={Some(restored_config)}
196
+ on_change={on_change.clone()}
197
+ column_name={props.column_name.clone()}
198
+ selected_theme={props.selected_theme.clone()}
199
+ session={props.session.clone()}
200
+ keys={keys.clone()}
201
+ />
202
+ }
203
+ },
204
+ ControlSpec::NumberFormat => {
205
+ let restored_config: CustomNumberFormatConfig = raw_config
206
+ .as_ref()
207
+ .and_then(|m| m.get("number_format"))
208
+ .and_then(|v| serde_json::from_value(v.clone()).ok())
209
+ .unwrap_or_default();
154
210
 
155
- if let Some(default_config) = opts.symbols {
156
- let restored_config = config
157
- .as_ref()
158
- .map(|config| config.symbols.clone())
159
- .unwrap_or_default();
160
-
161
- components.push(("Symbols", html! {
162
- <SymbolStyle
163
- {default_config}
164
- {restored_config}
165
- on_change={on_change.clone()}
166
- column_name={props.column_name.clone()}
167
- selected_theme={props.selected_theme.clone()}
168
- session={props.session.clone()}
169
- />
170
- }))
171
- }
211
+ html! {
212
+ <CustomNumberFormat
213
+ {restored_config}
214
+ on_change={on_change.clone()}
215
+ view_type={props.ty.unwrap()}
216
+ column_name={props.column_name.clone()}
217
+ keys={keys.clone()}
218
+ />
219
+ }
220
+ },
221
+ ControlSpec::Enum {
222
+ key,
223
+ variants,
224
+ default,
225
+ } => {
226
+ let current = raw_config
227
+ .as_ref()
228
+ .and_then(|m| m.get(&key))
229
+ .and_then(|v| v.as_str().map(|s| s.to_string()));
172
230
 
173
- if opts.number_string_format.unwrap_or_default() {
174
- let restored_config = config
175
- .as_ref()
176
- .and_then(|config| config.number_format.clone())
177
- .unwrap_or_default();
178
-
179
- components.push(("Number Formatting", html! {
180
- <CustomNumberFormat
181
- {restored_config}
182
- on_change={on_change.clone()}
183
- view_type={props.ty.unwrap()}
184
- column_name={props.column_name.clone()}
185
- />
186
- }));
187
- }
231
+ html! {
232
+ <EnumField
233
+ field_key={key}
234
+ {variants}
235
+ {default}
236
+ {current}
237
+ on_change={on_change.clone()}
238
+ />
239
+ }
240
+ },
241
+ ControlSpec::Bool { key, default } => {
242
+ let current = raw_config
243
+ .as_ref()
244
+ .and_then(|m| m.get(&key))
245
+ .and_then(|v| v.as_bool());
246
+ html! {
247
+ <BoolField
248
+ field_key={key}
249
+ {default}
250
+ {current}
251
+ on_change={on_change.clone()}
252
+ />
253
+ }
254
+ },
255
+ ControlSpec::Color { key, default } => {
256
+ let current = raw_config
257
+ .as_ref()
258
+ .and_then(|m| m.get(&key))
259
+ .and_then(|v| v.as_str().map(|s| s.to_string()));
260
+ html! {
261
+ <ColorField
262
+ field_key={key}
263
+ {default}
264
+ {current}
265
+ on_change={on_change.clone()}
266
+ />
267
+ }
268
+ },
269
+ ControlSpec::ColorRange {
270
+ key_pos,
271
+ key_neg,
272
+ default_pos,
273
+ default_neg,
274
+ is_gradient,
275
+ } => {
276
+ let current_pos = raw_config
277
+ .as_ref()
278
+ .and_then(|m| m.get(&key_pos))
279
+ .and_then(|v| v.as_str().map(|s| s.to_string()));
280
+ let current_neg = raw_config
281
+ .as_ref()
282
+ .and_then(|m| m.get(&key_neg))
283
+ .and_then(|v| v.as_str().map(|s| s.to_string()));
284
+ html! {
285
+ <ColorRangeField
286
+ field_key_pos={key_pos}
287
+ field_key_neg={key_neg}
288
+ {default_pos}
289
+ {default_neg}
290
+ {current_pos}
291
+ {current_neg}
292
+ {is_gradient}
293
+ on_change={on_change.clone()}
294
+ />
295
+ }
296
+ },
297
+ ControlSpec::Number {
298
+ key,
299
+ default,
300
+ min,
301
+ max,
302
+ step,
303
+ include,
304
+ } => {
305
+ let current = raw_config
306
+ .as_ref()
307
+ .and_then(|m| m.get(&key))
308
+ .and_then(|v| v.as_f64());
309
+ html! {
310
+ <NumberFieldPrimitive
311
+ field_key={key}
312
+ {default}
313
+ {current}
314
+ {min}
315
+ {max}
316
+ {step}
317
+ {include}
318
+ on_change={on_change.clone()}
319
+ />
320
+ }
321
+ },
322
+ // String primitive has no caller yet — wire when a
323
+ // plugin emits one.
324
+ ControlSpec::String { .. } => {
325
+ return None;
326
+ },
327
+ };
188
328
 
189
- components
190
- .into_iter()
191
- .map(|(_title, component)| {
192
- html! {
193
- <fieldset class="style-control">
194
- // <legend >{ title }</legend>
195
- { component }
196
- </fieldset>
197
- }
329
+ Some(html! { <fieldset class="style-control">{ component }</fieldset> })
198
330
  })
199
331
  .collect_vec()
200
332
  })
201
333
  .unwrap_or_else(|error| {
202
- vec![html! {
203
- <Stub message="Could not render column styles" error={Some(format!("{error:?}"))} />
204
- }]
334
+ tracing::error!("{}", error);
335
+ vec![]
205
336
  });
206
337
 
207
338
  html! {