@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
@@ -10,75 +10,94 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ //! Apply a [`ViewConfigUpdate`] to the active [`Session`], validate it, then
14
+ //! draw with the active [`Renderer`]. The companion `*_callback` helpers
15
+ //! return [`yew::Callback`]s suitable for wiring as Yew child-component
16
+ //! props.
17
+
13
18
  use perspective_client::config::ViewConfigUpdate;
14
19
  use yew::prelude::*;
15
20
 
16
- use super::structural::*;
17
21
  use crate::renderer::Renderer;
18
22
  use crate::session::Session;
19
23
  use crate::utils::*;
20
24
  use crate::*;
21
25
 
22
- /// A model trait for updating both `View` state and completing a render.
23
- ///
24
- /// While `Renderer` manages the plugin and thus the render call itself, the
25
- /// current `View` is handled by the `Session` which must be validated and
26
- /// locked while drawing is in progress. `UpdateAndRender` provides methods
27
- /// that synchronize this behavior, so these methods should be used to initiate
28
- /// rendering of the current `Plugin` and `View`.
29
- pub trait UpdateAndRender: HasRenderer + HasSession {
30
- /// Create a `Callback` that renders from the current `View` and `Plugin`.
31
- fn render_callback(&self) -> Callback<()> {
32
- clone!(self.session(), self.renderer());
33
- Callback::from(move |_| {
34
- clone!(session, renderer);
35
- ApiFuture::spawn(async move {
36
- renderer.draw(async { Ok(session.get_view()) }).await?;
37
- Ok(())
38
- })
26
+ /// Create a [`Callback`] that renders from the current `View` and `Plugin`.
27
+ pub fn render_callback(session: &Session, renderer: &Renderer) -> Callback<()> {
28
+ clone!(session, renderer);
29
+ Callback::from(move |_| {
30
+ clone!(session, renderer);
31
+ ApiFuture::spawn(async move {
32
+ renderer.draw(async { Ok(session.get_view()) }).await?;
33
+ Ok(())
39
34
  })
40
- }
35
+ })
36
+ }
41
37
 
42
- /// Create a `Callback` that resizes from the current `View` and `Plugin`.
43
- fn resize_callback(&self) -> Callback<()> {
44
- clone!(self.renderer(), self.session());
45
- Callback::from(move |_| {
46
- clone!(renderer, session);
47
- ApiFuture::spawn(async move {
48
- if !renderer.is_plugin_activated()? {
49
- update_and_render(session, renderer).await?
50
- } else {
51
- renderer.resize().await?;
52
- }
38
+ /// Create a [`Callback`] that resizes from the current `View` and `Plugin`.
39
+ pub fn resize_callback(session: &Session, renderer: &Renderer) -> Callback<()> {
40
+ clone!(session, renderer);
41
+ Callback::from(move |_| {
42
+ clone!(renderer, session);
43
+ ApiFuture::spawn(async move {
44
+ if !renderer.is_plugin_activated()? {
45
+ update_and_render_inner(session, renderer).await?
46
+ } else {
47
+ renderer.resize().await?;
48
+ }
53
49
 
54
- Ok(())
55
- })
50
+ Ok(())
56
51
  })
57
- }
52
+ })
53
+ }
58
54
 
59
- /// Apply a `ViewConfigUpdate` to the current `View` and render.
60
- fn update_and_render(&self, update: ViewConfigUpdate) -> ApiResult<ApiFuture<()>> {
61
- self.session().update_view_config(update)?;
62
- clone!(self.session(), self.renderer());
63
- Ok(ApiFuture::new(update_and_render(session, renderer)))
64
- }
55
+ /// Apply a `ViewConfigUpdate` to the current `View` and render.
56
+ pub fn update_and_render(
57
+ session: &Session,
58
+ renderer: &Renderer,
59
+ update: ViewConfigUpdate,
60
+ ) -> ApiResult<ApiFuture<()>> {
61
+ session.update_view_config(update)?;
62
+ clone!(session, renderer);
63
+ Ok(ApiFuture::new(update_and_render_inner(session, renderer)))
64
+ }
65
65
 
66
- fn just_render(&self) -> ApiResult<ApiFuture<()>> {
67
- clone!(self.session(), self.renderer());
68
- Ok(ApiFuture::new(update_and_render(session, renderer)))
69
- }
66
+ /// Re-render the current `View` and `Plugin` without applying a new
67
+ /// `ViewConfigUpdate`.
68
+ pub fn just_render(session: &Session, renderer: &Renderer) -> ApiResult<ApiFuture<()>> {
69
+ clone!(session, renderer);
70
+ Ok(ApiFuture::new(update_and_render_inner(session, renderer)))
70
71
  }
71
72
 
72
73
  #[tracing::instrument(level = "debug", skip(session, renderer))]
73
- async fn update_and_render(session: Session, renderer: Renderer) -> ApiResult<()> {
74
+ async fn update_and_render_inner(session: Session, renderer: Renderer) -> ApiResult<()> {
74
75
  // The previous call which acquired the lock errored, so skip this render
75
76
  if session.get_error().is_some() {
76
77
  return Ok(());
77
78
  }
78
79
 
79
- renderer.apply_pending_plugin()?;
80
+ let plugin_swapped = renderer.apply_pending_plugin()?;
81
+ if plugin_swapped {
82
+ // `commit_plugin_idx` already restored the new plugin from its
83
+ // raw bucket; re-run with the materialized snapshot so any
84
+ // `include: true` schema defaults (e.g. Datagrid's
85
+ // `fg_gradient` when `number_fg_mode = "bar"`) make it into
86
+ // the plugin's state before the first draw. The Session is in
87
+ // scope here but not at `commit_plugin_idx`'s call sites in
88
+ // the column-selector tree, so we do the second restore at
89
+ // the caller instead of plumbing `&Session` through every
90
+ // `apply_pending_plugin` site.
91
+ let view_config_snapshot = session.get_view_config().clone();
92
+ let plugin_token = wasm_bindgen::JsValue::from_serde_ext(&renderer.get_plugin_config())
93
+ .unwrap_or(wasm_bindgen::JsValue::NULL);
94
+ let columns_config =
95
+ renderer.all_columns_configs_materialized(&view_config_snapshot, &session);
96
+ renderer
97
+ .get_active_plugin()?
98
+ .restore(&plugin_token, Some(&columns_config))?;
99
+ }
100
+
80
101
  let view = session.validate().await?;
81
102
  renderer.draw(view.create_view()).await
82
103
  }
83
-
84
- impl<T: HasRenderer + HasSession> UpdateAndRender for T {}
@@ -10,41 +10,42 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use yew::*;
13
+ //! Theme reset / set task. Replaces the inlined StatusBar callback that
14
+ //! reached into `Session::get_view()` for the post-update restyle.
14
15
 
15
- #[derive(Properties)]
16
- pub struct TrapDoorPanelProps {
17
- pub id: Option<&'static str>,
18
- pub class: Option<&'static str>,
19
- pub children: Children,
20
- }
16
+ use perspective_js::utils::*;
21
17
 
22
- impl PartialEq for TrapDoorPanelProps {
23
- fn eq(&self, _other: &Self) -> bool {
24
- false
25
- }
26
- }
18
+ use crate::presentation::Presentation;
19
+ use crate::renderer::Renderer;
20
+ use crate::session::Session;
27
21
 
28
- /// A simple panel with an invisible inner `<div>` which stretches to fit the
29
- /// width of the container, but will not shrink (unless the state is reset).
30
- #[function_component(TrapDoorPanel)]
31
- pub fn trap_door_panel(props: &TrapDoorPanelProps) -> Html {
32
- let sizer = use_node_ref();
33
- let width = use_state_eq(|| 0.0);
34
- use_effect_with((width.setter(), sizer.clone()), |(width, sizer)| {
35
- width.set(
36
- sizer
37
- .cast::<web_sys::HtmlElement>()
38
- .unwrap()
39
- .get_bounding_client_rect()
40
- .width(),
41
- )
42
- });
22
+ /// Apply a theme change and restyle the active view.
23
+ ///
24
+ /// `theme = None` resets to the first available theme; `theme = Some(name)`
25
+ /// sets the named theme. In both cases the post-update path is the same:
26
+ /// fetch the current view from the session and ask the renderer to
27
+ /// re-style it. Components dispatch this task instead of reading
28
+ /// `Session::get_view()` themselves.
29
+ pub fn update_theme(
30
+ session: &Session,
31
+ renderer: &Renderer,
32
+ presentation: &Presentation,
33
+ theme: Option<String>,
34
+ ) {
35
+ let session = session.clone();
36
+ let renderer = renderer.clone();
37
+ let presentation = presentation.clone();
38
+ ApiFuture::spawn(async move {
39
+ match theme {
40
+ Some(name) => {
41
+ presentation.set_theme_name(Some(&name)).await?;
42
+ },
43
+ None => {
44
+ presentation.reset_theme().await?;
45
+ },
46
+ }
43
47
 
44
- html! {
45
- <div id={props.id} class={props.class} ref={sizer}>
46
- { props.children.clone() }
47
- <div class="scroll-panel-auto-width" style={format!("width:{}px", *width)} />
48
- </div>
49
- }
48
+ let view = session.get_view().into_apierror()?;
49
+ renderer.restyle_all(&view).await
50
+ });
50
51
  }
@@ -0,0 +1,61 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ //! Per-keystroke expression validation with stale-result rejection.
14
+ //!
15
+ //! The component owns a monotonically increasing `req_id` and increments
16
+ //! it on each keystroke. The dispatched task echoes the `req_id` back in
17
+ //! its result payload so the component can drop validations that
18
+ //! resolved out-of-order.
19
+
20
+ use perspective_client::ExprValidationError;
21
+ use perspective_js::utils::*;
22
+ use yew::prelude::*;
23
+
24
+ use crate::queries::validate_expr;
25
+ use crate::session::Session;
26
+
27
+ /// Result payload from a dispatched validation task.
28
+ #[derive(Debug, Clone)]
29
+ pub struct ExprValidation {
30
+ /// Request id echoed from the dispatch site so the caller can drop
31
+ /// stale results from races between rapid keystrokes.
32
+ pub req_id: u64,
33
+ /// `None` if the expression validated cleanly; otherwise the
34
+ /// per-expression error returned by the engine.
35
+ pub error: Option<ExprValidationError>,
36
+ }
37
+
38
+ /// Validate `expr` against the active table; dispatch the result back
39
+ /// through `cb` with the given `req_id`. Errors from the engine are
40
+ /// logged to the console and surfaced as `error: None` (i.e. the
41
+ /// expression is treated as valid, matching the prior in-line behavior
42
+ /// from `expression_editor.rs::SetExpr`).
43
+ pub fn validate_expression(
44
+ session: &Session,
45
+ cb: Callback<ExprValidation>,
46
+ req_id: u64,
47
+ expr: String,
48
+ ) {
49
+ let session = session.clone();
50
+ ApiFuture::spawn(async move {
51
+ let error = match validate_expr(&session, &expr).await {
52
+ Ok(x) => x,
53
+ Err(err) => {
54
+ web_sys::console::error_1(&format!("{err:?}").into());
55
+ None
56
+ },
57
+ };
58
+ cb.emit(ExprValidation { req_id, error });
59
+ Ok::<_, ApiError>(())
60
+ });
61
+ }
@@ -51,16 +51,16 @@ impl CaretPosition for web_sys::HtmlElement {
51
51
  }
52
52
 
53
53
  fn get_caret_position(&self) -> Option<u32> {
54
- maybe! {
54
+ (|| -> ApiResult<u32> {
55
55
  let root = self.get_root_node().unchecked_into::<web_sys::Document>();
56
56
  let selection = root.get_selection()?.into_apierror()?;
57
57
  if selection.range_count() > 0 {
58
58
  let range = selection.get_range_at(0)?;
59
- range.end_offset()
59
+ Ok(range.end_offset()?)
60
60
  } else {
61
- Err(wasm_bindgen::JsValue::UNDEFINED)
61
+ Err(wasm_bindgen::JsValue::UNDEFINED.into())
62
62
  }
63
- }
63
+ })()
64
64
  .ok()
65
65
  }
66
66
  }
@@ -41,66 +41,3 @@ pub use perspective_client::clone;
41
41
  pub use ptr_eq_rc::*;
42
42
  pub use pubsub::*;
43
43
  pub use weak_scope::*;
44
-
45
- /// An implementaiton of `try_blocks` feature in a non-nightly macro.
46
- #[macro_export]
47
- macro_rules! maybe {
48
- ($($exp:stmt);*) => {{
49
- let x = ({
50
- #[inline(always)]
51
- || {
52
- $($exp)*
53
- }
54
- })();
55
- x
56
- }};
57
- }
58
-
59
- /// As `maybe!`, but returns `()` and just logs errors.
60
- #[macro_export]
61
- macro_rules! maybe_log {
62
- ($($exp:tt)+) => {{
63
- let x = ({
64
- #[inline(always)]
65
- || {
66
- {
67
- $($exp)+
68
- };
69
- Ok(())
70
- }
71
- })();
72
- x.unwrap_or_else(|e| web_sys::console::warn_1(&e))
73
- }};
74
- }
75
-
76
- #[macro_export]
77
- macro_rules! maybe_log_or_default {
78
- ($($exp:tt)+) => {{
79
- let x = ({
80
- #[inline(always)]
81
- || {
82
- $($exp)+
83
- }
84
- })();
85
- x.unwrap_or_else(|e| {
86
- web_sys::console::warn_1(&e);
87
- Default::default()
88
- })
89
- }};
90
- }
91
-
92
- #[macro_export]
93
- macro_rules! maybe_or_default {
94
- ($($exp:tt)+) => {{
95
- let x = ({
96
- #[inline(always)]
97
- || {
98
- $($exp)+
99
- }
100
- })();
101
- x.unwrap_or_else(|| {
102
- web_sys::console::warn_1("Unwrap on Noner");
103
- Default::default()
104
- })
105
- }};
106
- }
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
3
+ viewBox="0 0 80 70" style="enable-background:new 0 0 80 70;" xml:space="preserve" width="40" height="35">
4
+ <style type="text/css">
5
+ .st0{fill:#042121;}
6
+ </style>
7
+ <g id="Layer_1">
8
+ <g>
9
+ <path class="st0" d="M11,58c-0.6,0-1-0.4-1-1V10H8v47c0,1.7,1.3,3,3,3h61v-2H11z"/>
10
+ <circle class="st0" cx="28" cy="38" r="2.5"/>
11
+ <circle class="st0" cx="28" cy="38" r="6" fill="none" stroke="#042121" stroke-width="0.9" opacity="0.7"/>
12
+ <circle class="st0" cx="28" cy="38" r="10" fill="none" stroke="#042121" stroke-width="0.6" opacity="0.4"/>
13
+ <circle class="st0" cx="28" cy="38" r="14" fill="none" stroke="#042121" stroke-width="0.45" opacity="0.22"/>
14
+ <circle class="st0" cx="52" cy="30" r="2.5"/>
15
+ <circle class="st0" cx="52" cy="30" r="6" fill="none" stroke="#042121" stroke-width="0.9" opacity="0.7"/>
16
+ <circle class="st0" cx="52" cy="30" r="10" fill="none" stroke="#042121" stroke-width="0.6" opacity="0.4"/>
17
+ <circle class="st0" cx="52" cy="30" r="14" fill="none" stroke="#042121" stroke-width="0.45" opacity="0.22"/>
18
+ <circle class="st0" cx="62" cy="46" r="1.6"/>
19
+ <circle class="st0" cx="62" cy="46" r="4" fill="none" stroke="#042121" stroke-width="0.7" opacity="0.55"/>
20
+ <circle class="st0" cx="62" cy="46" r="7" fill="none" stroke="#042121" stroke-width="0.45" opacity="0.3"/>
21
+ </g>
22
+ </g>
23
+ </svg>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
3
+ viewBox="0 0 80 70" style="enable-background:new 0 0 80 70;" xml:space="preserve" width="40" height="35">
4
+ <style type="text/css">
5
+ .st0{fill:#042121;}
6
+ </style>
7
+ <g id="Layer_5">
8
+ <g>
9
+ <path class="st0" d="M71.5,57.9l-4.9-22c-0.2-0.9-1-1.6-2-1.6H50.4l3.5-6.8c2-3.8,1.8-8.5-0.4-12.2c-2.3-3.7-6.2-5.8-10.5-5.8
10
+ s-8.2,2.2-10.5,5.8c-2.3,3.7-2.4,8.3-0.4,12.2l3.5,6.8H15.4c-0.9,0-1.7,0.6-2,1.6l-4.9,22c-0.1,0.6,0,1.2,0.4,1.7
11
+ c0.4,0.5,1,0.8,1.6,0.8h59c0.6,0,1.2-0.3,1.6-0.7C71.4,59.2,71.6,58.5,71.5,57.9z M34.2,16.5c1.9-3.1,5.2-4.9,8.8-4.9
12
+ s6.9,1.8,8.8,4.9c1.9,3.1,2,7,0.4,10.2L43,44.2l-9.1-17.5C32.2,23.5,32.3,19.6,34.2,16.5z M10.5,58.4l4.9-22h21.2L43,48.5
13
+ l6.3-12.2l15.2,0l4.9,22H10.5z"/>
14
+ <path class="st0" d="M50,21.4c0-3.9-3.1-7-7-7s-7,3.1-7,7s3.1,7,7,7S50,25.2,50,21.4z M38,21.4c0-2.8,2.2-5,5-5s5,2.2,5,5
15
+ s-2.2,5-5,5S38,24.1,38,21.4z"/>
16
+ <circle class="st0" cx="22" cy="51" r="1.6"/>
17
+ <circle cx="22" cy="51" r="4" fill="none" stroke="#042121" stroke-width="0.7" opacity="0.55"/>
18
+ <circle cx="22" cy="51" r="6.5" fill="none" stroke="#042121" stroke-width="0.45" opacity="0.3"/>
19
+ <circle class="st0" cx="60" cy="49" r="1.6"/>
20
+ <circle cx="60" cy="49" r="4" fill="none" stroke="#042121" stroke-width="0.7" opacity="0.55"/>
21
+ <circle cx="60" cy="49" r="6.5" fill="none" stroke="#042121" stroke-width="0.45" opacity="0.3"/>
22
+ </g>
23
+ </g>
24
+ </svg>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
3
+ viewBox="0 0 80 70" style="enable-background:new 0 0 80 70;" xml:space="preserve" width="40" height="35">
4
+ <style type="text/css">
5
+ .st0{fill:#042121;}
6
+ </style>
7
+ <g id="Layer_5">
8
+ <g>
9
+ <path class="st0" d="M71.5,57.9l-4.9-22c-0.2-0.9-1-1.6-2-1.6H50.4l3.5-6.8c2-3.8,1.8-8.5-0.4-12.2c-2.3-3.7-6.2-5.8-10.5-5.8
10
+ s-8.2,2.2-10.5,5.8c-2.3,3.7-2.4,8.3-0.4,12.2l3.5,6.8H15.4c-0.9,0-1.7,0.6-2,1.6l-4.9,22c-0.1,0.6,0,1.2,0.4,1.7
11
+ c0.4,0.5,1,0.8,1.6,0.8h59c0.6,0,1.2-0.3,1.6-0.7C71.4,59.2,71.6,58.5,71.5,57.9z M34.2,16.5c1.9-3.1,5.2-4.9,8.8-4.9
12
+ s6.9,1.8,8.8,4.9c1.9,3.1,2,7,0.4,10.2L43,44.2l-9.1-17.5C32.2,23.5,32.3,19.6,34.2,16.5z M10.5,58.4l4.9-22h21.2L43,48.5
13
+ l6.3-12.2l15.2,0l4.9,22H10.5z"/>
14
+ <path class="st0" d="M50,21.4c0-3.9-3.1-7-7-7s-7,3.1-7,7s3.1,7,7,7S50,25.2,50,21.4z M38,21.4c0-2.8,2.2-5,5-5s5,2.2,5,5
15
+ s-2.2,5-5,5S38,24.1,38,21.4z"/>
16
+ <polyline points="13,53 20,48.5 27,51 33,46.5 53,47.5 60,52.5 68,49.5" fill="none" stroke="#042121" stroke-width="1.4" stroke-linejoin="round" stroke-linecap="round"/>
17
+ </g>
18
+ </g>
19
+ </svg>
@@ -55,64 +55,38 @@ perspective-viewer[theme="Botanical"] {
55
55
  --psp-datagrid--pos-cell--color: #7bc96f;
56
56
  --psp-datagrid--neg-cell--color: #ebac21;
57
57
 
58
- /* perspective-viewer-botanical--d3fc */
59
- --psp-d3fc--legend--color: #b8c9ad;
60
- --psp-d3fc--treemap--labels: #e0ead8;
61
- --psp-d3fc--treemap--hover-highlight: #e0ead8;
62
- --psp-d3fc--tooltip--color: #e0ead8;
63
- --psp-d3fc--axis-ticks--color: #b8c9ad;
64
- --psp-d3fc--axis-lines--color: #526b4a;
65
- --psp-d3fc--gridline--color: #2a4228;
66
- --psp-d3fc--tooltip--background: rgba(30, 52, 32, 1);
67
- --psp-d3fc--tooltip--border-color: #1a2e1a;
68
- --psp-d3fc--legend--background: var(--psp--background-color);
69
-
70
- --psp-d3fc--series--color: rgb(90, 158, 75);
71
- --psp-d3fc--series-1--color: rgb(90, 158, 75);
72
- --psp-d3fc--series-2--color: rgb(206, 176, 104);
73
- --psp-d3fc--series-3--color: rgb(160, 110, 180);
74
- --psp-d3fc--series-4--color: rgb(80, 170, 150);
75
- --psp-d3fc--series-5--color: rgb(140, 170, 90);
76
- --psp-d3fc--series-6--color: rgb(200, 120, 140);
77
- --psp-d3fc--series-7--color: rgb(100, 150, 190);
78
- --psp-d3fc--series-8--color: rgb(210, 140, 80);
79
- --psp-d3fc--series-9--color: rgb(130, 120, 190);
80
- --psp-d3fc--series-10--color: rgb(170, 200, 110);
81
-
82
- --psp-d3fc--full-gradient--background: linear-gradient(
58
+ /* perspective-viewer-botanical--webgl */
59
+ --psp-charts--legend--color: #b8c9ad;
60
+ --psp-charts--treemap--labels: #e0ead8;
61
+ --psp-charts--treemap--hover-highlight: #e0ead8;
62
+ --psp-charts--tooltip--color: #e0ead8;
63
+ --psp-charts--axis-ticks--color: #b8c9ad;
64
+ --psp-charts--axis-lines--color: #526b4a;
65
+ --psp-charts--gridline--color: #2a4228;
66
+ --psp-charts--tooltip--background: rgba(30, 52, 32, 1);
67
+ --psp-charts--tooltip--border-color: #1a2e1a;
68
+ --psp-charts--legend--background: var(--psp--background-color);
69
+
70
+ --psp-charts--series--color: rgb(90, 158, 75);
71
+ --psp-charts--series-1--color: rgb(90, 158, 75);
72
+ --psp-charts--series-2--color: rgb(206, 176, 104);
73
+ --psp-charts--series-3--color: rgb(160, 110, 180);
74
+ --psp-charts--series-4--color: rgb(80, 170, 150);
75
+ --psp-charts--series-5--color: rgb(140, 170, 90);
76
+ --psp-charts--series-6--color: rgb(200, 120, 140);
77
+ --psp-charts--series-7--color: rgb(100, 150, 190);
78
+ --psp-charts--series-8--color: rgb(210, 140, 80);
79
+ --psp-charts--series-9--color: rgb(130, 120, 190);
80
+ --psp-charts--series-10--color: rgb(170, 200, 110);
81
+
82
+ --psp-charts--gradient--background: linear-gradient(
83
83
  #e8836a 0%,
84
84
  #1a2e1a 50%,
85
85
  #5a9e4b 100%
86
86
  );
87
87
 
88
- --psp-d3fc--pos-gradient--background: linear-gradient(
89
- #1a2e1a 0%,
90
- #5a9e4b 100%
91
- );
92
- --psp-d3fc--neg-gradient--background: linear-gradient(
93
- #e8836a 0%,
94
- #1a2e1a 100%
95
- );
96
-
97
- /* perspective-viewer-botanical--openlayers */
98
- --psp-openlayers--tile-url: "http://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png";
99
- --psp-openlayers--attribution--filter: invert(1) hue-rotate(180deg);
100
- --psp-openlayers--element--background: #1e3420;
101
- --psp-openlayers--category-1--color: rgb(90, 158, 75);
102
- --psp-openlayers--category-2--color: rgb(206, 176, 104);
103
- --psp-openlayers--category-3--color: rgb(160, 110, 180);
104
- --psp-openlayers--category-4--color: rgb(80, 170, 150);
105
- --psp-openlayers--category-5--color: rgb(140, 170, 90);
106
- --psp-openlayers--category-6--color: rgb(200, 120, 140);
107
- --psp-openlayers--category-7--color: rgb(100, 150, 190);
108
- --psp-openlayers--category-8--color: rgb(210, 140, 80);
109
- --psp-openlayers--category-9--color: rgb(130, 120, 190);
110
- --psp-openlayers--category-10--color: rgb(170, 200, 110);
111
- --psp-openlayers--gradient--background: linear-gradient(
112
- #e8836a 0%,
113
- #1a2e1a 50%,
114
- #5a9e4b 100%
115
- );
88
+ --psp-charts--map-tiles--url: "http://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png";
89
+ --psp-charts--map-tiles--attribution-: "I <3 Open Source";
116
90
  }
117
91
 
118
92
  perspective-copy-menu[theme="Botanical"],
@@ -68,30 +68,30 @@ perspective-string-column-style {
68
68
  --psp-icon--select-arrow-light--mask-image
69
69
  );
70
70
 
71
- /* D3FC */
72
- --psp-label--d3fc-y1--content: "arrow_upward";
73
- --psp-label--d3fc-y2--content: "arrow_downward";
74
- --psp-d3fc--treemap-axis--lines: none;
75
- --psp-d3fc--tooltip--background-color: rgba(155, 155, 155, 0.8);
76
- --psp-d3fc--tooltip--color: #161616;
77
- --psp-d3fc--tooltip--border-color: #fff;
78
- --psp-d3fc--tooltip--box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%);
79
- --psp-d3fc--gridline--color: #eaedef;
80
- --psp-d3fc--axis-ticks--color: #161616;
81
- --psp-d3fc--axis-lines--color: #c5c9d0;
82
- --psp-d3fc--legend--background: var(--psp--background-color);
83
- --psp-d3fc--series--color: rgba(31, 119, 180, 0.8);
84
- --psp-d3fc--series-1--color: #0366d6;
85
- --psp-d3fc--series-2--color: #ff7f0e;
86
- --psp-d3fc--series-3--color: #2ca02c;
87
- --psp-d3fc--series-4--color: #d62728;
88
- --psp-d3fc--series-5--color: #9467bd;
89
- --psp-d3fc--series-6--color: #8c564b;
90
- --psp-d3fc--series-7--color: #e377c2;
91
- --psp-d3fc--series-8--color: #7f7f7f;
92
- --psp-d3fc--series-9--color: #bcbd22;
93
- --psp-d3fc--series-10--color: #17becf;
94
- --psp-d3fc--full-gradient--background: linear-gradient(
71
+ /* webgl */
72
+ --psp-label--webgl-y1--content: "arrow_upward";
73
+ --psp-label--webgl-y2--content: "arrow_downward";
74
+ --psp-charts--treemap-axis--lines: none;
75
+ --psp-charts--tooltip--background-color: rgba(155, 155, 155, 0.8);
76
+ --psp-charts--tooltip--color: #161616;
77
+ --psp-charts--tooltip--border-color: #fff;
78
+ --psp-charts--tooltip--box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%);
79
+ --psp-charts--gridline--color: #eaedef;
80
+ --psp-charts--axis-ticks--color: #161616;
81
+ --psp-charts--axis-lines--color: #c5c9d0;
82
+ --psp-charts--legend--background: var(--psp--background-color);
83
+ --psp-charts--series--color: rgba(31, 119, 180, 0.8);
84
+ --psp-charts--series-1--color: #0366d6;
85
+ --psp-charts--series-2--color: #ff7f0e;
86
+ --psp-charts--series-3--color: #2ca02c;
87
+ --psp-charts--series-4--color: #d62728;
88
+ --psp-charts--series-5--color: #9467bd;
89
+ --psp-charts--series-6--color: #8c564b;
90
+ --psp-charts--series-7--color: #e377c2;
91
+ --psp-charts--series-8--color: #7f7f7f;
92
+ --psp-charts--series-9--color: #bcbd22;
93
+ --psp-charts--series-10--color: #17becf;
94
+ --psp-charts--gradient--background: linear-gradient(
95
95
  #4d342f 0%,
96
96
  #e4521b 22.5%,
97
97
  #feeb65 42.5%,
@@ -100,18 +100,6 @@ perspective-string-column-style {
100
100
  #42b3d5 67.5%,
101
101
  #1a237e 100%
102
102
  );
103
- --psp-d3fc--pos-gradient--background: linear-gradient(
104
- #f0f0f0 0%,
105
- #dcedc8 10%,
106
- #42b3d5 50%,
107
- #1a237e 100%
108
- );
109
- --psp-d3fc--neg-gradient--background: linear-gradient(
110
- #4d342f 0%,
111
- #e4521b 50%,
112
- #feeb65 90%,
113
- #f0f0f0 100%
114
- );
115
103
 
116
104
  /* Datagrid */
117
105
  --psp-datagrid--pos-cell--color: #338dcd;