@perspective-dev/viewer 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/perspective-viewer.js +2 -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/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/perspective-viewer.inline.js +2 -2
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +2 -2
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/wasm/perspective-viewer.d.ts +57 -53
- package/dist/wasm/perspective-viewer.js +190 -165
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
- package/package.json +7 -5
- package/src/{less/aggregate-selector.less → css/aggregate-selector.css} +23 -20
- package/src/css/column-dropdown.css +109 -0
- package/src/{less/column-selector.less → css/column-selector.css} +160 -158
- package/src/{less/column-settings-panel.less → css/column-settings-panel.css} +69 -59
- package/src/{less/column-style.less → css/column-style.css} +52 -66
- package/src/{less/column-symbol-attributes.less → css/column-symbol-attributes.css} +15 -14
- package/src/{less/config-selector.less → css/config-selector.css} +151 -135
- package/src/{less/containers/dropdown-menu.less → css/containers/dropdown-menu.css} +20 -19
- package/src/{less/containers/pairs-list.less → css/containers/pairs-list.css} +13 -12
- package/src/{themes/variables.less → css/containers/scroll-panel.css} +25 -22
- package/src/{less/containers/split-panel.less → css/containers/split-panel.css} +15 -14
- package/src/{less/containers/tabs.less → css/containers/tabs.css} +17 -19
- package/src/css/dom/checkbox.css +102 -0
- package/src/css/dom/scrollbar.css +35 -0
- package/src/{less/dom/select.less → css/dom/select.css} +17 -18
- package/src/{less/empty-column.less → css/empty-column.css} +19 -18
- package/src/{less/expression-editor.less → css/expression-editor.css} +19 -18
- package/src/{less/filter-dropdown.less → css/filter-dropdown.css} +12 -11
- package/src/{less/filter-item.less → css/filter-item.css} +16 -15
- package/src/{less/form/code-editor.less → css/form/code-editor.css} +26 -30
- package/src/{less/form/debug.less → css/form/debug.css} +19 -18
- package/src/{less/function-dropdown.less → css/function-dropdown.css} +12 -11
- package/src/css/plugin-selector.css +261 -0
- package/src/{less/render-warning.less → css/render-warning.css} +18 -17
- package/src/{less/status-bar.less → css/status-bar.css} +156 -144
- package/src/css/type-icon.css +116 -0
- package/src/{less/viewer.less → css/viewer.css} +112 -146
- package/src/rust/components/column_dropdown.rs +229 -119
- package/src/rust/components/column_selector/active_column.rs +81 -62
- package/src/rust/components/column_selector/add_expression_button.rs +1 -0
- package/src/rust/components/column_selector/aggregate_selector.rs +25 -15
- package/src/rust/components/column_selector/config_selector.rs +315 -199
- package/src/rust/components/column_selector/empty_column.rs +2 -2
- package/src/rust/components/column_selector/expr_edit_button.rs +8 -2
- package/src/rust/components/column_selector/filter_column.rs +37 -26
- package/src/rust/components/column_selector/inactive_column.rs +41 -29
- package/src/rust/components/column_selector/invalid_column.rs +7 -18
- package/src/rust/components/column_selector/pivot_column.rs +11 -5
- package/src/rust/components/column_selector/sort_column.rs +23 -13
- package/src/rust/components/column_selector.rs +163 -84
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +3 -2
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +3 -2
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +2 -3
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +7 -1
- package/src/rust/components/column_settings_sidebar/style_tab.rs +153 -112
- package/src/rust/components/column_settings_sidebar.rs +91 -53
- package/src/rust/components/containers/dragdrop_list.rs +2 -1
- package/src/rust/components/containers/sidebar_close_button.rs +1 -1
- package/src/rust/components/containers/split_panel.rs +1 -0
- package/src/rust/components/containers/tab_list.rs +1 -1
- package/src/rust/components/copy_dropdown.rs +7 -28
- package/src/rust/components/datetime_column_style/custom.rs +2 -2
- package/src/rust/components/datetime_column_style/simple.rs +2 -2
- package/src/rust/components/datetime_column_style.rs +4 -2
- package/src/rust/components/editable_header.rs +7 -4
- package/src/rust/components/empty_row.rs +1 -1
- package/src/rust/components/export_dropdown.rs +4 -30
- package/src/rust/components/expression_editor.rs +19 -10
- package/src/rust/components/filter_dropdown.rs +246 -102
- package/src/rust/components/font_loader.rs +11 -28
- package/src/rust/components/form/code_editor.rs +17 -2
- package/src/rust/components/form/color_range_selector.rs +19 -6
- package/src/rust/components/form/debug.rs +30 -13
- package/src/rust/components/function_dropdown.rs +186 -113
- package/src/rust/components/main_panel.rs +71 -89
- package/src/rust/components/mod.rs +1 -1
- package/src/rust/components/modal.rs +7 -1
- package/src/rust/components/number_column_style.rs +22 -7
- package/src/rust/components/plugin_selector.rs +34 -102
- package/src/rust/components/portal.rs +274 -0
- package/src/rust/components/render_warning.rs +72 -123
- package/src/rust/components/settings_panel.rs +115 -11
- package/src/rust/components/status_bar.rs +222 -98
- package/src/rust/components/status_bar_counter.rs +8 -20
- package/src/rust/components/status_indicator.rs +64 -114
- package/src/rust/components/string_column_style.rs +2 -2
- package/src/rust/components/style/style_cache.rs +5 -1
- package/src/rust/components/viewer.rs +391 -39
- package/src/rust/custom_elements/copy_dropdown.rs +102 -21
- package/src/rust/custom_elements/export_dropdown.rs +102 -20
- package/src/rust/custom_elements/mod.rs +0 -7
- package/src/rust/custom_elements/modal.rs +7 -103
- package/src/rust/custom_elements/viewer.rs +99 -35
- package/src/rust/custom_events.rs +23 -2
- package/src/rust/dragdrop.rs +149 -10
- package/src/{less/containers/scroll-panel.less → rust/engines.rs} +15 -13
- package/src/rust/js/plugin.rs +1 -1
- package/src/rust/lib.rs +5 -4
- package/src/rust/presentation/props.rs +39 -0
- package/src/rust/presentation/sheets.rs +3 -3
- package/src/rust/presentation.rs +44 -8
- package/src/rust/renderer/limits.rs +32 -3
- package/src/{less/dom/scrollbar.less → rust/renderer/props.rs} +18 -19
- package/src/rust/renderer.rs +83 -9
- package/src/rust/session/column_defaults_update.rs +1 -1
- package/src/rust/session/metadata.rs +23 -2
- package/src/rust/session/props.rs +178 -0
- package/src/rust/session.rs +124 -117
- package/src/rust/tasks/column_locator.rs +133 -0
- package/src/rust/{model → tasks}/columns_iter_set.rs +14 -23
- package/src/rust/{model → tasks}/edit_expression.rs +34 -10
- package/src/rust/{model → tasks}/eject.rs +2 -2
- package/src/rust/{model → tasks}/get_viewer_config.rs +0 -11
- package/src/rust/{model → tasks}/intersection_observer.rs +19 -3
- package/src/{less/containers/radio-list.less → rust/tasks/is_invalid_drop.rs} +21 -14
- package/src/rust/tasks/mod.rs +52 -0
- package/src/rust/{model → tasks}/plugin_column_styles.rs +69 -46
- package/src/rust/{model → tasks}/resize_observer.rs +39 -6
- package/src/rust/{model → tasks}/send_plugin_config.rs +1 -1
- package/src/rust/tasks/structural.rs +53 -0
- package/src/rust/utils/mod.rs +4 -0
- package/src/rust/utils/modal_position.rs +110 -0
- package/src/rust/utils/ptr_eq_rc.rs +74 -0
- package/src/rust/utils/pubsub.rs +11 -1
- package/src/svg/bg-pattern.png +0 -0
- package/src/svg/close-icon.svg +1 -1
- package/src/svg/expression.svg +1 -1
- package/src/svg/mega-menu-icons-candlestick.svg +1 -1
- package/src/svg/mega-menu-icons-datagrid.svg +1 -2
- package/src/svg/mega-menu-icons-heatmap.svg +1 -1
- package/src/svg/mega-menu-icons-map-scatter.svg +1 -1
- package/src/svg/mega-menu-icons-ohlc.svg +1 -1
- package/src/svg/mega-menu-icons-sunburst.svg +1 -1
- package/src/svg/mega-menu-icons-treemap.svg +1 -1
- package/src/svg/mega-menu-icons-x-bar.svg +1 -1
- package/src/svg/mega-menu-icons-x-y-line.svg +1 -1
- package/src/svg/mega-menu-icons-x-y-scatter.svg +1 -1
- package/src/svg/mega-menu-icons-y-area.svg +1 -1
- package/src/svg/mega-menu-icons-y-bar.svg +1 -1
- package/src/svg/mega-menu-icons-y-line.svg +1 -1
- package/src/svg/mega-menu-icons-y-scatter.svg +1 -1
- package/src/svg/radio-hover.svg +1 -1
- package/src/svg/radio-off.svg +1 -1
- package/src/svg/radio-on.svg +1 -1
- package/src/themes/botanical.css +157 -0
- package/src/themes/defaults.css +139 -0
- package/src/themes/dracula.css +233 -0
- package/src/themes/gruvbox-dark.css +255 -0
- package/src/themes/gruvbox.css +134 -0
- package/src/themes/icons.css +124 -0
- package/src/themes/intl/de.css +102 -0
- package/src/themes/intl/es.css +102 -0
- package/src/themes/intl/fr.css +102 -0
- package/src/themes/intl/ja.css +102 -0
- package/src/themes/intl/pt.css +102 -0
- package/src/themes/intl/zh.css +102 -0
- package/src/themes/intl.css +102 -0
- package/src/themes/monokai.css +233 -0
- package/src/themes/pro-dark.css +158 -0
- package/src/themes/{themes.less → pro.css} +17 -21
- package/src/themes/solarized-dark.css +135 -0
- package/src/themes/solarized.css +95 -0
- package/src/themes/themes.css +22 -0
- package/src/themes/vaporwave.css +256 -0
- package/dist/css/variables.css +0 -0
- package/src/less/column-dropdown.less +0 -95
- package/src/less/dom/checkbox.less +0 -100
- package/src/less/plugin-selector.less +0 -183
- package/src/less/type-icon.less +0 -68
- package/src/rust/components/error_message.rs +0 -56
- package/src/rust/custom_elements/column_dropdown.rs +0 -123
- package/src/rust/custom_elements/filter_dropdown.rs +0 -179
- package/src/rust/custom_elements/function_dropdown.rs +0 -115
- package/src/rust/model/column_locator.rs +0 -82
- package/src/rust/model/is_invalid_drop.rs +0 -36
- package/src/rust/model/mod.rs +0 -100
- package/src/rust/model/reset_all.rs +0 -38
- package/src/rust/model/structural.rs +0 -244
- package/src/themes/botanical.less +0 -142
- package/src/themes/dracula.less +0 -101
- package/src/themes/gruvbox-dark.less +0 -116
- package/src/themes/gruvbox.less +0 -152
- package/src/themes/icons.less +0 -130
- package/src/themes/intl/de.less +0 -102
- package/src/themes/intl/es.less +0 -102
- package/src/themes/intl/fr.less +0 -102
- package/src/themes/intl/ja.less +0 -102
- package/src/themes/intl/pt.less +0 -102
- package/src/themes/intl/zh.less +0 -102
- package/src/themes/intl.less +0 -102
- package/src/themes/monokai.less +0 -107
- package/src/themes/pro-dark.less +0 -147
- package/src/themes/pro.less +0 -186
- package/src/themes/solarized-dark.less +0 -78
- package/src/themes/solarized.less +0 -102
- package/src/themes/vaporwave.less +0 -145
- /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline4.js +0 -0
- /package/src/rust/{model → tasks}/copy_export.rs +0 -0
- /package/src/rust/{model → tasks}/export_app.rs +0 -0
- /package/src/rust/{model → tasks}/export_method.rs +0 -0
- /package/src/rust/{model → tasks}/restore_and_render.rs +0 -0
- /package/src/rust/{model → tasks}/update_and_render.rs +0 -0
|
@@ -40,8 +40,8 @@ impl ModalLink<DatetimeStyleCustom> for DatetimeStyleCustomProps {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
impl PartialEq for DatetimeStyleCustomProps {
|
|
43
|
-
fn eq(&self,
|
|
44
|
-
|
|
43
|
+
fn eq(&self, other: &Self) -> bool {
|
|
44
|
+
self.enable_time_config == other.enable_time_config && self.config == other.config
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -36,8 +36,8 @@ impl ModalLink<DatetimeStyleSimple> for DatetimeStyleSimpleProps {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
impl PartialEq for DatetimeStyleSimpleProps {
|
|
39
|
-
fn eq(&self,
|
|
40
|
-
|
|
39
|
+
fn eq(&self, other: &Self) -> bool {
|
|
40
|
+
self.enable_time_config == other.enable_time_config && self.config == other.config
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -56,8 +56,10 @@ impl ModalLink<DatetimeColumnStyle> for DatetimeColumnStyleProps {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
impl PartialEq for DatetimeColumnStyleProps {
|
|
59
|
-
fn eq(&self,
|
|
60
|
-
|
|
59
|
+
fn eq(&self, other: &Self) -> bool {
|
|
60
|
+
self.enable_time_config == other.enable_time_config
|
|
61
|
+
&& self.config == other.config
|
|
62
|
+
&& self.default_config == other.default_config
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -18,10 +18,10 @@ use yew::{Callback, Component, Html, NodeRef, Properties, TargetCast, classes, h
|
|
|
18
18
|
|
|
19
19
|
use super::type_icon::TypeIconType;
|
|
20
20
|
use crate::components::type_icon::TypeIcon;
|
|
21
|
-
use crate::
|
|
22
|
-
use crate
|
|
21
|
+
use crate::maybe;
|
|
22
|
+
use crate::session::{Session, SessionMetadataRc};
|
|
23
23
|
|
|
24
|
-
#[derive(Clone, PartialEq, Properties
|
|
24
|
+
#[derive(Clone, PartialEq, Properties)]
|
|
25
25
|
pub struct EditableHeaderProps {
|
|
26
26
|
pub icon_type: Option<TypeIconType>,
|
|
27
27
|
pub on_change: Callback<(Option<String>, bool)>,
|
|
@@ -32,6 +32,9 @@ pub struct EditableHeaderProps {
|
|
|
32
32
|
#[prop_or_default]
|
|
33
33
|
pub reset_count: u8,
|
|
34
34
|
|
|
35
|
+
/// Session metadata snapshot — threaded from `SessionProps`.
|
|
36
|
+
pub metadata: SessionMetadataRc,
|
|
37
|
+
|
|
35
38
|
// State
|
|
36
39
|
pub session: Session,
|
|
37
40
|
}
|
|
@@ -111,7 +114,7 @@ impl Component for EditableHeader {
|
|
|
111
114
|
if !self.edited {
|
|
112
115
|
return Some(true);
|
|
113
116
|
}
|
|
114
|
-
let metadata = ctx.props().
|
|
117
|
+
let metadata = &ctx.props().metadata;
|
|
115
118
|
let expressions = metadata.get_expression_columns();
|
|
116
119
|
let found = metadata
|
|
117
120
|
.get_table_columns()?
|
|
@@ -19,9 +19,9 @@ use wasm_bindgen::JsCast;
|
|
|
19
19
|
use web_sys::*;
|
|
20
20
|
use yew::prelude::*;
|
|
21
21
|
|
|
22
|
+
use crate::components::filter_dropdown::FilterDropDownElement;
|
|
22
23
|
use crate::components::style::LocalStyle;
|
|
23
24
|
use crate::css;
|
|
24
|
-
use crate::custom_elements::FilterDropDownElement;
|
|
25
25
|
|
|
26
26
|
#[derive(Properties, Derivative)]
|
|
27
27
|
#[derivative(Debug)]
|
|
@@ -12,16 +12,12 @@
|
|
|
12
12
|
|
|
13
13
|
use std::rc::Rc;
|
|
14
14
|
|
|
15
|
-
use session::Session;
|
|
16
15
|
use yew::prelude::*;
|
|
17
16
|
|
|
18
17
|
use super::containers::dropdown_menu::*;
|
|
19
|
-
use super::modal::{ModalLink, SetModalLink};
|
|
20
|
-
use super::style::StyleProvider;
|
|
21
|
-
use crate::model::*;
|
|
22
18
|
use crate::renderer::*;
|
|
23
|
-
use crate::
|
|
24
|
-
use crate::*;
|
|
19
|
+
use crate::session::Session;
|
|
20
|
+
use crate::tasks::*;
|
|
25
21
|
|
|
26
22
|
pub type ExportDropDownMenuItem = DropDownMenuItem<ExportFile>;
|
|
27
23
|
|
|
@@ -30,16 +26,6 @@ pub struct ExportDropDownMenuProps {
|
|
|
30
26
|
pub renderer: Renderer,
|
|
31
27
|
pub session: Session,
|
|
32
28
|
pub callback: Callback<ExportFile>,
|
|
33
|
-
pub root: web_sys::HtmlElement,
|
|
34
|
-
|
|
35
|
-
#[prop_or_default]
|
|
36
|
-
weak_link: WeakScope<ExportDropDownMenu>,
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
impl ModalLink<ExportDropDownMenu> for ExportDropDownMenuProps {
|
|
40
|
-
fn weak_link(&self) -> &'_ utils::WeakScope<ExportDropDownMenu> {
|
|
41
|
-
&self.weak_link
|
|
42
|
-
}
|
|
43
29
|
}
|
|
44
30
|
|
|
45
31
|
pub enum ExportDropDownMenuMsg {
|
|
@@ -49,7 +35,6 @@ pub enum ExportDropDownMenuMsg {
|
|
|
49
35
|
#[derive(Default)]
|
|
50
36
|
pub struct ExportDropDownMenu {
|
|
51
37
|
title: String,
|
|
52
|
-
_sub: Option<Subscription>,
|
|
53
38
|
input_ref: NodeRef,
|
|
54
39
|
invalid: bool,
|
|
55
40
|
}
|
|
@@ -61,11 +46,9 @@ impl Component for ExportDropDownMenu {
|
|
|
61
46
|
fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
|
|
62
47
|
let callback = ctx.link().callback(|_| ExportDropDownMenuMsg::TitleChange);
|
|
63
48
|
let plugin = ctx.props().renderer.get_active_plugin().unwrap();
|
|
64
|
-
// let has_render = js_sys::Reflect::has(&plugin,
|
|
65
|
-
// js_intern::js_intern!("render")).unwrap();
|
|
66
49
|
let is_chart = plugin.name().as_str() != "Datagrid";
|
|
67
50
|
html! {
|
|
68
|
-
|
|
51
|
+
<>
|
|
69
52
|
<span class="dropdown-group-label">{ "Save as" }</span>
|
|
70
53
|
<input
|
|
71
54
|
class={if self.invalid { "invalid" } else { "" }}
|
|
@@ -77,7 +60,7 @@ impl Component for ExportDropDownMenu {
|
|
|
77
60
|
values={Rc::new(get_menu_items(&self.title, is_chart))}
|
|
78
61
|
callback={&ctx.props().callback}
|
|
79
62
|
/>
|
|
80
|
-
|
|
63
|
+
</>
|
|
81
64
|
}
|
|
82
65
|
}
|
|
83
66
|
|
|
@@ -97,21 +80,12 @@ impl Component for ExportDropDownMenu {
|
|
|
97
80
|
}
|
|
98
81
|
|
|
99
82
|
fn create(ctx: &Context<Self>) -> Self {
|
|
100
|
-
ctx.set_modal_link();
|
|
101
|
-
let _sub = Some(
|
|
102
|
-
ctx.props()
|
|
103
|
-
.renderer
|
|
104
|
-
.plugin_changed
|
|
105
|
-
.add_listener(ctx.link().callback(|_| ExportDropDownMenuMsg::TitleChange)),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
83
|
Self {
|
|
109
84
|
title: ctx
|
|
110
85
|
.props()
|
|
111
86
|
.session
|
|
112
87
|
.get_title()
|
|
113
88
|
.unwrap_or_else(|| "untitled".to_owned()),
|
|
114
|
-
_sub,
|
|
115
89
|
..Default::default()
|
|
116
90
|
}
|
|
117
91
|
}
|
|
@@ -17,11 +17,10 @@ use yew::prelude::*;
|
|
|
17
17
|
|
|
18
18
|
use super::form::code_editor::*;
|
|
19
19
|
use super::style::LocalStyle;
|
|
20
|
-
use crate::
|
|
21
|
-
use crate::session::Session;
|
|
20
|
+
use crate::session::{Session, SessionMetadata, SessionMetadataRc};
|
|
22
21
|
use crate::*;
|
|
23
22
|
|
|
24
|
-
#[derive(Properties, PartialEq,
|
|
23
|
+
#[derive(Properties, PartialEq, Clone)]
|
|
25
24
|
pub struct ExpressionEditorProps {
|
|
26
25
|
pub on_save: Callback<()>,
|
|
27
26
|
pub on_validate: Callback<bool>,
|
|
@@ -32,6 +31,13 @@ pub struct ExpressionEditorProps {
|
|
|
32
31
|
#[prop_or_default]
|
|
33
32
|
pub reset_count: u8,
|
|
34
33
|
|
|
34
|
+
/// Session metadata snapshot — threaded from `SessionProps`.
|
|
35
|
+
pub metadata: SessionMetadataRc,
|
|
36
|
+
|
|
37
|
+
/// Selected theme name, threaded for PortalModal consumers.
|
|
38
|
+
#[prop_or_default]
|
|
39
|
+
pub selected_theme: Option<String>,
|
|
40
|
+
|
|
35
41
|
// State
|
|
36
42
|
pub session: Session,
|
|
37
43
|
}
|
|
@@ -55,7 +61,7 @@ impl Component for ExpressionEditor {
|
|
|
55
61
|
|
|
56
62
|
fn create(ctx: &Context<Self>) -> Self {
|
|
57
63
|
let oninput = ctx.link().callback(ExpressionEditorMsg::SetExpr);
|
|
58
|
-
let expr = initial_expr(&ctx.props().
|
|
64
|
+
let expr = initial_expr(&ctx.props().metadata, &ctx.props().alias);
|
|
59
65
|
ctx.link()
|
|
60
66
|
.send_message(Self::Message::SetExpr(expr.clone()));
|
|
61
67
|
|
|
@@ -89,8 +95,8 @@ impl Component for ExpressionEditor {
|
|
|
89
95
|
if self.error.is_none() {
|
|
90
96
|
maybe!({
|
|
91
97
|
let alias = ctx.props().alias.as_ref()?;
|
|
92
|
-
let session = ctx.props().session
|
|
93
|
-
let old =
|
|
98
|
+
let session = &ctx.props().session;
|
|
99
|
+
let old = ctx.props().metadata.get_expression_by_alias(alias)?;
|
|
94
100
|
let is_edited = *self.expr != old;
|
|
95
101
|
session
|
|
96
102
|
.metadata_mut()
|
|
@@ -124,6 +130,7 @@ impl Component for ExpressionEditor {
|
|
|
124
130
|
{disabled}
|
|
125
131
|
oninput={self.oninput.clone()}
|
|
126
132
|
onsave={ctx.props().on_save.clone()}
|
|
133
|
+
theme={ctx.props().selected_theme.clone().unwrap_or_default()}
|
|
127
134
|
/>
|
|
128
135
|
<div id="psp-expression-editor-meta">
|
|
129
136
|
<div class="error">
|
|
@@ -136,11 +143,13 @@ impl Component for ExpressionEditor {
|
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
|
|
139
|
-
if ctx.props().alias != old_props.alias
|
|
146
|
+
if ctx.props().alias != old_props.alias
|
|
147
|
+
|| ctx.props().reset_count != old_props.reset_count
|
|
148
|
+
|| (ctx.props().alias.is_some() && ctx.props().metadata != old_props.metadata)
|
|
140
149
|
{
|
|
141
150
|
ctx.link()
|
|
142
151
|
.send_message(ExpressionEditorMsg::SetExpr(initial_expr(
|
|
143
|
-
&ctx.props().
|
|
152
|
+
&ctx.props().metadata,
|
|
144
153
|
&ctx.props().alias,
|
|
145
154
|
)));
|
|
146
155
|
false
|
|
@@ -150,10 +159,10 @@ impl Component for ExpressionEditor {
|
|
|
150
159
|
}
|
|
151
160
|
}
|
|
152
161
|
|
|
153
|
-
fn initial_expr(
|
|
162
|
+
fn initial_expr(metadata: &SessionMetadata, alias: &Option<String>) -> Rc<String> {
|
|
154
163
|
alias
|
|
155
164
|
.as_ref()
|
|
156
|
-
.and_then(|alias|
|
|
165
|
+
.and_then(|alias| metadata.get_expression_by_alias(alias))
|
|
157
166
|
.unwrap_or_default()
|
|
158
167
|
.into()
|
|
159
168
|
}
|
|
@@ -10,135 +10,279 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use std::cell::RefCell;
|
|
14
|
+
use std::collections::HashSet;
|
|
15
|
+
use std::rc::Rc;
|
|
16
|
+
|
|
17
|
+
use perspective_client::clone;
|
|
13
18
|
use web_sys::*;
|
|
19
|
+
use yew::html::ImplicitClone;
|
|
14
20
|
use yew::prelude::*;
|
|
15
21
|
|
|
16
|
-
use super::
|
|
17
|
-
use crate::
|
|
22
|
+
use super::portal::PortalModal;
|
|
23
|
+
use crate::session::Session;
|
|
24
|
+
use crate::utils::*;
|
|
25
|
+
use crate::*;
|
|
18
26
|
|
|
19
27
|
static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/filter-dropdown.css"));
|
|
20
28
|
|
|
21
|
-
#[derive(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
impl ModalLink<FilterDropDown> for FilterDropDownProps {
|
|
28
|
-
fn weak_link(&self) -> &'_ WeakScope<FilterDropDown> {
|
|
29
|
-
&self.weak_link
|
|
30
|
-
}
|
|
29
|
+
#[derive(Default)]
|
|
30
|
+
struct FilterDropDownState {
|
|
31
|
+
values: Vec<String>,
|
|
32
|
+
selected: usize,
|
|
33
|
+
on_select: Option<Callback<String>>,
|
|
34
|
+
target: Option<HtmlElement>,
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
#[derive(Clone)]
|
|
38
|
+
pub struct FilterDropDownElement {
|
|
39
|
+
state: Rc<RefCell<FilterDropDownState>>,
|
|
40
|
+
session: Session,
|
|
41
|
+
column: Rc<RefCell<Option<(usize, String)>>>,
|
|
42
|
+
all_values: Rc<RefCell<Option<Vec<String>>>>,
|
|
43
|
+
notify: Rc<PubSub<()>>,
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
impl PartialEq for FilterDropDownElement {
|
|
47
|
+
fn eq(&self, other: &Self) -> bool {
|
|
48
|
+
Rc::ptr_eq(&self.state, &other.state)
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
impl
|
|
48
|
-
type Message = FilterDropDownMsg;
|
|
49
|
-
type Properties = FilterDropDownProps;
|
|
52
|
+
impl ImplicitClone for FilterDropDownElement {}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
impl FilterDropDownElement {
|
|
55
|
+
pub fn new(session: Session) -> Self {
|
|
53
56
|
Self {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
state: Default::default(),
|
|
58
|
+
session,
|
|
59
|
+
column: Default::default(),
|
|
60
|
+
all_values: Default::default(),
|
|
61
|
+
notify: Rc::default(),
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
fn
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
self.on_select.as_ref().unwrap().emit(x.clone());
|
|
80
|
-
false
|
|
81
|
-
},
|
|
82
|
-
}
|
|
65
|
+
pub fn reautocomplete(&self) {
|
|
66
|
+
// Re-open portal with current target
|
|
67
|
+
self.notify.emit(());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
pub fn autocomplete(
|
|
71
|
+
&self,
|
|
72
|
+
column: (usize, String),
|
|
73
|
+
input: String,
|
|
74
|
+
exclude: HashSet<String>,
|
|
75
|
+
target: HtmlElement,
|
|
76
|
+
callback: Callback<String>,
|
|
77
|
+
) {
|
|
78
|
+
let current_column = self.column.borrow().clone();
|
|
79
|
+
match current_column {
|
|
80
|
+
Some(filter_col) if filter_col == column => {
|
|
81
|
+
let values = filter_values(&input, &self.all_values, &exclude);
|
|
82
|
+
if values.len() == 1 && values[0] == input {
|
|
83
|
+
let _ = self.hide();
|
|
83
84
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
let mut s = self.state.borrow_mut();
|
|
86
|
+
s.values = values;
|
|
87
|
+
s.selected = 0;
|
|
88
|
+
s.on_select = Some(callback);
|
|
89
|
+
if s.target.is_none() {
|
|
90
|
+
s.target = Some(target);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
drop(s);
|
|
94
|
+
self.notify.emit(());
|
|
86
95
|
}
|
|
87
96
|
},
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
self.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
_ => {
|
|
98
|
+
clone!(
|
|
99
|
+
self.state,
|
|
100
|
+
self.session,
|
|
101
|
+
self.all_values,
|
|
102
|
+
self.notify,
|
|
103
|
+
old_column = self.column
|
|
104
|
+
);
|
|
105
|
+
ApiFuture::spawn(async move {
|
|
106
|
+
let fetched = session.get_column_values(column.1.clone()).await?;
|
|
107
|
+
*all_values.borrow_mut() = Some(fetched);
|
|
108
|
+
let values = filter_values(&input, &all_values, &exclude);
|
|
109
|
+
let should_hide = values.len() == 1 && values[0] == input;
|
|
110
|
+
|
|
111
|
+
*old_column.borrow_mut() = Some(column);
|
|
112
|
+
{
|
|
113
|
+
let mut s = state.borrow_mut();
|
|
114
|
+
s.on_select = Some(callback);
|
|
115
|
+
if should_hide {
|
|
116
|
+
let fv = self::filter_values("", &all_values, &exclude);
|
|
117
|
+
s.values = fv;
|
|
118
|
+
s.target = Some(target);
|
|
119
|
+
} else {
|
|
120
|
+
s.values = values;
|
|
121
|
+
s.target = Some(target);
|
|
122
|
+
}
|
|
123
|
+
s.selected = 0;
|
|
124
|
+
}
|
|
125
|
+
if should_hide {
|
|
126
|
+
state.borrow_mut().target = None;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
notify.emit(());
|
|
130
|
+
Ok(())
|
|
131
|
+
});
|
|
107
132
|
},
|
|
108
133
|
}
|
|
109
134
|
}
|
|
110
135
|
|
|
111
|
-
fn
|
|
112
|
-
|
|
136
|
+
pub fn item_select(&self) {
|
|
137
|
+
let state = self.state.borrow();
|
|
138
|
+
if let Some(value) = state.values.get(state.selected)
|
|
139
|
+
&& let Some(ref cb) = state.on_select
|
|
140
|
+
{
|
|
141
|
+
cb.emit(value.clone());
|
|
142
|
+
}
|
|
113
143
|
}
|
|
114
144
|
|
|
115
|
-
fn
|
|
116
|
-
let
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
pub fn item_down(&self) {
|
|
146
|
+
let mut state = self.state.borrow_mut();
|
|
147
|
+
state.selected += 1;
|
|
148
|
+
if state.selected >= state.values.len() {
|
|
149
|
+
state.selected = 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
drop(state);
|
|
153
|
+
self.notify.emit(());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn item_up(&self) {
|
|
157
|
+
let mut state = self.state.borrow_mut();
|
|
158
|
+
if state.selected < 1 {
|
|
159
|
+
state.selected = state.values.len();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
state.selected -= 1;
|
|
163
|
+
drop(state);
|
|
164
|
+
self.notify.emit(());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
pub fn hide(&self) -> ApiResult<()> {
|
|
168
|
+
self.state.borrow_mut().target = None;
|
|
169
|
+
self.column.borrow_mut().take();
|
|
170
|
+
self.notify.emit(());
|
|
171
|
+
Ok(())
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
#[derive(Properties, PartialEq)]
|
|
176
|
+
pub struct FilterDropDownPortalProps {
|
|
177
|
+
pub element: FilterDropDownElement,
|
|
178
|
+
pub theme: String,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub struct FilterDropDownPortal {
|
|
182
|
+
_sub: Subscription,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
impl Component for FilterDropDownPortal {
|
|
186
|
+
type Message = ();
|
|
187
|
+
type Properties = FilterDropDownPortalProps;
|
|
188
|
+
|
|
189
|
+
fn create(ctx: &Context<Self>) -> Self {
|
|
190
|
+
let link = ctx.link().clone();
|
|
191
|
+
let sub = ctx
|
|
192
|
+
.props()
|
|
193
|
+
.element
|
|
194
|
+
.notify
|
|
195
|
+
.add_listener(move |()| link.send_message(()));
|
|
196
|
+
Self { _sub: sub }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn update(&mut self, _ctx: &Context<Self>, _msg: ()) -> bool {
|
|
200
|
+
true
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
204
|
+
let state = ctx.props().element.state.borrow();
|
|
205
|
+
let target = state.target.clone();
|
|
206
|
+
let on_close = {
|
|
207
|
+
let element = ctx.props().element.clone();
|
|
208
|
+
Callback::from(move |()| {
|
|
209
|
+
let _ = element.hide();
|
|
210
|
+
})
|
|
140
211
|
};
|
|
141
212
|
|
|
142
|
-
|
|
213
|
+
if target.is_some() {
|
|
214
|
+
let values = state.values.clone();
|
|
215
|
+
let selected = state.selected;
|
|
216
|
+
let on_select = state.on_select.clone();
|
|
217
|
+
drop(state);
|
|
218
|
+
|
|
219
|
+
html! {
|
|
220
|
+
<PortalModal
|
|
221
|
+
tag_name="perspective-dropdown"
|
|
222
|
+
{target}
|
|
223
|
+
own_focus=false
|
|
224
|
+
{on_close}
|
|
225
|
+
theme={ctx.props().theme.clone()}
|
|
226
|
+
>
|
|
227
|
+
<FilterDropDownView {values} {selected} {on_select} />
|
|
228
|
+
</PortalModal>
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
html! {}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#[derive(Properties, PartialEq)]
|
|
237
|
+
struct FilterDropDownViewProps {
|
|
238
|
+
values: Vec<String>,
|
|
239
|
+
selected: usize,
|
|
240
|
+
on_select: Option<Callback<String>>,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[function_component]
|
|
244
|
+
fn FilterDropDownView(props: &FilterDropDownViewProps) -> Html {
|
|
245
|
+
let body = html! {
|
|
246
|
+
if !props.values.is_empty() {
|
|
247
|
+
{ for props.values
|
|
248
|
+
.iter()
|
|
249
|
+
.enumerate()
|
|
250
|
+
.map(|(idx, value)| {
|
|
251
|
+
let click = props.on_select.as_ref().unwrap().reform({
|
|
252
|
+
let value = value.clone();
|
|
253
|
+
move |_: MouseEvent| value.clone()
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
html! {
|
|
257
|
+
if idx == props.selected {
|
|
258
|
+
<span onmousedown={click} class="selected">{ value }</span>
|
|
259
|
+
} else {
|
|
260
|
+
<span onmousedown={click}>{ value }</span>
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}) }
|
|
264
|
+
} else {
|
|
265
|
+
<span class="no-results">{ "No Completions" }</span>
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
html! { <><style>{ CSS }</style>{ body }</> }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
fn filter_values(
|
|
273
|
+
input: &str,
|
|
274
|
+
values: &Rc<RefCell<Option<Vec<String>>>>,
|
|
275
|
+
exclude: &HashSet<String>,
|
|
276
|
+
) -> Vec<String> {
|
|
277
|
+
let input = input.to_lowercase();
|
|
278
|
+
if let Some(values) = &*values.borrow() {
|
|
279
|
+
values
|
|
280
|
+
.iter()
|
|
281
|
+
.filter(|x| x.to_lowercase().contains(&input) && !exclude.contains(x.as_str()))
|
|
282
|
+
.take(10)
|
|
283
|
+
.cloned()
|
|
284
|
+
.collect::<Vec<String>>()
|
|
285
|
+
} else {
|
|
286
|
+
vec![]
|
|
143
287
|
}
|
|
144
288
|
}
|