@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
|
@@ -15,9 +15,9 @@ 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
20
|
use crate::session::Session;
|
|
20
|
-
use crate::tasks::*;
|
|
21
21
|
|
|
22
22
|
pub type ExportDropDownMenuItem = DropDownMenuItem<ExportFile>;
|
|
23
23
|
|
|
@@ -45,8 +45,7 @@ impl Component for ExportDropDownMenu {
|
|
|
45
45
|
|
|
46
46
|
fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
|
|
47
47
|
let callback = ctx.link().callback(|_| ExportDropDownMenuMsg::TitleChange);
|
|
48
|
-
let
|
|
49
|
-
let is_chart = plugin.name().as_str() != "Datagrid";
|
|
48
|
+
let is_chart = ctx.props().renderer.is_chart();
|
|
50
49
|
html! {
|
|
51
50
|
<>
|
|
52
51
|
<span class="dropdown-group-label">{ "Save as" }</span>
|
|
@@ -18,6 +18,7 @@ use yew::prelude::*;
|
|
|
18
18
|
use super::form::code_editor::*;
|
|
19
19
|
use super::style::LocalStyle;
|
|
20
20
|
use crate::session::{Session, SessionMetadata, SessionMetadataRc};
|
|
21
|
+
use crate::tasks::{ExprValidation, validate_expression};
|
|
21
22
|
use crate::*;
|
|
22
23
|
|
|
23
24
|
#[derive(Properties, PartialEq, Clone)]
|
|
@@ -45,7 +46,7 @@ pub struct ExpressionEditorProps {
|
|
|
45
46
|
#[derive(Debug)]
|
|
46
47
|
pub enum ExpressionEditorMsg {
|
|
47
48
|
SetExpr(Rc<String>),
|
|
48
|
-
ValidateComplete(
|
|
49
|
+
ValidateComplete(ExprValidation),
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/// Expression editor component `CodeEditor` and a button toolbar.
|
|
@@ -53,6 +54,13 @@ pub struct ExpressionEditor {
|
|
|
53
54
|
expr: Rc<String>,
|
|
54
55
|
error: Option<ExprValidationError>,
|
|
55
56
|
oninput: Callback<Rc<String>>,
|
|
57
|
+
/// Monotonically increasing request id used to drop stale
|
|
58
|
+
/// validation results when the user types faster than the engine
|
|
59
|
+
/// can validate.
|
|
60
|
+
validation_req_id: u64,
|
|
61
|
+
/// The id of the most recently dispatched validation; the result
|
|
62
|
+
/// is only applied when its echoed id matches.
|
|
63
|
+
last_dispatched_req_id: u64,
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
impl Component for ExpressionEditor {
|
|
@@ -69,6 +77,8 @@ impl Component for ExpressionEditor {
|
|
|
69
77
|
error: None,
|
|
70
78
|
expr,
|
|
71
79
|
oninput,
|
|
80
|
+
validation_req_id: 0,
|
|
81
|
+
last_dispatched_req_id: 0,
|
|
72
82
|
}
|
|
73
83
|
}
|
|
74
84
|
|
|
@@ -77,23 +87,25 @@ impl Component for ExpressionEditor {
|
|
|
77
87
|
ExpressionEditorMsg::SetExpr(val) => {
|
|
78
88
|
ctx.props().on_input.emit(val.clone());
|
|
79
89
|
self.expr = val.clone();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
90
|
+
self.validation_req_id += 1;
|
|
91
|
+
self.last_dispatched_req_id = self.validation_req_id;
|
|
92
|
+
let cb = ctx.link().callback(ExpressionEditorMsg::ValidateComplete);
|
|
93
|
+
validate_expression(
|
|
94
|
+
&ctx.props().session,
|
|
95
|
+
cb,
|
|
96
|
+
self.validation_req_id,
|
|
97
|
+
(*val).clone(),
|
|
98
|
+
);
|
|
91
99
|
true
|
|
92
100
|
},
|
|
93
|
-
ExpressionEditorMsg::ValidateComplete(
|
|
94
|
-
self.
|
|
101
|
+
ExpressionEditorMsg::ValidateComplete(result) => {
|
|
102
|
+
if result.req_id != self.last_dispatched_req_id {
|
|
103
|
+
// Stale result from a superseded request — ignore.
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
self.error = result.error;
|
|
95
107
|
if self.error.is_none() {
|
|
96
|
-
|
|
108
|
+
let _: Option<bool> = try {
|
|
97
109
|
let alias = ctx.props().alias.as_ref()?;
|
|
98
110
|
let session = &ctx.props().session;
|
|
99
111
|
let old = ctx.props().metadata.get_expression_by_alias(alias)?;
|
|
@@ -102,8 +114,8 @@ impl Component for ExpressionEditor {
|
|
|
102
114
|
.metadata_mut()
|
|
103
115
|
.set_edit_by_alias(alias, self.expr.to_string());
|
|
104
116
|
|
|
105
|
-
|
|
106
|
-
}
|
|
117
|
+
is_edited
|
|
118
|
+
};
|
|
107
119
|
|
|
108
120
|
ctx.props().on_validate.emit(true);
|
|
109
121
|
} else {
|
|
@@ -103,7 +103,8 @@ impl FilterDropDownElement {
|
|
|
103
103
|
old_column = self.column
|
|
104
104
|
);
|
|
105
105
|
ApiFuture::spawn(async move {
|
|
106
|
-
let fetched =
|
|
106
|
+
let fetched =
|
|
107
|
+
crate::queries::get_column_values(&session, column.1.clone()).await?;
|
|
107
108
|
*all_values.borrow_mut() = Some(fetched);
|
|
108
109
|
let values = filter_values(&input, &all_values, &exclude);
|
|
109
110
|
let should_hide = values.len() == 1 && values[0] == input;
|
|
@@ -16,7 +16,13 @@ use yew::prelude::*;
|
|
|
16
16
|
|
|
17
17
|
#[derive(Properties, PartialEq, Clone)]
|
|
18
18
|
pub struct ColorRangeProps {
|
|
19
|
-
|
|
19
|
+
/// Extra class appended to the positive input's `class` attribute,
|
|
20
|
+
/// so callers can target the pos / neg sides independently from CSS.
|
|
21
|
+
/// Paired with `neg_class`; both default to empty.
|
|
22
|
+
#[prop_or_default]
|
|
23
|
+
pub pos_class: Classes,
|
|
24
|
+
#[prop_or_default]
|
|
25
|
+
pub neg_class: Classes,
|
|
20
26
|
pub pos_color: String,
|
|
21
27
|
pub neg_color: String,
|
|
22
28
|
pub is_gradient: bool,
|
|
@@ -68,32 +74,33 @@ pub fn color_chooser_component(props: &ColorRangeProps) -> Html {
|
|
|
68
74
|
|
|
69
75
|
let on_reset = use_callback(props.clone(), |_: MouseEvent, deps| deps.on_reset.emit(()));
|
|
70
76
|
|
|
77
|
+
let pos_class = classes!("parameter", "pos-color-param", props.pos_class.clone());
|
|
78
|
+
let neg_class = classes!("parameter", "neg-color-param", props.neg_class.clone());
|
|
79
|
+
|
|
71
80
|
html! {
|
|
72
81
|
<>
|
|
73
82
|
<label id="color-range-label" />
|
|
74
83
|
<div class="color-gradient-container">
|
|
75
84
|
<div style={fg_pos} class="color-selector">
|
|
76
85
|
<input
|
|
77
|
-
|
|
78
|
-
class="parameter pos-color-param"
|
|
86
|
+
class={pos_class}
|
|
79
87
|
type="color"
|
|
80
88
|
value={props.pos_color.to_owned()}
|
|
81
89
|
data-value={props.pos_color.to_owned()}
|
|
82
90
|
oninput={on_pos_color}
|
|
83
91
|
/>
|
|
84
|
-
<label
|
|
92
|
+
<label class="color-label">{ "+" }</label>
|
|
85
93
|
</div>
|
|
86
94
|
<div class="color-thermometer" {style} />
|
|
87
95
|
<div style={fg_neg} class="color-selector">
|
|
88
96
|
<input
|
|
89
|
-
|
|
90
|
-
class="parameter neg-color-param"
|
|
97
|
+
class={neg_class}
|
|
91
98
|
type="color"
|
|
92
99
|
value={props.neg_color.to_owned()}
|
|
93
100
|
data-value={props.neg_color.to_owned()}
|
|
94
101
|
oninput={on_neg_color}
|
|
95
102
|
/>
|
|
96
|
-
<label
|
|
103
|
+
<label class="color-label">{ "-" }</label>
|
|
97
104
|
</div>
|
|
98
105
|
if props.is_modified {
|
|
99
106
|
<span class="reset-default-style" onclick={on_reset} />
|
|
@@ -17,7 +17,6 @@ use perspective_js::utils::{ApiFuture, JsValueSerdeExt};
|
|
|
17
17
|
use wasm_bindgen::prelude::*;
|
|
18
18
|
use yew::prelude::*;
|
|
19
19
|
|
|
20
|
-
use crate::components::containers::trap_door_panel::TrapDoorPanel;
|
|
21
20
|
use crate::components::form::code_editor::CodeEditor;
|
|
22
21
|
use crate::components::style::LocalStyle;
|
|
23
22
|
use crate::css;
|
|
@@ -25,7 +24,6 @@ use crate::js::{MimeType, copy_to_clipboard, paste_from_clipboard};
|
|
|
25
24
|
use crate::presentation::*;
|
|
26
25
|
use crate::renderer::*;
|
|
27
26
|
use crate::session::*;
|
|
28
|
-
use crate::tasks::*;
|
|
29
27
|
use crate::utils::*;
|
|
30
28
|
|
|
31
29
|
#[derive(Clone, PartialEq, Properties)]
|
|
@@ -33,24 +31,17 @@ pub struct DebugPanelProps {
|
|
|
33
31
|
pub presentation: Presentation,
|
|
34
32
|
pub renderer: Renderer,
|
|
35
33
|
pub session: Session,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
impl HasPresentation for DebugPanelProps {
|
|
39
|
-
fn presentation(&self) -> &Presentation {
|
|
40
|
-
&self.presentation
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
34
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
/// Trap-door width pinned by the parent `SettingsPanel` so switching
|
|
36
|
+
/// tabs doesn't shrink the panel. Threaded into the hidden sizer
|
|
37
|
+
/// `<div class="scroll-panel-auto-width">`.
|
|
38
|
+
#[prop_or_default]
|
|
39
|
+
pub initial_width: f64,
|
|
49
40
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
/// Fires once on mount with this panel's measured natural width.
|
|
42
|
+
/// Routed up to `SettingsPanel` which keeps the running max.
|
|
43
|
+
#[prop_or_default]
|
|
44
|
+
pub on_auto_width: Callback<f64>,
|
|
54
45
|
}
|
|
55
46
|
|
|
56
47
|
#[function_component(DebugPanel)]
|
|
@@ -60,6 +51,18 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
|
|
|
60
51
|
let select_all = use_memo((), |()| PubSub::default());
|
|
61
52
|
let modified = use_state_eq(|| false);
|
|
62
53
|
|
|
54
|
+
// Measure natural width on mount and route up to `SettingsPanel`.
|
|
55
|
+
let sizer = use_node_ref();
|
|
56
|
+
use_effect_with(expr.clone(), {
|
|
57
|
+
let sizer = sizer.clone();
|
|
58
|
+
let on_auto_width = props.on_auto_width.clone();
|
|
59
|
+
move |_| {
|
|
60
|
+
if let Some(elem) = sizer.cast::<web_sys::HtmlElement>() {
|
|
61
|
+
on_auto_width.emit(elem.get_bounding_client_rect().width());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
63
66
|
use_effect_with((expr.setter(), props.clone()), {
|
|
64
67
|
clone!(error, modified);
|
|
65
68
|
move |(text, state)| {
|
|
@@ -166,16 +169,12 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
|
|
|
166
169
|
<LocalStyle href={css!("containers/tabs")} />
|
|
167
170
|
<LocalStyle href={css!("form/debug")} />
|
|
168
171
|
<div id="debug-panel-overflow">
|
|
169
|
-
<
|
|
170
|
-
<div
|
|
171
|
-
<
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
</
|
|
175
|
-
<span class="tab tab-padding">
|
|
176
|
-
<div class="tab-title" />
|
|
177
|
-
<div class="tab-border" />
|
|
178
|
-
</span>
|
|
172
|
+
<div id="debug-panel" class="sidebar_column" ref={sizer}>
|
|
173
|
+
<div id="debug-panel-controls">
|
|
174
|
+
<button disabled={!*modified} onclick={onapply}>{ "Apply" }</button>
|
|
175
|
+
<button disabled={!*modified} onclick={onreset}>{ "Reset" }</button>
|
|
176
|
+
<button onclick={oncopy}>{ "Copy" }</button>
|
|
177
|
+
<button onclick={onpaste}>{ "Paste" }</button>
|
|
179
178
|
</div>
|
|
180
179
|
<div id="debug-panel-editor">
|
|
181
180
|
<CodeEditor
|
|
@@ -187,13 +186,11 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
|
|
|
187
186
|
error={(*error).clone()}
|
|
188
187
|
/>
|
|
189
188
|
</div>
|
|
190
|
-
<div
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
</div>
|
|
196
|
-
</TrapDoorPanel>
|
|
189
|
+
<div
|
|
190
|
+
class="scroll-panel-auto-width"
|
|
191
|
+
style={format!("width:{}px", props.initial_width)}
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
197
194
|
</div>
|
|
198
195
|
</>
|
|
199
196
|
}
|
|
@@ -203,7 +200,12 @@ impl DebugPanelProps {
|
|
|
203
200
|
fn set_text(&self, setter: UseStateSetter<Rc<String>>) {
|
|
204
201
|
let props = self.clone();
|
|
205
202
|
ApiFuture::spawn(async move {
|
|
206
|
-
let config =
|
|
203
|
+
let config = crate::queries::get_viewer_config(
|
|
204
|
+
&props.session,
|
|
205
|
+
&props.renderer,
|
|
206
|
+
&props.presentation,
|
|
207
|
+
)
|
|
208
|
+
.await?;
|
|
207
209
|
let json = JsValue::from_serde_ext(&config)?;
|
|
208
210
|
let js_string =
|
|
209
211
|
js_sys::JSON::stringify_with_replacer_and_space(&json, &JsValue::NULL, &2.into())?;
|
|
@@ -238,7 +240,15 @@ impl DebugPanelProps {
|
|
|
238
240
|
ApiFuture::spawn(async move {
|
|
239
241
|
match serde_json::from_str(&text) {
|
|
240
242
|
Ok(config) => {
|
|
241
|
-
match
|
|
243
|
+
match crate::tasks::restore_and_render(
|
|
244
|
+
&props.session,
|
|
245
|
+
&props.renderer,
|
|
246
|
+
&props.presentation,
|
|
247
|
+
config,
|
|
248
|
+
async { Ok(()) },
|
|
249
|
+
)
|
|
250
|
+
.await
|
|
251
|
+
{
|
|
242
252
|
Ok(_) => {
|
|
243
253
|
modified.set(false);
|
|
244
254
|
},
|
|
@@ -10,18 +10,15 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use perspective_js::utils::*;
|
|
14
13
|
use wasm_bindgen::prelude::*;
|
|
15
14
|
use yew::prelude::*;
|
|
16
15
|
|
|
17
16
|
use super::render_warning::RenderWarning;
|
|
18
17
|
use super::status_bar::StatusBar;
|
|
19
|
-
use crate::
|
|
20
|
-
use crate::presentation::Presentation;
|
|
21
|
-
use crate::renderer::limits::RenderLimits;
|
|
18
|
+
use crate::presentation::{Presentation, PresentationProps};
|
|
22
19
|
use crate::renderer::*;
|
|
23
|
-
use crate::session::{Session,
|
|
24
|
-
use crate::
|
|
20
|
+
use crate::session::{Session, SessionProps};
|
|
21
|
+
use crate::tasks::dismiss_render_warning_callback;
|
|
25
22
|
|
|
26
23
|
#[derive(Clone, Properties)]
|
|
27
24
|
pub struct MainPanelProps {
|
|
@@ -32,28 +29,19 @@ pub struct MainPanelProps {
|
|
|
32
29
|
/// + column configs), `false` for config-only.
|
|
33
30
|
pub on_reset: Callback<bool>,
|
|
34
31
|
|
|
35
|
-
///
|
|
36
|
-
///
|
|
37
|
-
|
|
38
|
-
pub
|
|
32
|
+
/// Snapshots threaded from root. Read for `has_table`, `title` here in
|
|
33
|
+
/// the panel itself; threaded wholesale to `StatusBar`/`StatusIndicator`.
|
|
34
|
+
pub session_props: SessionProps,
|
|
35
|
+
pub renderer_props: RendererProps,
|
|
36
|
+
pub presentation_props: PresentationProps,
|
|
39
37
|
|
|
40
|
-
///
|
|
41
|
-
/// `StatusIndicator`.
|
|
42
|
-
pub has_table: Option<TableLoadState>,
|
|
43
|
-
pub is_errored: bool,
|
|
44
|
-
pub stats: Option<ViewStats>,
|
|
45
|
-
pub update_count: u32,
|
|
46
|
-
pub error: Option<TableErrorState>,
|
|
47
|
-
pub title: Option<String>,
|
|
48
|
-
|
|
49
|
-
/// Value props from root's `PresentationProps`, threaded to `StatusBar`.
|
|
38
|
+
/// Derived from root: `settings_open && has_table_loaded`.
|
|
50
39
|
pub is_settings_open: bool,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
pub
|
|
40
|
+
|
|
41
|
+
/// Root-managed in-flight render counter (not engine state).
|
|
42
|
+
pub update_count: u32,
|
|
54
43
|
|
|
55
44
|
/// State
|
|
56
|
-
pub custom_events: CustomEvents,
|
|
57
45
|
pub session: Session,
|
|
58
46
|
pub renderer: Renderer,
|
|
59
47
|
pub presentation: Presentation,
|
|
@@ -61,23 +49,17 @@ pub struct MainPanelProps {
|
|
|
61
49
|
|
|
62
50
|
impl PartialEq for MainPanelProps {
|
|
63
51
|
fn eq(&self, rhs: &Self) -> bool {
|
|
64
|
-
self.
|
|
65
|
-
&& self.
|
|
66
|
-
&& self.
|
|
67
|
-
&& self.update_count == rhs.update_count
|
|
68
|
-
&& self.error == rhs.error
|
|
69
|
-
&& self.title == rhs.title
|
|
52
|
+
self.session_props == rhs.session_props
|
|
53
|
+
&& self.renderer_props == rhs.renderer_props
|
|
54
|
+
&& self.presentation_props == rhs.presentation_props
|
|
70
55
|
&& self.is_settings_open == rhs.is_settings_open
|
|
71
|
-
&& self.
|
|
72
|
-
&& self.available_themes == rhs.available_themes
|
|
73
|
-
&& self.is_workspace == rhs.is_workspace
|
|
74
|
-
&& self.render_limits == rhs.render_limits
|
|
56
|
+
&& self.update_count == rhs.update_count
|
|
75
57
|
}
|
|
76
58
|
}
|
|
77
59
|
|
|
78
60
|
impl MainPanelProps {
|
|
79
61
|
fn is_title(&self) -> bool {
|
|
80
|
-
self.title.is_some()
|
|
62
|
+
self.session_props.title.is_some()
|
|
81
63
|
}
|
|
82
64
|
}
|
|
83
65
|
|
|
@@ -109,10 +91,7 @@ impl Component for MainPanel {
|
|
|
109
91
|
.cast::<web_sys::HtmlElement>()
|
|
110
92
|
.map(JsValue::from)
|
|
111
93
|
{
|
|
112
|
-
ctx.props()
|
|
113
|
-
.custom_events
|
|
114
|
-
.dispatch_event(format!("statusbar-{}", event.type_()).as_str(), &event)
|
|
115
|
-
.unwrap();
|
|
94
|
+
ctx.props().presentation.statusbar_pointer_event.emit(event);
|
|
116
95
|
}
|
|
117
96
|
|
|
118
97
|
false
|
|
@@ -126,16 +105,13 @@ impl Component for MainPanel {
|
|
|
126
105
|
|
|
127
106
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
128
107
|
let Self::Properties {
|
|
129
|
-
custom_events,
|
|
130
108
|
presentation,
|
|
131
109
|
renderer,
|
|
132
110
|
session,
|
|
133
111
|
..
|
|
134
112
|
} = ctx.props();
|
|
135
113
|
|
|
136
|
-
let is_settings_open = ctx.props().is_settings_open
|
|
137
|
-
&& matches!(ctx.props().has_table, Some(TableLoadState::Loaded));
|
|
138
|
-
|
|
114
|
+
let is_settings_open = ctx.props().is_settings_open;
|
|
139
115
|
let on_settings = (!is_settings_open).then(|| ctx.props().on_settings.clone());
|
|
140
116
|
|
|
141
117
|
let mut class = classes!();
|
|
@@ -148,17 +124,7 @@ impl Component for MainPanel {
|
|
|
148
124
|
}
|
|
149
125
|
|
|
150
126
|
let pointerdown = ctx.link().callback(MainPanelMsg::PointerEvent);
|
|
151
|
-
let on_dismiss_warning =
|
|
152
|
-
clone!(renderer, session);
|
|
153
|
-
Callback::from(move |_: ()| {
|
|
154
|
-
clone!(renderer, session);
|
|
155
|
-
ApiFuture::spawn(async move {
|
|
156
|
-
renderer.disable_active_plugin_render_warning();
|
|
157
|
-
let view_task = session.get_view();
|
|
158
|
-
renderer.update(view_task).await
|
|
159
|
-
});
|
|
160
|
-
})
|
|
161
|
-
};
|
|
127
|
+
let on_dismiss_warning = dismiss_render_warning_callback(session, renderer);
|
|
162
128
|
|
|
163
129
|
html! {
|
|
164
130
|
<div id="main_column">
|
|
@@ -166,17 +132,10 @@ impl Component for MainPanel {
|
|
|
166
132
|
id="status_bar"
|
|
167
133
|
{on_settings}
|
|
168
134
|
on_reset={ctx.props().on_reset.clone()}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
stats={ctx.props().stats.clone()}
|
|
172
|
-
update_count={ctx.props().update_count}
|
|
173
|
-
error={ctx.props().error.clone()}
|
|
174
|
-
title={ctx.props().title.clone()}
|
|
135
|
+
session_props={ctx.props().session_props.clone()}
|
|
136
|
+
presentation_props={ctx.props().presentation_props.clone()}
|
|
175
137
|
is_settings_open={ctx.props().is_settings_open}
|
|
176
|
-
|
|
177
|
-
available_themes={ctx.props().available_themes.clone()}
|
|
178
|
-
is_workspace={ctx.props().is_workspace}
|
|
179
|
-
{custom_events}
|
|
138
|
+
update_count={ctx.props().update_count}
|
|
180
139
|
{presentation}
|
|
181
140
|
{renderer}
|
|
182
141
|
{session}
|
|
@@ -189,7 +148,7 @@ impl Component for MainPanel {
|
|
|
189
148
|
>
|
|
190
149
|
<RenderWarning
|
|
191
150
|
on_dismiss={on_dismiss_warning}
|
|
192
|
-
dimensions={ctx.props().render_limits}
|
|
151
|
+
dimensions={ctx.props().renderer_props.render_limits}
|
|
193
152
|
/>
|
|
194
153
|
<slot />
|
|
195
154
|
</div>
|
|
@@ -30,8 +30,9 @@ pub mod form;
|
|
|
30
30
|
pub mod function_dropdown;
|
|
31
31
|
pub mod main_panel;
|
|
32
32
|
pub mod modal;
|
|
33
|
-
pub mod
|
|
33
|
+
pub mod number_series_style;
|
|
34
34
|
pub mod plugin_selector;
|
|
35
|
+
pub mod plugin_tab;
|
|
35
36
|
pub mod portal;
|
|
36
37
|
pub mod render_warning;
|
|
37
38
|
pub mod settings_panel;
|
|
@@ -0,0 +1,161 @@
|
|
|
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 web_sys::{HtmlInputElement, InputEvent};
|
|
14
|
+
use yew::prelude::*;
|
|
15
|
+
|
|
16
|
+
use super::modal::{ModalLink, SetModalLink};
|
|
17
|
+
use super::style::LocalStyle;
|
|
18
|
+
use crate::components::form::select_enum_field::SelectEnumField;
|
|
19
|
+
use crate::config::*;
|
|
20
|
+
use crate::css;
|
|
21
|
+
use crate::utils::WeakScope;
|
|
22
|
+
|
|
23
|
+
#[derive(Properties)]
|
|
24
|
+
pub struct NumberSeriesStyleProps {
|
|
25
|
+
pub config: Option<NumberSeriesStyleConfig>,
|
|
26
|
+
pub default_config: NumberSeriesStyleDefaultConfig,
|
|
27
|
+
|
|
28
|
+
#[prop_or_default]
|
|
29
|
+
pub on_change: Callback<ColumnConfigFieldUpdate>,
|
|
30
|
+
|
|
31
|
+
#[prop_or_default]
|
|
32
|
+
pub keys: Vec<String>,
|
|
33
|
+
|
|
34
|
+
#[prop_or_default]
|
|
35
|
+
weak_link: WeakScope<NumberSeriesStyle>,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl ModalLink<NumberSeriesStyle> for NumberSeriesStyleProps {
|
|
39
|
+
fn weak_link(&self) -> &'_ WeakScope<NumberSeriesStyle> {
|
|
40
|
+
&self.weak_link
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
impl PartialEq for NumberSeriesStyleProps {
|
|
45
|
+
fn eq(&self, other: &Self) -> bool {
|
|
46
|
+
self.config == other.config && self.default_config == other.default_config
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pub enum NumberSeriesStyleMsg {
|
|
51
|
+
ChartTypeChanged(Option<ChartType>),
|
|
52
|
+
StackChanged(Option<bool>),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Form control for the per-column `chart_type` + `stack` picker. Rendered
|
|
56
|
+
/// inside the column-settings sidebar when the active plugin returns a
|
|
57
|
+
/// `ControlSpec::NumberSeriesStyle` from its `column_config_schema` hook.
|
|
58
|
+
pub struct NumberSeriesStyle {
|
|
59
|
+
config: NumberSeriesStyleConfig,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl Component for NumberSeriesStyle {
|
|
63
|
+
type Message = NumberSeriesStyleMsg;
|
|
64
|
+
type Properties = NumberSeriesStyleProps;
|
|
65
|
+
|
|
66
|
+
fn create(ctx: &Context<Self>) -> Self {
|
|
67
|
+
ctx.set_modal_link();
|
|
68
|
+
Self {
|
|
69
|
+
config: ctx.props().config.clone().unwrap_or_default(),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
|
|
74
|
+
let new_config = ctx.props().config.clone().unwrap_or_default();
|
|
75
|
+
if self.config != new_config {
|
|
76
|
+
self.config = new_config;
|
|
77
|
+
true
|
|
78
|
+
} else {
|
|
79
|
+
false
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
84
|
+
match msg {
|
|
85
|
+
NumberSeriesStyleMsg::ChartTypeChanged(val) => {
|
|
86
|
+
self.config.chart_type = val.unwrap_or_default();
|
|
87
|
+
// Hiding the stack checkbox on Line/Scatter also clears any
|
|
88
|
+
// lingering override so the JSON stays empty by default.
|
|
89
|
+
if !self.config.chart_type.supports_stack() {
|
|
90
|
+
self.config.stack = None;
|
|
91
|
+
}
|
|
92
|
+
self.dispatch_config(ctx);
|
|
93
|
+
true
|
|
94
|
+
},
|
|
95
|
+
NumberSeriesStyleMsg::StackChanged(val) => {
|
|
96
|
+
self.config.stack = val;
|
|
97
|
+
self.dispatch_config(ctx);
|
|
98
|
+
true
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
104
|
+
let chart_type_changed = ctx.link().callback(NumberSeriesStyleMsg::ChartTypeChanged);
|
|
105
|
+
|
|
106
|
+
let stack_controls = if self.config.chart_type.supports_stack() {
|
|
107
|
+
// Default: bar/area stack. `None` == inherit the default.
|
|
108
|
+
let checked = self.config.stack.unwrap_or(true);
|
|
109
|
+
let oninput = ctx.link().callback(move |e: InputEvent| {
|
|
110
|
+
let input: HtmlInputElement = e.target_unchecked_into();
|
|
111
|
+
let next = input.checked();
|
|
112
|
+
// Persist explicit `false` overrides; the "stacked" default
|
|
113
|
+
// round-trips as `None` to keep JSON empty.
|
|
114
|
+
NumberSeriesStyleMsg::StackChanged(if next { None } else { Some(false) })
|
|
115
|
+
});
|
|
116
|
+
html! {
|
|
117
|
+
<div class="row">
|
|
118
|
+
<label id="stack-label" />
|
|
119
|
+
<input type="checkbox" id="stack-checkbox" {checked} {oninput} />
|
|
120
|
+
</div>
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
html! {}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
html! {
|
|
127
|
+
<>
|
|
128
|
+
<LocalStyle href={css!("column-style")} />
|
|
129
|
+
<div id="column-style-container" class="number-series-style-container">
|
|
130
|
+
<SelectEnumField<ChartType>
|
|
131
|
+
label="chart-type"
|
|
132
|
+
on_change={chart_type_changed}
|
|
133
|
+
current_value={self.config.chart_type}
|
|
134
|
+
/>
|
|
135
|
+
{ stack_controls }
|
|
136
|
+
</div>
|
|
137
|
+
</>
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
impl NumberSeriesStyle {
|
|
143
|
+
/// Dispatch the current config as an update. The default (Bar + no
|
|
144
|
+
/// stack override) round-trips as an empty JSON object via
|
|
145
|
+
/// `skip_serializing_if`, which means a field-level reset for this
|
|
146
|
+
/// schema field.
|
|
147
|
+
fn dispatch_config(&self, ctx: &Context<Self>) {
|
|
148
|
+
let value = if self.config == NumberSeriesStyleConfig::default() {
|
|
149
|
+
serde_json::Map::new()
|
|
150
|
+
} else {
|
|
151
|
+
match serde_json::to_value(&self.config) {
|
|
152
|
+
Ok(serde_json::Value::Object(m)) => m,
|
|
153
|
+
_ => serde_json::Map::new(),
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
ctx.props().on_change.emit(ColumnConfigFieldUpdate {
|
|
157
|
+
keys: ctx.props().keys.clone(),
|
|
158
|
+
value,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|