@perspective-dev/viewer 4.4.1 → 4.5.1

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 (227) 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 +302 -148
  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 +44 -9
  44. package/src/css/column-style.css +35 -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 +2 -6
  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 +143 -3
  53. package/src/rust/components/column_dropdown.rs +3 -1
  54. package/src/rust/components/column_selector/active_column.rs +16 -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 +10 -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 +395 -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 +44 -49
  66. package/src/rust/components/containers/dragdrop_list.rs +32 -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 +17 -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 +141 -174
  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 +169 -132
  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 +703 -60
  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 +98 -0
  136. package/src/rust/tasks/resize_observer.rs +11 -33
  137. package/src/rust/tasks/restore_and_render.rs +128 -90
  138. package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +39 -35
  139. package/src/rust/tasks/send_plugin_config.rs +33 -33
  140. package/src/rust/tasks/update_and_render.rs +75 -49
  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/checkbox-checked-icon.svg +1 -1
  146. package/src/svg/checkbox-unchecked-icon.svg +1 -1
  147. package/src/svg/mega-menu-icons-density.svg +23 -0
  148. package/src/svg/mega-menu-icons-map-density.svg +24 -0
  149. package/src/svg/mega-menu-icons-map-line.svg +19 -0
  150. package/src/themes/botanical.css +27 -53
  151. package/src/themes/defaults.css +24 -36
  152. package/src/themes/dracula.css +36 -54
  153. package/src/themes/gruvbox-dark.css +39 -59
  154. package/src/themes/gruvbox.css +16 -28
  155. package/src/themes/icons.css +5 -0
  156. package/src/themes/intl/de.css +43 -6
  157. package/src/themes/intl/es.css +43 -6
  158. package/src/themes/intl/fr.css +43 -6
  159. package/src/themes/intl/ja.css +43 -6
  160. package/src/themes/intl/pt.css +43 -6
  161. package/src/themes/intl/zh.css +43 -6
  162. package/src/themes/intl.css +38 -4
  163. package/src/themes/monokai.css +45 -61
  164. package/src/themes/phosphor.css +20 -29
  165. package/src/themes/pro-dark.css +25 -34
  166. package/src/themes/solarized-dark.css +21 -36
  167. package/src/themes/solarized.css +13 -23
  168. package/src/themes/vaporwave.css +40 -74
  169. package/src/ts/bootstrap.ts +14 -3
  170. package/src/ts/column-format.ts +162 -0
  171. package/src/ts/extensions.ts +4 -0
  172. package/src/ts/perspective-viewer.ts +9 -1
  173. package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
  174. package/src/ts/plugin.ts +25 -101
  175. package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
  176. package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
  177. package/src/ts/ts-rs/ViewerConfig.ts +1 -2
  178. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
  179. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
  180. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
  181. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
  182. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
  183. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  184. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
  185. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  186. package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
  187. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
  188. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
  189. package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
  190. package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
  191. package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
  192. package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
  193. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
  194. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
  195. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  196. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
  197. package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
  198. package/src/rust/components/number_column_style.rs +0 -491
  199. package/src/rust/config/number_column_style.rs +0 -136
  200. package/src/rust/dragdrop.rs +0 -481
  201. package/src/rust/tasks/plugin_column_styles.rs +0 -98
  202. package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
  203. package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
  204. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
  205. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
  206. package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
  207. package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
  208. package/src/ts/ts-rs/FormatMode.ts +0 -3
  209. package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
  210. package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
  211. package/src/ts/ts-rs/PluginConfig.ts +0 -4
  212. package/src/ts/ts-rs/RoundingMode.ts +0 -3
  213. package/src/ts/ts-rs/RoundingPriority.ts +0 -3
  214. package/src/ts/ts-rs/SignDisplay.ts +0 -3
  215. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
  216. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
  217. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  218. package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
  219. package/src/ts/ts-rs/UseGrouping.ts +0 -3
  220. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline0.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline1.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline2.js +0 -0
  223. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline3.js +0 -0
  224. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline4.js +0 -0
  225. /package/src/rust/{tasks → config}/export_method.rs +0 -0
  226. /package/src/rust/{tasks → queries}/export_app.rs +0 -0
  227. /package/src/rust/{tasks → queries}/is_invalid_drop.rs +0 -0
@@ -12,7 +12,7 @@
12
12
  mod attributes_tab;
