@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
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
//! Plugin-scoped settings tab. Mirrors `style_tab` but operates on the
|
|
14
|
+
//! active plugin's `save()`/`restore()` token rather than a per-column
|
|
15
|
+
//! config map. The schema comes from `plugin.plugin_config_schema()`;
|
|
16
|
+
//! field updates are dispatched through `tasks::send_plugin_config`.
|
|
17
|
+
|
|
18
|
+
use itertools::Itertools;
|
|
19
|
+
use perspective_client::config::ViewConfig;
|
|
20
|
+
use yew::prelude::*;
|
|
21
|
+
|
|
22
|
+
use crate::components::column_settings_sidebar::style_tab::primitive_field::{
|
|
23
|
+
BoolField, ColorField, ColorRangeField, EnumField, NumberFieldPrimitive,
|
|
24
|
+
};
|
|
25
|
+
use crate::components::style::LocalStyle;
|
|
26
|
+
use crate::config::ControlSpec;
|
|
27
|
+
use crate::css;
|
|
28
|
+
use crate::queries::get_plugin_config_schema;
|
|
29
|
+
use crate::renderer::Renderer;
|
|
30
|
+
use crate::session::Session;
|
|
31
|
+
use crate::tasks::send_plugin_config;
|
|
32
|
+
use crate::utils::PtrEqRc;
|
|
33
|
+
|
|
34
|
+
#[derive(Clone, PartialEq, Properties)]
|
|
35
|
+
pub struct PluginTabProps {
|
|
36
|
+
/// View config snapshot — passed to the plugin schema callback in
|
|
37
|
+
/// case the plugin wants to gate fields based on it.
|
|
38
|
+
pub view_config: PtrEqRc<ViewConfig>,
|
|
39
|
+
|
|
40
|
+
/// Active plugin's `plugin_config` bucket — threaded as a value
|
|
41
|
+
/// snapshot from `RendererProps`. Changes on every mutation path
|
|
42
|
+
/// that fires `plugin_config_changed` (in-tab edit,
|
|
43
|
+
/// `restore_and_render` JSON paste, `reset_all` with `all=true`)
|
|
44
|
+
/// AND on plugin switch (the active bucket is keyed by plugin
|
|
45
|
+
/// name, so `to_props()` produces a fresh `Rc` after
|
|
46
|
+
/// `commit_plugin_idx`). PluginTab is a pure function of this
|
|
47
|
+
/// prop — no `Renderer::get_plugin_config()` reads against the
|
|
48
|
+
/// interior-mutable handle.
|
|
49
|
+
pub plugin_config: PtrEqRc<serde_json::Map<String, serde_json::Value>>,
|
|
50
|
+
|
|
51
|
+
// State
|
|
52
|
+
pub renderer: Renderer,
|
|
53
|
+
pub session: Session,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[function_component]
|
|
57
|
+
pub fn PluginTab(props: &PluginTabProps) -> Html {
|
|
58
|
+
// Memoize the JS-side `plugin_config_schema` call. The schema is a
|
|
59
|
+
// function of (active plugin, current plugin_config values,
|
|
60
|
+
// view_config); each of those arrives as a prop so the deps tuple
|
|
61
|
+
// uses cheap pointer-equality / value-equality. Yew re-runs the
|
|
62
|
+
// closure only when one of them actually changed, so the JS
|
|
63
|
+
// round-trip doesn't fire on unrelated re-renders.
|
|
64
|
+
//
|
|
65
|
+
// The closure captures `renderer` to dispatch `_plugin_config_schema`
|
|
66
|
+
// through the active plugin handle, but resolves it via the props
|
|
67
|
+
// at call time so the schema query is bound to the same atomic
|
|
68
|
+
// snapshot the rendered controls read from. No race window between
|
|
69
|
+
// a plugin swap and the schema fetch — both observe the same
|
|
70
|
+
// `RendererProps` value.
|
|
71
|
+
let schema = {
|
|
72
|
+
let renderer = props.renderer.clone();
|
|
73
|
+
let view_config = props.view_config.clone();
|
|
74
|
+
use_memo(
|
|
75
|
+
(props.plugin_config.clone(), props.view_config.clone()),
|
|
76
|
+
move |_| match get_plugin_config_schema(&renderer, &view_config) {
|
|
77
|
+
Ok(schema) => schema.fields,
|
|
78
|
+
Err(error) => {
|
|
79
|
+
tracing::error!("{}", error);
|
|
80
|
+
vec![]
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
let on_change = {
|
|
87
|
+
let session = props.session.clone();
|
|
88
|
+
let renderer = props.renderer.clone();
|
|
89
|
+
yew::Callback::from(move |update: crate::config::ColumnConfigFieldUpdate| {
|
|
90
|
+
// `send_plugin_config` emits `plugin_config_changed`,
|
|
91
|
+
// which the root component's subscription
|
|
92
|
+
// (`create_subscriptions`) turns into an `UpdateRenderer`
|
|
93
|
+
// dispatch carrying a fresh `RendererProps`. Yew's prop
|
|
94
|
+
// diff propagates the new `plugin_config` into this
|
|
95
|
+
// component automatically — no manual revision bump.
|
|
96
|
+
send_plugin_config(&session, &renderer, update);
|
|
97
|
+
})
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
let raw_config = &*props.plugin_config;
|
|
101
|
+
let components = schema
|
|
102
|
+
.iter()
|
|
103
|
+
.cloned()
|
|
104
|
+
.filter_map(|spec| {
|
|
105
|
+
let component = match spec {
|
|
106
|
+
ControlSpec::Enum {
|
|
107
|
+
key,
|
|
108
|
+
variants,
|
|
109
|
+
default,
|
|
110
|
+
} => {
|
|
111
|
+
let current = raw_config
|
|
112
|
+
.get(&key)
|
|
113
|
+
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
|
114
|
+
html! {
|
|
115
|
+
<EnumField
|
|
116
|
+
field_key={key}
|
|
117
|
+
{variants}
|
|
118
|
+
{default}
|
|
119
|
+
{current}
|
|
120
|
+
on_change={on_change.clone()}
|
|
121
|
+
/>
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
ControlSpec::Bool { key, default } => {
|
|
125
|
+
let current = raw_config.get(&key).and_then(|v| v.as_bool());
|
|
126
|
+
html! {
|
|
127
|
+
<BoolField
|
|
128
|
+
field_key={key}
|
|
129
|
+
{default}
|
|
130
|
+
{current}
|
|
131
|
+
on_change={on_change.clone()}
|
|
132
|
+
/>
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
ControlSpec::Color { key, default } => {
|
|
136
|
+
let current = raw_config
|
|
137
|
+
.get(&key)
|
|
138
|
+
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
|
139
|
+
html! {
|
|
140
|
+
<ColorField
|
|
141
|
+
field_key={key}
|
|
142
|
+
{default}
|
|
143
|
+
{current}
|
|
144
|
+
on_change={on_change.clone()}
|
|
145
|
+
/>
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
ControlSpec::ColorRange {
|
|
149
|
+
key_pos,
|
|
150
|
+
key_neg,
|
|
151
|
+
default_pos,
|
|
152
|
+
default_neg,
|
|
153
|
+
is_gradient,
|
|
154
|
+
} => {
|
|
155
|
+
let current_pos = raw_config
|
|
156
|
+
.get(&key_pos)
|
|
157
|
+
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
|
158
|
+
let current_neg = raw_config
|
|
159
|
+
.get(&key_neg)
|
|
160
|
+
.and_then(|v| v.as_str().map(|s| s.to_string()));
|
|
161
|
+
html! {
|
|
162
|
+
<ColorRangeField
|
|
163
|
+
field_key_pos={key_pos}
|
|
164
|
+
field_key_neg={key_neg}
|
|
165
|
+
{default_pos}
|
|
166
|
+
{default_neg}
|
|
167
|
+
{current_pos}
|
|
168
|
+
{current_neg}
|
|
169
|
+
{is_gradient}
|
|
170
|
+
on_change={on_change.clone()}
|
|
171
|
+
/>
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
ControlSpec::Number {
|
|
175
|
+
key,
|
|
176
|
+
default,
|
|
177
|
+
min,
|
|
178
|
+
max,
|
|
179
|
+
step,
|
|
180
|
+
include,
|
|
181
|
+
} => {
|
|
182
|
+
let current = raw_config.get(&key).and_then(|v| v.as_f64());
|
|
183
|
+
html! {
|
|
184
|
+
<NumberFieldPrimitive
|
|
185
|
+
field_key={key}
|
|
186
|
+
{default}
|
|
187
|
+
{current}
|
|
188
|
+
{min}
|
|
189
|
+
{max}
|
|
190
|
+
{step}
|
|
191
|
+
{include}
|
|
192
|
+
on_change={on_change.clone()}
|
|
193
|
+
/>
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
// Column-scoped variants don't apply to
|
|
197
|
+
// plugin-level config; drop silently.
|
|
198
|
+
ControlSpec::AggregateDepth
|
|
199
|
+
| ControlSpec::NumberSeriesStyle { .. }
|
|
200
|
+
| ControlSpec::DatetimeFormat
|
|
201
|
+
| ControlSpec::StringFormat
|
|
202
|
+
| ControlSpec::Symbols { .. }
|
|
203
|
+
| ControlSpec::NumberFormat
|
|
204
|
+
| ControlSpec::String { .. } => {
|
|
205
|
+
return None;
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
Some(html! { <fieldset class="style-control">{ component }</fieldset> })
|
|
210
|
+
})
|
|
211
|
+
.collect_vec();
|
|
212
|
+
|
|
213
|
+
html! {
|
|
214
|
+
<div id="plugin-tab" class="sidebar_column scrollable">
|
|
215
|
+
<LocalStyle href={css!("column-style")} />
|
|
216
|
+
<LocalStyle href={css!("plugin-settings-panel")} />
|
|
217
|
+
<LocalStyle href={css!("containers/tabs")} />
|
|
218
|
+
<div id="plugin-config-container" class="tab-section">{ components }</div>
|
|
219
|
+
</div>
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -18,21 +18,22 @@ use yew::prelude::*;
|
|
|
18
18
|
|
|
19
19
|
use super::column_selector::ColumnSelector;
|
|
20
20
|
use super::plugin_selector::PluginSelector;
|
|
21
|
+
use super::plugin_tab::PluginTab;
|
|
21
22
|
use crate::components::containers::sidebar_close_button::SidebarCloseButton;
|
|
23
|
+
use crate::components::form::debug::DebugPanel;
|
|
22
24
|
use crate::config::PluginUpdate;
|
|
23
|
-
use crate::dragdrop::*;
|
|
24
25
|
use crate::presentation::{ColumnLocator, OpenColumnSettings, Presentation};
|
|
25
26
|
use crate::renderer::*;
|
|
26
27
|
use crate::session::column_defaults_update::*;
|
|
27
28
|
use crate::session::*;
|
|
28
|
-
use crate::tasks::
|
|
29
|
+
use crate::tasks::update_and_render;
|
|
29
30
|
use crate::utils::*;
|
|
30
31
|
|
|
31
32
|
#[derive(Clone, Properties)]
|
|
32
33
|
pub struct SettingsPanelProps {
|
|
33
34
|
pub on_close: Callback<()>,
|
|
34
35
|
pub on_resize: Rc<PubSub<()>>,
|
|
35
|
-
pub on_select_column: Callback<ColumnLocator
|
|
36
|
+
pub on_select_column: Callback<Option<ColumnLocator>>,
|
|
36
37
|
pub on_debug: Callback<()>,
|
|
37
38
|
pub is_debug: bool,
|
|
38
39
|
|
|
@@ -42,12 +43,20 @@ pub struct SettingsPanelProps {
|
|
|
42
43
|
pub has_table: Option<TableLoadState>,
|
|
43
44
|
pub named_column_count: usize,
|
|
44
45
|
pub view_config: PtrEqRc<ViewConfig>,
|
|
46
|
+
|
|
47
|
+
/// Snapshot of the active plugin's `plugin_config` bucket, threaded
|
|
48
|
+
/// from `RendererProps`. Forwarded into `PluginTab` so the tab is
|
|
49
|
+
/// prop-driven instead of reading `Renderer` directly.
|
|
50
|
+
pub plugin_config: PtrEqRc<serde_json::Map<String, serde_json::Value>>,
|
|
51
|
+
|
|
45
52
|
/// Column currently being dragged (if any) — threaded to show drag
|
|
46
53
|
/// highlights without per-component `DragDrop` PubSub subscriptions.
|
|
47
54
|
pub drag_column: Option<String>,
|
|
55
|
+
|
|
48
56
|
/// Cloned session metadata snapshot — threaded from `SessionProps`
|
|
49
57
|
/// so that metadata changes trigger re-renders via prop diffing.
|
|
50
58
|
pub metadata: SessionMetadataRc,
|
|
59
|
+
|
|
51
60
|
/// Snapshot of the column-settings sidebar state — threaded from
|
|
52
61
|
/// `PresentationProps` so that open/close triggers re-renders.
|
|
53
62
|
pub open_column_settings: OpenColumnSettings,
|
|
@@ -55,8 +64,29 @@ pub struct SettingsPanelProps {
|
|
|
55
64
|
/// Selected theme name, threaded for PortalModal consumers.
|
|
56
65
|
pub selected_theme: Option<String>,
|
|
57
66
|
|
|
67
|
+
/// Controlled: the currently selected tab. Lifted to `PerspectiveViewer`
|
|
68
|
+
/// so that messages like `OpenColumnSettings` can revert the tab without
|
|
69
|
+
/// the panel owning the state.
|
|
70
|
+
pub selected_tab: SelectedTab,
|
|
71
|
+
|
|
72
|
+
/// Controlled: the running max of measured tab widths. Lifted so that
|
|
73
|
+
/// `SettingsPanelSizeUpdate(None)` (divider reset) can clear it.
|
|
74
|
+
pub auto_width: f64,
|
|
75
|
+
|
|
76
|
+
/// Callback invoked when the user clicks a tab.
|
|
77
|
+
pub on_select_tab: Callback<SelectedTab>,
|
|
78
|
+
|
|
79
|
+
/// Callback invoked by tab subtrees reporting their natural width.
|
|
80
|
+
pub on_auto_width: Callback<f64>,
|
|
81
|
+
|
|
82
|
+
/// Fires when the outer split-panel divider is reset; threaded into
|
|
83
|
+
/// `ColumnSelector` so its inner `ScrollPanel` can drop its persistent
|
|
84
|
+
/// `viewport_width` and re-measure honestly. Without this, the
|
|
85
|
+
/// `auto_width` reset in `PerspectiveViewer` rebounds immediately as
|
|
86
|
+
/// the ScrollPanel republishes its stale cached width.
|
|
87
|
+
pub on_dimensions_reset: Rc<PubSub<()>>,
|
|
88
|
+
|
|
58
89
|
/// State
|
|
59
|
-
pub dragdrop: DragDrop,
|
|
60
90
|
pub session: Session,
|
|
61
91
|
pub renderer: Renderer,
|
|
62
92
|
pub presentation: Presentation,
|
|
@@ -70,17 +100,27 @@ impl PartialEq for SettingsPanelProps {
|
|
|
70
100
|
&& self.has_table == rhs.has_table
|
|
71
101
|
&& self.named_column_count == rhs.named_column_count
|
|
72
102
|
&& self.view_config == rhs.view_config
|
|
103
|
+
&& self.plugin_config == rhs.plugin_config
|
|
73
104
|
&& self.drag_column == rhs.drag_column
|
|
74
105
|
&& self.metadata == rhs.metadata
|
|
75
106
|
&& self.open_column_settings == rhs.open_column_settings
|
|
76
107
|
&& self.selected_theme == rhs.selected_theme
|
|
108
|
+
&& self.selected_tab == rhs.selected_tab
|
|
109
|
+
&& self.auto_width == rhs.auto_width
|
|
77
110
|
}
|
|
78
111
|
}
|
|
79
112
|
|
|
113
|
+
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
|
114
|
+
pub enum SelectedTab {
|
|
115
|
+
#[default]
|
|
116
|
+
Query,
|
|
117
|
+
Plugin,
|
|
118
|
+
Debug,
|
|
119
|
+
}
|
|
120
|
+
|
|
80
121
|
#[function_component]
|
|
81
122
|
pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
82
123
|
let SettingsPanelProps {
|
|
83
|
-
dragdrop,
|
|
84
124
|
presentation,
|
|
85
125
|
renderer,
|
|
86
126
|
session,
|
|
@@ -91,7 +131,7 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
91
131
|
let locator = props.open_column_settings.locator.clone();
|
|
92
132
|
let config = &props.view_config;
|
|
93
133
|
locator.filter(|locator| match locator {
|
|
94
|
-
ColumnLocator::Table(
|
|
134
|
+
ColumnLocator::Table(_name) => {
|
|
95
135
|
locator
|
|
96
136
|
.name()
|
|
97
137
|
.map(|n| {
|
|
@@ -103,8 +143,7 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
103
143
|
|| config.sort.iter().any(|col| &col.0 == n)
|
|
104
144
|
})
|
|
105
145
|
.unwrap_or_default()
|
|
106
|
-
&&
|
|
107
|
-
.unwrap_or_default()
|
|
146
|
+
&& props.renderer.can_render_column_styles()
|
|
108
147
|
},
|
|
109
148
|
_ => true,
|
|
110
149
|
})
|
|
@@ -112,48 +151,88 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
112
151
|
|
|
113
152
|
let plugin_name = props.plugin_name.clone();
|
|
114
153
|
let available_plugins = props.available_plugins.clone();
|
|
154
|
+
let selected = props.selected_tab;
|
|
155
|
+
|
|
156
|
+
// Shared trap-door width across tabs. Each tab subtree measures its
|
|
157
|
+
// natural width and feeds the result back through `on_auto_width`;
|
|
158
|
+
// the parent keeps the running max so a tab switch never shrinks the
|
|
159
|
+
// panel, and clears it on divider reset.
|
|
160
|
+
let width = props.auto_width;
|
|
161
|
+
let on_auto_width = props.on_auto_width.clone();
|
|
115
162
|
|
|
116
|
-
// Dispatch callback: captures engine handles, constructs config update,
|
|
163
|
+
// Dispatch callback: captures engine handles, constructs config update,
|
|
164
|
+
// hands the apply+draw work to `tasks::update_and_render`.
|
|
117
165
|
let on_select_plugin = {
|
|
118
166
|
clone!(renderer, session, presentation);
|
|
119
167
|
let session_metadata = props.metadata.clone();
|
|
168
|
+
let view_config = props.view_config.clone();
|
|
120
169
|
Callback::from(move |plugin_name: String| {
|
|
121
|
-
if
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ApiFuture::spawn(async move {
|
|
147
|
-
renderer.apply_pending_plugin()?;
|
|
148
|
-
renderer.draw(session.validate().await?.create_view()).await
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
presentation.set_open_column_settings(None);
|
|
170
|
+
if session.is_errored() {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
let metadata = renderer.get_next_plugin_metadata(&PluginUpdate::Update(plugin_name));
|
|
174
|
+
let prev_metadata = renderer.metadata();
|
|
175
|
+
let plugin_config = metadata.as_deref().unwrap_or(&*prev_metadata);
|
|
176
|
+
let rollup_features = session_metadata
|
|
177
|
+
.get_features()
|
|
178
|
+
.map(|x| x.get_group_rollup_modes())
|
|
179
|
+
.unwrap();
|
|
180
|
+
|
|
181
|
+
let group_rollups = plugin_config.get_group_rollups(&rollup_features);
|
|
182
|
+
let mut update = ViewConfigUpdate {
|
|
183
|
+
group_rollup_mode: group_rollups.first().cloned(),
|
|
184
|
+
..ViewConfigUpdate::default()
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
update.set_update_column_defaults(
|
|
188
|
+
&session_metadata,
|
|
189
|
+
&view_config.columns,
|
|
190
|
+
plugin_config,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if let Ok(task) = update_and_render(&session, &renderer, update) {
|
|
194
|
+
ApiFuture::spawn(task);
|
|
153
195
|
}
|
|
196
|
+
|
|
197
|
+
presentation.set_open_column_settings(None);
|
|
154
198
|
})
|
|
155
199
|
};
|
|
156
200
|
|
|
201
|
+
let cb1 = props.on_select_column.clone();
|
|
202
|
+
let set_debug = use_callback(
|
|
203
|
+
props.on_select_tab.clone(),
|
|
204
|
+
move |_: PointerEvent, on_select_tab| {
|
|
205
|
+
on_select_tab.emit(SelectedTab::Debug);
|
|
206
|
+
cb1.emit(None)
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
let cb2 = props.on_select_column.clone();
|
|
211
|
+
let set_plugin = use_callback(
|
|
212
|
+
props.on_select_tab.clone(),
|
|
213
|
+
move |_: PointerEvent, on_select_tab| {
|
|
214
|
+
on_select_tab.emit(SelectedTab::Plugin);
|
|
215
|
+
cb2.emit(None)
|
|
216
|
+
},
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
let set_query = use_callback(
|
|
220
|
+
props.on_select_tab.clone(),
|
|
221
|
+
|_: PointerEvent, on_select_tab| on_select_tab.emit(SelectedTab::Query),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
let tab_class = |l_tab: SelectedTab, r_tab: SelectedTab| {
|
|
225
|
+
if l_tab == r_tab {
|
|
226
|
+
"settings_tab selected_tab"
|
|
227
|
+
} else {
|
|
228
|
+
"settings_tab"
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
let on_open_expr_panel = use_callback(props.on_select_column.clone(), |c, on_select| {
|
|
233
|
+
on_select.emit(Some(c))
|
|
234
|
+
});
|
|
235
|
+
|
|
157
236
|
html! {
|
|
158
237
|
<div id="settings_panel" class="sidebar_column noselect split-panel orient-vertical">
|
|
159
238
|
if selected_column.is_none() {
|
|
@@ -162,25 +241,68 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
162
241
|
on_close_sidebar={&props.on_close.clone()}
|
|
163
242
|
/>
|
|
164
243
|
}
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<PluginSelector {plugin_name} {available_plugins} {on_select_plugin} />
|
|
170
|
-
<ColumnSelector
|
|
171
|
-
on_resize={&props.on_resize}
|
|
172
|
-
on_open_expr_panel={&props.on_select_column}
|
|
173
|
-
{selected_column}
|
|
174
|
-
has_table={props.has_table.clone()}
|
|
175
|
-
named_column_count={props.named_column_count}
|
|
176
|
-
view_config={props.view_config.clone()}
|
|
177
|
-
drag_column={props.drag_column.clone()}
|
|
178
|
-
metadata={props.metadata.clone()}
|
|
179
|
-
selected_theme={props.selected_theme.clone()}
|
|
180
|
-
{dragdrop}
|
|
181
|
-
renderer={renderer.clone()}
|
|
182
|
-
session={session.clone()}
|
|
244
|
+
<PluginSelector
|
|
245
|
+
{plugin_name}
|
|
246
|
+
{available_plugins}
|
|
247
|
+
{on_select_plugin}
|
|
183
248
|
/>
|
|
249
|
+
<div id="settings_tab_bar" class="settings_tab_bar_scroll_offset">
|
|
250
|
+
<div
|
|
251
|
+
id="query_tabbar_tab"
|
|
252
|
+
class={tab_class(selected, SelectedTab::Query)}
|
|
253
|
+
onpointerdown={set_query}
|
|
254
|
+
/>
|
|
255
|
+
<div
|
|
256
|
+
id="plugin_tabbar_tab"
|
|
257
|
+
class={tab_class(selected, SelectedTab::Plugin)}
|
|
258
|
+
onpointerdown={set_plugin}
|
|
259
|
+
/>
|
|
260
|
+
<div
|
|
261
|
+
id="debug_tabbar_tab"
|
|
262
|
+
class={tab_class(selected, SelectedTab::Debug)}
|
|
263
|
+
onpointerdown={set_debug}
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
if selected == SelectedTab::Query {
|
|
267
|
+
<ColumnSelector
|
|
268
|
+
on_resize={&props.on_resize}
|
|
269
|
+
{on_open_expr_panel}
|
|
270
|
+
{selected_column}
|
|
271
|
+
has_table={props.has_table.clone()}
|
|
272
|
+
named_column_count={props.named_column_count}
|
|
273
|
+
view_config={props.view_config.clone()}
|
|
274
|
+
drag_column={props.drag_column.clone()}
|
|
275
|
+
metadata={props.metadata.clone()}
|
|
276
|
+
selected_theme={props.selected_theme.clone()}
|
|
277
|
+
presentation={presentation.clone()}
|
|
278
|
+
renderer={renderer.clone()}
|
|
279
|
+
session={session.clone()}
|
|
280
|
+
initial_width={width}
|
|
281
|
+
on_auto_width={on_auto_width.clone()}
|
|
282
|
+
on_dimensions_reset={&props.on_dimensions_reset}
|
|
283
|
+
/>
|
|
284
|
+
} else if selected == SelectedTab::Plugin {
|
|
285
|
+
<PluginTab
|
|
286
|
+
view_config={props.view_config.clone()}
|
|
287
|
+
plugin_config={props.plugin_config.clone()}
|
|
288
|
+
renderer={renderer.clone()}
|
|
289
|
+
session={session.clone()}
|
|
290
|
+
// initial_width={width}
|
|
291
|
+
// on_auto_width={on_auto_width.clone()}
|
|
292
|
+
/>
|
|
293
|
+
} else {
|
|
294
|
+
<DebugPanel
|
|
295
|
+
{presentation}
|
|
296
|
+
{renderer}
|
|
297
|
+
{session}
|
|
298
|
+
initial_width={width}
|
|
299
|
+
on_auto_width={on_auto_width.clone()}
|
|
300
|
+
/>
|
|
301
|
+
}
|
|
302
|
+
// Sibling sizer keeps the panel width pinned across tab
|
|
303
|
+
// switches; lives outside the tab-body so it survives the
|
|
304
|
+
// tab subtree's unmount.
|
|
305
|
+
<div class="scroll-panel-auto-width" style={format!("width:{}px", width)} />
|
|
184
306
|
</div>
|
|
185
307
|
}
|
|
186
308
|
}
|