@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.
- package/dist/cdn/perspective-viewer.js +1 -2
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/esm/bootstrap.d.ts +2 -1
- package/dist/esm/column-format.d.ts +51 -0
- package/dist/esm/extensions.d.ts +2 -0
- package/dist/esm/perspective-viewer.d.ts +3 -1
- package/dist/esm/perspective-viewer.inline.js +1 -2
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +1 -2
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/perspective-viewer.worker.d.ts +2 -0
- package/dist/esm/plugin.d.ts +16 -72
- package/dist/esm/ts-rs/ColumnSelectMode.d.ts +1 -0
- package/dist/esm/ts-rs/PluginStaticConfig.d.ts +77 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -3
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -4
- package/dist/wasm/perspective-viewer.d.ts +77 -18
- package/dist/wasm/perspective-viewer.js +302 -148
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +20 -15
- package/package.json +24 -2
- package/src/css/column-selector.css +3 -2
- package/src/css/column-settings-panel.css +44 -9
- package/src/css/column-style.css +35 -2
- package/src/css/containers/scroll-panel.css +2 -1
- package/src/css/containers/tabs.css +8 -52
- package/src/css/dom/checkbox.css +2 -6
- package/src/css/form/code-editor.css +1 -0
- package/src/css/form/debug.css +3 -10
- package/src/css/plugin-selector.css +33 -0
- package/src/css/plugin-settings-panel.css +99 -0
- package/src/css/viewer.css +143 -3
- package/src/rust/components/column_dropdown.rs +3 -1
- package/src/rust/components/column_selector/active_column.rs +16 -19
- package/src/rust/components/column_selector/config_selector.rs +20 -20
- package/src/rust/components/column_selector/filter_column.rs +14 -14
- package/src/rust/components/column_selector/inactive_column.rs +10 -15
- package/src/rust/components/column_selector/pivot_column.rs +7 -7
- package/src/rust/components/column_selector/sort_column.rs +7 -7
- package/src/rust/components/column_selector.rs +55 -37
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +15 -7
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +395 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +15 -6
- package/src/rust/components/column_settings_sidebar/style_tab.rs +267 -136
- package/src/rust/components/column_settings_sidebar.rs +44 -49
- package/src/rust/components/containers/dragdrop_list.rs +32 -5
- package/src/rust/components/containers/mod.rs +0 -1
- package/src/rust/components/containers/scroll_panel.rs +21 -7
- package/src/rust/components/containers/sidebar.rs +8 -6
- package/src/rust/components/containers/split_panel.rs +3 -3
- package/src/rust/components/containers/tab_list.rs +3 -9
- package/src/rust/components/copy_dropdown.rs +2 -3
- package/src/rust/components/datetime_column_style.rs +19 -81
- package/src/rust/components/editable_header.rs +17 -3
- package/src/rust/components/export_dropdown.rs +2 -3
- package/src/rust/components/expression_editor.rs +29 -17
- package/src/rust/components/filter_dropdown.rs +2 -1
- package/src/rust/components/form/color_range_selector.rs +14 -7
- package/src/rust/components/form/debug.rs +47 -37
- package/src/rust/components/main_panel.rs +24 -65
- package/src/rust/components/mod.rs +2 -1
- package/src/rust/components/number_series_style.rs +161 -0
- package/src/rust/components/plugin_tab.rs +221 -0
- package/src/rust/components/settings_panel.rs +181 -59
- package/src/rust/components/status_bar.rs +141 -174
- package/src/rust/components/status_indicator.rs +15 -22
- package/src/rust/components/string_column_style.rs +20 -82
- package/src/rust/components/style_controls/number_string_format.rs +14 -30
- package/src/rust/components/viewer.rs +169 -132
- package/src/rust/config/column_config_schema.rs +195 -0
- package/src/rust/config/columns_config.rs +4 -97
- package/src/rust/config/datetime_column_style.rs +0 -5
- package/src/rust/config/mod.rs +8 -2
- package/src/rust/config/number_series_style.rs +79 -0
- package/src/rust/config/plugin_static_config.rs +144 -0
- package/src/rust/config/string_column_style.rs +0 -5
- package/src/rust/config/viewer_config.rs +5 -6
- package/src/rust/custom_elements/copy_dropdown.rs +30 -18
- package/src/rust/custom_elements/debug_plugin.rs +1 -3
- package/src/rust/custom_elements/export_dropdown.rs +26 -18
- package/src/rust/custom_elements/viewer.rs +62 -73
- package/src/rust/custom_events.rs +181 -224
- package/src/rust/js/plugin.rs +45 -117
- package/src/rust/lib.rs +34 -5
- package/src/rust/presentation/drag_helpers.rs +206 -0
- package/src/rust/presentation/props.rs +8 -0
- package/src/rust/presentation.rs +256 -41
- package/src/rust/{tasks → queries}/column_locator.rs +17 -73
- package/src/rust/queries/column_values.rs +59 -0
- package/src/rust/{tasks → queries}/columns_iter_set.rs +11 -18
- package/src/rust/queries/exports.rs +96 -0
- package/src/rust/queries/fetch_column_stats.rs +94 -0
- package/src/rust/queries/get_viewer_config.rs +54 -0
- package/src/rust/queries/mod.rs +44 -0
- package/src/rust/queries/plugin_column_styles.rs +101 -0
- package/src/rust/{engines.rs → queries/validate_expression.rs} +26 -15
- package/src/rust/renderer/activate.rs +1 -0
- package/src/rust/renderer/limits.rs +9 -4
- package/src/rust/renderer/plugin_store.rs +12 -0
- package/src/rust/renderer/props.rs +28 -3
- package/src/rust/renderer/registry.rs +40 -15
- package/src/rust/renderer.rs +703 -60
- package/src/rust/session/column_defaults_update.rs +20 -28
- package/src/rust/session/drag_drop_update.rs +10 -10
- package/src/rust/session/metadata.rs +31 -16
- package/src/rust/session/props.rs +15 -6
- package/src/rust/session/view_subscription.rs +10 -0
- package/src/rust/session.rs +109 -147
- package/src/rust/tasks/copy_export.rs +178 -158
- package/src/rust/tasks/{structural.rs → dismiss_render_warning.rs} +20 -40
- package/src/rust/tasks/edit_expression.rs +68 -88
- package/src/rust/tasks/eject.rs +25 -22
- package/src/rust/tasks/intersection_observer.rs +8 -21
- package/src/rust/tasks/mod.rs +19 -21
- package/src/rust/tasks/reset_all.rs +98 -0
- package/src/rust/tasks/resize_observer.rs +11 -33
- package/src/rust/tasks/restore_and_render.rs +128 -90
- package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +39 -35
- package/src/rust/tasks/send_plugin_config.rs +33 -33
- package/src/rust/tasks/update_and_render.rs +75 -49
- package/src/rust/{components/containers/trap_door_panel.rs → tasks/update_theme.rs} +34 -33
- package/src/rust/tasks/validate_expression.rs +61 -0
- package/src/rust/utils/browser/selection.rs +4 -4
- package/src/rust/utils/mod.rs +0 -63
- package/src/svg/checkbox-checked-icon.svg +1 -1
- package/src/svg/checkbox-unchecked-icon.svg +1 -1
- package/src/svg/mega-menu-icons-density.svg +23 -0
- package/src/svg/mega-menu-icons-map-density.svg +24 -0
- package/src/svg/mega-menu-icons-map-line.svg +19 -0
- package/src/themes/botanical.css +27 -53
- package/src/themes/defaults.css +24 -36
- package/src/themes/dracula.css +36 -54
- package/src/themes/gruvbox-dark.css +39 -59
- package/src/themes/gruvbox.css +16 -28
- package/src/themes/icons.css +5 -0
- package/src/themes/intl/de.css +43 -6
- package/src/themes/intl/es.css +43 -6
- package/src/themes/intl/fr.css +43 -6
- package/src/themes/intl/ja.css +43 -6
- package/src/themes/intl/pt.css +43 -6
- package/src/themes/intl/zh.css +43 -6
- package/src/themes/intl.css +38 -4
- package/src/themes/monokai.css +45 -61
- package/src/themes/phosphor.css +20 -29
- package/src/themes/pro-dark.css +25 -34
- package/src/themes/solarized-dark.css +21 -36
- package/src/themes/solarized.css +13 -23
- package/src/themes/vaporwave.css +40 -74
- package/src/ts/bootstrap.ts +14 -3
- package/src/ts/column-format.ts +162 -0
- package/src/ts/extensions.ts +4 -0
- package/src/ts/perspective-viewer.ts +9 -1
- package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
- package/src/ts/plugin.ts +25 -101
- package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
- package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
- package/src/ts/ts-rs/ViewerConfig.ts +1 -2
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
- package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
- package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
- package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
- package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
- package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
- package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
- package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
- package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
- package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
- package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
- package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
- package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
- package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
- package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
- package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
- package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
- package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
- package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
- package/src/rust/components/number_column_style.rs +0 -491
- package/src/rust/config/number_column_style.rs +0 -136
- package/src/rust/dragdrop.rs +0 -481
- package/src/rust/tasks/plugin_column_styles.rs +0 -98
- package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
- package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
- package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
- package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
- package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
- package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
- package/src/ts/ts-rs/FormatMode.ts +0 -3
- package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
- package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
- package/src/ts/ts-rs/PluginConfig.ts +0 -4
- package/src/ts/ts-rs/RoundingMode.ts +0 -3
- package/src/ts/ts-rs/RoundingPriority.ts +0 -3
- package/src/ts/ts-rs/SignDisplay.ts +0 -3
- package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
- package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
- package/src/ts/ts-rs/StringColorMode.ts +0 -3
- package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
- package/src/ts/ts-rs/UseGrouping.ts +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline4.js +0 -0
- /package/src/rust/{tasks → config}/export_method.rs +0 -0
- /package/src/rust/{tasks → queries}/export_app.rs +0 -0
- /package/src/rust/{tasks → queries}/is_invalid_drop.rs +0 -0
|
@@ -10,32 +10,11 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use perspective_client::config::
|
|
13
|
+
use perspective_client::config::ViewConfig;
|
|
14
14
|
|
|
15
|
-
use super::HasSession;
|
|
16
|
-
use super::plugin_column_styles::can_render_column_styles;
|
|
17
15
|
use crate::presentation::{ColumnLocator, OpenColumnSettings};
|
|
18
16
|
use crate::renderer::Renderer;
|
|
19
17
|
use crate::session::SessionMetadata;
|
|
20
|
-
use crate::tasks::{HasPresentation, HasRenderer, PluginColumnStyles};
|
|
21
|
-
|
|
22
|
-
/// Returns the column name for a locator, generating a default for new
|
|
23
|
-
/// expressions.
|
|
24
|
-
pub fn locator_name_or_default(metadata: &SessionMetadata, locator: &ColumnLocator) -> String {
|
|
25
|
-
match locator {
|
|
26
|
-
ColumnLocator::Table(s) | ColumnLocator::Expression(s) => s.clone(),
|
|
27
|
-
ColumnLocator::NewExpression => metadata.make_new_column_name(None),
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/// Returns the view type for a locator's column, if available.
|
|
32
|
-
pub fn locator_view_type(
|
|
33
|
-
metadata: &SessionMetadata,
|
|
34
|
-
locator: &ColumnLocator,
|
|
35
|
-
) -> Option<ColumnType> {
|
|
36
|
-
let name = locator.name().cloned().unwrap_or_default();
|
|
37
|
-
metadata.get_column_view_type(name.as_str())
|
|
38
|
-
}
|
|
39
18
|
|
|
40
19
|
/// Returns a [`ColumnLocator`] for a given column name, or [`None`] if no
|
|
41
20
|
/// such column exists.
|
|
@@ -57,17 +36,20 @@ pub fn get_column_locator(
|
|
|
57
36
|
|
|
58
37
|
/// Gets a [`ColumnLocator`] for the current UI's column settings state,
|
|
59
38
|
/// or [`None`] if it is not currently active.
|
|
39
|
+
///
|
|
40
|
+
/// Table columns only have a useful sidebar (the Style tab)
|
|
41
|
+
/// when they're in `view_config.columns`.
|
|
60
42
|
pub fn get_current_column_locator(
|
|
61
43
|
open_column_settings: &OpenColumnSettings,
|
|
62
44
|
renderer: &Renderer,
|
|
63
45
|
view_config: &ViewConfig,
|
|
64
|
-
|
|
46
|
+
_metadata: &SessionMetadata,
|
|
65
47
|
) -> Option<ColumnLocator> {
|
|
66
48
|
open_column_settings
|
|
67
49
|
.locator
|
|
68
50
|
.clone()
|
|
69
51
|
.filter(|locator| match locator {
|
|
70
|
-
ColumnLocator::Table(
|
|
52
|
+
ColumnLocator::Table(_name) => {
|
|
71
53
|
locator
|
|
72
54
|
.name()
|
|
73
55
|
.map(|name| {
|
|
@@ -76,58 +58,20 @@ pub fn get_current_column_locator(
|
|
|
76
58
|
.as_ref()
|
|
77
59
|
.map(|col| col == name)
|
|
78
60
|
.unwrap_or_default()
|
|
79
|
-
|
|
80
|
-
|| view_config.
|
|
81
|
-
|
|
82
|
-
|
|
61
|
+
|
|
62
|
+
// }) || view_config.group_by.iter().any(|col|
|
|
63
|
+
// col == name) ||
|
|
64
|
+
// view_config.split_by.iter().any(|col| col ==
|
|
65
|
+
// name) ||
|
|
66
|
+
// view_config.filter.iter().any(|col| col.column()
|
|
67
|
+
// == name) ||
|
|
68
|
+
// view_config.sort.iter().any(|col| &col.0 == name)
|
|
69
|
+
// })
|
|
70
|
+
})
|
|
83
71
|
})
|
|
84
72
|
.unwrap_or_default()
|
|
85
|
-
&& can_render_column_styles(
|
|
86
|
-
.unwrap_or_default()
|
|
73
|
+
&& renderer.can_render_column_styles()
|
|
87
74
|
},
|
|
88
75
|
_ => true,
|
|
89
76
|
})
|
|
90
77
|
}
|
|
91
|
-
|
|
92
|
-
/// Trait facade — delegates to standalone functions above.
|
|
93
|
-
pub trait ColumnLocatorExt: HasSession {
|
|
94
|
-
fn locator_name_or_default(&self, locator: &ColumnLocator) -> String {
|
|
95
|
-
locator_name_or_default(&self.session().metadata(), locator)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fn is_locator_active(&self, locator: &ColumnLocator) -> bool {
|
|
99
|
-
locator
|
|
100
|
-
.name()
|
|
101
|
-
.map(|name| self.session().to_props().is_column_active(name))
|
|
102
|
-
.unwrap_or_default()
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
fn locator_view_type(&self, locator: &ColumnLocator) -> Option<ColumnType> {
|
|
106
|
-
locator_view_type(&self.session().metadata(), locator)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
fn get_column_locator(&self, name: Option<String>) -> Option<ColumnLocator> {
|
|
110
|
-
get_column_locator(&self.session().metadata(), name)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
impl<T: HasSession> ColumnLocatorExt for T {}
|
|
115
|
-
|
|
116
|
-
/// Trait facade for `get_current_column_locator`.
|
|
117
|
-
pub trait ColumnLocatorCurrentExt:
|
|
118
|
-
HasPresentation + HasRenderer + HasSession + PluginColumnStyles
|
|
119
|
-
{
|
|
120
|
-
fn get_current_column_locator(&self) -> Option<ColumnLocator> {
|
|
121
|
-
get_current_column_locator(
|
|
122
|
-
&self.presentation().get_open_column_settings(),
|
|
123
|
-
self.renderer(),
|
|
124
|
-
&self.session().get_view_config(),
|
|
125
|
-
&self.session().metadata(),
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
impl<T: HasPresentation + HasRenderer + HasSession + PluginColumnStyles> ColumnLocatorCurrentExt
|
|
131
|
-
for T
|
|
132
|
-
{
|
|
133
|
-
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
use perspective_client::config::ViewConfigUpdate;
|
|
14
|
+
use perspective_client::{ViewWindow, clone};
|
|
15
|
+
use perspective_js::utils::{ApiError, ApiFuture};
|
|
16
|
+
|
|
17
|
+
use crate::session::Session;
|
|
18
|
+
|
|
19
|
+
/// Get all unique column values for a given column name.
|
|
20
|
+
///
|
|
21
|
+
/// Use the `.to_csv()` method, as I suspected copying this large string
|
|
22
|
+
/// once was more efficient than copying many smaller strings, and
|
|
23
|
+
/// string copying shows up frequently when doing performance analysis.
|
|
24
|
+
///
|
|
25
|
+
/// TODO Does not work with expressions yet.
|
|
26
|
+
pub async fn get_column_values(session: &Session, column: String) -> Result<Vec<String>, ApiError> {
|
|
27
|
+
let expressions = Some(session.get_view_config().expressions.clone());
|
|
28
|
+
let config = ViewConfigUpdate {
|
|
29
|
+
group_by: Some(vec![column]),
|
|
30
|
+
columns: Some(vec![]),
|
|
31
|
+
expressions,
|
|
32
|
+
..ViewConfigUpdate::default()
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
let table = session
|
|
36
|
+
.get_table()
|
|
37
|
+
.ok_or_else(|| ApiError::from("No table set"))?;
|
|
38
|
+
let view = table.view(Some(config.clone())).await?;
|
|
39
|
+
let csv = view.to_csv(ViewWindow::default()).await?;
|
|
40
|
+
|
|
41
|
+
clone!(view);
|
|
42
|
+
ApiFuture::spawn(async move {
|
|
43
|
+
view.delete().await?;
|
|
44
|
+
Ok(())
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
let res = csv
|
|
48
|
+
.lines()
|
|
49
|
+
.map(|val| {
|
|
50
|
+
if val.starts_with('\"') && val.ends_with('\"') {
|
|
51
|
+
(val[1..val.len() - 1]).to_owned()
|
|
52
|
+
} else {
|
|
53
|
+
val.to_owned()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.skip(2)
|
|
57
|
+
.collect::<Vec<String>>();
|
|
58
|
+
Ok(res)
|
|
59
|
+
}
|
|
@@ -16,11 +16,10 @@ use itertools::Itertools;
|
|
|
16
16
|
use perspective_client::config::*;
|
|
17
17
|
|
|
18
18
|
use super::is_invalid_columns_column;
|
|
19
|
-
use crate::
|
|
19
|
+
use crate::presentation::Presentation;
|
|
20
20
|
use crate::renderer::*;
|
|
21
21
|
use crate::session::SessionMetadata;
|
|
22
22
|
use crate::utils::DragTarget;
|
|
23
|
-
use crate::*;
|
|
24
23
|
|
|
25
24
|
/// The possible states of a column (row) in the active columns list, including
|
|
26
25
|
/// the `Option<String>` label type.
|
|
@@ -76,10 +75,10 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
76
75
|
config: &'a ViewConfig,
|
|
77
76
|
metadata: &'a SessionMetadata,
|
|
78
77
|
renderer: &'a Renderer,
|
|
79
|
-
|
|
78
|
+
presentation: &Presentation,
|
|
80
79
|
) -> ColumnsIteratorSet<'a> {
|
|
81
|
-
let is_dragover_column =
|
|
82
|
-
let named_columns = renderer.metadata().
|
|
80
|
+
let is_dragover_column = presentation.is_dragover(DragTarget::Active);
|
|
81
|
+
let named_columns = renderer.metadata().config_column_names.clone();
|
|
83
82
|
ColumnsIteratorSet {
|
|
84
83
|
config,
|
|
85
84
|
renderer,
|
|
@@ -92,12 +91,7 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
92
91
|
/// Generate an iterator for active columns, which are represented as
|
|
93
92
|
/// `Option` for dragover and missing columns.
|
|
94
93
|
pub fn active(&'a self) -> impl Iterator<Item = ActiveColumnState> + 'a {
|
|
95
|
-
let min_cols = self
|
|
96
|
-
.renderer
|
|
97
|
-
.metadata()
|
|
98
|
-
.names
|
|
99
|
-
.as_ref()
|
|
100
|
-
.map_or(0, |x| x.len());
|
|
94
|
+
let min_cols = self.renderer.metadata().config_column_names.len();
|
|
101
95
|
|
|
102
96
|
let last_col = self.config.columns.last().and_then(|x| x.as_ref());
|
|
103
97
|
let has_blank_tail = last_col.is_none()
|
|
@@ -122,7 +116,7 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
122
116
|
.position(|x| x.as_ref() == Some(from_column));
|
|
123
117
|
|
|
124
118
|
let is_from_required = from_index
|
|
125
|
-
.and_then(|x| self.renderer.metadata().
|
|
119
|
+
.and_then(|x| self.renderer.metadata().min_config_columns.map(|y| x < y))
|
|
126
120
|
.unwrap_or_default();
|
|
127
121
|
|
|
128
122
|
let is_from_swap = from_index
|
|
@@ -131,7 +125,7 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
131
125
|
|
|
132
126
|
let is_swap_invalid = is_invalid_columns_column(
|
|
133
127
|
self.config,
|
|
134
|
-
self.renderer.metadata().
|
|
128
|
+
self.renderer.metadata().min_config_columns,
|
|
135
129
|
from_column,
|
|
136
130
|
*to_index,
|
|
137
131
|
);
|
|
@@ -254,12 +248,11 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
254
248
|
&'a self,
|
|
255
249
|
values: impl Iterator<Item = &'a String>,
|
|
256
250
|
) -> impl Iterator<Item = OrderedColumn<'a>> {
|
|
257
|
-
let is_drag_active =
|
|
251
|
+
let is_drag_active: Option<bool> = try {
|
|
258
252
|
let dragover_col = self.is_dragover_column.as_ref();
|
|
259
253
|
let cols = &self.config.columns;
|
|
260
254
|
let (_, drag_name) = dragover_col?;
|
|
261
|
-
|
|
262
|
-
Some(is_drag_active)
|
|
255
|
+
cols.iter().flatten().any(|x| x == drag_name)
|
|
263
256
|
};
|
|
264
257
|
|
|
265
258
|
let col_set = self.config.columns.iter().collect::<HashSet<_>>();
|
|
@@ -282,11 +275,11 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
282
275
|
let dragover_col = self.is_dragover_column.as_ref();
|
|
283
276
|
let cols = &self.config.columns;
|
|
284
277
|
let is_active = col_set.contains(&Some(name.to_string())); // cols.iter().flatten().any(|x| x == name);
|
|
285
|
-
let is_swap_over =
|
|
278
|
+
let is_swap_over: Option<bool> = try {
|
|
286
279
|
let (drop_index, _) = dragover_col?;
|
|
287
280
|
let is_swap = self.renderer.metadata().is_swap(*drop_index);
|
|
288
281
|
let is_over = cols.get(*drop_index)?.as_ref()? == name;
|
|
289
|
-
|
|
282
|
+
is_swap && is_over && !is_drag_active?
|
|
290
283
|
};
|
|
291
284
|
|
|
292
285
|
if !is_active || is_swap_over.unwrap_or_default() {
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
//! Pure read-only `View` → bytes/string derivations. `flat = true` means
|
|
14
|
+
//! "ignore the active view config and re-fetch the un-pivoted table view";
|
|
15
|
+
//! `flat = false` means "use the session's bound view".
|
|
16
|
+
|
|
17
|
+
use perspective_client::{View, ViewWindow};
|
|
18
|
+
use perspective_js::utils::{ApiError, ApiResult};
|
|
19
|
+
use wasm_bindgen::JsCast;
|
|
20
|
+
|
|
21
|
+
use crate::session::Session;
|
|
22
|
+
|
|
23
|
+
async fn flat_view(session: &Session, flat: bool) -> ApiResult<View> {
|
|
24
|
+
if flat {
|
|
25
|
+
let table = session
|
|
26
|
+
.get_table()
|
|
27
|
+
.ok_or_else(|| ApiError::from("No table set"))?;
|
|
28
|
+
Ok(table.view(None).await?)
|
|
29
|
+
} else {
|
|
30
|
+
session
|
|
31
|
+
.get_view()
|
|
32
|
+
.ok_or_else(|| ApiError::from("No view created"))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub async fn arrow_as_vec(
|
|
37
|
+
session: &Session,
|
|
38
|
+
flat: bool,
|
|
39
|
+
window: Option<ViewWindow>,
|
|
40
|
+
) -> Result<Vec<u8>, ApiError> {
|
|
41
|
+
Ok(flat_view(session, flat)
|
|
42
|
+
.await?
|
|
43
|
+
.to_arrow(window.unwrap_or_default())
|
|
44
|
+
.await?
|
|
45
|
+
.into())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub async fn arrow_as_jsvalue(
|
|
49
|
+
session: &Session,
|
|
50
|
+
flat: bool,
|
|
51
|
+
window: Option<ViewWindow>,
|
|
52
|
+
) -> Result<js_sys::ArrayBuffer, ApiError> {
|
|
53
|
+
let arrow = flat_view(session, flat)
|
|
54
|
+
.await?
|
|
55
|
+
.to_arrow(window.unwrap_or_default())
|
|
56
|
+
.await?;
|
|
57
|
+
Ok(js_sys::Uint8Array::from(&arrow[..])
|
|
58
|
+
.buffer()
|
|
59
|
+
.unchecked_into())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub async fn ndjson_as_jsvalue(
|
|
63
|
+
session: &Session,
|
|
64
|
+
flat: bool,
|
|
65
|
+
window: Option<ViewWindow>,
|
|
66
|
+
) -> Result<js_sys::JsString, ApiError> {
|
|
67
|
+
let json: String = flat_view(session, flat)
|
|
68
|
+
.await?
|
|
69
|
+
.to_ndjson(window.unwrap_or_default())
|
|
70
|
+
.await?;
|
|
71
|
+
|
|
72
|
+
Ok(json.into())
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pub async fn json_as_jsvalue(
|
|
76
|
+
session: &Session,
|
|
77
|
+
flat: bool,
|
|
78
|
+
window: Option<ViewWindow>,
|
|
79
|
+
) -> Result<js_sys::Object, ApiError> {
|
|
80
|
+
let json: String = flat_view(session, flat)
|
|
81
|
+
.await?
|
|
82
|
+
.to_columns_string(window.unwrap_or_default())
|
|
83
|
+
.await?;
|
|
84
|
+
|
|
85
|
+
Ok(js_sys::JSON::parse(&json)?.unchecked_into())
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pub async fn csv_as_jsvalue(
|
|
89
|
+
session: &Session,
|
|
90
|
+
flat: bool,
|
|
91
|
+
window: Option<ViewWindow>,
|
|
92
|
+
) -> Result<js_sys::JsString, ApiError> {
|
|
93
|
+
let window = window.unwrap_or_default();
|
|
94
|
+
let csv = flat_view(session, flat).await?.to_csv(window).await;
|
|
95
|
+
Ok(csv.map(js_sys::JsString::from)?)
|
|
96
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
//! Async dispatch task for per-column numeric stats.
|
|
14
|
+
//!
|
|
15
|
+
//! `resolve_abs_max` is the cache-or-fetch core: hit the
|
|
16
|
+
//! [`Session`] stats cache first, fall back to `View::get_min_max`
|
|
17
|
+
//! for numeric columns on miss, write-through on success.
|
|
18
|
+
//!
|
|
19
|
+
//! `fetch_column_abs_max` is the fire-and-forget pre-warm wrapper used
|
|
20
|
+
//! by the StyleTab on column-settings panel open so the gradient
|
|
21
|
+
//! defaults are ready before the user can trigger any UI update.
|
|
22
|
+
|
|
23
|
+
use perspective_client::View;
|
|
24
|
+
use perspective_client::config::{ColumnType, Scalar};
|
|
25
|
+
use perspective_js::utils::ApiFuture;
|
|
26
|
+
|
|
27
|
+
use crate::session::{Session, SessionMetadata};
|
|
28
|
+
|
|
29
|
+
fn scalar_to_f64(s: &Scalar) -> Option<f64> {
|
|
30
|
+
match s {
|
|
31
|
+
Scalar::Float(x) => Some(*x),
|
|
32
|
+
_ => None,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Extract `abs_max = max(|min|, |max|)` from a `(Scalar, Scalar)`
|
|
37
|
+
/// `View::get_min_max` result. Returns `None` for non-numeric scalars.
|
|
38
|
+
pub fn min_max_to_abs_max(min: &Scalar, max: &Scalar) -> Option<f64> {
|
|
39
|
+
let min = scalar_to_f64(min)?;
|
|
40
|
+
let max = scalar_to_f64(max)?;
|
|
41
|
+
Some(min.abs().max(max.abs()))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// `true` if the column's view type is `Integer` or `Float`. Used to
|
|
45
|
+
/// skip the `View::get_min_max` round trip for non-numeric columns
|
|
46
|
+
/// (which would either error or return non-`Float` scalars).
|
|
47
|
+
pub fn is_numeric_column(metadata: &SessionMetadata, col_name: &str) -> bool {
|
|
48
|
+
matches!(
|
|
49
|
+
metadata.get_column_view_type(col_name),
|
|
50
|
+
Some(ColumnType::Integer | ColumnType::Float)
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Resolve the cached `abs_max` for `col_name`; on miss, await
|
|
55
|
+
/// `View::get_min_max` for numeric columns, populate the cache, and
|
|
56
|
+
/// return the value. Non-numeric columns and missing views return
|
|
57
|
+
/// `None` without a fetch.
|
|
58
|
+
pub async fn resolve_abs_max(
|
|
59
|
+
session: &Session,
|
|
60
|
+
metadata: &SessionMetadata,
|
|
61
|
+
view: Option<&View>,
|
|
62
|
+
col_name: &str,
|
|
63
|
+
) -> Option<f64> {
|
|
64
|
+
if let Some(stats) = session.get_column_stats(col_name)
|
|
65
|
+
&& let Some(v) = stats.abs_max
|
|
66
|
+
{
|
|
67
|
+
return Some(v);
|
|
68
|
+
}
|
|
69
|
+
if !is_numeric_column(metadata, col_name) {
|
|
70
|
+
return None;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let view = view?;
|
|
74
|
+
let (min, max) = view.get_min_max(col_name.to_string()).await.ok()?;
|
|
75
|
+
let v = min_max_to_abs_max(&min, &max)?;
|
|
76
|
+
session.set_column_abs_max(col_name.to_string(), v);
|
|
77
|
+
Some(v)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Fire-and-forget pre-warm of the stats cache. Spawns
|
|
81
|
+
/// [`resolve_abs_max`] on the active session's view; the result is
|
|
82
|
+
/// discarded — the side effect of populating
|
|
83
|
+
/// [`Session::set_column_abs_max`] is the goal. Used by the
|
|
84
|
+
/// `StyleTab`'s `use_effect_with` to warm gradient defaults before
|
|
85
|
+
/// the user can trigger a `plugin.restore`.
|
|
86
|
+
pub fn fetch_column_abs_max(session: &Session, column_name: String) {
|
|
87
|
+
let session = session.clone();
|
|
88
|
+
ApiFuture::spawn(async move {
|
|
89
|
+
let metadata = session.metadata().clone();
|
|
90
|
+
let view = session.get_view();
|
|
91
|
+
resolve_abs_max(&session, &metadata, view.as_ref(), &column_name).await;
|
|
92
|
+
Ok::<_, perspective_js::utils::ApiError>(())
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
use crate::config::*;
|
|
14
|
+
use crate::presentation::Presentation;
|
|
15
|
+
use crate::renderer::Renderer;
|
|
16
|
+
use crate::session::Session;
|
|
17
|
+
use crate::*;
|
|
18
|
+
|
|
19
|
+
/// Compose the current [`ViewerConfig`] from across the engine handles.
|
|
20
|
+
///
|
|
21
|
+
/// The `view_config` field is read from the snapshot the currently-bound
|
|
22
|
+
/// `View` was constructed from, so it is consistent with what the active
|
|
23
|
+
/// plugin is rendering. Falls back to the live session config if no
|
|
24
|
+
/// `View` exists yet.
|
|
25
|
+
pub async fn get_viewer_config(
|
|
26
|
+
session: &Session,
|
|
27
|
+
renderer: &Renderer,
|
|
28
|
+
presentation: &Presentation,
|
|
29
|
+
) -> ApiResult<ViewerConfig> {
|
|
30
|
+
let version = config::API_VERSION.to_string();
|
|
31
|
+
let view_config = if let Some(rendered) = session.get_rendered_view_config() {
|
|
32
|
+
(*rendered).clone()
|
|
33
|
+
} else {
|
|
34
|
+
session.get_view_config().clone()
|
|
35
|
+
};
|
|
36
|
+
let settings = presentation.is_settings_open();
|
|
37
|
+
let plugin = renderer.metadata().name.clone();
|
|
38
|
+
let plugin_config = renderer.get_plugin_config();
|
|
39
|
+
let theme = presentation.get_selected_theme_name().await;
|
|
40
|
+
let title = session.get_title();
|
|
41
|
+
let table = session.get_table().map(|x| x.get_name().to_owned());
|
|
42
|
+
let columns_config = renderer.all_columns_configs();
|
|
43
|
+
Ok(ViewerConfig {
|
|
44
|
+
version,
|
|
45
|
+
plugin,
|
|
46
|
+
title,
|
|
47
|
+
plugin_config,
|
|
48
|
+
columns_config,
|
|
49
|
+
settings,
|
|
50
|
+
table,
|
|
51
|
+
view_config,
|
|
52
|
+
theme,
|
|
53
|
+
})
|
|
54
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
//! Read-only async utilities across the engine handles.
|
|
14
|
+
//!
|
|
15
|
+
//! Each function in this module reads from one or more of [`Session`],
|
|
16
|
+
//! [`Renderer`], [`Presentation`] and produces a derived value without
|
|
17
|
+
//! mutating engine state. Compare [`crate::tasks`], which holds the
|
|
18
|
+
//! state-mutating async business logic dispatched from user actions.
|
|
19
|
+
//!
|
|
20
|
+
//! [`Session`]: crate::session::Session
|
|
21
|
+
//! [`Renderer`]: crate::renderer::Renderer
|
|
22
|
+
//! [`Presentation`]: crate::presentation::Presentation
|
|
23
|
+
|
|
24
|
+
mod column_locator;
|
|
25
|
+
mod column_values;
|
|
26
|
+
mod columns_iter_set;
|
|
27
|
+
pub mod export_app;
|
|
28
|
+
mod exports;
|
|
29
|
+
mod fetch_column_stats;
|
|
30
|
+
mod get_viewer_config;
|
|
31
|
+
mod is_invalid_drop;
|
|
32
|
+
mod plugin_column_styles;
|
|
33
|
+
mod validate_expression;
|
|
34
|
+
|
|
35
|
+
pub use self::column_locator::*;
|
|
36
|
+
pub use self::column_values::*;
|
|
37
|
+
pub use self::columns_iter_set::*;
|
|
38
|
+
pub use self::export_app::*;
|
|
39
|
+
pub use self::exports::*;
|
|
40
|
+
pub use self::fetch_column_stats::*;
|
|
41
|
+
pub use self::get_viewer_config::*;
|
|
42
|
+
pub use self::is_invalid_drop::*;
|
|
43
|
+
pub use self::plugin_column_styles::*;
|
|
44
|
+
pub use self::validate_expression::*;
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
use itertools::Itertools;
|
|
14
|
+
use perspective_client::config::ViewConfig;
|
|
15
|
+
use perspective_js::utils::{ApiResult, JsValueSerdeExt};
|
|
16
|
+
use serde::Serialize;
|
|
17
|
+
|
|
18
|
+
use crate::config::ColumnConfigSchema;
|
|
19
|
+
use crate::renderer::Renderer;
|
|
20
|
+
use crate::session::SessionMetadata;
|
|
21
|
+
|
|
22
|
+
/// Stats payload passed to `plugin.column_config_schema` as the
|
|
23
|
+
/// `column_stats` arg. The caller (e.g. the StyleTab) owns the value —
|
|
24
|
+
/// typically populated by `fetch_column_abs_max` resolving into a
|
|
25
|
+
/// component-local `use_state`. Missing when no fetch has resolved yet.
|
|
26
|
+
#[derive(Default, Serialize)]
|
|
27
|
+
struct ColumnStats {
|
|
28
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
29
|
+
abs_max: Option<f64>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Queries the active plugin for its plugin-scoped
|
|
33
|
+
/// [`ColumnConfigSchema`]. Mirrors [`get_column_config_schema`] but
|
|
34
|
+
/// drops the column-specific args — the schema describes the shape
|
|
35
|
+
/// of the plugin's own config bucket on the renderer.
|
|
36
|
+
pub fn get_plugin_config_schema(
|
|
37
|
+
renderer: &Renderer,
|
|
38
|
+
view_config: &ViewConfig,
|
|
39
|
+
) -> ApiResult<ColumnConfigSchema> {
|
|
40
|
+
let plugin = renderer.get_active_plugin()?;
|
|
41
|
+
let view_config_js =
|
|
42
|
+
wasm_bindgen::JsValue::from_serde_ext(view_config).unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
43
|
+
let raw = plugin._plugin_config_schema(&view_config_js)?;
|
|
44
|
+
serde_wasm_bindgen::from_value(raw).map_err(|e| e.into())
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Queries the active plugin for the per-column [`ColumnConfigSchema`].
|
|
48
|
+
///
|
|
49
|
+
/// `current_value` is the column's existing flat JSON config (if any);
|
|
50
|
+
/// plugins use it to dynamically gate fields based on prior state.
|
|
51
|
+
/// `abs_max` is the caller-owned numeric stat (typically a Yew
|
|
52
|
+
/// `use_state` populated by an in-flight `fetch_column_abs_max` task);
|
|
53
|
+
/// `None` means the fetch has not yet resolved and gradient defaults
|
|
54
|
+
/// fall back to 0.
|
|
55
|
+
pub fn get_column_config_schema(
|
|
56
|
+
renderer: &Renderer,
|
|
57
|
+
view_config: &ViewConfig,
|
|
58
|
+
metadata: &SessionMetadata,
|
|
59
|
+
column_name: &str,
|
|
60
|
+
current_value: Option<&serde_json::Map<String, serde_json::Value>>,
|
|
61
|
+
abs_max: Option<f64>,
|
|
62
|
+
) -> ApiResult<ColumnConfigSchema> {
|
|
63
|
+
let plugin = renderer.get_active_plugin()?;
|
|
64
|
+
let plugin_config = renderer.metadata();
|
|
65
|
+
let names = &plugin_config.config_column_names;
|
|
66
|
+
let group = view_config
|
|
67
|
+
.columns
|
|
68
|
+
.iter()
|
|
69
|
+
.find_position(|maybe_s| maybe_s.as_deref() == Some(column_name))
|
|
70
|
+
.and_then(|(idx, _)| names.get(idx))
|
|
71
|
+
.map(|s| s.as_str());
|
|
72
|
+
|
|
73
|
+
let view_type = if let Some(x) = metadata.get_column_view_type(column_name) {
|
|
74
|
+
x
|
|
75
|
+
} else {
|
|
76
|
+
return Ok(ColumnConfigSchema { fields: vec![] });
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Route through serde_json so maps serialize as plain JS objects
|
|
80
|
+
// rather than `Map` instances — plugin code accesses these with
|
|
81
|
+
// property syntax, not `.get()`.
|
|
82
|
+
let current_js = wasm_bindgen::JsValue::from_serde_ext(¤t_value)
|
|
83
|
+
.unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
84
|
+
let view_config_js =
|
|
85
|
+
wasm_bindgen::JsValue::from_serde_ext(view_config).unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
86
|
+
|
|
87
|
+
let stats = ColumnStats { abs_max };
|
|
88
|
+
let stats_js =
|
|
89
|
+
wasm_bindgen::JsValue::from_serde_ext(&stats).unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
90
|
+
|
|
91
|
+
let raw = plugin._column_config_schema(
|
|
92
|
+
&view_type.to_string(),
|
|
93
|
+
group,
|
|
94
|
+
column_name,
|
|
95
|
+
¤t_js,
|
|
96
|
+
&view_config_js,
|
|
97
|
+
&stats_js,
|
|
98
|
+
)?;
|
|
99
|
+
|
|
100
|
+
serde_wasm_bindgen::from_value(raw).map_err(|e| e.into())
|
|
101
|
+
}
|