@perspective-dev/viewer 4.0.1 → 4.1.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/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/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 +23 -2
- package/dist/esm/perspective-viewer.d.ts +2 -7
- 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/plugin.d.ts +1 -1
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +1 -0
- package/dist/wasm/perspective-viewer.d.ts +218 -46
- package/dist/wasm/perspective-viewer.js +1250 -761
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +38 -19
- package/package.json +1 -1
- package/src/less/containers/scroll-panel.less +0 -1
- package/src/less/plugin-selector.less +15 -5
- package/src/less/status-bar.less +75 -27
- package/src/less/viewer.less +140 -58
- package/src/rust/components/column_dropdown.rs +21 -21
- package/src/rust/components/column_selector/active_column.rs +131 -120
- package/src/rust/components/column_selector/add_expression_button.rs +5 -0
- package/src/rust/components/column_selector/aggregate_selector.rs +8 -4
- package/src/rust/components/column_selector/config_selector.rs +170 -161
- package/src/rust/components/column_selector/empty_column.rs +16 -11
- package/src/rust/components/column_selector/{expression_toolbar.rs → expr_edit_button.rs} +7 -0
- package/src/rust/components/column_selector/filter_column.rs +195 -194
- package/src/rust/components/column_selector/inactive_column.rs +82 -67
- package/src/rust/components/column_selector/pivot_column.rs +16 -11
- package/src/rust/components/column_selector/sort_column.rs +9 -7
- package/src/rust/components/column_selector.rs +42 -37
- package/src/rust/components/column_settings_sidebar/save_settings.rs +3 -1
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +58 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +6 -6
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +2 -94
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +111 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +3 -3
- package/src/rust/components/column_settings_sidebar/style_tab.rs +23 -83
- package/src/rust/components/{column_settings_sidebar/sidebar.rs → column_settings_sidebar.rs} +198 -171
- package/src/rust/components/containers/dragdrop_list.rs +20 -20
- package/src/rust/components/containers/dropdown_menu.rs +4 -6
- package/src/rust/components/containers/mod.rs +1 -4
- package/src/rust/components/containers/scroll_panel.rs +80 -80
- package/src/rust/components/containers/scroll_panel_item.rs +36 -36
- package/src/rust/components/containers/select.rs +46 -44
- package/src/rust/components/containers/sidebar.rs +3 -19
- package/src/rust/components/{column_settings_sidebar/style_tab/symbol/symbol_config.rs → containers/sidebar_close_button.rs} +15 -9
- package/src/rust/components/containers/split_panel.rs +212 -200
- package/src/rust/components/containers/tab_list.rs +11 -11
- package/src/rust/components/copy_dropdown.rs +22 -25
- package/src/rust/components/datetime_column_style/custom.rs +19 -19
- package/src/rust/components/datetime_column_style/simple.rs +13 -14
- package/src/rust/components/datetime_column_style.rs +75 -76
- package/src/rust/components/editable_header.rs +18 -14
- package/src/rust/components/empty_row.rs +5 -5
- package/src/rust/components/export_dropdown.rs +42 -42
- package/src/rust/components/expression_editor.rs +25 -19
- package/src/rust/components/filter_dropdown.rs +22 -22
- package/src/rust/components/font_loader.rs +11 -9
- package/src/rust/components/form/code_editor.rs +106 -105
- package/src/rust/components/form/color_range_selector.rs +14 -12
- package/src/rust/components/form/color_selector.rs +3 -1
- package/src/rust/components/form/debug.rs +95 -94
- package/src/rust/components/form/highlight.rs +5 -3
- package/src/rust/components/form/mod.rs +3 -2
- package/src/rust/components/form/optional_field.rs +2 -2
- package/src/rust/components/form/{select_field.rs → select_enum_field.rs} +1 -46
- package/src/rust/components/form/select_value_field.rs +64 -0
- package/src/rust/components/function_dropdown.rs +21 -21
- package/src/rust/components/main_panel.rs +219 -0
- package/src/rust/components/mod.rs +6 -6
- package/src/rust/components/modal.rs +42 -42
- package/src/rust/components/number_column_style.rs +34 -88
- package/src/rust/components/plugin_selector.rs +22 -25
- package/src/rust/components/render_warning.rs +9 -6
- package/src/rust/components/settings_panel.rs +82 -0
- package/src/rust/components/status_bar.rs +250 -146
- package/src/rust/components/status_bar_counter.rs +26 -119
- package/src/rust/components/status_indicator.rs +95 -79
- package/src/rust/components/string_column_style.rs +45 -45
- package/src/rust/components/style/style_provider.rs +1 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format.rs +45 -46
- package/src/rust/components/type_icon.rs +14 -11
- package/src/rust/components/viewer.rs +241 -384
- package/src/rust/config/columns_config.rs +2 -2
- package/src/rust/config/datetime_column_style.rs +1 -6
- package/src/rust/config/mod.rs +1 -0
- package/src/rust/config/number_column_style.rs +0 -6
- package/src/rust/config/number_string_format.rs +27 -4
- package/src/rust/config/viewer_config.rs +27 -167
- package/src/rust/custom_elements/copy_dropdown.rs +14 -6
- package/src/rust/custom_elements/export_dropdown.rs +15 -7
- package/src/rust/custom_elements/filter_dropdown.rs +4 -4
- package/src/rust/custom_elements/mod.rs +3 -0
- package/src/rust/custom_elements/viewer.rs +353 -161
- package/src/rust/custom_events.rs +55 -32
- package/src/rust/dragdrop.rs +4 -24
- package/src/rust/exprtk/cursor.rs +10 -1
- package/src/rust/exprtk/mod.rs +2 -0
- package/src/rust/exprtk/tokenize.rs +20 -3
- package/src/rust/js/clipboard.rs +2 -2
- package/src/rust/js/mimetype.rs +2 -7
- package/src/rust/js/mod.rs +0 -1
- package/src/rust/js/plugin.rs +7 -0
- package/src/rust/lib.rs +18 -5
- package/src/rust/model/column_locator.rs +82 -0
- package/src/rust/model/columns_iter_set.rs +1 -0
- package/src/rust/model/copy_export.rs +50 -14
- package/src/rust/model/edit_expression.rs +2 -5
- package/src/rust/model/eject.rs +41 -0
- package/src/rust/model/get_viewer_config.rs +4 -28
- package/src/rust/model/intersection_observer.rs +20 -8
- package/src/rust/model/mod.rs +11 -4
- package/src/rust/model/plugin_column_styles.rs +0 -31
- package/src/rust/model/reset_all.rs +38 -0
- package/src/rust/model/resize_observer.rs +34 -7
- package/src/rust/model/restore_and_render.rs +12 -7
- package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
- package/src/rust/model/structural.rs +194 -23
- package/src/rust/model/update_and_render.rs +14 -4
- package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
- package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
- package/src/rust/presentation.rs +60 -119
- package/src/rust/renderer/activate.rs +20 -5
- package/src/rust/renderer/limits.rs +0 -149
- package/src/rust/renderer/render_timer.rs +1 -1
- package/src/rust/renderer.rs +34 -18
- package/src/rust/root.rs +50 -0
- package/src/rust/session/column_defaults_update.rs +4 -4
- package/src/rust/session/drag_drop_update.rs +1 -1
- package/src/rust/session/metadata.rs +3 -17
- package/src/rust/session/replace_expression_update.rs +1 -2
- package/src/rust/session.rs +162 -82
- package/src/rust/utils/browser/blob.rs +16 -2
- package/src/rust/utils/browser/download.rs +1 -0
- package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
- package/src/rust/utils/browser/mod.rs +8 -4
- package/src/rust/utils/browser/selection.rs +5 -0
- package/src/rust/utils/custom_element.rs +28 -13
- package/src/rust/utils/datetime.rs +5 -0
- package/src/rust/utils/debounce.rs +7 -1
- package/src/rust/utils/hooks/use_async_callback.rs +7 -17
- package/src/rust/utils/mod.rs +28 -40
- package/src/rust/utils/number_format.rs +6 -5
- package/src/rust/utils/pubsub.rs +15 -10
- package/src/rust/utils/weak_scope.rs +11 -1
- package/src/svg/bookmark-icon.svg +4 -0
- package/src/svg/drag-handle copy.svg +10 -0
- package/src/svg/drawer-tab-hover.svg +5 -7
- package/src/svg/drawer-tab-invert-hover.svg +4 -8
- package/src/svg/drawer-tab-invert.svg +4 -7
- package/src/svg/drawer-tab.svg +4 -6
- package/src/svg/status_ok.svg +24 -24
- package/src/ts/extensions.ts +51 -3
- package/src/ts/perspective-viewer.ts +2 -14
- package/src/ts/plugin.ts +1 -1
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
- package/src/rust/components/containers/tests/mod.rs +0 -11
- package/src/rust/components/containers/tests/split_panel.rs +0 -91
- package/src/rust/js/testing.rs +0 -149
- package/src/rust/utils/tee.rs +0 -88
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline4.js +0 -0
- /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
- /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
|
@@ -14,20 +14,19 @@
|
|
|
14
14
|
|
|
15
15
|
use std::cell::RefCell;
|
|
16
16
|
use std::rc::Rc;
|
|
17
|
-
use std::str::FromStr;
|
|
18
17
|
|
|
19
|
-
use ::
|
|
20
|
-
use
|
|
21
|
-
use js_sys::*;
|
|
18
|
+
use futures::channel::oneshot::channel;
|
|
19
|
+
use js_sys::{Array, JsString};
|
|
22
20
|
use perspective_client::config::ViewConfigUpdate;
|
|
23
|
-
use
|
|
21
|
+
use perspective_client::utils::PerspectiveResultExt;
|
|
22
|
+
use perspective_js::{JsViewConfig, JsViewWindow, Table, View, apierror};
|
|
24
23
|
use wasm_bindgen::JsCast;
|
|
25
24
|
use wasm_bindgen::prelude::*;
|
|
25
|
+
use wasm_bindgen_derive::try_from_js_option;
|
|
26
26
|
use wasm_bindgen_futures::JsFuture;
|
|
27
|
-
use web_sys
|
|
28
|
-
use yew::prelude::*;
|
|
27
|
+
use web_sys::HtmlElement;
|
|
29
28
|
|
|
30
|
-
use crate::components::viewer::{
|
|
29
|
+
use crate::components::viewer::{PerspectiveViewerMsg, PerspectiveViewerProps};
|
|
31
30
|
use crate::config::*;
|
|
32
31
|
use crate::custom_events::*;
|
|
33
32
|
use crate::dragdrop::*;
|
|
@@ -35,7 +34,8 @@ use crate::js::*;
|
|
|
35
34
|
use crate::model::*;
|
|
36
35
|
use crate::presentation::*;
|
|
37
36
|
use crate::renderer::*;
|
|
38
|
-
use crate::
|
|
37
|
+
use crate::root::Root;
|
|
38
|
+
use crate::session::{ResetOptions, Session};
|
|
39
39
|
use crate::utils::*;
|
|
40
40
|
use crate::*;
|
|
41
41
|
|
|
@@ -49,21 +49,36 @@ use crate::*;
|
|
|
49
49
|
/// const viewer = document.createElement("perspective-viewer");
|
|
50
50
|
/// window.body.appendChild(viewer);
|
|
51
51
|
/// ```
|
|
52
|
-
|
|
52
|
+
///
|
|
53
|
+
/// Complete example including loading and restoring the [`Table`]:
|
|
54
|
+
///
|
|
55
|
+
/// ```javascript
|
|
56
|
+
/// import perspective from "@perspective-dev/viewer";
|
|
57
|
+
/// import perspective from "@perspective-dev/client";
|
|
58
|
+
///
|
|
59
|
+
/// const viewer = document.createElement("perspective-viewer");
|
|
60
|
+
/// const worker = await perspective.worker();
|
|
61
|
+
///
|
|
62
|
+
/// await worker.table("x\n1", {name: "table_one"});
|
|
63
|
+
/// await viewer.load(worker);
|
|
64
|
+
/// await viewer.restore({table: "table_one"});
|
|
65
|
+
/// ```
|
|
66
|
+
#[derive(Clone, PerspectiveProperties!)]
|
|
53
67
|
#[wasm_bindgen]
|
|
54
68
|
pub struct PerspectiveViewerElement {
|
|
55
69
|
elem: HtmlElement,
|
|
56
|
-
root:
|
|
70
|
+
root: Root<components::viewer::PerspectiveViewer>,
|
|
57
71
|
resize_handle: Rc<RefCell<Option<ResizeObserverHandle>>>,
|
|
58
72
|
intersection_handle: Rc<RefCell<Option<IntersectionObserverHandle>>>,
|
|
59
73
|
session: Session,
|
|
60
74
|
renderer: Renderer,
|
|
61
75
|
presentation: Presentation,
|
|
62
|
-
|
|
63
|
-
_subscriptions: Rc<Subscription>,
|
|
76
|
+
custom_events: CustomEvents,
|
|
77
|
+
_subscriptions: Rc<[Subscription; 2]>,
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
derive_model!( Renderer, Session, Presentation for
|
|
80
|
+
// derive_model!( Renderer, Root, Session, Presentation for
|
|
81
|
+
// PerspectiveViewerElement);
|
|
67
82
|
|
|
68
83
|
impl CustomElementMetadata for PerspectiveViewerElement {
|
|
69
84
|
const CUSTOM_ELEMENT_NAME: &'static str = "perspective-viewer";
|
|
@@ -86,10 +101,10 @@ impl PerspectiveViewerElement {
|
|
|
86
101
|
|
|
87
102
|
fn new_from_shadow(elem: web_sys::HtmlElement, shadow_root: web_sys::Element) -> Self {
|
|
88
103
|
// Application State
|
|
89
|
-
let session = Session::
|
|
104
|
+
let session = Session::new();
|
|
90
105
|
let renderer = Renderer::new(&elem);
|
|
91
106
|
let presentation = Presentation::new(&elem);
|
|
92
|
-
let
|
|
107
|
+
let custom_events = CustomEvents::new(&elem, &session, &renderer, &presentation);
|
|
93
108
|
|
|
94
109
|
// Create Yew App
|
|
95
110
|
let props = yew::props!(PerspectiveViewerProps {
|
|
@@ -98,50 +113,84 @@ impl PerspectiveViewerElement {
|
|
|
98
113
|
renderer: renderer.clone(),
|
|
99
114
|
presentation: presentation.clone(),
|
|
100
115
|
dragdrop: DragDrop::default(),
|
|
101
|
-
custom_events:
|
|
102
|
-
weak_link: WeakScope::default(),
|
|
116
|
+
custom_events: custom_events.clone(),
|
|
103
117
|
});
|
|
104
118
|
|
|
105
|
-
let
|
|
119
|
+
let state = props.clone_state();
|
|
120
|
+
let root = Root::new(shadow_root, props);
|
|
106
121
|
|
|
107
122
|
// Create callbacks
|
|
108
123
|
let update_sub = session.table_updated.add_listener({
|
|
109
124
|
clone!(renderer, session);
|
|
110
125
|
move |_| {
|
|
111
126
|
clone!(renderer, session);
|
|
112
|
-
ApiFuture::spawn(async move { renderer.update(
|
|
127
|
+
ApiFuture::spawn(async move { renderer.update(session.get_view()).await })
|
|
113
128
|
}
|
|
114
129
|
});
|
|
115
130
|
|
|
116
|
-
let
|
|
131
|
+
let eject_sub = presentation.on_eject.add_listener({
|
|
132
|
+
let root = root.clone();
|
|
133
|
+
move |_| ApiFuture::spawn(state.delete_all(&root))
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
let resize_handle = ResizeObserverHandle::new(&elem, &renderer, &session, &root);
|
|
137
|
+
let intersect_handle =
|
|
138
|
+
IntersectionObserverHandle::new(&elem, &presentation, &session, &renderer);
|
|
139
|
+
|
|
117
140
|
Self {
|
|
118
141
|
elem,
|
|
119
|
-
root
|
|
142
|
+
root,
|
|
120
143
|
session,
|
|
121
144
|
renderer,
|
|
122
145
|
presentation,
|
|
123
146
|
resize_handle: Rc::new(RefCell::new(Some(resize_handle))),
|
|
124
|
-
intersection_handle: Rc::new(RefCell::new(
|
|
125
|
-
|
|
126
|
-
_subscriptions: Rc::new(update_sub),
|
|
147
|
+
intersection_handle: Rc::new(RefCell::new(Some(intersect_handle))),
|
|
148
|
+
custom_events,
|
|
149
|
+
_subscriptions: Rc::new([update_sub, eject_sub]),
|
|
127
150
|
}
|
|
128
151
|
}
|
|
129
152
|
|
|
130
153
|
#[doc(hidden)]
|
|
131
154
|
#[wasm_bindgen(js_name = "connectedCallback")]
|
|
132
|
-
pub fn connected_callback(&self) {
|
|
155
|
+
pub fn connected_callback(&self) -> ApiResult<()> {
|
|
133
156
|
tracing::debug!("Connected <perspective-viewer>");
|
|
157
|
+
Ok(())
|
|
134
158
|
}
|
|
135
159
|
|
|
136
|
-
/// Loads a [`
|
|
137
|
-
/// [`Table`]
|
|
160
|
+
/// Loads a [`Client`], or optionally [`Table`], or optionally a Javascript
|
|
161
|
+
/// `Promise` which returns a [`Client`] or [`Table`], in this viewer.
|
|
162
|
+
///
|
|
163
|
+
/// Loading a [`Client`] does not render, but subsequent calls to
|
|
164
|
+
/// [`PerspectiveViewerElement::restore`] will use this [`Client`] to look
|
|
165
|
+
/// up the proviced `table` name field for the provided
|
|
166
|
+
/// [`ViewerConfigUpdate`].
|
|
167
|
+
///
|
|
168
|
+
/// Loading a [`Table`] is equivalent to subsequently calling
|
|
169
|
+
/// [`Self::restore`] with the `table` field set to [`Table::get_name`], and
|
|
170
|
+
/// will render the UI in its default state when [`Self::load`] resolves.
|
|
171
|
+
/// If you plan to call [`Self::restore`] anyway, prefer passing a
|
|
172
|
+
/// [`Client`] argument to [`Self::load`] as it will conserve one render.
|
|
138
173
|
///
|
|
139
174
|
/// When [`PerspectiveViewerElement::load`] resolves, the first frame of the
|
|
140
175
|
/// UI + visualization is guaranteed to have been drawn. Awaiting the result
|
|
141
176
|
/// of this method in a `try`/`catch` block will capture any errors
|
|
142
|
-
/// thrown during the loading process, or from the [`
|
|
177
|
+
/// thrown during the loading process, or from the [`Client`] `Promise`
|
|
143
178
|
/// itself.
|
|
144
179
|
///
|
|
180
|
+
/// [`PerspectiveViewerElement::load`] may also be called with a [`Table`],
|
|
181
|
+
/// which is equivalent to:
|
|
182
|
+
///
|
|
183
|
+
/// ```javascript
|
|
184
|
+
/// await viewer.load(await table.get_client());
|
|
185
|
+
/// await viewer.restore({name: await table.get_name()})
|
|
186
|
+
/// ```
|
|
187
|
+
///
|
|
188
|
+
/// If you plan to call [`PerspectiveViewerElement::restore`] immediately
|
|
189
|
+
/// after [`PerspectiveViewerElement::load`] yourself, as is commonly
|
|
190
|
+
/// done when loading and configuring a new `<perspective-viewer>`, you
|
|
191
|
+
/// should use a [`Client`] as an argument and set the `table` field in the
|
|
192
|
+
/// restore call as
|
|
193
|
+
///
|
|
145
194
|
/// A [`Table`] can be created using the
|
|
146
195
|
/// [`@perspective-dev/client`](https://www.npmjs.com/package/@perspective-dev/client)
|
|
147
196
|
/// library from NPM (see [`perspective_js`] documentation for details).
|
|
@@ -152,76 +201,104 @@ impl PerspectiveViewerElement {
|
|
|
152
201
|
/// import perspective from "@perspective-dev/client";
|
|
153
202
|
///
|
|
154
203
|
/// const worker = await perspective.worker();
|
|
155
|
-
/// viewer.load(worker
|
|
204
|
+
/// viewer.load(worker);
|
|
156
205
|
/// ```
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
206
|
+
///
|
|
207
|
+
/// ... or
|
|
208
|
+
///
|
|
209
|
+
/// ```javascript
|
|
210
|
+
/// const table = await worker.table(data, {name: "superstore"});
|
|
211
|
+
/// viewer.load(table);
|
|
212
|
+
/// ```
|
|
213
|
+
///
|
|
214
|
+
/// Complete example:
|
|
215
|
+
///
|
|
216
|
+
/// ```javascript
|
|
217
|
+
/// const viewer = document.createElement("perspective-viewer");
|
|
218
|
+
/// const worker = await perspective.worker();
|
|
219
|
+
///
|
|
220
|
+
/// await worker.table("x\n1", {name: "table_one"});
|
|
221
|
+
/// await viewer.load(worker);
|
|
222
|
+
/// await viewer.restore({table: "table_one", columns: ["x"]});
|
|
223
|
+
/// ```
|
|
224
|
+
///
|
|
225
|
+
/// ... or, if you don't want to pass your own arguments to `restore`:
|
|
226
|
+
///
|
|
227
|
+
/// ```javascript
|
|
228
|
+
/// const viewer = document.createElement("perspective-viewer");
|
|
229
|
+
/// const worker = await perspective.worker();
|
|
230
|
+
///
|
|
231
|
+
/// const table = await worker.table("x\n1", {name: "table_one"});
|
|
232
|
+
/// await viewer.load(table);
|
|
233
|
+
/// ```
|
|
234
|
+
pub fn load(&self, table: JsValue) -> ApiResult<ApiFuture<()>> {
|
|
160
235
|
let promise = table
|
|
161
236
|
.clone()
|
|
162
237
|
.dyn_into::<js_sys::Promise>()
|
|
163
238
|
.unwrap_or_else(|_| js_sys::Promise::resolve(&table));
|
|
164
239
|
|
|
165
|
-
self.
|
|
166
|
-
let
|
|
240
|
+
let _plugin = self.renderer.get_active_plugin()?;
|
|
241
|
+
let task = self.session.reset(ResetOptions {
|
|
242
|
+
config: true,
|
|
243
|
+
expressions: true,
|
|
244
|
+
stats: true,
|
|
245
|
+
..ResetOptions::default()
|
|
246
|
+
});
|
|
247
|
+
|
|
167
248
|
let mut config = ViewConfigUpdate {
|
|
168
249
|
columns: Some(self.session.get_view_config().columns.clone()),
|
|
169
250
|
..ViewConfigUpdate::default()
|
|
170
251
|
};
|
|
171
252
|
|
|
253
|
+
let metadata = self.renderer.metadata();
|
|
172
254
|
self.session
|
|
173
|
-
.set_update_column_defaults(&mut config, &
|
|
255
|
+
.set_update_column_defaults(&mut config, &metadata);
|
|
256
|
+
self.session.update_view_config(config)?;
|
|
174
257
|
|
|
175
|
-
let update_task = self.session.update_view_config(config);
|
|
176
258
|
clone!(self.renderer, self.session);
|
|
177
|
-
ApiFuture::new(async move {
|
|
259
|
+
Ok(ApiFuture::new(async move {
|
|
178
260
|
let task = async {
|
|
179
|
-
|
|
261
|
+
// Ignore this error, which is blown away by the table anyway.
|
|
262
|
+
let _ = task.await;
|
|
180
263
|
let jstable = JsFuture::from(promise)
|
|
181
264
|
.await
|
|
182
265
|
.map_err(|x| apierror!(TableError(x)))?;
|
|
183
266
|
|
|
184
|
-
if let Some(table) =
|
|
185
|
-
|
|
267
|
+
if let Ok(Some(table)) =
|
|
268
|
+
try_from_js_option::<perspective_js::Table>(jstable.clone())
|
|
186
269
|
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"Table `{}` already loaded, skipping",
|
|
191
|
-
table.get_name().await
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
return Ok(&session);
|
|
195
|
-
} else {
|
|
196
|
-
tracing::debug!(
|
|
197
|
-
"New table {} vs {}",
|
|
198
|
-
table.get_table().get_name(),
|
|
199
|
-
existing_table.get_name()
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
270
|
+
let client = table.get_client().await;
|
|
271
|
+
session.set_client(client.get_client().clone());
|
|
272
|
+
let name = table.get_name().await;
|
|
204
273
|
tracing::debug!(
|
|
205
|
-
"
|
|
206
|
-
table.size().await
|
|
274
|
+
"Loading {:.0} rows from `Table` {}",
|
|
275
|
+
table.size().await?,
|
|
276
|
+
name
|
|
207
277
|
);
|
|
208
278
|
|
|
209
|
-
session.set_table(
|
|
210
|
-
|
|
279
|
+
if session.set_table(name).await? {
|
|
280
|
+
session.validate().await?.create_view().await?;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
Ok(session.get_view())
|
|
284
|
+
} else if let Ok(Some(client)) =
|
|
285
|
+
wasm_bindgen_derive::try_from_js_option::<perspective_js::Client>(jstable)
|
|
286
|
+
{
|
|
287
|
+
session.set_client(client.get_client().clone());
|
|
288
|
+
Ok(session.get_view())
|
|
211
289
|
} else {
|
|
212
|
-
Err(ApiError::new("Invalid
|
|
290
|
+
Err(ApiError::new("Invalid argument"))
|
|
213
291
|
}
|
|
214
292
|
};
|
|
215
293
|
|
|
216
294
|
renderer.set_throttle(None);
|
|
217
|
-
let
|
|
218
|
-
let result = draw.and(delete);
|
|
295
|
+
let result = renderer.draw(task).await;
|
|
219
296
|
if let Err(e) = &result {
|
|
220
297
|
session.set_error(false, e.clone()).await?;
|
|
221
298
|
}
|
|
222
299
|
|
|
223
300
|
result
|
|
224
|
-
})
|
|
301
|
+
}))
|
|
225
302
|
}
|
|
226
303
|
|
|
227
304
|
/// Delete the internal [`View`] and all associated state, rendering this
|
|
@@ -246,17 +323,7 @@ impl PerspectiveViewerElement {
|
|
|
246
323
|
/// await viewer.delete();
|
|
247
324
|
/// ```
|
|
248
325
|
pub fn delete(self) -> ApiFuture<()> {
|
|
249
|
-
|
|
250
|
-
ApiFuture::new(self.renderer.clone().with_lock(async move {
|
|
251
|
-
renderer.delete()?;
|
|
252
|
-
root.borrow_mut()
|
|
253
|
-
.take()
|
|
254
|
-
.ok_or("Already deleted!")?
|
|
255
|
-
.destroy();
|
|
256
|
-
session.delete().await?;
|
|
257
|
-
tracing::info!("Deleted <perspective-viewer>");
|
|
258
|
-
Ok(())
|
|
259
|
-
}))
|
|
326
|
+
self.delete_all(self.root())
|
|
260
327
|
}
|
|
261
328
|
|
|
262
329
|
/// Restart this `<perspective-viewer>` to its initial state, before
|
|
@@ -273,7 +340,7 @@ impl PerspectiveViewerElement {
|
|
|
273
340
|
);
|
|
274
341
|
|
|
275
342
|
std::mem::swap(self, &mut state);
|
|
276
|
-
state.delete()
|
|
343
|
+
ApiFuture::new(state.delete())
|
|
277
344
|
} else {
|
|
278
345
|
ApiFuture::new(async move { Ok(()) })
|
|
279
346
|
}
|
|
@@ -304,8 +371,23 @@ impl PerspectiveViewerElement {
|
|
|
304
371
|
ApiFuture::new(async move { Ok(session.get_view().ok_or("No table set")?.into()) })
|
|
305
372
|
}
|
|
306
373
|
|
|
374
|
+
/// Get a copy of the [`ViewConfig`] for the current [`View`]. This is
|
|
375
|
+
/// non-blocking as it does not need to access the plugin (unlike
|
|
376
|
+
/// [`PerspectiveViewerElement::save`]), and also makes no API calls to the
|
|
377
|
+
/// server (unlike [`PerspectiveViewerElement::getView`] followed by
|
|
378
|
+
/// [`View::get_config`])
|
|
379
|
+
#[wasm_bindgen]
|
|
380
|
+
pub fn getViewConfig(&self) -> ApiFuture<JsViewConfig> {
|
|
381
|
+
let session = self.session.clone();
|
|
382
|
+
ApiFuture::new(async move {
|
|
383
|
+
let config = session.get_view_config();
|
|
384
|
+
Ok(JsValue::from_serde_ext(&*config)?.unchecked_into())
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
307
388
|
/// Get the underlying [`Table`] for this viewer (as passed to
|
|
308
|
-
/// [`PerspectiveViewerElement::load`]
|
|
389
|
+
/// [`PerspectiveViewerElement::load`] or as the `table` field to
|
|
390
|
+
/// [`PerspectiveViewerElement::restore`]).
|
|
309
391
|
///
|
|
310
392
|
/// # Arguments
|
|
311
393
|
///
|
|
@@ -324,10 +406,40 @@ impl PerspectiveViewerElement {
|
|
|
324
406
|
ApiFuture::new(async move {
|
|
325
407
|
match session.get_table() {
|
|
326
408
|
Some(table) => Ok(table.into()),
|
|
327
|
-
None if !wait_for_table.unwrap_or_default() => Err("No
|
|
409
|
+
None if !wait_for_table.unwrap_or_default() => Err("No `Table` set".into()),
|
|
410
|
+
None => {
|
|
411
|
+
session.table_loaded.read_next().await?;
|
|
412
|
+
Ok(session.get_table().ok_or("No `Table` set")?.into())
|
|
413
|
+
},
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/// Get the underlying [`Client`] for this viewer (as passed to, or
|
|
419
|
+
/// associated with the [`Table`] passed to,
|
|
420
|
+
/// [`PerspectiveViewerElement::load`]).
|
|
421
|
+
///
|
|
422
|
+
/// # Arguments
|
|
423
|
+
///
|
|
424
|
+
/// - `wait_for_client` - whether to wait for
|
|
425
|
+
/// [`PerspectiveViewerElement::load`] to be called, or fail immediately
|
|
426
|
+
/// if [`PerspectiveViewerElement::load`] has not yet been called.
|
|
427
|
+
///
|
|
428
|
+
/// # JavaScript Examples
|
|
429
|
+
///
|
|
430
|
+
/// ```javascript
|
|
431
|
+
/// const client = await viewer.getClient();
|
|
432
|
+
/// ```
|
|
433
|
+
#[wasm_bindgen]
|
|
434
|
+
pub fn getClient(&self, wait_for_client: Option<bool>) -> ApiFuture<perspective_js::Client> {
|
|
435
|
+
let session = self.session.clone();
|
|
436
|
+
ApiFuture::new(async move {
|
|
437
|
+
match session.get_client() {
|
|
438
|
+
Some(client) => Ok(client.into()),
|
|
439
|
+
None if !wait_for_client.unwrap_or_default() => Err("No `Client` set".into()),
|
|
328
440
|
None => {
|
|
329
|
-
session.table_loaded.
|
|
330
|
-
Ok(session.
|
|
441
|
+
session.table_loaded.read_next().await?;
|
|
442
|
+
Ok(session.get_client().ok_or("No `Client` set")?.into())
|
|
331
443
|
},
|
|
332
444
|
}
|
|
333
445
|
})
|
|
@@ -370,13 +482,23 @@ impl PerspectiveViewerElement {
|
|
|
370
482
|
pub fn flush(&self) -> ApiFuture<()> {
|
|
371
483
|
clone!(self.renderer);
|
|
372
484
|
ApiFuture::new(async move {
|
|
485
|
+
// We must let two AFs pass to guarantee listeners to the DOM state
|
|
486
|
+
// have themselves triggered, or else `request_animation_frame`
|
|
487
|
+
// may finish before a `ResizeObserver` triggered before is
|
|
488
|
+
// notifiedd.
|
|
489
|
+
//
|
|
490
|
+
// https://github.com/w3c/csswg-drafts/issues/9560
|
|
491
|
+
// https://html.spec.whatwg.org/multipage/webappapis.html#update-the-rendering
|
|
492
|
+
request_animation_frame().await;
|
|
373
493
|
request_animation_frame().await;
|
|
494
|
+
renderer.clone().with_lock(async { Ok(()) }).await?;
|
|
374
495
|
renderer.with_lock(async { Ok(()) }).await
|
|
375
496
|
})
|
|
376
497
|
}
|
|
377
498
|
|
|
378
499
|
/// Restores this element from a full/partial
|
|
379
|
-
/// [`perspective_js::JsViewConfig`]
|
|
500
|
+
/// [`perspective_js::JsViewConfig`] (this element's user-configurable
|
|
501
|
+
/// state, including the `Table` name).
|
|
380
502
|
///
|
|
381
503
|
/// One of the best ways to use [`Self::restore`] is by first configuring
|
|
382
504
|
/// a `<perspective-viewer>` as you wish, then using either the `Debug`
|
|
@@ -390,42 +512,61 @@ impl PerspectiveViewerElement {
|
|
|
390
512
|
///
|
|
391
513
|
/// # JavaScript Examples
|
|
392
514
|
///
|
|
393
|
-
///
|
|
394
|
-
///
|
|
515
|
+
/// Loads a default plugin for the table named `"superstore"`:
|
|
516
|
+
///
|
|
517
|
+
/// ```javascript
|
|
518
|
+
/// await viewer.restore({table: "superstore"});
|
|
519
|
+
/// ```
|
|
520
|
+
///
|
|
521
|
+
/// Apply a `group_by` to the same `viewer` element, without
|
|
522
|
+
/// modifying/resetting other fields - you can omit the `table` field,
|
|
523
|
+
/// this has already been set once and is not modified:
|
|
395
524
|
///
|
|
396
525
|
/// ```javascript
|
|
397
526
|
/// await viewer.restore({group_by: ["State"]});
|
|
398
527
|
/// ```
|
|
399
528
|
pub fn restore(&self, update: JsValue) -> ApiFuture<()> {
|
|
400
|
-
tracing::info!("Restoring ViewerConfig");
|
|
401
529
|
let this = self.clone();
|
|
402
530
|
ApiFuture::new(async move {
|
|
403
531
|
let decoded_update = ViewerConfigUpdate::decode(&update)?;
|
|
532
|
+
tracing::info!("Restoring {}", decoded_update);
|
|
404
533
|
let root = this.root.clone();
|
|
405
534
|
let settings = decoded_update.settings.clone();
|
|
406
|
-
let
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
.send_message_async(move |x| {
|
|
411
|
-
PerspectiveViewerMsg::ToggleSettingsComplete(settings, x)
|
|
412
|
-
});
|
|
535
|
+
let (sender, receiver) = channel::<()>();
|
|
536
|
+
root.borrow().as_ref().into_apierror()?.send_message(
|
|
537
|
+
PerspectiveViewerMsg::ToggleSettingsComplete(settings, sender),
|
|
538
|
+
);
|
|
413
539
|
|
|
414
|
-
let
|
|
415
|
-
.restore_and_render(decoded_update,
|
|
540
|
+
let result = this
|
|
541
|
+
.restore_and_render(decoded_update.clone(), {
|
|
542
|
+
clone!(this, decoded_update.table);
|
|
543
|
+
async move {
|
|
544
|
+
if let OptionalUpdate::Update(name) = table {
|
|
545
|
+
this.session.set_table(name).await?;
|
|
546
|
+
this.session
|
|
547
|
+
.update_column_defaults(&this.renderer.metadata());
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// Something abnormal in the DOM happened, e.g. the
|
|
551
|
+
// element was disconnected while rendering.
|
|
552
|
+
receiver.await.unwrap_or_log();
|
|
553
|
+
Ok(())
|
|
554
|
+
}
|
|
555
|
+
})
|
|
416
556
|
.await;
|
|
417
557
|
|
|
418
|
-
if let Err(e) =
|
|
558
|
+
if let Err(e) = &result {
|
|
419
559
|
this.session().set_error(false, e.clone()).await?;
|
|
420
|
-
Err(e)
|
|
421
|
-
} else {
|
|
422
|
-
Ok(())
|
|
423
560
|
}
|
|
561
|
+
result
|
|
424
562
|
})
|
|
425
563
|
}
|
|
426
564
|
|
|
565
|
+
/// If this element is in an _errored_ state, this method will clear it and
|
|
566
|
+
/// re-render. Calling this method is equivalent to clicking the error reset
|
|
567
|
+
/// button in the UI.
|
|
427
568
|
pub fn resetError(&self) -> ApiFuture<()> {
|
|
428
|
-
self.session.
|
|
569
|
+
ApiFuture::spawn(self.session.reset(ResetOptions::default()));
|
|
429
570
|
let this = self.clone();
|
|
430
571
|
ApiFuture::new(async move {
|
|
431
572
|
this.update_and_render(ViewConfigUpdate::default())?.await?;
|
|
@@ -433,12 +574,8 @@ impl PerspectiveViewerElement {
|
|
|
433
574
|
})
|
|
434
575
|
}
|
|
435
576
|
|
|
436
|
-
/// Save this element
|
|
437
|
-
/// via the [`Self::restore`] method.
|
|
438
|
-
///
|
|
439
|
-
/// # Arguments
|
|
440
|
-
///
|
|
441
|
-
/// - `format` - Supports "json" (default), "arraybuffer" or "string".
|
|
577
|
+
/// Save this element's user-configurable state to a serialized state
|
|
578
|
+
/// object, one which can be restored via the [`Self::restore`] method.
|
|
442
579
|
///
|
|
443
580
|
/// # JavaScript Examples
|
|
444
581
|
///
|
|
@@ -456,26 +593,25 @@ impl PerspectiveViewerElement {
|
|
|
456
593
|
/// await viewer.restore(token);
|
|
457
594
|
/// });
|
|
458
595
|
/// ```
|
|
459
|
-
pub fn save(&self
|
|
596
|
+
pub fn save(&self) -> ApiFuture<JsValue> {
|
|
460
597
|
let this = self.clone();
|
|
461
598
|
ApiFuture::new(async move {
|
|
462
|
-
let
|
|
463
|
-
|
|
464
|
-
.
|
|
465
|
-
.
|
|
466
|
-
.
|
|
599
|
+
let viewer_config = this
|
|
600
|
+
.renderer
|
|
601
|
+
.clone()
|
|
602
|
+
.with_lock(async { this.get_viewer_config().await })
|
|
603
|
+
.await?;
|
|
467
604
|
|
|
468
|
-
|
|
469
|
-
viewer_config.encode(&format)
|
|
605
|
+
viewer_config.encode()
|
|
470
606
|
})
|
|
471
607
|
}
|
|
472
608
|
|
|
473
|
-
/// Download this viewer's internal [`View`] data
|
|
609
|
+
/// Download this viewer's internal [`View`] data via a browser download
|
|
610
|
+
/// event.
|
|
474
611
|
///
|
|
475
612
|
/// # Arguments
|
|
476
613
|
///
|
|
477
|
-
/// - `
|
|
478
|
-
/// to generate this data, or use the default.
|
|
614
|
+
/// - `method` - The `ExportMethod` to use to render the data to download.
|
|
479
615
|
///
|
|
480
616
|
/// # JavaScript Examples
|
|
481
617
|
///
|
|
@@ -484,16 +620,58 @@ impl PerspectiveViewerElement {
|
|
|
484
620
|
/// await viewer.download();
|
|
485
621
|
/// })
|
|
486
622
|
/// ```
|
|
487
|
-
pub fn download(&self,
|
|
488
|
-
let
|
|
623
|
+
pub fn download(&self, method: Option<JsString>) -> ApiFuture<()> {
|
|
624
|
+
let this = self.clone();
|
|
489
625
|
ApiFuture::new(async move {
|
|
490
|
-
let
|
|
491
|
-
.
|
|
492
|
-
.
|
|
493
|
-
|
|
626
|
+
let method = if let Some(method) = method
|
|
627
|
+
.map(|x| x.unchecked_into())
|
|
628
|
+
.map(serde_wasm_bindgen::from_value)
|
|
629
|
+
{
|
|
630
|
+
method?
|
|
631
|
+
} else {
|
|
632
|
+
ExportMethod::Csv
|
|
633
|
+
};
|
|
494
634
|
|
|
495
|
-
|
|
496
|
-
|
|
635
|
+
let blob = this.export_method_to_blob(method).await?;
|
|
636
|
+
let is_chart = this.renderer.is_chart();
|
|
637
|
+
download(
|
|
638
|
+
format!("untitled{}", method.as_filename(is_chart)).as_ref(),
|
|
639
|
+
&blob,
|
|
640
|
+
)
|
|
641
|
+
})
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/// Exports this viewer's internal [`View`] as a JavaSript data, the
|
|
645
|
+
/// exact type of which depends on the `method` but defaults to `String`
|
|
646
|
+
/// in CSV format.
|
|
647
|
+
///
|
|
648
|
+
/// This method is only really useful for the `"plugin"` method, which
|
|
649
|
+
/// will use the configured plugin's export (e.g. PNG for
|
|
650
|
+
/// `@perspective-dev/viewer-d3fc`). Otherwise, prefer to call the
|
|
651
|
+
/// equivalent method on the underlying [`View`] directly.
|
|
652
|
+
///
|
|
653
|
+
/// # Arguments
|
|
654
|
+
///
|
|
655
|
+
/// - `method` - The `ExportMethod` to use to render the data to download.
|
|
656
|
+
///
|
|
657
|
+
/// # JavaScript Examples
|
|
658
|
+
///
|
|
659
|
+
/// ```javascript
|
|
660
|
+
/// const data = await viewer.export("plugin");
|
|
661
|
+
/// ```
|
|
662
|
+
pub fn export(&self, method: Option<JsString>) -> ApiFuture<JsValue> {
|
|
663
|
+
let this = self.clone();
|
|
664
|
+
ApiFuture::new(async move {
|
|
665
|
+
let method = if let Some(method) = method
|
|
666
|
+
.map(|x| x.unchecked_into())
|
|
667
|
+
.map(serde_wasm_bindgen::from_value)
|
|
668
|
+
{
|
|
669
|
+
method?
|
|
670
|
+
} else {
|
|
671
|
+
ExportMethod::Csv
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
this.export_method_to_jsvalue(method).await
|
|
497
675
|
})
|
|
498
676
|
}
|
|
499
677
|
|
|
@@ -524,7 +702,7 @@ impl PerspectiveViewerElement {
|
|
|
524
702
|
ExportMethod::Csv
|
|
525
703
|
};
|
|
526
704
|
|
|
527
|
-
let js_task = this.
|
|
705
|
+
let js_task = this.export_method_to_blob(method);
|
|
528
706
|
copy_to_clipboard(js_task, MimeType::TextPlain).await
|
|
529
707
|
})
|
|
530
708
|
}
|
|
@@ -542,17 +720,17 @@ impl PerspectiveViewerElement {
|
|
|
542
720
|
/// await viewer.reset();
|
|
543
721
|
/// ```
|
|
544
722
|
pub fn reset(&self, reset_all: Option<bool>) -> ApiFuture<()> {
|
|
545
|
-
tracing::
|
|
723
|
+
tracing::debug!("Resetting config");
|
|
546
724
|
let root = self.root.clone();
|
|
547
725
|
let all = reset_all.unwrap_or_default();
|
|
548
726
|
ApiFuture::new(async move {
|
|
549
|
-
let
|
|
550
|
-
|
|
727
|
+
let (sender, receiver) = channel::<()>();
|
|
728
|
+
root.borrow()
|
|
551
729
|
.as_ref()
|
|
552
730
|
.ok_or("Already deleted")?
|
|
553
|
-
.
|
|
731
|
+
.send_message(PerspectiveViewerMsg::Reset(all, Some(sender)));
|
|
554
732
|
|
|
555
|
-
Ok(
|
|
733
|
+
Ok(receiver.await?)
|
|
556
734
|
})
|
|
557
735
|
}
|
|
558
736
|
|
|
@@ -584,8 +762,18 @@ impl PerspectiveViewerElement {
|
|
|
584
762
|
*self.resize_handle.borrow_mut() = None;
|
|
585
763
|
}
|
|
586
764
|
|
|
587
|
-
let
|
|
588
|
-
ApiFuture::new(async move {
|
|
765
|
+
let state = self.clone_state();
|
|
766
|
+
ApiFuture::new(async move {
|
|
767
|
+
if !state.renderer().is_plugin_activated()? {
|
|
768
|
+
state
|
|
769
|
+
.update_and_render(ViewConfigUpdate::default())?
|
|
770
|
+
.await?;
|
|
771
|
+
} else {
|
|
772
|
+
state.renderer().resize().await?;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
Ok(())
|
|
776
|
+
})
|
|
589
777
|
}
|
|
590
778
|
|
|
591
779
|
/// Sets the auto-size behavior of this component.
|
|
@@ -614,7 +802,8 @@ impl PerspectiveViewerElement {
|
|
|
614
802
|
let handle = Some(ResizeObserverHandle::new(
|
|
615
803
|
&self.elem,
|
|
616
804
|
&self.renderer,
|
|
617
|
-
self.
|
|
805
|
+
&self.session,
|
|
806
|
+
&self.root,
|
|
618
807
|
));
|
|
619
808
|
*self.resize_handle.borrow_mut() = handle;
|
|
620
809
|
} else {
|
|
@@ -645,9 +834,11 @@ impl PerspectiveViewerElement {
|
|
|
645
834
|
if autopause {
|
|
646
835
|
let handle = Some(IntersectionObserverHandle::new(
|
|
647
836
|
&self.elem,
|
|
837
|
+
&self.presentation,
|
|
648
838
|
&self.session,
|
|
649
839
|
&self.renderer,
|
|
650
840
|
));
|
|
841
|
+
|
|
651
842
|
*self.intersection_handle.borrow_mut() = handle;
|
|
652
843
|
} else {
|
|
653
844
|
*self.intersection_handle.borrow_mut() = None;
|
|
@@ -666,7 +857,7 @@ impl PerspectiveViewerElement {
|
|
|
666
857
|
pub fn setSelection(&self, window: Option<JsViewWindow>) -> ApiResult<()> {
|
|
667
858
|
let window = window.map(|x| x.into_serde_ext()).transpose()?;
|
|
668
859
|
if self.renderer.get_selection() != window {
|
|
669
|
-
self.
|
|
860
|
+
self.custom_events.dispatch_select(window.as_ref())?;
|
|
670
861
|
}
|
|
671
862
|
|
|
672
863
|
self.renderer.set_selection(window);
|
|
@@ -742,8 +933,10 @@ impl PerspectiveViewerElement {
|
|
|
742
933
|
.cloned();
|
|
743
934
|
|
|
744
935
|
changed = presentation.set_theme_name(reset_theme.as_deref()).await? || changed;
|
|
745
|
-
if changed
|
|
746
|
-
|
|
936
|
+
if changed {
|
|
937
|
+
if let Some(view) = session.get_view() {
|
|
938
|
+
return renderer.restyle_all(&view).await;
|
|
939
|
+
}
|
|
747
940
|
}
|
|
748
941
|
|
|
749
942
|
Ok(JsValue::UNDEFINED)
|
|
@@ -789,13 +982,12 @@ impl PerspectiveViewerElement {
|
|
|
789
982
|
let root = self.root.clone();
|
|
790
983
|
ApiFuture::new(async move {
|
|
791
984
|
let force = force.map(SettingsUpdate::Update);
|
|
792
|
-
let
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
.send_message_async(|x| PerspectiveViewerMsg::ToggleSettingsInit(force, Some(x)));
|
|
985
|
+
let (sender, receiver) = channel::<ApiResult<wasm_bindgen::JsValue>>();
|
|
986
|
+
root.borrow().as_ref().into_apierror()?.send_message(
|
|
987
|
+
PerspectiveViewerMsg::ToggleSettingsInit(force, Some(sender)),
|
|
988
|
+
);
|
|
797
989
|
|
|
798
|
-
|
|
990
|
+
receiver.await.map_err(|_| JsValue::from("Cancelled"))?
|
|
799
991
|
})
|
|
800
992
|
}
|
|
801
993
|
|
|
@@ -826,7 +1018,7 @@ impl PerspectiveViewerElement {
|
|
|
826
1018
|
#[doc(hidden)]
|
|
827
1019
|
#[allow(clippy::use_self)]
|
|
828
1020
|
#[wasm_bindgen]
|
|
829
|
-
pub fn
|
|
1021
|
+
pub fn __get_model(&self) -> PerspectiveViewerElement {
|
|
830
1022
|
self.clone()
|
|
831
1023
|
}
|
|
832
1024
|
|
|
@@ -840,17 +1032,17 @@ impl PerspectiveViewerElement {
|
|
|
840
1032
|
pub fn toggleColumnSettings(&self, column_name: String) -> ApiFuture<()> {
|
|
841
1033
|
clone!(self.session, self.root);
|
|
842
1034
|
ApiFuture::new(async move {
|
|
843
|
-
let locator = session.
|
|
844
|
-
let
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
.into_apierror()?
|
|
848
|
-
.send_message_async(|sender| PerspectiveViewerMsg::OpenColumnSettings {
|
|
1035
|
+
let locator = session.get_column_locator(Some(column_name));
|
|
1036
|
+
let (sender, receiver) = channel::<()>();
|
|
1037
|
+
root.borrow().as_ref().into_apierror()?.send_message(
|
|
1038
|
+
PerspectiveViewerMsg::OpenColumnSettings {
|
|
849
1039
|
locator,
|
|
850
1040
|
sender: Some(sender),
|
|
851
1041
|
toggle: true,
|
|
852
|
-
}
|
|
853
|
-
|
|
1042
|
+
},
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
receiver.await.map_err(|_| ApiError::from("Cancelled"))
|
|
854
1046
|
})
|
|
855
1047
|
}
|
|
856
1048
|
|
|
@@ -862,19 +1054,19 @@ impl PerspectiveViewerElement {
|
|
|
862
1054
|
column_name: Option<String>,
|
|
863
1055
|
toggle: Option<bool>,
|
|
864
1056
|
) -> ApiFuture<()> {
|
|
865
|
-
|
|
1057
|
+
let locator = self.get_column_locator(column_name);
|
|
1058
|
+
clone!(self.root);
|
|
866
1059
|
ApiFuture::new(async move {
|
|
867
|
-
let
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
.as_ref()
|
|
871
|
-
.into_apierror()?
|
|
872
|
-
.send_message_async(|sender| PerspectiveViewerMsg::OpenColumnSettings {
|
|
1060
|
+
let (sender, receiver) = channel::<()>();
|
|
1061
|
+
root.borrow().as_ref().into_apierror()?.send_message(
|
|
1062
|
+
PerspectiveViewerMsg::OpenColumnSettings {
|
|
873
1063
|
locator,
|
|
874
1064
|
sender: Some(sender),
|
|
875
1065
|
toggle: toggle.unwrap_or_default(),
|
|
876
|
-
}
|
|
877
|
-
|
|
1066
|
+
},
|
|
1067
|
+
);
|
|
1068
|
+
|
|
1069
|
+
receiver.await.map_err(|_| ApiError::from("Cancelled"))
|
|
878
1070
|
})
|
|
879
1071
|
}
|
|
880
1072
|
}
|