@perspective-dev/viewer 5.5.0 → 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/ExportMethod.d.ts +1 -1
- 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 +126 -35
- package/dist/wasm/perspective-viewer.js +320 -185
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
- package/package.json +33 -20
- 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/export_dropdown.rs +6 -0
- 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 +80 -21
- package/src/rust/components/status_indicator.rs +8 -1
- package/src/rust/components/viewer/filters.rs +15 -3
- package/src/rust/components/viewer/msg.rs +8 -0
- package/src/rust/components/viewer/panels.rs +44 -4
- 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 +18 -1
- package/src/rust/components/viewer.rs +47 -27
- package/src/rust/components/window_editor.rs +5 -4
- package/src/rust/config/column_config_schema.rs +38 -377
- package/src/rust/config/export_method.rs +24 -0
- 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 +189 -129
- 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 +35 -19
- 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 +931 -419
- 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 +72 -0
- package/src/rust/tasks/create_panel.rs +46 -21
- package/src/rust/tasks/derive.rs +312 -0
- package/src/rust/tasks/mod.rs +6 -2
- package/src/rust/tasks/pipeline.rs +152 -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 +40 -13
- 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/{tests/mod.rs → in_flight.rs} +62 -2
- package/src/rust/utils/local_fonts.rs +0 -36
- package/src/rust/utils/mod.rs +4 -3
- package/src/rust/utils/modal_position.rs +15 -21
- package/src/rust/utils/pending_effects.rs +6 -30
- 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/themes/monokai.css +1 -1
- package/src/themes/vaporwave.css +1 -1
- 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/ExportMethod.ts +1 -1
- 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/pubsub.rs +0 -95
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use
|
|
14
|
-
use std::rc::Rc;
|
|
15
|
-
|
|
16
|
-
use super::pubsub::PubSub;
|
|
13
|
+
use super::in_flight::{InFlight, InFlightGuard};
|
|
17
14
|
|
|
18
15
|
/// The element-scoped ledger of in-flight EFFECTS: every effectful public
|
|
19
16
|
/// method (and the internal flows they schedule, e.g. the `before-resize`
|
|
@@ -22,41 +19,20 @@ use super::pubsub::PubSub;
|
|
|
22
19
|
/// after the ledger drains, so "called before `flush`" implies "applied
|
|
23
20
|
/// before `flush` resolves".
|
|
24
21
|
#[derive(Clone, Default)]
|
|
25
|
-
pub struct EffectLedger(
|
|
26
|
-
|
|
27
|
-
#[derive(Default)]
|
|
28
|
-
struct EffectLedgerData {
|
|
29
|
-
count: Cell<u32>,
|
|
30
|
-
on_drain: PubSub<()>,
|
|
31
|
-
}
|
|
22
|
+
pub struct EffectLedger(InFlight);
|
|
32
23
|
|
|
33
|
-
pub
|
|
24
|
+
pub type EffectGuard = InFlightGuard;
|
|
34
25
|
|
|
35
26
|
impl EffectLedger {
|
|
36
27
|
pub fn guard(&self) -> EffectGuard {
|
|
37
|
-
self.0.
|
|
38
|
-
EffectGuard(self.clone())
|
|
28
|
+
self.0.guard()
|
|
39
29
|
}
|
|
40
30
|
|
|
41
31
|
pub fn is_empty(&self) -> bool {
|
|
42
|
-
self.0.
|
|
32
|
+
self.0.is_empty()
|
|
43
33
|
}
|
|
44
34
|
|
|
45
35
|
pub async fn settle(&self) {
|
|
46
|
-
|
|
47
|
-
if self.0.on_drain.read_next().await.is_err() {
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
impl Drop for EffectGuard {
|
|
55
|
-
fn drop(&mut self) {
|
|
56
|
-
let data = &self.0.0;
|
|
57
|
-
data.count.set(data.count.get() - 1);
|
|
58
|
-
if data.count.get() == 0 {
|
|
59
|
-
data.on_drain.emit(());
|
|
60
|
-
}
|
|
36
|
+
self.0.settle().await
|
|
61
37
|
}
|
|
62
38
|
}
|
|
@@ -10,4 +10,34 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
use web_sys::Element;
|
|
14
|
+
|
|
15
|
+
/// Mirror the host `<perspective-viewer>`'s `settings` attribute and
|
|
16
|
+
/// `shift-active` class onto one slotted plugin element so plugin stylesheets
|
|
17
|
+
/// can match them with `:host(...)`.
|
|
18
|
+
pub fn stamp_plugin_host_state(viewer: &Element, plugin: &Element) {
|
|
19
|
+
let _ = plugin.toggle_attribute_with_force("settings", viewer.has_attribute("settings"));
|
|
20
|
+
let _ = plugin
|
|
21
|
+
.class_list()
|
|
22
|
+
.toggle_with_force("shift-active", viewer.class_list().contains("shift-active"));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Apply [`stamp_plugin_host_state`] to every plugin element slotted into
|
|
26
|
+
/// `viewer`.
|
|
27
|
+
pub fn stamp_plugin_host_state_all(viewer: &Element) {
|
|
28
|
+
let children = viewer.children();
|
|
29
|
+
for i in 0..children.length() {
|
|
30
|
+
let Some(el) = children.item(i) else {
|
|
31
|
+
continue;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let is_plugin = el.tag_name().starts_with("PERSPECTIVE-VIEWER-")
|
|
35
|
+
&& el
|
|
36
|
+
.get_attribute("slot")
|
|
37
|
+
.is_some_and(|slot| !slot.starts_with("tab-"));
|
|
38
|
+
|
|
39
|
+
if is_plugin {
|
|
40
|
+
stamp_plugin_host_state(viewer, &el);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/rust/utils/pubsub.rs
CHANGED
|
@@ -127,7 +127,6 @@ impl<T: Clone + 'static> PubSub<T> {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
/// Convert [`PubSub::emit`] to a `Box<dyn Fn(T)>`.
|
|
130
|
-
#[must_use]
|
|
131
130
|
pub fn as_boxfn(&self) -> Box<dyn Fn(T) + 'static> {
|
|
132
131
|
let internal = PubSub(self.0.clone());
|
|
133
132
|
Box::new(move |val: T| internal.emit(val))
|
package/src/rust/workspace.rs
CHANGED
|
@@ -13,13 +13,16 @@
|
|
|
13
13
|
//! The multi-panel model backing a single `<perspective-viewer>`.
|
|
14
14
|
|
|
15
15
|
use std::cell::{Cell, RefCell};
|
|
16
|
+
use std::collections::BTreeMap;
|
|
16
17
|
use std::rc::Rc;
|
|
17
18
|
|
|
18
19
|
use perspective_client::Client;
|
|
19
20
|
use perspective_client::config::Filter;
|
|
20
21
|
|
|
22
|
+
use crate::config::{DerivedTableConfig, TableRef};
|
|
23
|
+
use crate::derived::{DerivedRegistry, DerivedTable};
|
|
21
24
|
use crate::renderer::Renderer;
|
|
22
|
-
use crate::session::Session;
|
|
25
|
+
use crate::session::{OverlayClause, Session};
|
|
23
26
|
use crate::utils::{EffectLedger, PubSub, Subscription, spawn_owned};
|
|
24
27
|
|
|
25
28
|
/// A unique identifier for a [`Panel`] within a [`Workspace`].
|
|
@@ -54,22 +57,37 @@ impl From<&str> for PanelId {
|
|
|
54
57
|
#[derive(Default)]
|
|
55
58
|
struct GlobalFilterSet {
|
|
56
59
|
restored: Vec<Filter>,
|
|
57
|
-
|
|
60
|
+
|
|
61
|
+
/// Each master's selection, every clause typed as the MASTER types its
|
|
62
|
+
/// column — what decides which listeners it applies to.
|
|
63
|
+
contributions: Vec<(PanelId, Vec<OverlayClause>)>,
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
impl GlobalFilterSet {
|
|
61
67
|
/// The effective set: `restored`, then each contribution in order,
|
|
62
68
|
/// deduped by clause equality.
|
|
63
69
|
fn flatten(&self) -> Vec<Filter> {
|
|
64
|
-
|
|
70
|
+
self.overlay().into_iter().map(|x| x.filter).collect()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// [`Self::flatten`] with each clause's broadcaster type — untyped for the
|
|
74
|
+
/// restored bucket, which has no broadcaster.
|
|
75
|
+
fn overlay(&self) -> Vec<OverlayClause> {
|
|
76
|
+
let mut flat: Vec<OverlayClause> = Vec::new();
|
|
65
77
|
let all = self
|
|
66
78
|
.restored
|
|
67
79
|
.iter()
|
|
68
|
-
.
|
|
80
|
+
.cloned()
|
|
81
|
+
.map(OverlayClause::from)
|
|
82
|
+
.chain(
|
|
83
|
+
self.contributions
|
|
84
|
+
.iter()
|
|
85
|
+
.flat_map(|(_, fs)| fs.iter().cloned()),
|
|
86
|
+
);
|
|
69
87
|
|
|
70
|
-
for
|
|
71
|
-
if !flat.
|
|
72
|
-
flat.push(
|
|
88
|
+
for clause in all {
|
|
89
|
+
if !flat.iter().any(|x| x.filter == clause.filter) {
|
|
90
|
+
flat.push(clause);
|
|
73
91
|
}
|
|
74
92
|
}
|
|
75
93
|
|
|
@@ -86,7 +104,8 @@ impl GlobalFilterSet {
|
|
|
86
104
|
}
|
|
87
105
|
|
|
88
106
|
/// Replace `id`'s contribution.
|
|
89
|
-
fn set_contribution(&mut self, id: &PanelId, filters: Vec<
|
|
107
|
+
fn set_contribution(&mut self, id: &PanelId, filters: Vec<impl Into<OverlayClause>>) -> bool {
|
|
108
|
+
let filters = filters.into_iter().map(Into::into).collect::<Vec<_>>();
|
|
90
109
|
self.with_change(|s| {
|
|
91
110
|
if filters.is_empty() {
|
|
92
111
|
s.contributions.retain(|(pid, _)| pid != id);
|
|
@@ -109,14 +128,14 @@ impl GlobalFilterSet {
|
|
|
109
128
|
let owners = self
|
|
110
129
|
.contributions
|
|
111
130
|
.iter()
|
|
112
|
-
.filter(|(_, fs)| fs.
|
|
131
|
+
.filter(|(_, fs)| fs.iter().any(|f| f.filter == clause))
|
|
113
132
|
.map(|(pid, _)| pid.clone())
|
|
114
133
|
.collect();
|
|
115
134
|
|
|
116
135
|
let changed = self.with_change(|s| {
|
|
117
136
|
s.restored.retain(|f| f != &clause);
|
|
118
137
|
for (_, fs) in s.contributions.iter_mut() {
|
|
119
|
-
fs.retain(|f| f !=
|
|
138
|
+
fs.retain(|f| f.filter != clause);
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
s.contributions.retain(|(_, fs)| !fs.is_empty());
|
|
@@ -308,9 +327,22 @@ struct WorkspaceData {
|
|
|
308
327
|
filters: GlobalFilterSet,
|
|
309
328
|
filters_changed: Rc<PubSub<()>>,
|
|
310
329
|
|
|
330
|
+
/// The set of hosted `Table`s changed: a client pushed a hosted-tables
|
|
331
|
+
/// update (observed post-sweep), or a derived `Table` was registered or
|
|
332
|
+
/// removed.
|
|
333
|
+
hosted_tables_changed: Rc<PubSub<()>>,
|
|
334
|
+
|
|
335
|
+
/// A panel's table binding changed (a rebind/pend committed, or a panel
|
|
336
|
+
/// entered or left the placed set), which is the input to per-table
|
|
337
|
+
/// consumer counts ([`Workspace::derived_consumers`]).
|
|
338
|
+
bindings_changed: Rc<PubSub<()>>,
|
|
339
|
+
|
|
311
340
|
/// The loaded-clients registry + default designation.
|
|
312
341
|
clients: ClientRegistry,
|
|
313
342
|
|
|
343
|
+
/// The live derived `Table`s this workspace owns.
|
|
344
|
+
derived: DerivedRegistry,
|
|
345
|
+
|
|
314
346
|
/// Monotonic counter backing [`Workspace::generate_id`].
|
|
315
347
|
next_id: usize,
|
|
316
348
|
|
|
@@ -356,7 +388,10 @@ impl Workspace {
|
|
|
356
388
|
active: None,
|
|
357
389
|
filters: GlobalFilterSet::default(),
|
|
358
390
|
filters_changed: Rc::new(PubSub::default()),
|
|
391
|
+
hosted_tables_changed: Rc::new(PubSub::default()),
|
|
392
|
+
bindings_changed: Rc::new(PubSub::default()),
|
|
359
393
|
clients: ClientRegistry::default(),
|
|
394
|
+
derived: DerivedRegistry::default(),
|
|
360
395
|
next_id: 0,
|
|
361
396
|
reservation: Reservation::Idle,
|
|
362
397
|
pending_layout: None,
|
|
@@ -477,12 +512,26 @@ impl Workspace {
|
|
|
477
512
|
self.0.borrow().filters.flatten()
|
|
478
513
|
}
|
|
479
514
|
|
|
515
|
+
/// [`Self::global_filters`] as broadcast to the panels: each clause with
|
|
516
|
+
/// the type its column has in the master that selected it.
|
|
517
|
+
pub fn overlay(&self) -> Vec<OverlayClause> {
|
|
518
|
+
self.0.borrow().filters.overlay()
|
|
519
|
+
}
|
|
520
|
+
|
|
480
521
|
/// A handle to the `filters_changed` PubSub (fires after any change to the
|
|
481
522
|
/// global filter set).
|
|
482
523
|
pub fn filters_changed(&self) -> Rc<PubSub<()>> {
|
|
483
524
|
self.0.borrow().filters_changed.clone()
|
|
484
525
|
}
|
|
485
526
|
|
|
527
|
+
pub fn hosted_tables_changed(&self) -> Rc<PubSub<()>> {
|
|
528
|
+
self.0.borrow().hosted_tables_changed.clone()
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
pub fn bindings_changed(&self) -> Rc<PubSub<()>> {
|
|
532
|
+
self.0.borrow().bindings_changed.clone()
|
|
533
|
+
}
|
|
534
|
+
|
|
486
535
|
/// Run `f` against the filter set inside the borrow, then emit
|
|
487
536
|
/// `filters_changed` — outside it — iff the effective set changed.
|
|
488
537
|
/// Returns `f`'s auxiliary payload.
|
|
@@ -507,13 +556,13 @@ impl Workspace {
|
|
|
507
556
|
|
|
508
557
|
/// Replace master `id`'s contribution with a new selection (empty =
|
|
509
558
|
/// deselect). See [`GlobalFilterSet::set_contribution`].
|
|
510
|
-
pub fn set_contribution(&self, id: &PanelId, filters: Vec<
|
|
559
|
+
pub fn set_contribution(&self, id: &PanelId, filters: Vec<OverlayClause>) {
|
|
511
560
|
self.mutate_filters(|s| (s.set_contribution(id, filters), ()));
|
|
512
561
|
}
|
|
513
562
|
|
|
514
563
|
/// Drop master `id`'s contribution (deselect / demote / close).
|
|
515
564
|
pub fn clear_contribution(&self, id: &PanelId) {
|
|
516
|
-
self.mutate_filters(|s| (s.set_contribution(id, Vec
|
|
565
|
+
self.mutate_filters(|s| (s.set_contribution(id, Vec::<OverlayClause>::new()), ()));
|
|
517
566
|
}
|
|
518
567
|
|
|
519
568
|
/// Remove the effective-set clause at `index`.
|
|
@@ -574,12 +623,19 @@ impl Workspace {
|
|
|
574
623
|
}
|
|
575
624
|
}
|
|
576
625
|
|
|
577
|
-
/// Generate a fresh [`PanelId`], unique within this workspace
|
|
626
|
+
/// Generate a fresh [`PanelId`], unique within this workspace — skipping
|
|
627
|
+
/// any taken by a placed panel (a restored config may carry saved
|
|
628
|
+
/// generated ids).
|
|
578
629
|
pub fn generate_id(&self) -> PanelId {
|
|
579
630
|
let mut data = self.0.borrow_mut();
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
631
|
+
loop {
|
|
632
|
+
let n = data.next_id;
|
|
633
|
+
data.next_id += 1;
|
|
634
|
+
let id = PanelId(format!("PERSPECTIVE_GENERATED_ID_{n}"));
|
|
635
|
+
if !data.panels.iter().any(|p| p.panel.id == id) {
|
|
636
|
+
return id;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
583
639
|
}
|
|
584
640
|
|
|
585
641
|
/// The id of the active panel, or `None` with zero panels.
|
|
@@ -702,6 +758,8 @@ impl Workspace {
|
|
|
702
758
|
if let Some(pubsub) = staged_pubsub {
|
|
703
759
|
pubsub.emit(());
|
|
704
760
|
}
|
|
761
|
+
|
|
762
|
+
self.bindings_changed().emit(());
|
|
705
763
|
}
|
|
706
764
|
|
|
707
765
|
/// Hold `panel` in the reservation slot ([`Reservation::Pending`]):
|
|
@@ -805,7 +863,9 @@ impl Workspace {
|
|
|
805
863
|
pub fn remove_panel(&self, id: &PanelId) -> Option<Panel> {
|
|
806
864
|
let (removed, changed, filters_pubsub, was_staged, staged_pubsub) = {
|
|
807
865
|
let mut data = self.0.borrow_mut();
|
|
808
|
-
let changed = data
|
|
866
|
+
let changed = data
|
|
867
|
+
.filters
|
|
868
|
+
.set_contribution(id, Vec::<OverlayClause>::new());
|
|
809
869
|
let (removed, was_staged) = match data.panels.iter().position(|p| &p.panel.id == id) {
|
|
810
870
|
Some(idx) => {
|
|
811
871
|
let entry = data.panels.remove(idx);
|
|
@@ -838,6 +898,10 @@ impl Workspace {
|
|
|
838
898
|
staged_pubsub.emit(());
|
|
839
899
|
}
|
|
840
900
|
|
|
901
|
+
if removed.is_some() {
|
|
902
|
+
self.bindings_changed().emit(());
|
|
903
|
+
}
|
|
904
|
+
|
|
841
905
|
removed
|
|
842
906
|
}
|
|
843
907
|
|
|
@@ -1013,136 +1077,119 @@ impl Workspace {
|
|
|
1013
1077
|
None => current.is_none().then(|| self.default_client()).flatten(),
|
|
1014
1078
|
}
|
|
1015
1079
|
}
|
|
1016
|
-
}
|
|
1017
1080
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
use super::*;
|
|
1023
|
-
|
|
1024
|
-
fn f(col: &str, term: &str) -> Filter {
|
|
1025
|
-
Filter::new(col, "==", FilterTerm::Scalar(term.into()))
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
fn p(id: &str) -> PanelId {
|
|
1029
|
-
PanelId::from(id)
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
#[test]
|
|
1033
|
-
fn flatten_orders_and_dedups_across_buckets() {
|
|
1034
|
-
let mut s = GlobalFilterSet::default();
|
|
1035
|
-
assert!(s.set_restored(vec![f("a", "1"), f("b", "2")]));
|
|
1036
|
-
// A non-empty contribution drops the restored bucket entirely.
|
|
1037
|
-
assert!(s.set_contribution(&p("x"), vec![f("b", "2"), f("c", "3")]));
|
|
1038
|
-
assert_eq!(s.flatten(), vec![f("b", "2"), f("c", "3")]);
|
|
1039
|
-
assert!(s.set_contribution(&p("y"), vec![f("c", "3"), f("d", "4")]));
|
|
1040
|
-
assert_eq!(s.flatten(), vec![f("b", "2"), f("c", "3"), f("d", "4")]);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
#[test]
|
|
1044
|
-
fn set_contribution_replaces_not_accumulates() {
|
|
1045
|
-
let mut s = GlobalFilterSet::default();
|
|
1046
|
-
assert!(s.set_contribution(&p("x"), vec![f("a", "1")]));
|
|
1047
|
-
assert!(s.set_contribution(&p("x"), vec![f("a", "2")]));
|
|
1048
|
-
assert_eq!(s.flatten(), vec![f("a", "2")]);
|
|
1049
|
-
// Re-selecting the same value reports no visible change.
|
|
1050
|
-
assert!(!s.set_contribution(&p("x"), vec![f("a", "2")]));
|
|
1051
|
-
// Empty = deselect: the entry is removed.
|
|
1052
|
-
assert!(s.set_contribution(&p("x"), Vec::new()));
|
|
1053
|
-
assert_eq!(s.flatten(), Vec::<Filter>::new());
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
#[test]
|
|
1057
|
-
fn contributions_are_per_panel() {
|
|
1058
|
-
let mut s = GlobalFilterSet::default();
|
|
1059
|
-
s.set_contribution(&p("x"), vec![f("a", "1")]);
|
|
1060
|
-
s.set_contribution(&p("y"), vec![f("b", "2")]);
|
|
1061
|
-
// Clearing one master's contribution leaves the other's intact.
|
|
1062
|
-
assert!(s.set_contribution(&p("x"), Vec::new()));
|
|
1063
|
-
assert_eq!(s.flatten(), vec![f("b", "2")]);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
#[test]
|
|
1067
|
-
fn remove_clause_removes_from_every_bucket_and_reports_owners() {
|
|
1068
|
-
let mut s = GlobalFilterSet::default();
|
|
1069
|
-
s.set_contribution(&p("x"), vec![f("a", "1"), f("b", "2")]);
|
|
1070
|
-
s.set_contribution(&p("y"), vec![f("a", "1")]);
|
|
1071
|
-
// flatten = [a==1, b==2]; removing index 0 must drop BOTH copies of
|
|
1072
|
-
// a==1 (owners x AND y), and the now-empty "y" entry with it.
|
|
1073
|
-
let (changed, owners) = s.remove_clause(0);
|
|
1074
|
-
assert!(changed);
|
|
1075
|
-
assert_eq!(owners, vec![p("x"), p("y")]);
|
|
1076
|
-
assert_eq!(s.flatten(), vec![f("b", "2")]);
|
|
1077
|
-
// Out-of-range is a no-op.
|
|
1078
|
-
assert_eq!(s.remove_clause(5), (false, Vec::new()));
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
#[test]
|
|
1082
|
-
fn restored_bucket_survives_deselect_but_not_selection() {
|
|
1083
|
-
let mut s = GlobalFilterSet::default();
|
|
1084
|
-
s.set_restored(vec![f("a", "1")]);
|
|
1085
|
-
// A deselect (empty contribution) does NOT drop the restored bucket.
|
|
1086
|
-
assert!(!s.set_contribution(&p("x"), Vec::new()));
|
|
1087
|
-
assert_eq!(s.flatten(), vec![f("a", "1")]);
|
|
1088
|
-
// A real selection replaces it.
|
|
1089
|
-
assert!(s.set_contribution(&p("x"), vec![f("b", "2")]));
|
|
1090
|
-
assert_eq!(s.flatten(), vec![f("b", "2")]);
|
|
1091
|
-
assert!(s.restored.is_empty());
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
#[test]
|
|
1095
|
-
fn clear_drops_everything_and_reports_owners() {
|
|
1096
|
-
let mut s = GlobalFilterSet::default();
|
|
1097
|
-
s.set_restored(vec![f("a", "1")]);
|
|
1098
|
-
s.set_contribution(&p("x"), vec![f("b", "2")]);
|
|
1099
|
-
let (changed, owners) = s.clear();
|
|
1100
|
-
assert!(changed);
|
|
1101
|
-
assert_eq!(owners, vec![p("x")]);
|
|
1102
|
-
assert_eq!(s.flatten(), Vec::<Filter>::new());
|
|
1103
|
-
assert_eq!(s.clear(), (false, Vec::new()));
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
#[test]
|
|
1107
|
-
fn flush_state_spawns_exactly_once_per_cycle() {
|
|
1108
|
-
let (state, spawn) = FlushState::Idle.mark(true, false);
|
|
1109
|
-
assert!(spawn);
|
|
1110
|
-
assert_eq!(state, FlushState::Queued {
|
|
1111
|
-
layout: true,
|
|
1112
|
-
active: false
|
|
1113
|
-
});
|
|
1081
|
+
/// Every derivation's persisted definition, for `saveWorkspace`.
|
|
1082
|
+
pub fn derived_configs(&self) -> BTreeMap<String, DerivedTableConfig> {
|
|
1083
|
+
self.0.borrow().derived.configs()
|
|
1084
|
+
}
|
|
1114
1085
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
assert_eq!(state, FlushState::Queued {
|
|
1119
|
-
layout: true,
|
|
1120
|
-
active: true
|
|
1121
|
-
});
|
|
1086
|
+
pub fn is_derived(&self, name: &str) -> bool {
|
|
1087
|
+
self.0.borrow().derived.contains(name)
|
|
1088
|
+
}
|
|
1122
1089
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
assert_eq!(state, FlushState::Queued {
|
|
1126
|
-
layout: true,
|
|
1127
|
-
active: true
|
|
1128
|
-
});
|
|
1090
|
+
pub fn derived_names(&self) -> Vec<String> {
|
|
1091
|
+
self.0.borrow().derived.names()
|
|
1129
1092
|
}
|
|
1130
1093
|
|
|
1131
|
-
|
|
1132
|
-
fn
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1094
|
+
/// The recorded definition of a derived `Table`.
|
|
1095
|
+
pub fn derived_config(&self, name: &str) -> Option<DerivedTableConfig> {
|
|
1096
|
+
self.0.borrow().derived.get(name).map(|x| x.config())
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/// The name of a live derivation structurally equal to `config`.
|
|
1100
|
+
pub fn derived_find_structural(&self, config: &DerivedTableConfig) -> Option<String> {
|
|
1101
|
+
self.0.borrow().derived.find_structural(config)
|
|
1102
|
+
}
|
|
1138
1103
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1104
|
+
/// `name`'s definition as a self-contained [`TableRef`], recursively
|
|
1105
|
+
/// inlining each derived ancestor down to a concrete table name.
|
|
1106
|
+
pub fn inline_table_ref(&self, name: &str) -> TableRef {
|
|
1107
|
+
let ancestors = self.derived_ancestors(name);
|
|
1108
|
+
let Some(base) = ancestors.last() else {
|
|
1109
|
+
return TableRef::Name(name.to_owned());
|
|
1142
1110
|
};
|
|
1143
1111
|
|
|
1144
|
-
let
|
|
1145
|
-
|
|
1146
|
-
|
|
1112
|
+
let mut out = TableRef::Name(base.clone());
|
|
1113
|
+
let chain = std::iter::once(name)
|
|
1114
|
+
.chain(ancestors[..ancestors.len() - 1].iter().map(String::as_str))
|
|
1115
|
+
.collect::<Vec<_>>();
|
|
1116
|
+
|
|
1117
|
+
for level in chain.into_iter().rev() {
|
|
1118
|
+
match self.derived_config(level) {
|
|
1119
|
+
Some(config) => {
|
|
1120
|
+
out = TableRef::Derived(Box::new(DerivedTableConfig {
|
|
1121
|
+
table: out,
|
|
1122
|
+
view_config: config.view_config,
|
|
1123
|
+
}));
|
|
1124
|
+
},
|
|
1125
|
+
None => return TableRef::Name(name.to_owned()),
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
out
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/// The `Table` names `name` derives from, nearest first.
|
|
1133
|
+
pub fn derived_ancestors(&self, name: &str) -> Vec<String> {
|
|
1134
|
+
self.0.borrow().derived.ancestors(name)
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/// How many panels and derivations read `name`.
|
|
1138
|
+
pub fn derived_consumers(&self, name: &str) -> usize {
|
|
1139
|
+
let panels = self.panels();
|
|
1140
|
+
let data = self.0.borrow();
|
|
1141
|
+
let bound = panels
|
|
1142
|
+
.iter()
|
|
1143
|
+
.filter(|panel| {
|
|
1144
|
+
panel
|
|
1145
|
+
.session
|
|
1146
|
+
.get_table()
|
|
1147
|
+
.map(|table| table.get_name().to_owned())
|
|
1148
|
+
.or_else(|| panel.session.pending_table())
|
|
1149
|
+
.is_some_and(|bound| bound == name)
|
|
1150
|
+
})
|
|
1151
|
+
.count();
|
|
1152
|
+
|
|
1153
|
+
bound + data.derived.children(name).len()
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
pub fn register_derived(&self, name: String, table: DerivedTable) {
|
|
1157
|
+
self.0.borrow_mut().derived.insert(name, table);
|
|
1158
|
+
self.hosted_tables_changed().emit(());
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/// Take a derivation out of the registry so the caller can await its
|
|
1162
|
+
/// deletion outside the borrow.
|
|
1163
|
+
pub fn take_derived(&self, name: &str) -> Option<DerivedTable> {
|
|
1164
|
+
let derived = self.0.borrow_mut().derived.remove(name);
|
|
1165
|
+
if derived.is_some() {
|
|
1166
|
+
self.hosted_tables_changed().emit(());
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
derived
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/// Which of `order` may be reused rather than rebuilt.
|
|
1173
|
+
pub fn derived_reusable(
|
|
1174
|
+
&self,
|
|
1175
|
+
order: &[String],
|
|
1176
|
+
configs: &BTreeMap<String, DerivedTableConfig>,
|
|
1177
|
+
) -> std::collections::BTreeSet<String> {
|
|
1178
|
+
crate::derived::reusable(order, configs, &self.0.borrow().derived)
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/// The definitions waiting on a parent that is not yet hosted.
|
|
1182
|
+
pub fn derived_pending(&self) -> BTreeMap<String, DerivedTableConfig> {
|
|
1183
|
+
self.0.borrow().derived.pending()
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/// Whether `name` is a derived definition awaiting its parent, which a
|
|
1187
|
+
/// panel bind must PEND on rather than reject as unknown.
|
|
1188
|
+
pub fn is_derived_pending(&self, name: &str) -> bool {
|
|
1189
|
+
self.0.borrow().derived.pending().contains_key(name)
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
pub fn set_derived_pending(&self, pending: BTreeMap<String, DerivedTableConfig>) {
|
|
1193
|
+
self.0.borrow_mut().derived.set_pending(pending);
|
|
1147
1194
|
}
|
|
1148
1195
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="#042121"
|
|
2
|
+
stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
+
<circle cx="3.5" cy="3.5" r="1.75"/>
|
|
4
|
+
<circle cx="9.5" cy="9.5" r="1.75"/>
|
|
5
|
+
<path d="M3.5 5.25v2.25a2 2 0 0 0 2 2h2.25"/>
|
|
6
|
+
</svg>
|
package/src/themes/icons.css
CHANGED
|
@@ -63,6 +63,7 @@ perspective-viewer-tab {
|
|
|
63
63
|
--psp-icon--reset--mask-image: url("../svg/revert-icon.svg");
|
|
64
64
|
--psp-icon--copy--mask-image: url("../svg/duplicate-icon.svg");
|
|
65
65
|
--psp-icon--new-panel--mask-image: url("../svg/new-panel-icon.svg");
|
|
66
|
+
--psp-derived--mask-image: url("../svg/derived.svg");
|
|
66
67
|
--psp-icon--theme--mask-image: url("../svg/theme-icon.svg");
|
|
67
68
|
|
|
68
69
|
--psp-icon--drawer-tab--mask-image: url(../svg/drawer-tab.svg);
|
package/src/themes/intl/ar.css
CHANGED
|
@@ -69,6 +69,8 @@ perspective-viewer {
|
|
|
69
69
|
--psp-label--scroll-lock-alt-toggle--content: "تمرير محاذى";
|
|
70
70
|
|
|
71
71
|
--psp-label--color--content: "اللون";
|
|
72
|
+
--psp-label--palette--content: "لوحة الألوان";
|
|
73
|
+
--psp-label--gradient--content: "تدرج";
|
|
72
74
|
--psp-label--timezone--content: "المنطقة الزمنية";
|
|
73
75
|
--psp-label--date-style--content: "نمط التاريخ";
|
|
74
76
|
--psp-label--time-style--content: "نمط الوقت";
|
|
@@ -195,4 +197,12 @@ perspective-viewer {
|
|
|
195
197
|
--psp-label--legend-opacity--content: "العتامة";
|
|
196
198
|
--psp-label--tooltip-max-column-px--content: "أقصى عرض للعمود";
|
|
197
199
|
--psp-label--tooltip-opacity--content: "العتامة";
|
|
200
|
+
|
|
201
|
+
/* Derived tables */
|
|
202
|
+
--psp-label--derive-table--content: "اشتقاق جدول";
|
|
203
|
+
--psp-label--derive-table-title--content: "اشتقاق جدول جديد من هذا العرض";
|
|
204
|
+
--psp-label--derived-snapshot--content: "لقطة";
|
|
205
|
+
--psp-label--derived-snapshot-title--content: "مصدر البيانات هذا لا يبث التحديثات - بيانات هذا الجدول ثابتة عند لحظة اشتقاقه.";
|
|
206
|
+
--psp-label--derived-release-title--content: "العودة إلى الجدول الأصل";
|
|
207
|
+
--psp-label--derived-release-delete-title--content: "العودة إلى الجدول الأصل - انقر مع الضغط على Shift لحذفه";
|
|
198
208
|
}
|
package/src/themes/intl/de.css
CHANGED
|
@@ -74,6 +74,8 @@ perspective-viewer {
|
|
|
74
74
|
|
|
75
75
|
/* Column Settings */
|
|
76
76
|
--psp-label--color--content: "Farbe";
|
|
77
|
+
--psp-label--palette--content: "Palette";
|
|
78
|
+
--psp-label--gradient--content: "Gradient";
|
|
77
79
|
--psp-label--timezone--content: "Zeitzone";
|
|
78
80
|
--psp-label--date-style--content: "Datumsstil";
|
|
79
81
|
--psp-label--time-style--content: "Zeitstil";
|
|
@@ -204,4 +206,12 @@ perspective-viewer {
|
|
|
204
206
|
--psp-label--group-column--content: "Spalte";
|
|
205
207
|
--psp-label--group-format--content: "Format";
|
|
206
208
|
--psp-label--column-size-override--content: "Spaltenbreite";
|
|
209
|
+
|
|
210
|
+
/* Derived tables */
|
|
211
|
+
--psp-label--derive-table--content: "Tabelle ableiten";
|
|
212
|
+
--psp-label--derive-table-title--content: "Eine neue Tabelle aus dieser Ansicht ableiten";
|
|
213
|
+
--psp-label--derived-snapshot--content: "Snapshot";
|
|
214
|
+
--psp-label--derived-snapshot-title--content: "Diese Datenquelle streamt nicht - die Daten dieser Tabelle sind auf den Stand ihrer Ableitung eingefroren.";
|
|
215
|
+
--psp-label--derived-release-title--content: "Zurück zur übergeordneten Tabelle";
|
|
216
|
+
--psp-label--derived-release-delete-title--content: "Zurück zur übergeordneten Tabelle - Umschalt-Klick löscht sie";
|
|
207
217
|
}
|
package/src/themes/intl/es.css
CHANGED
|
@@ -74,6 +74,8 @@ perspective-viewer {
|
|
|
74
74
|
|
|
75
75
|
/* Column Settings */
|
|
76
76
|
--psp-label--color--content: "Color";
|
|
77
|
+
--psp-label--palette--content: "Paleta";
|
|
78
|
+
--psp-label--gradient--content: "Gradiente";
|
|
77
79
|
--psp-label--timezone--content: "Zona horaria";
|
|
78
80
|
--psp-label--date-style--content: "Estilo de fecha";
|
|
79
81
|
--psp-label--time-style--content: "Estilo de hora";
|
|
@@ -204,4 +206,12 @@ perspective-viewer {
|
|
|
204
206
|
--psp-label--group-column--content: "Columna";
|
|
205
207
|
--psp-label--group-format--content: "Formato";
|
|
206
208
|
--psp-label--column-size-override--content: "Ancho de columna";
|
|
209
|
+
|
|
210
|
+
/* Derived tables */
|
|
211
|
+
--psp-label--derive-table--content: "Derivar tabla";
|
|
212
|
+
--psp-label--derive-table-title--content: "Derivar una nueva tabla a partir de esta vista";
|
|
213
|
+
--psp-label--derived-snapshot--content: "instantánea";
|
|
214
|
+
--psp-label--derived-snapshot-title--content: "Esta fuente de datos no transmite en continuo - los datos de esta tabla quedan fijados en el momento de su derivación.";
|
|
215
|
+
--psp-label--derived-release-title--content: "Volver a la tabla padre";
|
|
216
|
+
--psp-label--derived-release-delete-title--content: "Volver a la tabla padre - mayús+clic para eliminarla";
|
|
207
217
|
}
|
package/src/themes/intl/fr.css
CHANGED
|
@@ -74,6 +74,8 @@ perspective-viewer {
|
|
|
74
74
|
|
|
75
75
|
/* Column Settings */
|
|
76
76
|
--psp-label--color--content: "Couleur";
|
|
77
|
+
--psp-label--palette--content: "Palette";
|
|
78
|
+
--psp-label--gradient--content: "Dégradé";
|
|
77
79
|
--psp-label--timezone--content: "Fuseau horaire";
|
|
78
80
|
--psp-label--date-style--content: "Style de date";
|
|
79
81
|
--psp-label--time-style--content: "Style d'heure";
|
|
@@ -204,4 +206,12 @@ perspective-viewer {
|
|
|
204
206
|
--psp-label--group-column--content: "Colonne";
|
|
205
207
|
--psp-label--group-format--content: "Format";
|
|
206
208
|
--psp-label--column-size-override--content: "Largeur de colonne";
|
|
209
|
+
|
|
210
|
+
/* Derived tables */
|
|
211
|
+
--psp-label--derive-table--content: "Dériver une table";
|
|
212
|
+
--psp-label--derive-table-title--content: "Dériver une nouvelle table à partir de cette vue";
|
|
213
|
+
--psp-label--derived-snapshot--content: "instantané";
|
|
214
|
+
--psp-label--derived-snapshot-title--content: "Cette source de données ne diffuse pas en continu - les données de cette table sont figées au moment de sa dérivation.";
|
|
215
|
+
--psp-label--derived-release-title--content: "Revenir à la table parente";
|
|
216
|
+
--psp-label--derived-release-delete-title--content: "Revenir à la table parente - maj-clic pour la supprimer";
|
|
207
217
|
}
|