@perspective-dev/viewer 5.5.1 → 5.6.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/README.md +82 -3
- package/dist/cdn/perspective-viewer.js +9 -9
- package/dist/cdn/perspective-viewer.js.map +3 -3
- package/dist/css/blueprint.css +1 -1
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.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/ar.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/ledger.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -1
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/css/velvet.css +1 -1
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/column-format.js +2 -0
- package/dist/esm/column-format.js.map +7 -0
- package/dist/esm/extensions.d.ts +3 -13
- package/dist/esm/perspective-viewer.d.ts +1 -1
- package/dist/esm/perspective-viewer.js +9 -9
- package/dist/esm/perspective-viewer.js.map +3 -3
- package/dist/esm/plugin.d.ts +4 -1
- package/dist/esm/select-detail.d.ts +15 -0
- package/dist/esm/select-detail.js +2 -0
- package/dist/esm/select-detail.js.map +7 -0
- package/dist/esm/ts-rs/DerivedTableConfig.d.ts +35 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/RestoreOptions.d.ts +3 -7
- package/dist/esm/ts-rs/TableRef.d.ts +9 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -5
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +9 -0
- package/dist/wasm/perspective-viewer.d.ts +115 -35
- package/dist/wasm/perspective-viewer.js +314 -190
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
- package/package.json +31 -18
- package/src/css/column-selector.css +205 -50
- package/src/css/ui/containers/context-menu.css +12 -1
- package/src/css/ui/containers/dropdown-menu-items.css +29 -0
- package/src/css/ui/containers/scroll-panel.css +4 -1
- package/src/rust/agent/client/protocol.rs +0 -89
- package/src/rust/agent/markdown/mod.rs +0 -238
- package/src/rust/agent/preamble.md +1 -0
- package/src/rust/agent/tools.rs +2 -16
- package/src/rust/components/column_selector/active_column.rs +15 -5
- package/src/rust/components/column_selector/add_expression_button.rs +9 -26
- package/src/rust/components/column_selector/config_selector.rs +34 -6
- package/src/rust/components/column_selector/derived_control.rs +229 -0
- package/src/rust/components/column_selector/expr_edit_button.rs +3 -2
- package/src/rust/components/column_selector/filter_column.rs +0 -2
- package/src/rust/components/column_selector/inactive_column.rs +4 -5
- package/src/rust/components/column_selector/pivot_column.rs +21 -1
- package/src/rust/components/column_selector/sort_column.rs +4 -3
- package/src/rust/components/column_selector.rs +21 -1
- package/src/rust/components/column_settings_sidebar/save_settings.rs +5 -3
- package/src/rust/components/column_settings_sidebar/style_tab.rs +1 -0
- package/src/rust/components/column_settings_sidebar.rs +25 -19
- package/src/rust/components/debug_panel.rs +38 -29
- package/src/rust/components/dragdrop_list.rs +8 -9
- package/src/rust/components/main_panel/msg.rs +6 -4
- package/src/rust/components/main_panel/reconcile.rs +6 -1
- package/src/rust/components/main_panel/render.rs +70 -51
- package/src/rust/components/main_panel/update.rs +39 -1
- package/src/rust/components/main_panel.rs +97 -59
- package/src/rust/components/new_panel_menu.rs +69 -1
- package/src/rust/components/panel_menu.rs +63 -12
- package/src/rust/components/plugin_tab.rs +3 -2
- package/src/rust/components/render_warning.rs +2 -1
- package/src/rust/components/settings_panel.rs +4 -4
- package/src/rust/components/status_bar.rs +74 -24
- package/src/rust/components/status_indicator.rs +8 -1
- package/src/rust/components/viewer/filters.rs +13 -1
- package/src/rust/components/viewer/msg.rs +4 -0
- package/src/rust/components/viewer/panels.rs +41 -1
- package/src/rust/components/viewer/render.rs +1 -0
- package/src/rust/components/viewer/settings.rs +0 -22
- package/src/rust/components/viewer/snapshots.rs +2 -2
- package/src/rust/components/viewer/wiring.rs +14 -0
- package/src/rust/components/viewer.rs +45 -25
- package/src/rust/components/window_editor.rs +5 -4
- package/src/rust/config/column_config_schema.rs +38 -377
- package/src/rust/config/number_string_format.rs +0 -94
- package/src/rust/config/options.rs +3 -7
- package/src/rust/config/plugin_static_config.rs +0 -42
- package/src/rust/config/viewer_config.rs +47 -17
- package/src/rust/config/workspace_config.rs +158 -128
- package/src/rust/custom_elements/viewer.rs +187 -131
- package/src/rust/custom_events.rs +33 -18
- package/src/rust/derived.rs +266 -0
- package/src/rust/exprtk/tokenize.rs +0 -54
- package/src/rust/js/plugin.rs +1 -1
- package/src/rust/lib.rs +3 -0
- package/src/rust/presentation.rs +23 -4
- package/src/rust/queries/column_locator.rs +55 -48
- package/src/rust/queries/get_viewer_config.rs +1 -1
- package/src/rust/queries/plugin_column_styles.rs +22 -23
- package/src/rust/renderer/dispatch.rs +2 -0
- package/src/rust/renderer/plugin_config.rs +652 -487
- package/src/rust/renderer/props.rs +4 -0
- package/src/rust/renderer/registry.rs +2 -6
- package/src/rust/renderer/state.rs +205 -0
- package/src/rust/renderer.rs +201 -59
- package/src/rust/session/metadata.rs +30 -9
- package/src/rust/session/op_queue.rs +430 -0
- package/src/rust/session/panel_state.rs +327 -0
- package/src/rust/session/view_subscription.rs +42 -3
- package/src/rust/session.rs +900 -395
- package/src/rust/tasks/apply_global_filters.rs +49 -35
- package/src/rust/tasks/auto_pause.rs +22 -12
- package/src/rust/tasks/copy_export.rs +5 -0
- package/src/rust/tasks/create_panel.rs +34 -9
- package/src/rust/tasks/derive.rs +312 -0
- package/src/rust/tasks/mod.rs +6 -2
- package/src/rust/tasks/pipeline.rs +145 -89
- package/src/rust/tasks/reset_all.rs +31 -20
- package/src/rust/tasks/restore_derived.rs +200 -0
- package/src/rust/tasks/restore_panel.rs +153 -141
- package/src/rust/tasks/send_column_config.rs +56 -29
- package/src/rust/tasks/send_plugin_config.rs +52 -23
- package/src/rust/tasks/set_edit_mode.rs +33 -25
- package/src/rust/tasks/sync_update_panels.rs +39 -12
- package/src/rust/tasks/table_lifecycle.rs +70 -34
- package/src/rust/tasks/transactional_restore.rs +387 -0
- package/src/rust/tasks/update_theme.rs +45 -6
- package/src/rust/ui/containers/scroll_panel.rs +17 -10
- package/src/rust/ui/containers/split_panel.rs +0 -41
- package/src/rust/ui/context_menu.rs +157 -18
- package/src/rust/ui/font_loader.rs +23 -35
- package/src/rust/ui/form/mirrored_textarea.rs +0 -12
- package/src/rust/ui/form/multi_stop_gradient_selector.rs +0 -89
- package/src/rust/ui/form/palette_selector.rs +0 -51
- package/src/rust/ui/portal.rs +4 -10
- package/src/rust/utils/browser/mod.rs +2 -3
- package/src/{ts/perspective-viewer.inline.ts → rust/utils/browser/mouse.rs} +11 -10
- package/src/rust/utils/browser/request_animation_frame.rs +27 -8
- package/src/rust/utils/css.rs +0 -360
- package/src/rust/utils/local_fonts.rs +0 -36
- package/src/rust/utils/mod.rs +2 -3
- package/src/rust/utils/modal_position.rs +15 -21
- package/src/rust/utils/{browser/tests/mod.rs → plugin_host_state.rs} +31 -1
- package/src/rust/utils/pubsub.rs +0 -1
- package/src/rust/workspace.rs +187 -140
- package/src/svg/derived.svg +6 -0
- package/src/themes/icons.css +1 -0
- package/src/themes/intl/ar.css +10 -0
- package/src/themes/intl/de.css +10 -0
- package/src/themes/intl/es.css +10 -0
- package/src/themes/intl/fr.css +10 -0
- package/src/themes/intl/ja.css +10 -0
- package/src/themes/intl/pt.css +10 -0
- package/src/themes/intl/zh.css +10 -0
- package/src/themes/intl.css +10 -0
- package/src/ts/extensions.ts +3 -36
- package/src/ts/perspective-viewer.ts +1 -1
- package/src/ts/plugin.ts +4 -1
- package/src/{rust/utils/tests/request_animation_frame.rs → ts/select-detail.ts} +32 -26
- package/src/ts/ts-rs/DerivedTableConfig.ts +22 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +7 -6
- package/src/ts/ts-rs/RestoreOptions.ts +3 -7
- package/src/ts/ts-rs/TableRef.ts +9 -0
- package/src/ts/ts-rs/ViewerConfig.ts +7 -6
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +7 -5
- package/src/ts/ts-rs/WorkspaceConfig.ts +7 -1
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +7 -0
- package/dist/esm/perspective-viewer.inline.d.ts +0 -3
- package/dist/esm/perspective-viewer.inline.js +0 -21
- package/dist/esm/perspective-viewer.inline.js.map +0 -7
- package/src/rust/tasks/restore_and_render.rs +0 -107
- package/src/rust/utils/browser/tests/debounce.rs +0 -163
- package/src/rust/utils/tests/mod.rs +0 -14
- package/src/rust/utils/tests/pubsub.rs +0 -95
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
|
@@ -19,7 +19,6 @@ use futures::channel::oneshot::channel;
|
|
|
19
19
|
use futures::future::join_all;
|
|
20
20
|
use js_sys::{Array, JsString};
|
|
21
21
|
use perspective_client::config::ViewConfigUpdate;
|
|
22
|
-
use perspective_client::utils::PerspectiveResultExt;
|
|
23
22
|
use perspective_js::utils::global;
|
|
24
23
|
use perspective_js::{JsViewConfig, JsViewWindow, Table, View, apierror};
|
|
25
24
|
use wasm_bindgen::JsCast;
|
|
@@ -38,7 +37,7 @@ use crate::js::*;
|
|
|
38
37
|
use crate::presentation::*;
|
|
39
38
|
use crate::queries::*;
|
|
40
39
|
use crate::root::Root;
|
|
41
|
-
use crate::session::{Disposal, MissingTable, ResetOptions, TableLoadState};
|
|
40
|
+
use crate::session::{BindPlan, Disposal, MissingTable, OpKind, ResetOptions, TableLoadState};
|
|
42
41
|
use crate::tasks::*;
|
|
43
42
|
use crate::utils::*;
|
|
44
43
|
use crate::workspace::{Panel, PanelId, Workspace};
|
|
@@ -212,13 +211,13 @@ impl PerspectiveViewerElement {
|
|
|
212
211
|
.panels()
|
|
213
212
|
.into_iter()
|
|
214
213
|
.filter(|panel| !fresh.contains(&panel.id))
|
|
215
|
-
.filter(|panel| stamp_global_overlay(&self.workspace, &panel.id, &panel.session))
|
|
216
214
|
.collect::<Vec<_>>();
|
|
217
215
|
|
|
218
|
-
join_all(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
join_all(
|
|
217
|
+
retained
|
|
218
|
+
.iter()
|
|
219
|
+
.map(|panel| broadcast_overlay(&self.workspace, panel)),
|
|
220
|
+
)
|
|
222
221
|
.await
|
|
223
222
|
.into_iter()
|
|
224
223
|
.collect::<ApiResult<Vec<_>>>()?;
|
|
@@ -237,7 +236,8 @@ impl PerspectiveViewerElement {
|
|
|
237
236
|
|
|
238
237
|
async fn workspace_config(this: Self, full_palette: bool) -> ApiResult<JsValue> {
|
|
239
238
|
this.workspace.effects().settle().await;
|
|
240
|
-
let mut panels: std::collections::BTreeMap<String, PanelViewerConfig
|
|
239
|
+
let mut panels: std::collections::BTreeMap<String, PanelViewerConfig<String, String>> =
|
|
240
|
+
Default::default();
|
|
241
241
|
for id in &this.workspace.panel_ids() {
|
|
242
242
|
let panel = this.workspace.panel(id).into_apierror()?;
|
|
243
243
|
let config = panel
|
|
@@ -288,6 +288,7 @@ impl PerspectiveViewerElement {
|
|
|
288
288
|
active,
|
|
289
289
|
layout,
|
|
290
290
|
panels,
|
|
291
|
+
derived_tables: this.workspace.derived_configs(),
|
|
291
292
|
global_filters: this.workspace.global_filters(),
|
|
292
293
|
masters: this
|
|
293
294
|
.workspace
|
|
@@ -492,41 +493,17 @@ impl PerspectiveViewerElement {
|
|
|
492
493
|
.dyn_into::<js_sys::Promise>()
|
|
493
494
|
.unwrap_or_else(|_| js_sys::Promise::resolve(&table));
|
|
494
495
|
|
|
495
|
-
// Resolve the target panel. On an EMPTY element (zero panels):
|
|
496
|
-
// - a synchronously-detectable `Client` registers inertly with NO panel (the
|
|
497
|
-
// common `load(client)` — no phantom panel is left behind);
|
|
498
|
-
// - otherwise (a resolved `Table`, or a `Promise` whose type isn't yet known)
|
|
499
|
-
// the first panel is RESERVED synchronously here — a full panel model held
|
|
500
|
-
// in the workspace's reservation slot, NOT placed — so its ordering position
|
|
501
|
-
// is fixed at the call site: a `restore()` fired right after an unawaited
|
|
502
|
-
// `load()` CLAIMS the reservation (placing it) and targets THIS panel, not a
|
|
503
|
-
// second one. The reservation is likewise placed when the payload proves to
|
|
504
|
-
// be a `Table` (or the load fails, surfacing its error), and discarded —
|
|
505
|
-
// only while still unclaimed — for an inert `Client` payload. Placement and
|
|
506
|
-
// discard are both atomic slot transfers (`Workspace::claim_reserved` /
|
|
507
|
-
// `Workspace::take_reserved`), so an inert payload disposing a panel a
|
|
508
|
-
// racing `restore` claimed is unrepresentable.
|
|
509
|
-
// A pre-existing active panel is used as-is (a `Client` registers
|
|
510
|
-
// inertly against it, never clearing its table).
|
|
511
496
|
let (panel, notify) = match self.workspace.active_panel() {
|
|
512
497
|
Some(panel) => (panel, None),
|
|
513
498
|
None => {
|
|
514
|
-
// Empty element — classify the payload synchronously where possible.
|
|
515
499
|
if let Ok(Some(client)) =
|
|
516
500
|
try_from_js_option::<perspective_js::Client>(table.clone())
|
|
517
501
|
{
|
|
518
|
-
// A resolved `Client` registers SYNCHRONOUSLY (so an unawaited
|
|
519
|
-
// `load(client)` is visible to a `restore()` fired right after,
|
|
520
|
-
// which creates the first panel and federates against loaded
|
|
521
|
-
// clients) and creates NO panel — inert.
|
|
522
502
|
self.workspace
|
|
523
503
|
.set_default_client(client.get_client().clone());
|
|
524
504
|
return Ok(ApiFuture::new(async { Ok(()) }));
|
|
525
505
|
}
|
|
526
506
|
|
|
527
|
-
// A resolved `Table` (or a `Promise` whose type isn't yet known)
|
|
528
|
-
// adopts the pending reservation (a second `load()` on a
|
|
529
|
-
// still-empty element), else reserves a fresh panel model.
|
|
530
507
|
let panel = self.workspace.reserved_panel().unwrap_or_else(|| {
|
|
531
508
|
create_panel_model(
|
|
532
509
|
&self.elem,
|
|
@@ -548,23 +525,28 @@ impl PerspectiveViewerElement {
|
|
|
548
525
|
|
|
549
526
|
let session = panel.session;
|
|
550
527
|
let renderer = panel.renderer;
|
|
551
|
-
let load = session.begin_pending_load();
|
|
552
528
|
clone!(self.workspace, self.presentation);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
Evicted(Panel),
|
|
558
|
-
}
|
|
529
|
+
enum Discard {
|
|
530
|
+
Unclaimed(Panel),
|
|
531
|
+
Evicted(Panel),
|
|
532
|
+
}
|
|
559
533
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
534
|
+
let table_known = matches!(
|
|
535
|
+
try_from_js_option::<perspective_js::Table>(table.clone()),
|
|
536
|
+
Ok(Some(_))
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
let ticket = session.submit(OpKind::Load { table_known }, {
|
|
540
|
+
clone!(session, renderer);
|
|
541
|
+
move |load| {
|
|
542
|
+
Box::pin(async move {
|
|
543
|
+
let _effect = effect;
|
|
544
|
+
renderer.set_throttle(None);
|
|
545
|
+
let result = {
|
|
546
|
+
clone!(session, renderer, workspace, notify, load);
|
|
547
|
+
renderer
|
|
565
548
|
.clone()
|
|
566
549
|
.render_task(|guard| async move {
|
|
567
|
-
seed_panel_theme(&presentation, &renderer).await;
|
|
568
550
|
renderer.stamp_theme(None);
|
|
569
551
|
let jstable = JsFuture::from(promise)
|
|
570
552
|
.await
|
|
@@ -574,30 +556,17 @@ impl PerspectiveViewerElement {
|
|
|
574
556
|
try_from_js_option::<perspective_js::Table>(jstable.clone())
|
|
575
557
|
{
|
|
576
558
|
tracing::warn!("{}", DEPRECATED_TABLE_MESSAGE);
|
|
577
|
-
|
|
559
|
+
if load.is_superseded() {
|
|
578
560
|
return Ok(None);
|
|
579
|
-
}
|
|
561
|
+
}
|
|
580
562
|
|
|
581
563
|
if let Some(notify) = ¬ify {
|
|
582
564
|
place_reserved(&workspace, notify, true);
|
|
583
565
|
}
|
|
584
566
|
|
|
585
|
-
let _plugin = renderer.ensure_plugin_selected()?;
|
|
586
|
-
let _ = renderer.mount_active_plugin();
|
|
587
|
-
session
|
|
588
|
-
.reset(ResetOptions {
|
|
589
|
-
config: true,
|
|
590
|
-
expressions: true,
|
|
591
|
-
stats: true,
|
|
592
|
-
table: Some(session::TableIntermediateState::Reloaded),
|
|
593
|
-
})
|
|
594
|
-
.await
|
|
595
|
-
.unwrap_or_log();
|
|
596
|
-
|
|
597
567
|
let client = table.get_client().await;
|
|
598
568
|
let inner_client = client.get_client().clone();
|
|
599
|
-
|
|
600
|
-
workspace.set_default_client(inner_client);
|
|
569
|
+
workspace.set_default_client(inner_client.clone());
|
|
601
570
|
let name = table.get_name().await;
|
|
602
571
|
tracing::debug!(
|
|
603
572
|
"Loading {:.0} rows from `Table` {}",
|
|
@@ -605,21 +574,40 @@ impl PerspectiveViewerElement {
|
|
|
605
574
|
name
|
|
606
575
|
);
|
|
607
576
|
|
|
608
|
-
session
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
577
|
+
let plan = match session::probe_table(
|
|
578
|
+
&inner_client,
|
|
579
|
+
&name,
|
|
580
|
+
MissingTable::Pend,
|
|
581
|
+
)
|
|
582
|
+
.await?
|
|
583
|
+
{
|
|
584
|
+
Some(table) => BindPlan::Bind {
|
|
585
|
+
client: inner_client,
|
|
586
|
+
table: Box::new(table),
|
|
587
|
+
reset: true,
|
|
588
|
+
},
|
|
589
|
+
None => BindPlan::Pend {
|
|
590
|
+
client: inner_client,
|
|
591
|
+
name,
|
|
592
|
+
reset: true,
|
|
593
|
+
},
|
|
594
|
+
};
|
|
616
595
|
|
|
617
|
-
crate::tasks::
|
|
618
|
-
&
|
|
596
|
+
let prepared = crate::tasks::prepare(
|
|
597
|
+
&session,
|
|
619
598
|
&renderer,
|
|
620
|
-
|
|
599
|
+
&presentation,
|
|
621
600
|
false,
|
|
622
|
-
|
|
601
|
+
plan,
|
|
602
|
+
renderer
|
|
603
|
+
.slot_name()
|
|
604
|
+
.map(|id| overlay_for(&workspace, &PanelId::from(id))),
|
|
605
|
+
ViewerConfigUpdate::default(),
|
|
606
|
+
)
|
|
607
|
+
.await?;
|
|
608
|
+
|
|
609
|
+
crate::tasks::commit_and_render_locked(
|
|
610
|
+
guard, &session, &renderer, prepared,
|
|
623
611
|
)
|
|
624
612
|
.await?;
|
|
625
613
|
|
|
@@ -631,11 +619,8 @@ impl PerspectiveViewerElement {
|
|
|
631
619
|
// INERT: register the client only — never rebind or
|
|
632
620
|
// reset the active panel (its table is preserved).
|
|
633
621
|
// Panels bind their client lazily at table-resolution
|
|
634
|
-
// time (`Workspace::resolve_client_for_table`).
|
|
635
|
-
|
|
636
|
-
// performs no reset, and any racing `restore`'s
|
|
637
|
-
// commits already applied live (`commit_view_config`).
|
|
638
|
-
let owned_window = load.claim().is_some();
|
|
622
|
+
// time (`Workspace::resolve_client_for_table`).
|
|
623
|
+
let owned_window = !load.is_superseded();
|
|
639
624
|
let discard = if owned_window && notify.is_some() {
|
|
640
625
|
match workspace.take_reserved() {
|
|
641
626
|
Some(panel) => Some(Discard::Unclaimed(panel)),
|
|
@@ -671,34 +656,36 @@ impl PerspectiveViewerElement {
|
|
|
671
656
|
workspace.set_default_client(client.get_client().clone());
|
|
672
657
|
Ok(discard)
|
|
673
658
|
} else {
|
|
674
|
-
load.close();
|
|
675
659
|
Err(ApiError::new("Invalid argument"))
|
|
676
660
|
}
|
|
677
661
|
})
|
|
678
662
|
.await
|
|
679
|
-
|
|
663
|
+
};
|
|
680
664
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
665
|
+
match result {
|
|
666
|
+
Err(e) => {
|
|
667
|
+
if let Some(notify) = ¬ify {
|
|
668
|
+
place_reserved(&workspace, notify, true);
|
|
669
|
+
}
|
|
687
670
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
671
|
+
session.set_error(false, e.clone()).await?;
|
|
672
|
+
Err(e)
|
|
673
|
+
},
|
|
674
|
+
Ok(Some(Discard::Unclaimed(panel))) => {
|
|
675
|
+
eject_panel(panel, Disposal::Reject).await?;
|
|
676
|
+
Ok(None)
|
|
677
|
+
},
|
|
678
|
+
Ok(Some(Discard::Evicted(panel))) => {
|
|
679
|
+
eject_panel(panel, Disposal::Resolve).await?;
|
|
680
|
+
Err(ApiError::new(CREATE_REQUIRES_TABLE))
|
|
681
|
+
},
|
|
682
|
+
Ok(None) => Ok(None),
|
|
683
|
+
}
|
|
684
|
+
})
|
|
700
685
|
}
|
|
701
|
-
})
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
Ok(ApiFuture::new_throttled(ticket.settle()))
|
|
702
689
|
}
|
|
703
690
|
|
|
704
691
|
/// Delete all internal [`View`]s and all associated state, rendering this
|
|
@@ -1006,6 +993,7 @@ impl PerspectiveViewerElement {
|
|
|
1006
993
|
|
|
1007
994
|
let mut fulfilled = false;
|
|
1008
995
|
for panel in &panels {
|
|
996
|
+
panel.session.settle_ops().await;
|
|
1009
997
|
panel.renderer.clone().with_lock(async { Ok(()) }).await?;
|
|
1010
998
|
panel.renderer.clone().with_lock(async { Ok(()) }).await?;
|
|
1011
999
|
panel.session.settle_dispatches().await?;
|
|
@@ -1013,6 +1001,7 @@ impl PerspectiveViewerElement {
|
|
|
1013
1001
|
&& presentation.is_visible()
|
|
1014
1002
|
&& !panel.renderer.is_plugin_activated()?
|
|
1015
1003
|
&& panel.session.get_error().is_none()
|
|
1004
|
+
&& panel.renderer.failure().is_none()
|
|
1016
1005
|
&& matches!(panel.session.has_table(), Some(TableLoadState::Loaded))
|
|
1017
1006
|
{
|
|
1018
1007
|
set_panel_paused(&panel.session, &panel.renderer, &presentation, true)
|
|
@@ -1064,11 +1053,9 @@ impl PerspectiveViewerElement {
|
|
|
1064
1053
|
/// either "json", "string" or "arraybuffer" format.
|
|
1065
1054
|
/// - `options.panel` - The panel to target, or the active panel when
|
|
1066
1055
|
/// omitted.
|
|
1067
|
-
/// - `options.suppress_errors` - when `true`, a
|
|
1068
|
-
///
|
|
1069
|
-
///
|
|
1070
|
-
/// already applied (theme, title, plugin, a successful `table` change)
|
|
1071
|
-
/// untouched.
|
|
1056
|
+
/// - `options.suppress_errors` - when `true`, a config that was applied and
|
|
1057
|
+
/// then failed to render only rejects the returned `Promise`, without
|
|
1058
|
+
/// raising the panel's visible error state.
|
|
1072
1059
|
/// - `options.wait_for_table` - when `true`, a `table` no loaded client
|
|
1073
1060
|
/// hosts yet leaves the panel empty and pending until the table is
|
|
1074
1061
|
/// created, instead of the default error.
|
|
@@ -1120,6 +1107,7 @@ impl PerspectiveViewerElement {
|
|
|
1120
1107
|
Create(Box<ViewerConfigInitial>),
|
|
1121
1108
|
}
|
|
1122
1109
|
|
|
1110
|
+
crate::tasks::resolve_table_ref(&this.workspace, &mut update, missing).await?;
|
|
1123
1111
|
let notify = this.layout_changed_notify();
|
|
1124
1112
|
let target = match this.workspace.panel_or_active(id.as_ref()) {
|
|
1125
1113
|
// An existing (or the active) panel — update it in place.
|
|
@@ -1142,14 +1130,24 @@ impl PerspectiveViewerElement {
|
|
|
1142
1130
|
},
|
|
1143
1131
|
};
|
|
1144
1132
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1133
|
+
let settings_toggle: Option<futures::future::LocalBoxFuture<'static, ApiResult<()>>> =
|
|
1134
|
+
(!matches!(settings, OptionalUpdate::Missing)).then(|| {
|
|
1135
|
+
let this = this.clone();
|
|
1136
|
+
Box::pin(async move {
|
|
1137
|
+
let (sender, receiver) = channel::<ApiResult<JsValue>>();
|
|
1138
|
+
this.root.borrow().as_ref().into_apierror()?.send_message(
|
|
1139
|
+
PerspectiveViewerMsg::ToggleSettingsInit(
|
|
1140
|
+
Some(settings),
|
|
1141
|
+
false,
|
|
1142
|
+
Some(sender),
|
|
1143
|
+
),
|
|
1144
|
+
);
|
|
1150
1145
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1146
|
+
receiver.await.map_err(|_| ApiError::new("Cancelled"))??;
|
|
1147
|
+
Ok(())
|
|
1148
|
+
})
|
|
1149
|
+
as futures::future::LocalBoxFuture<'static, ApiResult<()>>
|
|
1150
|
+
});
|
|
1153
1151
|
|
|
1154
1152
|
match target {
|
|
1155
1153
|
Target::Existing { panel, active } => {
|
|
@@ -1162,6 +1160,7 @@ impl PerspectiveViewerElement {
|
|
|
1162
1160
|
update,
|
|
1163
1161
|
errors,
|
|
1164
1162
|
missing,
|
|
1163
|
+
settings_toggle,
|
|
1165
1164
|
)
|
|
1166
1165
|
.await
|
|
1167
1166
|
},
|
|
@@ -1175,10 +1174,15 @@ impl PerspectiveViewerElement {
|
|
|
1175
1174
|
update,
|
|
1176
1175
|
errors,
|
|
1177
1176
|
missing,
|
|
1177
|
+
settings_toggle,
|
|
1178
1178
|
)
|
|
1179
1179
|
.await
|
|
1180
1180
|
},
|
|
1181
1181
|
Target::Create(config) => {
|
|
1182
|
+
if let Some(settings_toggle) = settings_toggle {
|
|
1183
|
+
settings_toggle.await?;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1182
1186
|
create_panel(
|
|
1183
1187
|
&this.elem,
|
|
1184
1188
|
&this.presentation,
|
|
@@ -1234,7 +1238,22 @@ impl PerspectiveViewerElement {
|
|
|
1234
1238
|
let _effect = effect;
|
|
1235
1239
|
let RestoreWorkspaceOptions { wait_for_table } = parse_options(options)?;
|
|
1236
1240
|
let missing = MissingTable::from_wait(wait_for_table);
|
|
1237
|
-
let (contents, eject_tasks) = sync_update_panels(&this, update)?;
|
|
1241
|
+
let (contents, eject_tasks, derived_tables) = sync_update_panels(&this, update)?;
|
|
1242
|
+
|
|
1243
|
+
// The outgoing panels' ejects must settle BEFORE the derived
|
|
1244
|
+
// pass: a stale derivation is undeletable (and so cannot be
|
|
1245
|
+
// redefined or pruned) while an ejecting panel still holds a
|
|
1246
|
+
// `View` on it. Their errors are surfaced LAST, after the
|
|
1247
|
+
// replacement set is in place.
|
|
1248
|
+
let ejected = join_all(eject_tasks)
|
|
1249
|
+
.await
|
|
1250
|
+
.into_iter()
|
|
1251
|
+
.collect::<ApiResult<Vec<_>>>();
|
|
1252
|
+
|
|
1253
|
+
if let Some(derived_tables) = derived_tables.clone() {
|
|
1254
|
+
crate::tasks::apply_derived_tables(&this.workspace, derived_tables).await?;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1238
1257
|
let fresh = contents
|
|
1239
1258
|
.iter()
|
|
1240
1259
|
.map(|(id, ..)| id.clone())
|
|
@@ -1242,9 +1261,9 @@ impl PerspectiveViewerElement {
|
|
|
1242
1261
|
let results = join_all(contents.into_iter().map(|(id, session, renderer, config)| {
|
|
1243
1262
|
let presentation = this.presentation.clone();
|
|
1244
1263
|
let workspace = this.workspace.clone();
|
|
1264
|
+
let notify = this.layout_changed_notify();
|
|
1245
1265
|
async move {
|
|
1246
|
-
|
|
1247
|
-
restore_panel(
|
|
1266
|
+
let result = restore_panel(
|
|
1248
1267
|
&session,
|
|
1249
1268
|
&renderer,
|
|
1250
1269
|
&presentation,
|
|
@@ -1253,8 +1272,15 @@ impl PerspectiveViewerElement {
|
|
|
1253
1272
|
config,
|
|
1254
1273
|
crate::tasks::RestoreErrors::Publish,
|
|
1255
1274
|
missing,
|
|
1275
|
+
None,
|
|
1256
1276
|
)
|
|
1257
|
-
.await
|
|
1277
|
+
.await;
|
|
1278
|
+
|
|
1279
|
+
if discard_rejected(&workspace, &id, &session, &result).await? {
|
|
1280
|
+
notify.emit(());
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
result?;
|
|
1258
1284
|
if workspace.is_master(&id) {
|
|
1259
1285
|
set_edit_mode(&session, &renderer, "SELECT_ROW_TREE");
|
|
1260
1286
|
}
|
|
@@ -1266,11 +1292,12 @@ impl PerspectiveViewerElement {
|
|
|
1266
1292
|
|
|
1267
1293
|
results.into_iter().collect::<ApiResult<Vec<_>>>()?;
|
|
1268
1294
|
this.rerender_retained_overlays(&fresh).await?;
|
|
1269
|
-
|
|
1270
|
-
.
|
|
1271
|
-
.
|
|
1272
|
-
|
|
1295
|
+
if let Some(derived_tables) = derived_tables {
|
|
1296
|
+
let keep = derived_tables.keys().cloned().collect();
|
|
1297
|
+
crate::tasks::prune_derived(&this.workspace, &keep).await?;
|
|
1298
|
+
}
|
|
1273
1299
|
|
|
1300
|
+
ejected?;
|
|
1274
1301
|
Ok(())
|
|
1275
1302
|
});
|
|
1276
1303
|
|
|
@@ -1286,10 +1313,19 @@ impl PerspectiveViewerElement {
|
|
|
1286
1313
|
};
|
|
1287
1314
|
|
|
1288
1315
|
let reset_effect = self.workspace.effects().guard();
|
|
1289
|
-
let
|
|
1316
|
+
let reset = panel.session.submit(OpKind::Restore { update: None }, {
|
|
1317
|
+
let session = panel.session.clone();
|
|
1318
|
+
move |_ctx| {
|
|
1319
|
+
Box::pin(async move {
|
|
1320
|
+
session.reset(ResetOptions::default()).await?;
|
|
1321
|
+
Ok(None)
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1290
1326
|
ApiFuture::spawn(async move {
|
|
1291
1327
|
let _effect = reset_effect;
|
|
1292
|
-
|
|
1328
|
+
reset.settle().await
|
|
1293
1329
|
});
|
|
1294
1330
|
|
|
1295
1331
|
let effect = self.workspace.effects().guard();
|
|
@@ -1342,7 +1378,9 @@ impl PerspectiveViewerElement {
|
|
|
1342
1378
|
})
|
|
1343
1379
|
.await?;
|
|
1344
1380
|
|
|
1345
|
-
viewer_config
|
|
1381
|
+
viewer_config
|
|
1382
|
+
.map_table(|name| this.workspace.inline_table_ref(&name))
|
|
1383
|
+
.encode()
|
|
1346
1384
|
});
|
|
1347
1385
|
|
|
1348
1386
|
js_sys::Promise::from(fut).unchecked_into()
|
|
@@ -1406,9 +1444,14 @@ impl PerspectiveViewerElement {
|
|
|
1406
1444
|
};
|
|
1407
1445
|
|
|
1408
1446
|
let panel = this.resolve_panel(name)?;
|
|
1409
|
-
let blob =
|
|
1410
|
-
|
|
1411
|
-
|
|
1447
|
+
let blob = export_method_to_blob(
|
|
1448
|
+
&panel.session,
|
|
1449
|
+
&panel.renderer,
|
|
1450
|
+
&this.presentation,
|
|
1451
|
+
&this.workspace,
|
|
1452
|
+
method,
|
|
1453
|
+
)
|
|
1454
|
+
.await?;
|
|
1412
1455
|
let is_chart = panel.renderer.is_chart();
|
|
1413
1456
|
download(
|
|
1414
1457
|
format!("untitled{}", method.as_filename(is_chart)).as_ref(),
|
|
@@ -1454,8 +1497,14 @@ impl PerspectiveViewerElement {
|
|
|
1454
1497
|
};
|
|
1455
1498
|
|
|
1456
1499
|
let panel = this.resolve_panel(name)?;
|
|
1457
|
-
export_method_to_jsvalue(
|
|
1458
|
-
.
|
|
1500
|
+
export_method_to_jsvalue(
|
|
1501
|
+
&panel.session,
|
|
1502
|
+
&panel.renderer,
|
|
1503
|
+
&this.presentation,
|
|
1504
|
+
&this.workspace,
|
|
1505
|
+
method,
|
|
1506
|
+
)
|
|
1507
|
+
.await
|
|
1459
1508
|
})
|
|
1460
1509
|
}
|
|
1461
1510
|
|
|
@@ -1493,8 +1542,13 @@ impl PerspectiveViewerElement {
|
|
|
1493
1542
|
};
|
|
1494
1543
|
|
|
1495
1544
|
let panel = this.resolve_panel(name)?;
|
|
1496
|
-
let js_task =
|
|
1497
|
-
|
|
1545
|
+
let js_task = export_method_to_blob(
|
|
1546
|
+
&panel.session,
|
|
1547
|
+
&panel.renderer,
|
|
1548
|
+
&this.presentation,
|
|
1549
|
+
&this.workspace,
|
|
1550
|
+
method,
|
|
1551
|
+
);
|
|
1498
1552
|
copy_to_clipboard(js_task, MimeType::TextPlain).await
|
|
1499
1553
|
})
|
|
1500
1554
|
}
|
|
@@ -1510,7 +1564,9 @@ impl PerspectiveViewerElement {
|
|
|
1510
1564
|
/// # Arguments
|
|
1511
1565
|
///
|
|
1512
1566
|
/// - `reset_all` - If set, will clear expressions and column settings as
|
|
1513
|
-
/// well
|
|
1567
|
+
/// well, and tear down the element's ENTIRE derived-table state: every
|
|
1568
|
+
/// panel bound to a derived `Table` is released to its root table, and
|
|
1569
|
+
/// every derivation (pending ones included) is deleted.
|
|
1514
1570
|
/// - `options` - An optional `{panel?: string}`; the panel to reset, or
|
|
1515
1571
|
/// every panel when omitted.
|
|
1516
1572
|
///
|
|
@@ -1834,7 +1890,7 @@ impl PerspectiveViewerElement {
|
|
|
1834
1890
|
// alone must repaint nothing.
|
|
1835
1891
|
let stale = theme.as_ref().is_none_or(|x| !available.contains(x));
|
|
1836
1892
|
if (stale || theme == previous) && theme != active {
|
|
1837
|
-
panel.renderer
|
|
1893
|
+
submit_theme(&panel.session, &panel.renderer, active.clone());
|
|
1838
1894
|
if panel.renderer.needs_restyle() {
|
|
1839
1895
|
panel.renderer.restyle_all().await?;
|
|
1840
1896
|
}
|