13
13
 
14
14
  mod save_settings;
15
- mod style_tab;
15
+ pub(crate) mod style_tab;
16
16
 
17
17
  use std::rc::Rc;
18
18
 
@@ -33,14 +33,10 @@ use crate::components::editable_header::EditableHeaderProps;
33
33
  use crate::components::expression_editor::ExpressionEditorProps;
34
34
  use crate::components::style::LocalStyle;
35
35
  use crate::components::type_icon::TypeIconType;
36
- use crate::custom_events::CustomEvents;
37
36
  use crate::presentation::{ColumnLocator, ColumnSettingsTab, Presentation};
38
37
  use crate::renderer::Renderer;
39
38
  use crate::session::{Session, SessionMetadataRc};
40
- use crate::tasks::{
41
- EditExpression, HasCustomEvents, HasPresentation, HasRenderer, HasSession,
42
- can_render_column_styles, locator_name_or_default, locator_view_type,
43
- };
39
+ use crate::tasks::{delete_expr, save_expr, update_expr};
44
40
  use crate::utils::PtrEqRc;
45
41
  use crate::*;
46
42
 
@@ -64,13 +60,13 @@ pub struct ColumnSettingsPanelProps {
64
60
  /// View config snapshot — threaded from `SessionProps`.
65
61
  pub view_config: PtrEqRc<ViewConfig>,
66
62
 
63
+ /// Per-column stats snapshot — threaded from `SessionProps`.
64
+ pub column_stats: PtrEqRc<std::collections::HashMap<String, crate::session::ColumnStats>>,
65
+
67
66
  /// Selected theme name, threaded for PortalModal consumers.
68
67
  pub selected_theme: Option<String>,
69
68
 
70
69
  // State
71
- #[derivative(Debug = "ignore")]
72
- pub custom_events: CustomEvents,
73
-
74
70
  #[derivative(Debug = "ignore")]
75
71
  pub presentation: Presentation,
76
72
 
@@ -88,34 +84,11 @@ impl PartialEq for ColumnSettingsPanelProps {
88
84
  && self.plugin_name == other.plugin_name
89
85
  && self.metadata == other.metadata
90
86
  && self.view_config == other.view_config
87
+ && self.column_stats == other.column_stats
91
88
  && self.selected_theme == other.selected_theme
92
89
  }
93
90
  }
94
91
 
95
- impl HasCustomEvents for ColumnSettingsPanelProps {
96
- fn custom_events(&self) -> &CustomEvents {
97
- &self.custom_events
98
- }
99
- }
100
-
101
- impl HasPresentation for ColumnSettingsPanelProps {
102
- fn presentation(&self) -> &Presentation {
103
- &self.presentation
104
- }
105
- }
106
-
107
- impl HasRenderer for ColumnSettingsPanelProps {
108
- fn renderer(&self) -> &Renderer {
109
- &self.renderer
110
- }
111
- }
112
-
113
- impl HasSession for ColumnSettingsPanelProps {
114
- fn session(&self) -> &Session {
115
- &self.session
116
- }
117
- }
118
-
119
92
  #[derive(Debug)]
