@perspective-dev/viewer 4.2.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 -0
- 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/extensions.d.ts +32 -1
- package/dist/esm/perspective-viewer.d.ts +1 -0
- 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/esm/ts-rs/GroupRollupMode.d.ts +1 -0
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +2 -0
- package/dist/wasm/perspective-viewer.d.ts +57 -53
- package/dist/wasm/perspective-viewer.js +197 -164
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
- package/package.json +9 -6
- 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} +161 -159
- 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/css/config-selector.css +441 -0
- 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 +374 -185
- 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 +21 -10
- package/src/rust/components/column_selector/sort_column.rs +23 -13
- package/src/rust/components/column_selector.rs +189 -100
- 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 +29 -7
- package/src/rust/components/containers/scroll_panel.rs +8 -1
- package/src/rust/components/containers/select.rs +3 -3
- package/src/rust/components/containers/sidebar_close_button.rs +1 -1
- package/src/rust/components/containers/split_panel.rs +3 -2
- 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 -92
- 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 -111
- 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 +20 -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/registry.rs +8 -1
- package/src/rust/renderer.rs +83 -9
- package/src/rust/session/column_defaults_update.rs +18 -0
- package/src/rust/session/metadata.rs +23 -2
- package/src/rust/session/props.rs +178 -0
- package/src/rust/session/replace_expression_update.rs +1 -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 +22 -4
- 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 -20
- 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/src/ts/extensions.ts +73 -2
- package/src/ts/perspective-viewer.ts +1 -0
- package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -1
- package/tsconfig.json +1 -0
- package/dist/css/variables.css +0 -0
- package/src/less/column-dropdown.less +0 -95
- package/src/less/config-selector.less +0 -363
- 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/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-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → 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
|
@@ -12,20 +12,23 @@
|
|
|
12
12
|
|
|
13
13
|
use std::rc::Rc;
|
|
14
14
|
|
|
15
|
+
use perspective_client::config::{ViewConfig, ViewConfigUpdate};
|
|
16
|
+
use perspective_js::utils::ApiFuture;
|
|
15
17
|
use yew::prelude::*;
|
|
16
18
|
|
|
17
19
|
use super::column_selector::ColumnSelector;
|
|
18
20
|
use super::plugin_selector::PluginSelector;
|
|
19
|
-
use crate::PerspectiveProperties;
|
|
20
21
|
use crate::components::containers::sidebar_close_button::SidebarCloseButton;
|
|
22
|
+
use crate::config::PluginUpdate;
|
|
21
23
|
use crate::dragdrop::*;
|
|
22
|
-
use crate::
|
|
23
|
-
use crate::presentation::{ColumnLocator, Presentation};
|
|
24
|
+
use crate::presentation::{ColumnLocator, OpenColumnSettings, Presentation};
|
|
24
25
|
use crate::renderer::*;
|
|
26
|
+
use crate::session::column_defaults_update::*;
|
|
25
27
|
use crate::session::*;
|
|
28
|
+
use crate::tasks::can_render_column_styles;
|
|
26
29
|
use crate::utils::*;
|
|
27
30
|
|
|
28
|
-
#[derive(Clone, Properties
|
|
31
|
+
#[derive(Clone, Properties)]
|
|
29
32
|
pub struct SettingsPanelProps {
|
|
30
33
|
pub on_close: Callback<()>,
|
|
31
34
|
pub on_resize: Rc<PubSub<()>>,
|
|
@@ -33,6 +36,25 @@ pub struct SettingsPanelProps {
|
|
|
33
36
|
pub on_debug: Callback<()>,
|
|
34
37
|
pub is_debug: bool,
|
|
35
38
|
|
|
39
|
+
/// Value props threaded from the root's `RendererProps` / `SessionProps`.
|
|
40
|
+
pub plugin_name: Option<String>,
|
|
41
|
+
pub available_plugins: PtrEqRc<Vec<String>>,
|
|
42
|
+
pub has_table: Option<TableLoadState>,
|
|
43
|
+
pub named_column_count: usize,
|
|
44
|
+
pub view_config: PtrEqRc<ViewConfig>,
|
|
45
|
+
/// Column currently being dragged (if any) — threaded to show drag
|
|
46
|
+
/// highlights without per-component `DragDrop` PubSub subscriptions.
|
|
47
|
+
pub drag_column: Option<String>,
|
|
48
|
+
/// Cloned session metadata snapshot — threaded from `SessionProps`
|
|
49
|
+
/// so that metadata changes trigger re-renders via prop diffing.
|
|
50
|
+
pub metadata: SessionMetadataRc,
|
|
51
|
+
/// Snapshot of the column-settings sidebar state — threaded from
|
|
52
|
+
/// `PresentationProps` so that open/close triggers re-renders.
|
|
53
|
+
pub open_column_settings: OpenColumnSettings,
|
|
54
|
+
|
|
55
|
+
/// Selected theme name, threaded for PortalModal consumers.
|
|
56
|
+
pub selected_theme: Option<String>,
|
|
57
|
+
|
|
36
58
|
/// State
|
|
37
59
|
pub dragdrop: DragDrop,
|
|
38
60
|
pub session: Session,
|
|
@@ -41,8 +63,17 @@ pub struct SettingsPanelProps {
|
|
|
41
63
|
}
|
|
42
64
|
|
|
43
65
|
impl PartialEq for SettingsPanelProps {
|
|
44
|
-
fn eq(&self,
|
|
45
|
-
|
|
66
|
+
fn eq(&self, rhs: &Self) -> bool {
|
|
67
|
+
self.is_debug == rhs.is_debug
|
|
68
|
+
&& self.plugin_name == rhs.plugin_name
|
|
69
|
+
&& self.available_plugins == rhs.available_plugins
|
|
70
|
+
&& self.has_table == rhs.has_table
|
|
71
|
+
&& self.named_column_count == rhs.named_column_count
|
|
72
|
+
&& self.view_config == rhs.view_config
|
|
73
|
+
&& self.drag_column == rhs.drag_column
|
|
74
|
+
&& self.metadata == rhs.metadata
|
|
75
|
+
&& self.open_column_settings == rhs.open_column_settings
|
|
76
|
+
&& self.selected_theme == rhs.selected_theme
|
|
46
77
|
}
|
|
47
78
|
}
|
|
48
79
|
|
|
@@ -55,7 +86,74 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
55
86
|
session,
|
|
56
87
|
..
|
|
57
88
|
} = &props;
|
|
58
|
-
|
|
89
|
+
|
|
90
|
+
let selected_column = {
|
|
91
|
+
let locator = props.open_column_settings.locator.clone();
|
|
92
|
+
let config = &props.view_config;
|
|
93
|
+
locator.filter(|locator| match locator {
|
|
94
|
+
ColumnLocator::Table(name) => {
|
|
95
|
+
locator
|
|
96
|
+
.name()
|
|
97
|
+
.map(|n| {
|
|
98
|
+
config.columns.iter().any(|maybe_col| {
|
|
99
|
+
maybe_col.as_ref().map(|col| col == n).unwrap_or_default()
|
|
100
|
+
}) || config.group_by.iter().any(|col| col == n)
|
|
101
|
+
|| config.split_by.iter().any(|col| col == n)
|
|
102
|
+
|| config.filter.iter().any(|col| col.column() == n)
|
|
103
|
+
|| config.sort.iter().any(|col| &col.0 == n)
|
|
104
|
+
})
|
|
105
|
+
.unwrap_or_default()
|
|
106
|
+
&& can_render_column_styles(&props.renderer, config, &props.metadata, name)
|
|
107
|
+
.unwrap_or_default()
|
|
108
|
+
},
|
|
109
|
+
_ => true,
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
let plugin_name = props.plugin_name.clone();
|
|
114
|
+
let available_plugins = props.available_plugins.clone();
|
|
115
|
+
|
|
116
|
+
// Dispatch callback: captures engine handles, constructs config update, renders
|
|
117
|
+
let on_select_plugin = {
|
|
118
|
+
clone!(renderer, session, presentation);
|
|
119
|
+
let session_metadata = props.metadata.clone();
|
|
120
|
+
Callback::from(move |plugin_name: String| {
|
|
121
|
+
if !session.is_errored() {
|
|
122
|
+
let metadata =
|
|
123
|
+
renderer.get_next_plugin_metadata(&PluginUpdate::Update(plugin_name));
|
|
124
|
+
|
|
125
|
+
let prev_metadata = renderer.metadata();
|
|
126
|
+
let requirements = metadata.as_ref().unwrap_or(&*prev_metadata);
|
|
127
|
+
let rollup_features = session_metadata
|
|
128
|
+
.get_features()
|
|
129
|
+
.map(|x| x.get_group_rollup_modes())
|
|
130
|
+
.unwrap();
|
|
131
|
+
|
|
132
|
+
let group_rollups = requirements.get_group_rollups(&rollup_features);
|
|
133
|
+
let mut update = ViewConfigUpdate {
|
|
134
|
+
group_rollup_mode: group_rollups.first().cloned(),
|
|
135
|
+
..ViewConfigUpdate::default()
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
update.set_update_column_defaults(
|
|
139
|
+
&session_metadata,
|
|
140
|
+
&session.get_view_config().columns,
|
|
141
|
+
requirements,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if session.update_view_config(update).is_ok() {
|
|
145
|
+
clone!(renderer, session);
|
|
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);
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
};
|
|
156
|
+
|
|
59
157
|
html! {
|
|
60
158
|
<div id="settings_panel" class="sidebar_column noselect split-panel orient-vertical">
|
|
61
159
|
if selected_column.is_none() {
|
|
@@ -68,14 +166,20 @@ pub fn SettingsPanel(props: &SettingsPanelProps) -> Html {
|
|
|
68
166
|
id={if props.is_debug {"debug_close_button"} else {"debug_open_button"}}
|
|
69
167
|
on_close_sidebar={&props.on_debug}
|
|
70
168
|
/>
|
|
71
|
-
<PluginSelector {
|
|
169
|
+
<PluginSelector {plugin_name} {available_plugins} {on_select_plugin} />
|
|
72
170
|
<ColumnSelector
|
|
73
171
|
on_resize={&props.on_resize}
|
|
74
172
|
on_open_expr_panel={&props.on_select_column}
|
|
75
|
-
|
|
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()}
|
|
76
180
|
{dragdrop}
|
|
77
|
-
{renderer}
|
|
78
|
-
{session}
|
|
181
|
+
renderer={renderer.clone()}
|
|
182
|
+
session={session.clone()}
|
|
79
183
|
/>
|
|
80
184
|
</div>
|
|
81
185
|
}
|
|
@@ -10,26 +10,27 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use
|
|
14
|
-
|
|
13
|
+
use wasm_bindgen_futures::spawn_local;
|
|
15
14
|
use web_sys::*;
|
|
16
15
|
use yew::prelude::*;
|
|
17
16
|
|
|
18
17
|
use super::status_indicator::StatusIndicator;
|
|
19
18
|
use super::style::LocalStyle;
|
|
20
19
|
use crate::components::containers::select::*;
|
|
20
|
+
use crate::components::copy_dropdown::CopyDropDownMenu;
|
|
21
|
+
use crate::components::export_dropdown::ExportDropDownMenu;
|
|
22
|
+
use crate::components::portal::PortalModal;
|
|
21
23
|
use crate::components::status_bar_counter::StatusBarRowsCounter;
|
|
22
|
-
use crate::custom_elements::copy_dropdown::*;
|
|
23
|
-
use crate::custom_elements::export_dropdown::*;
|
|
24
24
|
use crate::custom_events::CustomEvents;
|
|
25
|
-
use crate::
|
|
25
|
+
use crate::js::*;
|
|
26
26
|
use crate::presentation::Presentation;
|
|
27
27
|
use crate::renderer::*;
|
|
28
28
|
use crate::session::*;
|
|
29
|
+
use crate::tasks::*;
|
|
29
30
|
use crate::utils::*;
|
|
30
31
|
use crate::*;
|
|
31
32
|
|
|
32
|
-
#[derive(
|
|
33
|
+
#[derive(Clone, Properties)]
|
|
33
34
|
pub struct StatusBarProps {
|
|
34
35
|
// DOM Attribute
|
|
35
36
|
pub id: String,
|
|
@@ -41,6 +42,25 @@ pub struct StatusBarProps {
|
|
|
41
42
|
#[prop_or_default]
|
|
42
43
|
pub on_settings: Option<Callback<()>>,
|
|
43
44
|
|
|
45
|
+
// Value props threaded from the root's `SessionProps`.
|
|
46
|
+
// Using these avoids PubSub subscriptions for table_loaded / table_errored.
|
|
47
|
+
pub has_table: Option<TableLoadState>,
|
|
48
|
+
pub is_errored: bool,
|
|
49
|
+
pub stats: Option<ViewStats>,
|
|
50
|
+
/// In-flight render counter and full error, threaded to `StatusIndicator`.
|
|
51
|
+
pub update_count: u32,
|
|
52
|
+
pub error: Option<TableErrorState>,
|
|
53
|
+
/// Title string from session — threaded to avoid title_changed
|
|
54
|
+
/// subscription.
|
|
55
|
+
pub title: Option<String>,
|
|
56
|
+
/// Theme state from presentation — threaded to avoid theme_config_updated /
|
|
57
|
+
/// visibility_changed subscriptions.
|
|
58
|
+
pub is_settings_open: bool,
|
|
59
|
+
pub selected_theme: Option<String>,
|
|
60
|
+
pub available_themes: PtrEqRc<Vec<String>>,
|
|
61
|
+
/// Whether this viewer is hosted inside a `<perspective-workspace>`.
|
|
62
|
+
pub is_workspace: bool,
|
|
63
|
+
|
|
44
64
|
// State
|
|
45
65
|
pub custom_events: CustomEvents,
|
|
46
66
|
pub session: Session,
|
|
@@ -51,6 +71,48 @@ pub struct StatusBarProps {
|
|
|
51
71
|
impl PartialEq for StatusBarProps {
|
|
52
72
|
fn eq(&self, other: &Self) -> bool {
|
|
53
73
|
self.id == other.id
|
|
74
|
+
&& self.has_table == other.has_table
|
|
75
|
+
&& self.is_errored == other.is_errored
|
|
76
|
+
&& self.stats == other.stats
|
|
77
|
+
&& self.update_count == other.update_count
|
|
78
|
+
&& self.error == other.error
|
|
79
|
+
&& self.title == other.title
|
|
80
|
+
&& self.is_settings_open == other.is_settings_open
|
|
81
|
+
&& self.selected_theme == other.selected_theme
|
|
82
|
+
&& self.available_themes == other.available_themes
|
|
83
|
+
&& self.is_workspace == other.is_workspace
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
impl HasCustomEvents for StatusBarProps {
|
|
88
|
+
fn custom_events(&self) -> &CustomEvents {
|
|
89
|
+
&self.custom_events
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
impl HasPresentation for StatusBarProps {
|
|
94
|
+
fn presentation(&self) -> &Presentation {
|
|
95
|
+
&self.presentation
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
impl HasRenderer for StatusBarProps {
|
|
100
|
+
fn renderer(&self) -> &Renderer {
|
|
101
|
+
&self.renderer
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
impl HasSession for StatusBarProps {
|
|
106
|
+
fn session(&self) -> &Session {
|
|
107
|
+
&self.session
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
impl StateProvider for StatusBarProps {
|
|
112
|
+
type State = StatusBarProps;
|
|
113
|
+
|
|
114
|
+
fn clone_state(&self) -> Self::State {
|
|
115
|
+
self.clone()
|
|
54
116
|
}
|
|
55
117
|
}
|
|
56
118
|
|
|
@@ -58,9 +120,10 @@ pub enum StatusBarMsg {
|
|
|
58
120
|
Reset(MouseEvent),
|
|
59
121
|
Export,
|
|
60
122
|
Copy,
|
|
123
|
+
CloseExport,
|
|
124
|
+
CloseCopy,
|
|
61
125
|
Noop,
|
|
62
126
|
Eject,
|
|
63
|
-
SetThemeConfig((Rc<Vec<String>>, Option<usize>)),
|
|
64
127
|
SetTheme(String),
|
|
65
128
|
ResetTheme,
|
|
66
129
|
PointerEvent(web_sys::PointerEvent),
|
|
@@ -70,14 +133,16 @@ pub enum StatusBarMsg {
|
|
|
70
133
|
|
|
71
134
|
/// A toolbar with buttons, and `Table` & `View` status information.
|
|
72
135
|
pub struct StatusBar {
|
|
73
|
-
_subscriptions: [Subscription; 5],
|
|
74
136
|
copy_ref: NodeRef,
|
|
75
137
|
export_ref: NodeRef,
|
|
76
138
|
input_ref: NodeRef,
|
|
77
139
|
statusbar_ref: NodeRef,
|
|
78
|
-
|
|
79
|
-
|
|
140
|
+
/// Local title tracks the live `<input>` value before the user commits the
|
|
141
|
+
/// change (blur / Enter). Reset to the prop value whenever the prop
|
|
142
|
+
/// changes.
|
|
80
143
|
title: Option<String>,
|
|
144
|
+
copy_target: Option<HtmlElement>,
|
|
145
|
+
export_target: Option<HtmlElement>,
|
|
81
146
|
}
|
|
82
147
|
|
|
83
148
|
impl Component for StatusBar {
|
|
@@ -85,21 +150,26 @@ impl Component for StatusBar {
|
|
|
85
150
|
type Properties = StatusBarProps;
|
|
86
151
|
|
|
87
152
|
fn create(ctx: &Context<Self>) -> Self {
|
|
88
|
-
fetch_initial_theme(ctx);
|
|
89
153
|
Self {
|
|
90
|
-
_subscriptions: register_listeners(ctx),
|
|
91
154
|
copy_ref: NodeRef::default(),
|
|
92
155
|
export_ref: NodeRef::default(),
|
|
93
156
|
input_ref: NodeRef::default(),
|
|
94
157
|
statusbar_ref: NodeRef::default(),
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
158
|
+
title: ctx.props().title.clone(),
|
|
159
|
+
copy_target: None,
|
|
160
|
+
export_target: None,
|
|
98
161
|
}
|
|
99
162
|
}
|
|
100
163
|
|
|
101
|
-
fn changed(&mut self, ctx: &Context<Self>,
|
|
102
|
-
|
|
164
|
+
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
|
|
165
|
+
// Keep the local title in sync with the prop whenever the session title
|
|
166
|
+
// changes externally (e.g. restore() call) or the settings panel opens /
|
|
167
|
+
// closes (which resets the input element).
|
|
168
|
+
if ctx.props().title != old_props.title
|
|
169
|
+
|| ctx.props().is_settings_open != old_props.is_settings_open
|
|
170
|
+
{
|
|
171
|
+
self.title = ctx.props().title.clone();
|
|
172
|
+
}
|
|
103
173
|
true
|
|
104
174
|
}
|
|
105
175
|
|
|
@@ -111,47 +181,50 @@ impl Component for StatusBar {
|
|
|
111
181
|
false
|
|
112
182
|
},
|
|
113
183
|
StatusBarMsg::ResetTheme => {
|
|
114
|
-
let
|
|
184
|
+
let presentation = ctx.props().presentation.clone();
|
|
185
|
+
let session = ctx.props().session.clone();
|
|
186
|
+
let renderer = ctx.props().renderer.clone();
|
|
115
187
|
ApiFuture::spawn(async move {
|
|
116
|
-
|
|
117
|
-
let view =
|
|
118
|
-
|
|
188
|
+
presentation.reset_theme().await?;
|
|
189
|
+
let view = session.get_view().into_apierror()?;
|
|
190
|
+
renderer.restyle_all(&view).await
|
|
119
191
|
});
|
|
120
192
|
true
|
|
121
193
|
},
|
|
122
|
-
StatusBarMsg::SetThemeConfig((themes, index)) => {
|
|
123
|
-
let new_theme = index.and_then(|x| themes.get(x)).cloned();
|
|
124
|
-
let should_render = new_theme != self.theme || self.themes != themes;
|
|
125
|
-
self.theme = new_theme;
|
|
126
|
-
self.themes = themes;
|
|
127
|
-
should_render
|
|
128
|
-
},
|
|
129
194
|
StatusBarMsg::SetTheme(theme_name) => {
|
|
130
|
-
let
|
|
195
|
+
let presentation = ctx.props().presentation.clone();
|
|
196
|
+
let session = ctx.props().session.clone();
|
|
197
|
+
let renderer = ctx.props().renderer.clone();
|
|
131
198
|
ApiFuture::spawn(async move {
|
|
132
|
-
|
|
133
|
-
let view =
|
|
134
|
-
|
|
199
|
+
presentation.set_theme_name(Some(&theme_name)).await?;
|
|
200
|
+
let view = session.get_view().into_apierror()?;
|
|
201
|
+
renderer.restyle_all(&view).await
|
|
135
202
|
});
|
|
136
203
|
|
|
137
204
|
false
|
|
138
205
|
},
|
|
139
206
|
StatusBarMsg::Export => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
false
|
|
207
|
+
self.export_target = self.export_ref.cast::<HtmlElement>();
|
|
208
|
+
true
|
|
143
209
|
},
|
|
144
210
|
StatusBarMsg::Copy => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
211
|
+
self.copy_target = self.copy_ref.cast::<HtmlElement>();
|
|
212
|
+
true
|
|
213
|
+
},
|
|
214
|
+
StatusBarMsg::CloseExport => {
|
|
215
|
+
self.export_target = None;
|
|
216
|
+
true
|
|
217
|
+
},
|
|
218
|
+
StatusBarMsg::CloseCopy => {
|
|
219
|
+
self.copy_target = None;
|
|
220
|
+
true
|
|
148
221
|
},
|
|
149
222
|
StatusBarMsg::Eject => {
|
|
150
223
|
ctx.props().presentation().on_eject.emit(());
|
|
151
224
|
false
|
|
152
225
|
},
|
|
153
226
|
StatusBarMsg::Noop => {
|
|
154
|
-
self.title = ctx.props().
|
|
227
|
+
self.title = ctx.props().title.clone();
|
|
155
228
|
true
|
|
156
229
|
},
|
|
157
230
|
StatusBarMsg::TitleInputEvent => {
|
|
@@ -201,16 +274,21 @@ impl Component for StatusBar {
|
|
|
201
274
|
..
|
|
202
275
|
} = ctx.props();
|
|
203
276
|
|
|
277
|
+
let has_table = ctx.props().has_table.clone();
|
|
278
|
+
let is_errored = ctx.props().is_errored;
|
|
279
|
+
let is_settings_open = ctx.props().is_settings_open;
|
|
280
|
+
let title = &ctx.props().title;
|
|
281
|
+
|
|
204
282
|
let mut is_updating_class_name = classes!();
|
|
205
|
-
if
|
|
283
|
+
if title.is_some() {
|
|
206
284
|
is_updating_class_name.push("titled");
|
|
207
285
|
};
|
|
208
286
|
|
|
209
|
-
if !
|
|
287
|
+
if !is_settings_open {
|
|
210
288
|
is_updating_class_name.push(["settings-closed", "titled"]);
|
|
211
289
|
};
|
|
212
290
|
|
|
213
|
-
if !
|
|
291
|
+
if !matches!(has_table, Some(TableLoadState::Loaded)) {
|
|
214
292
|
is_updating_class_name.push("updating");
|
|
215
293
|
}
|
|
216
294
|
|
|
@@ -229,20 +307,58 @@ impl Component for StatusBar {
|
|
|
229
307
|
.link()
|
|
230
308
|
.callback(|_: InputEvent| StatusBarMsg::TitleInputEvent);
|
|
231
309
|
|
|
232
|
-
let is_menu =
|
|
310
|
+
let is_menu = matches!(has_table, Some(TableLoadState::Loaded))
|
|
311
|
+
&& ctx.props().on_settings.as_ref().is_none();
|
|
233
312
|
let is_title = is_menu
|
|
234
|
-
||
|
|
235
|
-
||
|
|
236
|
-
||
|
|
313
|
+
|| ctx.props().is_workspace
|
|
314
|
+
|| title.is_some()
|
|
315
|
+
|| is_errored
|
|
237
316
|
|| presentation.is_active(&self.input_ref.cast::<Element>());
|
|
238
317
|
|
|
239
|
-
let is_settings =
|
|
240
|
-
||
|
|
241
|
-
|| !
|
|
242
|
-
||
|
|
243
|
-
||
|
|
318
|
+
let is_settings = title.is_some()
|
|
319
|
+
|| ctx.props().is_workspace
|
|
320
|
+
|| !matches!(has_table, Some(TableLoadState::Loaded))
|
|
321
|
+
|| is_errored
|
|
322
|
+
|| is_settings_open
|
|
244
323
|
|| presentation.is_active(&self.input_ref.cast::<Element>());
|
|
245
324
|
|
|
325
|
+
let on_copy_select = {
|
|
326
|
+
let props = ctx.props().clone();
|
|
327
|
+
let link = ctx.link().clone();
|
|
328
|
+
Callback::from(move |x: ExportFile| {
|
|
329
|
+
let props = props.clone();
|
|
330
|
+
let link = link.clone();
|
|
331
|
+
spawn_local(async move {
|
|
332
|
+
let mime = x.method.mimetype(x.is_chart);
|
|
333
|
+
let task = props.export_method_to_blob(x.method);
|
|
334
|
+
let result = copy_to_clipboard(task, mime).await;
|
|
335
|
+
crate::maybe_log!({
|
|
336
|
+
result?;
|
|
337
|
+
link.send_message(StatusBarMsg::CloseCopy);
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
let on_export_select = {
|
|
344
|
+
let props = ctx.props().clone();
|
|
345
|
+
let link = ctx.link().clone();
|
|
346
|
+
Callback::from(move |x: ExportFile| {
|
|
347
|
+
if !x.name.is_empty() {
|
|
348
|
+
clone!(props, link);
|
|
349
|
+
spawn_local(async move {
|
|
350
|
+
let val = props.export_method_to_blob(x.method).await.unwrap();
|
|
351
|
+
let is_chart = props.renderer().is_chart();
|
|
352
|
+
download(&x.as_filename(is_chart), &val).unwrap();
|
|
353
|
+
link.send_message(StatusBarMsg::CloseExport);
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
let on_close_copy = ctx.link().callback(|_| StatusBarMsg::CloseCopy);
|
|
360
|
+
let on_close_export = ctx.link().callback(|_| StatusBarMsg::CloseExport);
|
|
361
|
+
|
|
246
362
|
if is_settings {
|
|
247
363
|
html! {
|
|
248
364
|
<>
|
|
@@ -253,7 +369,15 @@ impl Component for StatusBar {
|
|
|
253
369
|
class={is_updating_class_name}
|
|
254
370
|
{onpointerdown}
|
|
255
371
|
>
|
|
256
|
-
<StatusIndicator
|
|
372
|
+
<StatusIndicator
|
|
373
|
+
{custom_events}
|
|
374
|
+
{renderer}
|
|
375
|
+
{session}
|
|
376
|
+
update_count={ctx.props().update_count}
|
|
377
|
+
error={ctx.props().error.clone()}
|
|
378
|
+
has_table={ctx.props().has_table.clone()}
|
|
379
|
+
stats={ctx.props().stats.clone()}
|
|
380
|
+
/>
|
|
257
381
|
if is_title {
|
|
258
382
|
<label
|
|
259
383
|
class="input-sizer"
|
|
@@ -272,21 +396,22 @@ impl Component for StatusBar {
|
|
|
272
396
|
</label>
|
|
273
397
|
}
|
|
274
398
|
if is_title {
|
|
275
|
-
<StatusBarRowsCounter {
|
|
399
|
+
<StatusBarRowsCounter stats={ctx.props().stats.clone()} />
|
|
276
400
|
}
|
|
277
401
|
<div id="spacer" />
|
|
278
402
|
if is_menu {
|
|
279
403
|
<div id="menu-bar" class="section">
|
|
280
404
|
<ThemeSelector
|
|
281
|
-
theme={
|
|
282
|
-
themes={
|
|
405
|
+
theme={ctx.props().selected_theme.clone()}
|
|
406
|
+
themes={ctx.props().available_themes.clone()}
|
|
283
407
|
on_change={ctx.link().callback(StatusBarMsg::SetTheme)}
|
|
284
408
|
on_reset={ctx.link().callback(|_| StatusBarMsg::ResetTheme)}
|
|
285
409
|
/>
|
|
286
410
|
<div id="plugin-settings"><slot name="statusbar-extra" /></div>
|
|
287
411
|
<span class="hover-target">
|
|
288
412
|
<span id="reset" class="button" onmousedown={&onreset}>
|
|
289
|
-
<span />
|
|
413
|
+
<span class="icon" />
|
|
414
|
+
<span class="icon-label" />
|
|
290
415
|
</span>
|
|
291
416
|
</span>
|
|
292
417
|
<span
|
|
@@ -294,14 +419,20 @@ impl Component for StatusBar {
|
|
|
294
419
|
class="hover-target"
|
|
295
420
|
onmousedown={onexport}
|
|
296
421
|
>
|
|
297
|
-
<span id="export" class="button"
|
|
422
|
+
<span id="export" class="button">
|
|
423
|
+
<span class="icon" />
|
|
424
|
+
<span class="icon-label" />
|
|
425
|
+
</span>
|
|
298
426
|
</span>
|
|
299
427
|
<span
|
|
300
428
|
ref={&self.copy_ref}
|
|
301
429
|
class="hover-target"
|
|
302
430
|
onmousedown={oncopy}
|
|
303
431
|
>
|
|
304
|
-
<span id="copy" class="button"
|
|
432
|
+
<span id="copy" class="button">
|
|
433
|
+
<span class="icon" />
|
|
434
|
+
<span class="icon-label" />
|
|
435
|
+
</span>
|
|
305
436
|
</span>
|
|
306
437
|
</div>
|
|
307
438
|
}
|
|
@@ -310,17 +441,45 @@ impl Component for StatusBar {
|
|
|
310
441
|
id="settings_button"
|
|
311
442
|
class="noselect"
|
|
312
443
|
onmousedown={x.reform(|_| ())}
|
|
313
|
-
|
|
314
|
-
|
|
444
|
+
>
|
|
445
|
+
<span class="icon" />
|
|
446
|
+
</div>
|
|
447
|
+
<div id="close_button" class="noselect" onmousedown={onclose}>
|
|
448
|
+
<span class="icon" />
|
|
449
|
+
</div>
|
|
315
450
|
}
|
|
316
451
|
</div>
|
|
452
|
+
<PortalModal
|
|
453
|
+
tag_name="perspective-copy-menu"
|
|
454
|
+
target={self.copy_target.clone()}
|
|
455
|
+
own_focus=true
|
|
456
|
+
on_close={on_close_copy}
|
|
457
|
+
theme={ctx.props().selected_theme.clone().unwrap_or_default()}
|
|
458
|
+
>
|
|
459
|
+
<CopyDropDownMenu renderer={renderer.clone()} callback={on_copy_select} />
|
|
460
|
+
</PortalModal>
|
|
461
|
+
<PortalModal
|
|
462
|
+
tag_name="perspective-export-menu"
|
|
463
|
+
target={self.export_target.clone()}
|
|
464
|
+
own_focus=true
|
|
465
|
+
on_close={on_close_export}
|
|
466
|
+
theme={ctx.props().selected_theme.clone().unwrap_or_default()}
|
|
467
|
+
>
|
|
468
|
+
<ExportDropDownMenu
|
|
469
|
+
renderer={renderer.clone()}
|
|
470
|
+
session={session.clone()}
|
|
471
|
+
callback={on_export_select}
|
|
472
|
+
/>
|
|
473
|
+
</PortalModal>
|
|
317
474
|
</>
|
|
318
475
|
}
|
|
319
476
|
} else if let Some(x) = ctx.props().on_settings.as_ref() {
|
|
320
477
|
let class = classes!(is_updating_class_name, "floating");
|
|
321
478
|
html! {
|
|
322
479
|
<div id={ctx.props().id.clone()} {class}>
|
|
323
|
-
<div id="settings_button" class="noselect" onmousedown={x.reform(|_| ())}
|
|
480
|
+
<div id="settings_button" class="noselect" onmousedown={x.reform(|_| ())}>
|
|
481
|
+
<span class="icon" />
|
|
482
|
+
</div>
|
|
324
483
|
<div id="close_button" class="noselect" onmousedown={&onclose} />
|
|
325
484
|
</div>
|
|
326
485
|
}
|
|
@@ -330,46 +489,10 @@ impl Component for StatusBar {
|
|
|
330
489
|
}
|
|
331
490
|
}
|
|
332
491
|
|
|
333
|
-
fn register_listeners(ctx: &Context<StatusBar>) -> [Subscription; 5] {
|
|
334
|
-
[
|
|
335
|
-
ctx.props()
|
|
336
|
-
.presentation()
|
|
337
|
-
.theme_config_updated
|
|
338
|
-
.add_listener(ctx.link().callback(StatusBarMsg::SetThemeConfig)),
|
|
339
|
-
ctx.props()
|
|
340
|
-
.presentation()
|
|
341
|
-
.visibility_changed
|
|
342
|
-
.add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
|
|
343
|
-
ctx.props()
|
|
344
|
-
.session()
|
|
345
|
-
.title_changed
|
|
346
|
-
.add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
|
|
347
|
-
ctx.props()
|
|
348
|
-
.session()
|
|
349
|
-
.table_loaded
|
|
350
|
-
.add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
|
|
351
|
-
ctx.props()
|
|
352
|
-
.session()
|
|
353
|
-
.table_errored
|
|
354
|
-
.add_listener(ctx.link().callback(|_| StatusBarMsg::Noop)),
|
|
355
|
-
]
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
fn fetch_initial_theme(ctx: &Context<StatusBar>) {
|
|
359
|
-
ApiFuture::spawn({
|
|
360
|
-
let on_theme = ctx.link().callback(StatusBarMsg::SetThemeConfig);
|
|
361
|
-
clone!(ctx.props().presentation());
|
|
362
|
-
async move {
|
|
363
|
-
on_theme.emit(presentation.get_selected_theme_config().await?);
|
|
364
|
-
Ok(())
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
|
|
369
492
|
#[derive(Properties, PartialEq)]
|
|
370
493
|
struct ThemeSelectorProps {
|
|
371
494
|
pub theme: Option<String>,
|
|
372
|
-
pub themes:
|
|
495
|
+
pub themes: PtrEqRc<Vec<String>>,
|
|
373
496
|
pub on_reset: Callback<()>,
|
|
374
497
|
pub on_change: Callback<String>,
|
|
375
498
|
}
|
|
@@ -403,6 +526,7 @@ fn ThemeSelector(props: &ThemeSelectorProps) -> Html {
|
|
|
403
526
|
onclick={props.on_reset.reform(|_| ())}
|
|
404
527
|
/>
|
|
405
528
|
<span id="theme" class="button">
|
|
529
|
+
<span class="icon" />
|
|
406
530
|
<Select<String>
|
|
407
531
|
id="theme_selector"
|
|
408
532
|
class="invert"
|