120
93
  pub enum ColumnSettingsPanelMsg {
121
94
  SetExprValue(Rc<String>),
@@ -239,11 +212,20 @@ impl Component for ColumnSettingsPanel {
239
212
  ColumnLocator::Table(_) => {
240
213
  tracing::error!("Tried to save non-expression column!")
241
214
  },
242
- ColumnLocator::Expression(name) => {
243
- ctx.props().update_expr(name.clone(), new_expr)
244
- },
215
+ ColumnLocator::Expression(name) => update_expr(
216
+ &ctx.props().session,
217
+ &ctx.props().renderer,
218
+ &ctx.props().presentation,
219
+ name.clone(),
220
+ new_expr,
221
+ ),
245
222
  ColumnLocator::NewExpression => {
246
- if let Err(err) = ctx.props().save_expr(new_expr) {
223
+ if let Err(err) = save_expr(
224
+ &ctx.props().session,
225
+ &ctx.props().renderer,
226
+ &ctx.props().presentation,
227
+ new_expr,
228
+ ) {
247
229
  tracing::warn!("{}", err);
248
230
  }
249
231
  },
@@ -258,7 +240,12 @@ impl Component for ColumnSettingsPanel {
258
240
  },
259
241
  ColumnSettingsPanelMsg::OnDelete(()) => {
260
242
  if ctx.props().selected_column.is_saved_expr() {
261
- ctx.props().delete_expr(&self.column_name).unwrap_or_log();
243
+ delete_expr(
244
+ &ctx.props().session,
245
+ &ctx.props().renderer,
246
+ &self.column_name,
247
+ )
248
+ .unwrap_or_log();
262
249
  }
263
250
 
264
251
  ctx.props().on_close.emit(());
@@ -277,6 +264,7 @@ impl Component for ColumnSettingsPanel {
277
264
  ctx.props().selected_tab,
278
265
  Some(ColumnSettingsTab::Attributes)
279
266
  ),
267
+ update_on_input: true,
280
268
  icon_type: self
281
269
  .maybe_ty
282
270
  .map(|ty| ty.into())
@@ -347,8 +335,8 @@ impl Component for ColumnSettingsPanel {
347
335
  group_by_depth: ctx.props().view_config.group_by.len() as u32,
348
336
  view_config: ctx.props().view_config.clone(),
349
337
  metadata: ctx.props().metadata.clone(),
338
+ column_stats: ctx.props().column_stats.clone(),
350
339
  selected_theme: ctx.props().selected_theme.clone(),
351
- custom_events: ctx.props().custom_events.clone(),
352
340
  presentation: ctx.props().presentation.clone(),
353
341
  renderer: ctx.props().renderer.clone(),
354
342
  session: ctx.props().session.clone(),
@@ -398,8 +386,10 @@ impl ColumnSettingsPanel {
398
386
  }
399
387
 
400
388
  fn initialize(&mut self, ctx: &yew::prelude::Context<Self>) {
401
- let column_name =
402
- locator_name_or_default(&ctx.props().metadata, &ctx.props().selected_column);
389
+ let column_name = ctx
390
+ .props()
391
+ .metadata
392
+ .locator_name_or_default(&ctx.props().selected_column);
403
393
 
404
394
  let initial_expr_value = ctx
405
395
  .props()
@@ -411,19 +401,23 @@ impl ColumnSettingsPanel {
411
401
  let initial_header_value =
412
402
  (*initial_expr_value != column_name).then_some(column_name.clone());
413
403
 
414
- let maybe_ty = locator_view_type(&ctx.props().metadata, &ctx.props().selected_column);
404
+ let maybe_ty = ctx
405
+ .props()
406
+ .metadata
407
+ .locator_view_type(&ctx.props().selected_column);
415
408
 
416
409
  let tabs = {
417
410
  let mut tabs = vec![];
418
411
  let is_new_expr = ctx.props().selected_column.is_new_expr();
419
412
  let show_styles = !is_new_expr
420
- && can_render_column_styles(
421
- &ctx.props().renderer,
422
- &ctx.props().view_config,
423
- &ctx.props().metadata,
424
- &column_name,
425
- )
426
- .unwrap_or_default();
413
+ && ctx.props().renderer.can_render_column_styles()
414
+ && ctx.props().view_config.columns.contains(&Some(
415
+ ctx.props()
416
+ .selected_column
417
+ .name()
418
+ .map(|x| x.to_string())
419
+ .unwrap_or_default(),
420
+ ));
427
421
 
428
422
  if !is_new_expr && show_styles {
429
423
  tabs.push(ColumnSettingsTab::Style);
@@ -432,6 +426,7 @@ impl ColumnSettingsPanel {
432
426
  if ctx.props().selected_column.is_expr() {
433
427
  tabs.push(ColumnSettingsTab::Attributes);
434
428
  }
429
+
435
430
  tabs
436
431
  };
437
432
 
@@ -22,7 +22,7 @@ use yew::prelude::*;
22
22
  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
- use crate::dragdrop::*;
25
+ use crate::presentation::{DragDropContainer, Presentation};
26
26
  use crate::utils::DragTarget;
27
27
 
28
28
  #[derive(Properties, Derivative)]
@@ -35,7 +35,7 @@ where
35
35
  {
36
36
  pub parent: Scope<T>,
37
37
 
38
- pub dragdrop: DragDrop,
38
+ pub presentation: Presentation,
39
39
  pub name: &'static str,
40
40
  pub column_dropdown: ColumnDropDownElement,
41
41
  pub exclude: HashSet<String>,
@@ -185,14 +185,20 @@ where
185
185
  );
186
186
 
187
187
  let drop = Callback::from({
188
- let dragdrop = ctx.props().dragdrop.clone();
188
+ let presentation = ctx.props().presentation.clone();
189
189
  let link = ctx.link().clone();
190
190
  move |event| {
191
191
  link.send_message(DragDropListMsg::Freeze(false));
192
- dragdrop.notify_drop(&event);
192
+ presentation.notify_drop(&event);
193
193
  }
194
194
  });
195
195
 
196
+ // Held by per-row `ondragenter` closures below so they can re-arm
197
+ // the `safaridragleave` flag on the container element when the
198
+ // row stops dragenter from bubbling. See the comment inside the
199
+ // closure for why this matters.
200
+ let container_noderef = drag_container.noderef.clone();
201
+
196
202
  let invalid_drag: bool;
197
203
  let mut valid_duplicate_drag = false;
198
204
 
@@ -210,7 +216,7 @@ where
210
216
  let is_append = index == columns.len();
211
217
  let is_self_move = ctx
212
218
  .props()
213
- .dragdrop
219
+ .presentation
214
220
  .get_drag_target()
215
221
  .map(|x| V::is_self_move(x))
216
222
  .unwrap_or_default();
@@ -253,9 +259,30 @@ where
253
259
  let close = ctx.props().parent.callback(move |_| V::close(idx));
254
260
  let dragenter = ctx.props().parent.callback({
255
261
  let link = ctx.link().clone();
262
+ let container_noderef = container_noderef.clone();
256
263
  move |event: DragEvent| {
257
264
  event.stop_propagation();
258
265
  event.prevent_default();
266
+ // Safari: `relatedTarget` is always null on
267
+ // dragleave, so `dragleave_helper` uses a
268
+ // `data-safaridragleave` flag set by the
269
+ // container's own dragenter to distinguish
270
+ // child-crossing leaves (consume the flag)
271
+ // from real leaves (no flag → fire callback).
272
+ // The `stop_propagation` above blocks the
273
+ // container's dragenter, so the flag would
274
+ // never be re-armed after the first consume —
275
+ // any further internal boundary crossing
276
+ // would demote the state out of
277
+ // `DragOverInProgress` and the next drop
278
+ // would be silently rejected. Set the flag
279
+ // here so each row-targeted dragenter
280
+ // refills the pool.
281
+ if event.related_target().is_none()
282
+ && let Some(elem) = container_noderef.cast::<HtmlElement>()
283
+ {
284
+ let _ = elem.dataset().set("safaridragleave", "true");
285
+ }
259
286
  link.send_message(DragDropListMsg::Freeze(true));
260
287
  V::dragenter(idx)
261
288
  }
@@ -22,4 +22,3 @@ pub mod sidebar;
22
22
  pub mod sidebar_close_button;
23
23
  pub mod split_panel;
24
24
  pub mod tab_list;
25
- pub mod trap_door_panel;
@@ -63,6 +63,9 @@ pub struct ScrollPanelProps {
63
63
 
64
64
  #[prop_or_default]
65
65
  pub drop: Callback<DragEvent>,
66
+
67
+ #[prop_or_default]
68
+ pub omit_autosize_div: bool,
66
69
  }
67
70
 
68
71
  impl ScrollPanelProps {
@@ -146,16 +149,25 @@ impl Component for ScrollPanel {
146
149
  self.viewport_width = 0.0;
147
150
  self.calculate_window_content(ctx)
148
151
  },
152
+
153
+ // TODO don't hardcode pixel offsets - fix the CSS container to not
154
+ // require pixel offsets to work.
149
155
  ScrollPanelMsg::UpdateViewportDimensions => {
150
156
  let viewport = self.viewport_elem(ctx);
151
157
  let rect = viewport.get_bounding_client_rect();
152
158
  let viewport_height = rect.height() - 8.0;
153
- let viewport_width = self.viewport_width.max(rect.width() - 6.0);
159
+ let scroll_offset = if ctx.props().omit_autosize_div {
160
+ 0.0
161
+ } else {
162
+ 6.0
163
+ };
164
+
165
+ let viewport_width = self.viewport_width.max(rect.width() - scroll_offset);
154
166
  let re_render = self.viewport_height != viewport_height
155
167
  || self.viewport_width != viewport_width;
156
168
 
157
169
  self.viewport_height = rect.height() - 8.0;
158
- self.viewport_width = self.viewport_width.max(rect.width() - 6.0);
170
+ self.viewport_width = self.viewport_width.max(rect.width() - scroll_offset);
159
171
  ctx.props().on_auto_width.emit(self.viewport_width);
160
172
  re_render
161
173
  },
@@ -218,11 +230,13 @@ impl Component for ScrollPanel {
218
230
  >
219
231
  <div class="scroll-panel-container" style={window_style}>
220
232
  { for windowed_items.iter().cloned().map(Html::from) }
221
- <div
222
- key="__scroll-panel-auto-width__"
223
- class="scroll-panel-auto-width"
224
- style={width_style}
225
- />
233
+ if !ctx.props().omit_autosize_div {
234
+ <div
235
+ key="__scroll-panel-auto-width__"
236
+ class="scroll-panel-auto-width"
237
+ style={width_style}
238
+ />
239
+ }
226
240
  </div>
227
241
  <div class="scroll-panel-content" style={content_style} />
228
242
  </div>
@@ -62,12 +62,14 @@ pub fn Sidebar(p: &SidebarProps) -> Html {
62
62
 
63
63
  let width_style = format!("min-width: 200px; width: {}px", *auto_width);
64
64
  html! {
65
- <div class="sidebar_column" id={format!("{id}_sidebar")} ref={noderef}>
65
+ <>
66
66
  <SidebarCloseButton id={format!("{id}_close_button")} on_close_sidebar={&p.on_close} />
67
- <div class="sidebar_header"><EditableHeader ..p.header_props.clone() /></div>
68
- <div class="sidebar_border" id={format!("{id}_border")} />
69
- { p.children.iter().collect::<Html>() }
70
- <div class="sidebar-auto-width" style={width_style} />
71
- </div>
67
+ <div class="sidebar_column" id={format!("{id}_sidebar")} ref={noderef}>
68
+ <div class="sidebar_header"><EditableHeader ..p.header_props.clone() /></div>
69
+ <div class="sidebar_border" id={format!("{id}_border")} />
70
+ { p.children.iter().collect::<Html>() }
71
+ <div class="sidebar-auto-width" style={width_style} />
72
+ </div>
73
+ </>
72
74
  }
73
75
  }
@@ -20,7 +20,7 @@ use yew::html::Scope;
20
20
  use yew::prelude::*;
21
21
 
22
22
  use crate::components::style::LocalStyle;
23
- use crate::{css, maybe};
23
+ use crate::css;
24
24
 
25
25
  #[derive(Properties, Default)]
26
26
  pub struct SplitPanelProps {
@@ -359,14 +359,14 @@ impl Drop for ResizingState {
359
359
  /// `body`. Without this, the `Closure` objects would not leak, but the
360
360
  /// document will continue to call them, causing runtime exceptions.
361
361
  fn drop(&mut self) {
362
- let result: ApiResult<()> = maybe! {
362
+ let result: ApiResult<()> = (|| {
363
363
  let mousemove = self.mousemove.as_ref().unchecked_ref();
364
364
  global::body().remove_event_listener_with_callback("mousemove", mousemove)?;
365
365
  let mouseup = self.mouseup.as_ref().unchecked_ref();
366
366
  global::body().remove_event_listener_with_callback("mouseup", mouseup)?;
367
367
  self.release_cursor()?;
368
368
  Ok(())
369
- };
369
+ })();
370
370
 
371
371
  result.expect("Drop failed")
372
372
  }
@@ -71,9 +71,9 @@ impl<T: ColumnTab> Component for TabList<T> {
71
71
  fn view(&self, ctx: &yew::Context<Self>) -> Html {
72
72
  let p = ctx.props();
73
73
  let gutter_tabs = p.tabs.iter().enumerate().map(|(idx, tab)| {
74
- let mut class = classes!("tab");
74
+ let mut class = classes!("settings_tab");
75
75
  if idx == self.selected_idx {
76
- class.push("selected");
76
+ class.push("selected_tab");
77
77
  }
78
78
 
79
79
  let onclick = ctx.link().callback(move |_| TabListMsg::SetSelected(idx));
@@ -88,13 +88,7 @@ impl<T: ColumnTab> Component for TabList<T> {
88
88
  html! {
89
89
  <>
90
90
  <LocalStyle href={css!("containers/tabs")} />
91
- <div class="tab-gutter">
92
- { for gutter_tabs }
93
- <span class="tab tab-padding">
94
- <div class="tab-title">{ "\u{00a0}" }</div>
95
- <div class="tab-border" />
96
- </span>
97
- </div>
91
+ <div id="settings_tab_bar">{ for gutter_tabs }</div>
98
92
  <div id="format-tab" class="tab-content scrollable">
99
93
  { ctx.props().children.iter().nth(self.selected_idx) }
100
94
  </div>
@@ -15,8 +15,8 @@ use std::rc::Rc;
15
15
  use yew::prelude::*;
16
16
 
17
17
  use super::containers::dropdown_menu::*;
18
+ use crate::config::*;
18
19
  use crate::renderer::*;
19
- use crate::tasks::*;
20
20
 
21
21
  type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
22
22
 
@@ -41,8 +41,7 @@ impl Component for CopyDropDownMenu {
41
41
  }
42
42
 
43
43
  fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
44
- let plugin = ctx.props().renderer.get_active_plugin().unwrap();
45
- let is_chart = plugin.name().as_str() != "Datagrid";
44
+ let is_chart = ctx.props().renderer.is_chart();
46
45
  let has_selection = ctx.props().renderer.get_selection().is_some();
47
46
  html! {
48
47
  <>
@@ -21,28 +21,30 @@ use perspective_js::json;
21
21
  use perspective_js::utils::global::navigator;
22
22
  use wasm_bindgen::prelude::*;
23
23
  use yew::prelude::*;
24
- use yew::*;
25
24
 
26
- use super::form::color_selector::*;
27
25
  use super::modal::{ModalLink, SetModalLink};
28
26
  use super::style::LocalStyle;
29
27
  use crate::components::datetime_column_style::custom::DatetimeStyleCustom;
30
28
  use crate::components::datetime_column_style::simple::DatetimeStyleSimple;
31
- use crate::components::form::select_enum_field::SelectEnumField;
32
29
  use crate::components::form::select_value_field::SelectValueField;
33
30
  use crate::config::*;
34
31
  use crate::css;
35
32
  use crate::utils::WeakScope;
36
33
 
34
+ /// Format-only widget for `datetime` columns. Renders the `date_format`
35
+ /// hierarchy (Simple|Custom + timezone); color/color-mode UI is provided
36
+ /// externally as primitive `Enum` + `Color` schema fields.
37
37
  #[derive(Properties, Derivative)]
38
38
  #[derivative(Debug)]
39
39
  pub struct DatetimeColumnStyleProps {
40
40
  pub enable_time_config: bool,
41
41
  pub config: Option<DatetimeColumnStyleConfig>,
42
- pub default_config: DatetimeColumnStyleDefaultConfig,
43
42
 
44
43
  #[prop_or_default]
45
- pub on_change: Callback<ColumnConfigValueUpdate>,
44
+ pub on_change: Callback<ColumnConfigFieldUpdate>,
45
+
46
+ #[prop_or_default]
47
+ pub keys: Vec<String>,
46
48
 
47
49
  #[prop_or_default]
48
50
  #[derivative(Debug = "ignore")]
@@ -57,9 +59,7 @@ impl ModalLink<DatetimeColumnStyle> for DatetimeColumnStyleProps {
57
59
 
58
60
  impl PartialEq for DatetimeColumnStyleProps {
59
61
  fn eq(&self, other: &Self) -> bool {
60
- self.enable_time_config == other.enable_time_config
61
- && self.config == other.config
62
- && self.default_config == other.default_config
62
+ self.enable_time_config == other.enable_time_config && self.config == other.config
63
63
  }
64
64
  }
65
65
 
@@ -67,16 +67,11 @@ pub enum DatetimeColumnStyleMsg {
67
67
  SimpleDatetimeStyleConfigChanged(SimpleDatetimeStyleConfig),
68
68
  CustomDatetimeStyleConfigChanged(CustomDatetimeStyleConfig),
69
69
  TimezoneChanged(Option<String>),
70
- ColorModeChanged(Option<DatetimeColorMode>),
71
- ColorChanged(String),
72
- ColorReset,
73
70
  }
74
71
 
75
- /// Column style controls for the `datetime` type.
76
72
  #[derive(Debug)]
77
73
  pub struct DatetimeColumnStyle {
78
74
  config: DatetimeColumnStyleConfig,
79
- default_config: DatetimeColumnStyleDefaultConfig,
80
75
  }
81
76
 
82
77
  impl Component for DatetimeColumnStyle {
@@ -87,11 +82,9 @@ impl Component for DatetimeColumnStyle {
87
82
  ctx.set_modal_link();
88
83
  Self {
89
84
  config: ctx.props().config.clone().unwrap_or_default(),
90
- default_config: ctx.props().default_config.clone(),
91
85
  }
92
86
  }
93
87
 
94
- // Always re-render when config changes.
95
88
  fn changed(&mut self, ctx: &Context<Self>, old: &Self::Properties) -> bool {
96
89
  let mut rerender = false;
97
90
  let mut new_config = ctx.props().config.clone().unwrap_or_default();
@@ -105,7 +98,6 @@ impl Component for DatetimeColumnStyle {
105
98
  rerender
106
99
  }
107
100
 
108
- // TODO could be more conservative here with re-rendering
109
101
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
110
102
  match msg {
111
103
  DatetimeColumnStyleMsg::TimezoneChanged(val) => {
@@ -118,17 +110,6 @@ impl Component for DatetimeColumnStyle {
118
110
  self.dispatch_config(ctx);
119
111
  true
120
112
  },
121
- DatetimeColumnStyleMsg::ColorModeChanged(mode) => {
122
- self.config.datetime_color_mode = mode.unwrap_or_default();
123
- self.dispatch_config(ctx);
124
- true
125
- },
126
- DatetimeColumnStyleMsg::ColorChanged(color) => {
127
- self.config.color = Some(color);
128
- self.dispatch_config(ctx);
129
- true
130
- },
131
-
132
113
  DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged(simple) => {
133
114
  self.config.date_format = DatetimeFormatType::Simple(simple);
134
115
  self.dispatch_config(ctx);
@@ -139,40 +120,14 @@ impl Component for DatetimeColumnStyle {
139
120
  self.dispatch_config(ctx);
140
121
  true
141
122
  },
142
- DatetimeColumnStyleMsg::ColorReset => {
143
- self.config.color = Some(self.default_config.color.clone());
144
- self.dispatch_config(ctx);
145
- true
146
- },
147
123
  }
148
124
  }
149
125
 
150
126
  fn view(&self, ctx: &Context<Self>) -> Html {
151
- let selected_color_mode = self.config.datetime_color_mode;
152
- let color_mode_changed = ctx
153
- .link()
154
- .callback(DatetimeColumnStyleMsg::ColorModeChanged);
155
-
156
- let color_controls = match selected_color_mode {
157
- DatetimeColorMode::None => html! {},
158
- DatetimeColorMode::Foreground => {
159
- self.color_select_row(ctx, &DatetimeColorMode::Foreground, "foreground-label")
160
- },
161
- DatetimeColorMode::Background => {
162
- self.color_select_row(ctx, &DatetimeColorMode::Background, "background-label")
163
- },
164
- };
165
-
166
127
  html! {
167
128
  <>
168
129
  <LocalStyle href={css!("column-style")} />
169
130
  <div id="column-style-container" class="datetime-column-style-container">
170
- <SelectEnumField<DatetimeColorMode>
171
- label="color"
172
- on_change={color_mode_changed}
173
- current_value={selected_color_mode}
174
- />
175
- { color_controls }
176
131
  if ctx.props().enable_time_config {
177
132
  <SelectValueField<String>
178
133
  label="timezone"
@@ -251,35 +206,18 @@ static USER_TIMEZONE: LazyLock<String> = LazyLock::new(|| {
251
206
  impl DatetimeColumnStyle {
252
207
  /// When this config has changed, we must signal the wrapper element.
253
208
  fn dispatch_config(&self, ctx: &Context<Self>) {
254
- let update =
255
- Some(self.config.clone()).filter(|x| x != &DatetimeColumnStyleConfig::default());
256
- ctx.props()
257
- .on_change
258
- .emit(ColumnConfigValueUpdate::DatagridDatetimeStyle(update));
259
- }
260
-
261
- /// Generate a color selector component for a specific `StringColorMode`
262
- /// variant.
263
- fn color_select_row(&self, ctx: &Context<Self>, mode: &DatetimeColorMode, title: &str) -> Html {
264
- let on_color = ctx.link().callback(DatetimeColumnStyleMsg::ColorChanged);
265
- let color = self
266
- .config
267
- .color
268
- .clone()
269
- .unwrap_or_else(|| self.default_config.color.to_owned());
209
+ let value = if self.config == DatetimeColumnStyleConfig::default() {
210
+ serde_json::Map::new()
211
+ } else {
212
+ match serde_json::to_value(&self.config) {
213
+ Ok(serde_json::Value::Object(m)) => m,
214
+ _ => serde_json::Map::new(),
215
+ }
216
+ };
270
217
 
271
- let color_props = props!(ColorProps {
272
- title: title.to_owned(),
273
- on_color,
274
- is_modified: color != self.default_config.color,
275
- color,
276
- on_reset: ctx.link().callback(|_| DatetimeColumnStyleMsg::ColorReset)
218
+ ctx.props().on_change.emit(ColumnConfigFieldUpdate {
219
+ keys: ctx.props().keys.clone(),
220
+ value,
277
221
  });
278
-
279
- if &self.config.datetime_color_mode == mode {
280
- html! { <div class="row"><ColorSelector ..color_props /></div> }
281
- } else {
282
- html! {}
283
- }
284
222
  }
285
223
  }
@@ -18,7 +18,6 @@ use yew::{Callback, Component, Html, NodeRef, Properties, TargetCast, classes, h
18
18
 
19
19
  use super::type_icon::TypeIconType;
20
20
  use crate::components::type_icon::TypeIcon;
21
- use crate::maybe;
22
21
  use crate::session::{Session, SessionMetadataRc};
23
22
 
24
23
  #[derive(Clone, PartialEq, Properties)]
@@ -29,9 +28,13 @@ pub struct EditableHeaderProps {
29
28
  pub initial_value: Option<String>,
30
29
  pub placeholder: Rc<String>,
31
30
 
31
+ // TODO remove this pattern
32
32
  #[prop_or_default]
33
33
  pub reset_count: u8,
34
34
 
35
+ #[prop_or_default]
36
+ pub update_on_input: bool,
37
+
35
38
  /// Session metadata snapshot — threaded from `SessionProps`.
36
39
  pub metadata: SessionMetadataRc,
37
40
 
@@ -103,7 +106,7 @@ impl Component for EditableHeader {
103
106
  let maybe_value = (!new_value.is_empty()).then_some(new_value.clone());
104
107
  self.edited = ctx.props().initial_value != maybe_value;
105
108
 
106
- self.valid = maybe!({
109
+ self.valid = (|| -> Option<bool> {
107
110
  if maybe_value
108
111
  .as_ref()
109
112
  .map(|v| v == &self.placeholder)
@@ -122,7 +125,7 @@ impl Component for EditableHeader {
122
125
  .chain(expressions)
123
126
  .contains(&new_value);
124
127
  Some(!found)
125
- })
128
+ })()
126
129
  .unwrap_or(true);
127
130
 
128
131
  self.value.clone_from(&maybe_value);
@@ -164,6 +167,16 @@ impl Component for EditableHeader {
164
167
  EditableHeaderMsg::SetNewValue(value)
165
168
  });
166
169
 
170
+ let update_on_input = ctx.props().update_on_input;
171
+ let oninput = ctx.link().batch_callback(move |e: yew::InputEvent| {
172
+ if update_on_input {
173
+ let value = e.target_unchecked_into::<HtmlInputElement>().value();
174
+ vec![EditableHeaderMsg::SetNewValue(value)]
175
+ } else {
176
+ vec![]
177
+ }
178
+ });
179
+
167
180
  html! {
168
181
  <div class={classes} onclick={ctx.link().callback(|_| EditableHeaderMsg::OnClick(()))}>
169
182
  if let Some(icon) = ctx.props().icon_type { <TypeIcon ty={icon} /> }
@@ -174,6 +187,7 @@ impl Component for EditableHeader {
174
187
  disabled={!ctx.props().editable}
175
188
  {onblur}
176
189
  {onkeyup}
190
+ {oninput}
177
191
  value={self.value.clone()}
178
192
  placeholder={self.placeholder.clone()}
179
193
  />