@perspective-dev/viewer 5.1.0 → 5.3.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 +9 -9
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/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/docs/perspective-docs.json +1 -0
- package/dist/esm/column-format.d.ts +19 -28
- package/dist/esm/perspective-viewer.d.ts +3 -0
- package/dist/esm/perspective-viewer.inline.js +9 -9
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +9 -9
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/plugin.d.ts +10 -0
- package/dist/esm/providers.d.ts +79 -0
- package/dist/esm/ts-rs/CompactDisplay.d.ts +5 -0
- package/dist/esm/ts-rs/CurrencyCode.d.ts +1 -0
- package/dist/esm/ts-rs/CurrencyDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/CurrencyNumberFormatStyle.d.ts +8 -0
- package/dist/esm/ts-rs/CurrencySign.d.ts +1 -0
- package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +54 -0
- package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +34 -0
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
- package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
- package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
- package/dist/esm/ts-rs/Notation.d.ts +17 -0
- package/dist/esm/ts-rs/NumberFormatStyle.d.ts +18 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +42 -2
- package/dist/esm/ts-rs/PluginStaticConfig.d.ts +35 -0
- package/dist/esm/ts-rs/RestoreOptions.d.ts +19 -0
- package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
- package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
- package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
- package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +24 -0
- package/dist/esm/ts-rs/SplitRollupMode.d.ts +9 -0
- package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/Unit.d.ts +1 -0
- package/dist/esm/ts-rs/UnitDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/UnitNumberFormatStyle.d.ts +6 -0
- package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +42 -2
- package/dist/esm/ts-rs/ViewerConfigInitial.d.ts +175 -0
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +63 -0
- package/dist/esm/ts-rs/WindowSpec.d.ts +3 -2
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +20 -9
- package/dist/wasm/perspective-viewer.d.ts +157 -41
- package/dist/wasm/perspective-viewer.js +452 -151
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +22 -17
- package/package.json +2 -1
- package/src/css/_viewer_bundle.css +1 -0
- package/src/css/column-selector.css +8 -0
- package/src/css/column-settings-panel.css +9 -5
- package/src/css/column-style.css +209 -31
- package/src/css/config-selector.css +38 -3
- package/src/css/dom/select.css +2 -1
- package/src/css/form/code-editor.css +6 -7
- package/src/css/form/mirrored-textarea.css +81 -0
- package/src/css/viewer.css +328 -0
- package/src/rust/agent/client/mod.rs +19 -0
- package/src/rust/agent/client/protocol.rs +320 -0
- package/src/rust/agent/client/transport.rs +287 -0
- package/src/rust/agent/client/turn.rs +174 -0
- package/src/rust/agent/config.rs +159 -0
- package/src/rust/agent/docs.rs +293 -0
- package/src/rust/agent/markdown/block.rs +509 -0
- package/src/rust/agent/markdown/entity.rs +52 -0
- package/src/rust/agent/markdown/events.rs +71 -0
- package/src/rust/agent/markdown/inline.rs +497 -0
- package/src/rust/agent/markdown/mod.rs +466 -0
- package/src/rust/agent/mod.rs +23 -0
- package/src/rust/agent/preamble.md +16 -0
- package/src/rust/agent/runtime.rs +201 -0
- package/src/rust/agent/slot.rs +236 -0
- package/src/rust/agent/tools.rs +1141 -0
- package/src/rust/components/chat_panel.rs +289 -0
- package/src/rust/components/column_selector/active_column.rs +2 -2
- package/src/rust/components/column_selector/add_expression_button.rs +3 -3
- package/src/rust/components/column_selector/aggregate_selector.rs +2 -3
- package/src/rust/components/column_selector/config_selector.rs +84 -7
- package/src/rust/components/column_selector/expr_edit_button.rs +4 -10
- package/src/rust/components/column_selector/inactive_column.rs +2 -2
- package/src/rust/components/column_selector.rs +55 -4
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +293 -126
- package/src/rust/components/column_settings_sidebar/style_tab.rs +262 -225
- package/src/rust/components/column_settings_sidebar.rs +88 -69
- package/src/rust/components/containers/dragdrop_list.rs +37 -0
- package/src/rust/components/editable_header.rs +85 -144
- package/src/rust/components/expression_editor.rs +9 -16
- package/src/rust/components/form/code_editor.rs +29 -28
- package/src/rust/components/form/debug.rs +9 -5
- package/src/rust/components/form/mirrored_textarea.rs +165 -0
- package/src/rust/components/form/mod.rs +4 -1
- package/src/rust/components/form/multi_stop_gradient_selector.rs +476 -0
- package/src/rust/components/form/named_value_picker.rs +86 -0
- package/src/rust/components/form/palette_selector.rs +440 -0
- package/src/rust/components/main_panel/msg.rs +2 -2
- package/src/rust/components/main_panel/reconcile.rs +4 -4
- package/src/rust/components/mod.rs +2 -0
- package/src/rust/components/plugin_tab.rs +4 -28
- package/src/rust/components/settings_panel.rs +105 -22
- package/src/rust/components/viewer/msg.rs +46 -7
- package/src/rust/components/viewer/panels.rs +17 -20
- package/src/rust/components/viewer/render.rs +10 -3
- package/src/rust/components/viewer/settings.rs +163 -23
- package/src/rust/components/viewer/snapshots.rs +46 -21
- package/src/rust/components/viewer/wiring.rs +14 -25
- package/src/rust/components/viewer.rs +12 -8
- package/src/rust/components/window_editor.rs +75 -103
- package/src/rust/config/column_config_schema.rs +100 -41
- package/src/rust/config/css_scale.rs +882 -0
- package/src/rust/config/datetime_column_style/custom.rs +17 -0
- package/src/rust/config/datetime_column_style/simple.rs +10 -0
- package/src/rust/config/mod.rs +2 -0
- package/src/rust/config/number_string_format.rs +33 -0
- package/src/rust/config/options.rs +28 -0
- package/src/rust/config/plugin_static_config.rs +120 -5
- package/src/rust/config/viewer_config.rs +216 -3
- package/src/rust/config/workspace_config.rs +138 -10
- package/src/rust/custom_elements/viewer.rs +369 -119
- package/src/rust/custom_events.rs +43 -1
- package/src/rust/js/regular_layout.rs +3 -3
- package/src/rust/lib.rs +15 -0
- package/src/rust/presentation/column_locator.rs +19 -5
- package/src/rust/presentation.rs +200 -36
- package/src/rust/queries/column_locator.rs +74 -44
- package/src/rust/queries/get_viewer_config.rs +2 -1
- package/src/rust/queries/mod.rs +2 -0
- package/src/rust/queries/palette_set.rs +82 -0
- package/src/rust/queries/plugin_column_styles.rs +6 -2
- package/src/rust/renderer/dispatch.rs +16 -4
- package/src/rust/renderer/plugin_config.rs +309 -47
- package/src/rust/renderer.rs +15 -42
- package/src/rust/session/column_defaults_update.rs +32 -7
- package/src/rust/session/metadata.rs +23 -21
- package/src/rust/session/props.rs +1 -0
- package/src/rust/session/replace_expression_update.rs +1 -0
- package/src/rust/session.rs +9 -6
- package/src/rust/tasks/create_panel.rs +46 -49
- package/src/rust/tasks/edit_expression.rs +19 -23
- package/src/rust/tasks/edit_window.rs +9 -11
- package/src/rust/tasks/eject.rs +4 -1
- package/src/rust/tasks/pipeline.rs +2 -2
- package/src/rust/tasks/presize_panels.rs +44 -5
- package/src/rust/tasks/reset_all.rs +4 -4
- package/src/rust/tasks/restore_and_render.rs +17 -44
- package/src/rust/tasks/restore_panel.rs +37 -22
- package/src/rust/tasks/set_edit_mode.rs +3 -1
- package/src/rust/tasks/sync_update_panels.rs +18 -24
- package/src/rust/tasks/update_theme.rs +23 -18
- package/src/rust/utils/css_vars.rs +26 -0
- package/src/rust/utils/mod.rs +2 -0
- package/src/rust/utils/spawn.rs +1 -1
- package/src/rust/workspace.rs +454 -199
- package/src/svg/lock.svg +7 -0
- package/src/themes/botanical.css +10 -0
- package/src/themes/defaults.css +12 -0
- package/src/themes/dracula.css +17 -0
- package/src/themes/gruvbox-dark.css +20 -0
- package/src/themes/gruvbox.css +10 -0
- package/src/themes/icons.css +1 -0
- package/src/themes/intl/de.css +16 -0
- package/src/themes/intl/es.css +16 -0
- package/src/themes/intl/fr.css +16 -0
- package/src/themes/intl/ja.css +16 -0
- package/src/themes/intl/pt.css +16 -0
- package/src/themes/intl/zh.css +16 -0
- package/src/themes/intl.css +16 -0
- package/src/themes/monokai.css +22 -0
- package/src/themes/phosphor.css +5 -0
- package/src/themes/pro-dark.css +10 -0
- package/src/themes/solarized-dark.css +7 -0
- package/src/themes/solarized.css +7 -0
- package/src/themes/vaporwave.css +20 -0
- package/src/ts/column-format.ts +67 -46
- package/src/ts/perspective-viewer.ts +3 -0
- package/src/ts/plugin.ts +14 -0
- package/src/ts/providers.ts +96 -0
- package/src/ts/ts-rs/CompactDisplay.ts +3 -0
- package/src/ts/ts-rs/CurrencyCode.ts +3 -0
- package/src/ts/ts-rs/CurrencyDisplay.ts +3 -0
- package/src/ts/ts-rs/CurrencyNumberFormatStyle.ts +6 -0
- package/src/ts/ts-rs/CurrencySign.ts +3 -0
- package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +54 -0
- package/src/ts/ts-rs/CustomNumberFormatConfig.ts +25 -0
- package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
- package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
- package/src/ts/ts-rs/FormatMode.ts +3 -0
- package/src/ts/ts-rs/FormatUnit.ts +3 -0
- package/src/ts/ts-rs/Notation.ts +11 -0
- package/src/ts/ts-rs/NumberFormatStyle.ts +12 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +48 -2
- package/src/ts/ts-rs/PluginStaticConfig.ts +36 -1
- package/src/ts/ts-rs/RestoreOptions.ts +20 -0
- package/src/ts/ts-rs/RoundingMode.ts +3 -0
- package/src/ts/ts-rs/RoundingPriority.ts +3 -0
- package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
- package/src/ts/ts-rs/SignDisplay.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +25 -0
- package/src/ts/ts-rs/SplitRollupMode.ts +11 -0
- package/src/ts/ts-rs/StringColorMode.ts +3 -0
- package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
- package/src/ts/ts-rs/Unit.ts +3 -0
- package/src/ts/ts-rs/UnitDisplay.ts +3 -0
- package/src/ts/ts-rs/UnitNumberFormatStyle.ts +5 -0
- package/src/ts/ts-rs/UseGrouping.ts +3 -0
- package/src/ts/ts-rs/ViewerConfig.ts +48 -2
- package/src/ts/ts-rs/ViewerConfigInitial.ts +164 -0
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +72 -2
- package/src/ts/ts-rs/WindowSpec.ts +3 -2
- package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +18 -9
- package/dist/esm/ts-rs/WindowAggregate.d.ts +0 -1
- package/src/rust/components/form/color_range_selector.rs +0 -137
- package/src/ts/ts-rs/WindowAggregate.ts +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
|
@@ -42,6 +42,7 @@ use crate::tasks::{
|
|
|
42
42
|
delete_expr, delete_window, save_expr, save_window, update_expr, update_window,
|
|
43
43
|
};
|
|
44
44
|
use crate::utils::PtrEqRc;
|
|
45
|
+
use crate::workspace::Workspace;
|
|
45
46
|
|
|
46
47
|
#[derive(Clone, Derivative, Properties)]
|
|
47
48
|
#[derivative(Debug)]
|
|
@@ -91,12 +92,13 @@ pub struct ColumnSettingsPanelProps {
|
|
|
91
92
|
|
|
92
93
|
#[derivative(Debug = "ignore")]
|
|
93
94
|
pub session: Session,
|
|
95
|
+
|
|
96
|
+
#[derivative(Debug = "ignore")]
|
|
97
|
+
pub workspace: Workspace,
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
impl ColumnSettingsPanelProps {
|
|
97
|
-
|
|
98
|
-
/// invalidates the drafts that `initialize` rebuilds.
|
|
99
|
-
fn identity_eq(&self, other: &Self) -> bool {
|
|
100
|
+
impl PartialEq for ColumnSettingsPanelProps {
|
|
101
|
+
fn eq(&self, other: &Self) -> bool {
|
|
100
102
|
self.selected_column == other.selected_column
|
|
101
103
|
&& self.selected_tab == other.selected_tab
|
|
102
104
|
&& self.plugin_name == other.plugin_name
|
|
@@ -104,14 +106,46 @@ impl ColumnSettingsPanelProps {
|
|
|
104
106
|
&& self.view_config == other.view_config
|
|
105
107
|
&& self.column_stats == other.column_stats
|
|
106
108
|
&& self.selected_theme == other.selected_theme
|
|
109
|
+
&& self.auto_width == other.auto_width
|
|
110
|
+
&& self.is_pinned == other.is_pinned
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
#[derive(Clone, PartialEq)]
|
|
115
|
+
struct Initials {
|
|
116
|
+
column_name: String,
|
|
117
|
+
expr: Rc<String>,
|
|
118
|
+
header: Option<String>,
|
|
119
|
+
window: Option<WindowSpec>,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
impl Initials {
|
|
123
|
+
fn of(props: &ColumnSettingsPanelProps) -> Self {
|
|
124
|
+
let column_name = props
|
|
125
|
+
.metadata
|
|
126
|
+
.locator_name_or_default(&props.selected_column);
|
|
127
|
+
|
|
128
|
+
let expr = props
|
|
129
|
+
.metadata
|
|
130
|
+
.get_expression_by_alias(&column_name)
|
|
131
|
+
.or_else(|| props.view_config.expressions.get(&column_name).cloned())
|
|
132
|
+
.unwrap_or_default();
|
|
133
|
+
|
|
134
|
+
let expr = Rc::new(expr);
|
|
135
|
+
let header = (*expr != column_name).then_some(column_name.clone());
|
|
136
|
+
|
|
137
|
+
let window = props
|
|
138
|
+
.selected_column
|
|
139
|
+
.name()
|
|
140
|
+
.and_then(|name| props.view_config.windows.get(name))
|
|
141
|
+
.cloned();
|
|
142
|
+
|
|
143
|
+
Self {
|
|
144
|
+
column_name,
|
|
145
|
+
expr,
|
|
146
|
+
header,
|
|
147
|
+
window,
|
|
148
|
+
}
|
|
115
149
|
}
|
|
116
150
|
}
|
|
117
151
|
|
|
@@ -172,22 +206,23 @@ impl Component for ColumnSettingsPanel {
|
|
|
172
206
|
initial_window_value: None,
|
|
173
207
|
window_value: None,
|
|
174
208
|
tabs: vec![],
|
|
175
|
-
on_input:
|
|
176
|
-
on_save:
|
|
177
|
-
|
|
209
|
+
on_input: ctx.link().callback(ColumnSettingsPanelMsg::SetExprValue),
|
|
210
|
+
on_save: ctx
|
|
211
|
+
.link()
|
|
212
|
+
.callback(ColumnSettingsPanelMsg::OnSaveAttributes),
|
|
213
|
+
on_validate: ctx.link().callback(ColumnSettingsPanelMsg::SetExprValid),
|
|
178
214
|
};
|
|
179
215
|
|
|
180
|
-
this.
|
|
216
|
+
this.reset_to(ctx, Initials::of(ctx.props()));
|
|
181
217
|
this
|
|
182
218
|
}
|
|
183
219
|
|
|
184
220
|
fn changed(&mut self, ctx: &yew::prelude::Context<Self>, old_props: &Self::Properties) -> bool {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
self.initialize(ctx);
|
|
221
|
+
let next = Initials::of(ctx.props());
|
|
222
|
+
if ctx.props().selected_column != old_props.selected_column || next != self.initials() {
|
|
223
|
+
self.reset_to(ctx, next);
|
|
224
|
+
} else {
|
|
225
|
+
self.refresh_derived(ctx);
|
|
191
226
|
}
|
|
192
227
|
|
|
193
228
|
true
|
|
@@ -353,9 +388,9 @@ impl Component for ColumnSettingsPanel {
|
|
|
353
388
|
};
|
|
354
389
|
|
|
355
390
|
let header_props = props!(EditableHeaderProps {
|
|
391
|
+
value: self.header_value.clone(),
|
|
356
392
|
initial_value: self.initial_header_value.clone(),
|
|
357
393
|
placeholder: header_placeholder,
|
|
358
|
-
reset_count: self.reset_count,
|
|
359
394
|
editable: (ctx.props().selected_column.is_expr()
|
|
360
395
|
&& matches!(
|
|
361
396
|
ctx.props().selected_tab,
|
|
@@ -374,7 +409,6 @@ impl Component for ColumnSettingsPanel {
|
|
|
374
409
|
]
|
|
375
410
|
}),
|
|
376
411
|
metadata: ctx.props().metadata.clone(),
|
|
377
|
-
session: &ctx.props().session
|
|
378
412
|
});
|
|
379
413
|
|
|
380
414
|
let expr_editor = props!(ExpressionEditorProps {
|
|
@@ -382,6 +416,7 @@ impl Component for ColumnSettingsPanel {
|
|
|
382
416
|
on_save: self.on_save.clone(),
|
|
383
417
|
on_validate: self.on_validate.clone(),
|
|
384
418
|
alias: ctx.props().selected_column.name().cloned(),
|
|
419
|
+
initial_expr: self.initial_expr_value.clone(),
|
|
385
420
|
disabled: !ctx.props().selected_column.is_expr(),
|
|
386
421
|
reset_count: self.reset_count,
|
|
387
422
|
metadata: ctx.props().metadata.clone(),
|
|
@@ -457,6 +492,7 @@ impl Component for ColumnSettingsPanel {
|
|
|
457
492
|
presentation: ctx.props().presentation.clone(),
|
|
458
493
|
renderer: ctx.props().renderer.clone(),
|
|
459
494
|
session: ctx.props().session.clone(),
|
|
495
|
+
workspace: ctx.props().workspace.clone(),
|
|
460
496
|
};
|
|
461
497
|
|
|
462
498
|
let tab_children = self.tabs.iter().map(|tab| match tab {
|
|
@@ -506,38 +542,22 @@ impl ColumnSettingsPanel {
|
|
|
506
542
|
self.save_enabled = changed && valid;
|
|
507
543
|
}
|
|
508
544
|
|
|
509
|
-
fn
|
|
510
|
-
|
|
511
|
-
.
|
|
512
|
-
.
|
|
513
|
-
.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
.metadata
|
|
518
|
-
.get_expression_by_alias(&column_name)
|
|
519
|
-
.unwrap_or_default();
|
|
520
|
-
|
|
521
|
-
let initial_expr_value = Rc::new(initial_expr_value);
|
|
522
|
-
let initial_header_value =
|
|
523
|
-
(*initial_expr_value != column_name).then_some(column_name.clone());
|
|
545
|
+
fn initials(&self) -> Initials {
|
|
546
|
+
Initials {
|
|
547
|
+
column_name: self.column_name.clone(),
|
|
548
|
+
expr: self.initial_expr_value.clone(),
|
|
549
|
+
header: self.initial_header_value.clone(),
|
|
550
|
+
window: self.initial_window_value.clone(),
|
|
551
|
+
}
|
|
552
|
+
}
|
|
524
553
|
|
|
525
|
-
|
|
554
|
+
fn refresh_derived(&mut self, ctx: &yew::prelude::Context<Self>) {
|
|
555
|
+
self.maybe_ty = ctx
|
|
526
556
|
.props()
|
|
527
557
|
.metadata
|
|
528
558
|
.locator_view_type(&ctx.props().selected_column);
|
|
529
559
|
|
|
530
|
-
|
|
531
|
-
// editable header owns naming in the UI), so drafts and saved specs
|
|
532
|
-
// compare directly.
|
|
533
|
-
let initial_window_value = ctx
|
|
534
|
-
.props()
|
|
535
|
-
.selected_column
|
|
536
|
-
.name()
|
|
537
|
-
.and_then(|name| ctx.props().view_config.windows.get(name))
|
|
538
|
-
.cloned();
|
|
539
|
-
|
|
540
|
-
let tabs = {
|
|
560
|
+
self.tabs = {
|
|
541
561
|
let mut tabs = vec![];
|
|
542
562
|
let is_new_expr = ctx.props().selected_column.is_new_expr();
|
|
543
563
|
let show_styles = !is_new_expr
|
|
@@ -571,28 +591,27 @@ impl ColumnSettingsPanel {
|
|
|
571
591
|
|
|
572
592
|
tabs
|
|
573
593
|
};
|
|
594
|
+
}
|
|
574
595
|
|
|
575
|
-
|
|
576
|
-
let
|
|
577
|
-
.link()
|
|
578
|
-
.callback(ColumnSettingsPanelMsg::OnSaveAttributes);
|
|
579
|
-
|
|
580
|
-
let on_validate = ctx.link().callback(ColumnSettingsPanelMsg::SetExprValid);
|
|
581
|
-
*self = Self {
|
|
596
|
+
fn reset_to(&mut self, ctx: &yew::prelude::Context<Self>, initials: Initials) {
|
|
597
|
+
let Initials {
|
|
582
598
|
column_name,
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
599
|
+
expr,
|
|
600
|
+
header,
|
|
601
|
+
window,
|
|
602
|
+
} = initials;
|
|
603
|
+
|
|
604
|
+
self.column_name = column_name;
|
|
605
|
+
self.expr_value = expr.clone();
|
|
606
|
+
self.initial_expr_value = expr;
|
|
607
|
+
self.header_value = header.clone();
|
|
608
|
+
self.initial_header_value = header;
|
|
609
|
+
self.window_value = window.clone();
|
|
610
|
+
self.initial_window_value = window;
|
|
611
|
+
self.header_valid = true;
|
|
612
|
+
self.save_enabled = false;
|
|
613
|
+
self.reset_enabled = false;
|
|
614
|
+
self.reset_count = self.reset_count.wrapping_add(1);
|
|
615
|
+
self.refresh_derived(ctx);
|
|
597
616
|
}
|
|
598
617
|
}
|
|
@@ -60,6 +60,16 @@ where
|
|
|
60
60
|
#[prop_or_default]
|
|
61
61
|
pub single_slot: bool,
|
|
62
62
|
|
|
63
|
+
/// The plugin-declared visual role this slot fills, e.g. `"X Axis"`
|
|
64
|
+
/// for a `Y Line`'s `group_by` (see `PluginStaticConfig`). Rendered
|
|
65
|
+
/// as the slot's label THROUGH the intl indirection: the role is a
|
|
66
|
+
/// key, not display text, so `--psp-label--role--x-axis--content`
|
|
67
|
+
/// supplies the words and a language variant can override them. A
|
|
68
|
+
/// role with no such label falls back to the declared English, and
|
|
69
|
+
/// no role at all falls back to the slot's own generic label.
|
|
70
|
+
#[prop_or_default]
|
|
71
|
+
pub role_label: Option<AttrValue>,
|
|
72
|
+
|
|
63
73
|
/// The in-flight drag is INVALID for this list (a parent-defined rule,
|
|
64
74
|
/// e.g. the window editor's Table-columns-only slots): the dragover
|
|
65
75
|
/// preview is suppressed and the invalid-X overlay renders instead,
|
|
@@ -83,6 +93,7 @@ where
|
|
|
83
93
|
&& self.disabled == other.disabled
|
|
84
94
|
&& self.single_slot == other.single_slot
|
|
85
95
|
&& self.is_invalid == other.is_invalid
|
|
96
|
+
&& self.role_label == other.role_label
|
|
86
97
|
}
|
|
87
98
|
}
|
|
88
99
|
|
|
@@ -390,10 +401,36 @@ where
|
|
|
390
401
|
let class = classes!("rrow");
|
|
391
402
|
let is_enabled = true;
|
|
392
403
|
|
|
404
|
+
// The role is data from the plugin, so the var NAME is built
|
|
405
|
+
// here while the var VALUE stays in the stylesheets - custom
|
|
406
|
+
// properties inherit, so the label's `:before` picks it up.
|
|
407
|
+
let role_style = ctx.props().role_label.as_ref().map(|role| {
|
|
408
|
+
let slug = role
|
|
409
|
+
.chars()
|
|
410
|
+
.map(|x| {
|
|
411
|
+
if x.is_ascii_alphanumeric() {
|
|
412
|
+
x.to_ascii_lowercase()
|
|
413
|
+
} else {
|
|
414
|
+
'-'
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
.collect::<String>();
|
|
418
|
+
|
|
419
|
+
// The second var is a PRESENCE FLAG: CSS cannot ask whether a
|
|
420
|
+
// custom property is set, so the slot's stylesheet reads it as
|
|
421
|
+
// the secondary label's `display` and gets `none` by fallback
|
|
422
|
+
// when no role was declared.
|
|
423
|
+
format!(
|
|
424
|
+
"--psp-label--pivot--content: var(--psp-label--role--{slug}--content, \
|
|
425
|
+
\"{role}\"); --psp-label--pivot-secondary--display: inline-block"
|
|
426
|
+
)
|
|
427
|
+
});
|
|
428
|
+
|
|
393
429
|
html! {
|
|
394
430
|
<div ref={&self.elem} {class}>
|
|
395
431
|
<div
|
|
396
432
|
id={ctx.props().name}
|
|
433
|
+
style={role_style}
|
|
397
434
|
ondragover={is_enabled.then_some(dragover)}
|
|
398
435
|
ondragenter={is_enabled.then_some(drag_container.dragenter)}
|
|
399
436
|
ondragleave={is_enabled.then_some(drag_container.dragleave)}
|
|
@@ -14,32 +14,30 @@ use std::rc::Rc;
|
|
|
14
14
|
|
|
15
15
|
use itertools::Itertools;
|
|
16
16
|
use web_sys::{FocusEvent, HtmlInputElement, KeyboardEvent};
|
|
17
|
-
use yew::
|
|
17
|
+
use yew::prelude::*;
|
|
18
18
|
|
|
19
19
|
use super::type_icon::TypeIconType;
|
|
20
20
|
use crate::components::type_icon::TypeIcon;
|
|
21
|
-
use crate::session::
|
|
21
|
+
use crate::session::SessionMetadataRc;
|
|
22
22
|
|
|
23
23
|
#[derive(Clone, PartialEq, Properties)]
|
|
24
24
|
pub struct EditableHeaderProps {
|
|
25
25
|
pub icon_type: Option<TypeIconType>,
|
|
26
26
|
pub on_change: Callback<(Option<String>, bool)>,
|
|
27
27
|
pub editable: bool,
|
|
28
|
+
|
|
29
|
+
/// The owner's draft.
|
|
30
|
+
pub value: Option<String>,
|
|
31
|
+
|
|
32
|
+
/// The saved name the draft is measured against.
|
|
28
33
|
pub initial_value: Option<String>,
|
|
29
34
|
pub placeholder: Rc<String>,
|
|
30
35
|
|
|
31
|
-
// TODO remove this pattern
|
|
32
|
-
#[prop_or_default]
|
|
33
|
-
pub reset_count: u8,
|
|
34
|
-
|
|
35
36
|
#[prop_or_default]
|
|
36
37
|
pub update_on_input: bool,
|
|
37
38
|
|
|
38
39
|
/// Session metadata snapshot — threaded from `SessionProps`.
|
|
39
40
|
pub metadata: SessionMetadataRc,
|
|
40
|
-
|
|
41
|
-
// State
|
|
42
|
-
pub session: Session,
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
impl EditableHeaderProps {
|
|
@@ -55,152 +53,95 @@ impl EditableHeaderProps {
|
|
|
55
53
|
Some((idx, _)) => split[..idx].to_owned(),
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
pub enum EditableHeaderMsg {
|
|
61
|
-
SetNewValue(String),
|
|
62
|
-
OnClick(()),
|
|
63
|
-
}
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
valid: bool,
|
|
70
|
-
value: Option<String>,
|
|
71
|
-
placeholder: String,
|
|
72
|
-
}
|
|
57
|
+
fn is_valid(&self, value: &Option<String>, placeholder: &str) -> bool {
|
|
58
|
+
let Some(value) = value else {
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
73
61
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
type Properties = EditableHeaderProps;
|
|
77
|
-
|
|
78
|
-
fn create(ctx: &yew::prelude::Context<Self>) -> Self {
|
|
79
|
-
Self {
|
|
80
|
-
value: ctx.props().initial_value.clone(),
|
|
81
|
-
placeholder: ctx.props().split_placeholder(),
|
|
82
|
-
valid: true,
|
|
83
|
-
noderef: NodeRef::default(),
|
|
84
|
-
edited: false,
|
|
62
|
+
if value == placeholder || Some(value) == self.initial_value.as_ref() {
|
|
63
|
+
return true;
|
|
85
64
|
}
|
|
86
|
-
}
|
|
87
65
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
if ctx.props().initial_value != old_props.initial_value {
|
|
93
|
-
self.edited = false;
|
|
94
|
-
self.value.clone_from(&ctx.props().initial_value);
|
|
95
|
-
}
|
|
96
|
-
if !ctx.props().editable {
|
|
97
|
-
self.edited = false;
|
|
98
|
-
}
|
|
99
|
-
self.placeholder = ctx.props().split_placeholder();
|
|
100
|
-
ctx.props() != old_props
|
|
101
|
-
}
|
|
66
|
+
let metadata = &self.metadata;
|
|
67
|
+
let Some(table_columns) = metadata.get_table_columns() else {
|
|
68
|
+
return true;
|
|
69
|
+
};
|
|
102
70
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self.valid = (|| -> Option<bool> {
|
|
110
|
-
if maybe_value
|
|
111
|
-
.as_ref()
|
|
112
|
-
.map(|v| v == &self.placeholder)
|
|
113
|
-
.unwrap_or(true)
|
|
114
|
-
{
|
|
115
|
-
return Some(true);
|
|
116
|
-
}
|
|
117
|
-
if !self.edited {
|
|
118
|
-
return Some(true);
|
|
119
|
-
}
|
|
120
|
-
let metadata = &ctx.props().metadata;
|
|
121
|
-
let expressions = metadata.get_expression_columns();
|
|
122
|
-
let windows = metadata.get_window_columns();
|
|
123
|
-
let found = metadata
|
|
124
|
-
.get_table_columns()?
|
|
125
|
-
.iter()
|
|
126
|
-
.chain(expressions)
|
|
127
|
-
.chain(windows)
|
|
128
|
-
.contains(&new_value);
|
|
129
|
-
Some(!found)
|
|
130
|
-
})()
|
|
131
|
-
.unwrap_or(true);
|
|
132
|
-
|
|
133
|
-
self.value.clone_from(&maybe_value);
|
|
134
|
-
ctx.props().on_change.emit((maybe_value, self.valid));
|
|
135
|
-
true
|
|
136
|
-
},
|
|
137
|
-
EditableHeaderMsg::OnClick(()) => {
|
|
138
|
-
self.noderef
|
|
139
|
-
.cast::<HtmlInputElement>()
|
|
140
|
-
.unwrap()
|
|
141
|
-
.focus()
|
|
142
|
-
.unwrap();
|
|
143
|
-
false
|
|
144
|
-
},
|
|
145
|
-
}
|
|
71
|
+
!table_columns
|
|
72
|
+
.iter()
|
|
73
|
+
.chain(metadata.get_expression_columns())
|
|
74
|
+
.chain(metadata.get_window_columns())
|
|
75
|
+
.contains(value)
|
|
146
76
|
}
|
|
77
|
+
}
|
|
147
78
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if self.edited {
|
|
159
|
-
classes.push("edited");
|
|
160
|
-
}
|
|
79
|
+
#[function_component(EditableHeader)]
|
|
80
|
+
pub fn editable_header(props: &EditableHeaderProps) -> Html {
|
|
81
|
+
let noderef = use_node_ref();
|
|
82
|
+
let placeholder = props.split_placeholder();
|
|
83
|
+
let edited = props.value != props.initial_value;
|
|
84
|
+
let valid = props.is_valid(&props.value, &placeholder);
|
|
85
|
+
let mut classes = classes!("sidebar_header_contents");
|
|
86
|
+
if props.editable {
|
|
87
|
+
classes.push("editable");
|
|
88
|
+
}
|
|
161
89
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
90
|
+
if !valid {
|
|
91
|
+
classes.push("invalid");
|
|
92
|
+
}
|
|
166
93
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
94
|
+
if edited {
|
|
95
|
+
classes.push("edited");
|
|
96
|
+
}
|
|
171
97
|
|
|
172
|
-
|
|
173
|
-
let
|
|
98
|
+
let on_value = {
|
|
99
|
+
let props = props.clone();
|
|
100
|
+
let placeholder = placeholder.clone();
|
|
101
|
+
Callback::from(move |value: String| {
|
|
102
|
+
let value = (!value.is_empty()).then_some(value);
|
|
103
|
+
let valid = props.is_valid(&value, &placeholder);
|
|
104
|
+
props.on_change.emit((value, valid));
|
|
105
|
+
})
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let onkeyup =
|
|
109
|
+
on_value.reform(|e: KeyboardEvent| e.target_unchecked_into::<HtmlInputElement>().value());
|
|
110
|
+
let onblur =
|
|
111
|
+
on_value.reform(|e: FocusEvent| e.target_unchecked_into::<HtmlInputElement>().value());
|
|
112
|
+
let oninput = {
|
|
113
|
+
let update_on_input = props.update_on_input;
|
|
114
|
+
let on_value = on_value.clone();
|
|
115
|
+
Callback::from(move |e: InputEvent| {
|
|
174
116
|
if update_on_input {
|
|
175
|
-
|
|
176
|
-
vec![EditableHeaderMsg::SetNewValue(value)]
|
|
177
|
-
} else {
|
|
178
|
-
vec![]
|
|
117
|
+
on_value.emit(e.target_unchecked_into::<HtmlInputElement>().value());
|
|
179
118
|
}
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
119
|
+
})
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
let onclick = {
|
|
123
|
+
let noderef = noderef.clone();
|
|
124
|
+
Callback::from(move |_: MouseEvent| {
|
|
125
|
+
if let Some(input) = noderef.cast::<HtmlInputElement>() {
|
|
126
|
+
let _ = input.focus();
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
html! {
|
|
132
|
+
<div class={classes} {onclick}>
|
|
133
|
+
if let Some(icon) = props.icon_type { <TypeIcon ty={icon} /> }
|
|
134
|
+
<input
|
|
135
|
+
ref={noderef}
|
|
136
|
+
type="search"
|
|
137
|
+
class="sidebar_header_title"
|
|
138
|
+
disabled={!props.editable}
|
|
139
|
+
{onblur}
|
|
140
|
+
{onkeyup}
|
|
141
|
+
{oninput}
|
|
142
|
+
value={props.value.clone()}
|
|
143
|
+
{placeholder}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
198
146
|
}
|
|
199
147
|
}
|
|
200
|
-
|
|
201
|
-
#[derive(Default, Debug, PartialEq, Copy, Clone)]
|
|
202
|
-
pub enum ValueState {
|
|
203
|
-
#[default]
|
|
204
|
-
Unedited,
|
|
205
|
-
Edited,
|
|
206
|
-
}
|
|
@@ -16,7 +16,7 @@ use perspective_client::{ExprValidationError, clone};
|
|
|
16
16
|
use yew::prelude::*;
|
|
17
17
|
|
|
18
18
|
use super::form::code_editor::*;
|
|
19
|
-
use crate::session::{Session,
|
|
19
|
+
use crate::session::{Session, SessionMetadataRc};
|
|
20
20
|
use crate::tasks::{ExprValidation, validate_expression};
|
|
21
21
|
|
|
22
22
|
#[derive(Properties, PartialEq, Clone)]
|
|
@@ -27,6 +27,9 @@ pub struct ExpressionEditorProps {
|
|
|
27
27
|
pub alias: Option<String>,
|
|
28
28
|
pub disabled: bool,
|
|
29
29
|
|
|
30
|
+
/// The saved expression text this editor edits against.
|
|
31
|
+
pub initial_expr: Rc<String>,
|
|
32
|
+
|
|
30
33
|
#[prop_or_default]
|
|
31
34
|
pub reset_count: u8,
|
|
32
35
|
|
|
@@ -69,7 +72,7 @@ impl Component for ExpressionEditor {
|
|
|
69
72
|
|
|
70
73
|
fn create(ctx: &Context<Self>) -> Self {
|
|
71
74
|
let oninput = ctx.link().callback(ExpressionEditorMsg::SetExpr);
|
|
72
|
-
let expr =
|
|
75
|
+
let expr = ctx.props().initial_expr.clone();
|
|
73
76
|
ctx.link()
|
|
74
77
|
.send_message(Self::Message::SetExpr(expr.clone()));
|
|
75
78
|
|
|
@@ -156,24 +159,14 @@ impl Component for ExpressionEditor {
|
|
|
156
159
|
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
|
|
157
160
|
if ctx.props().alias != old_props.alias
|
|
158
161
|
|| ctx.props().reset_count != old_props.reset_count
|
|
159
|
-
||
|
|
162
|
+
|| ctx.props().initial_expr != old_props.initial_expr
|
|
160
163
|
{
|
|
161
|
-
ctx.link()
|
|
162
|
-
.
|
|
163
|
-
|
|
164
|
-
&ctx.props().alias,
|
|
165
|
-
)));
|
|
164
|
+
ctx.link().send_message(ExpressionEditorMsg::SetExpr(
|
|
165
|
+
ctx.props().initial_expr.clone(),
|
|
166
|
+
));
|
|
166
167
|
false
|
|
167
168
|
} else {
|
|
168
169
|
true
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
|
-
|
|
173
|
-
fn initial_expr(metadata: &SessionMetadata, alias: &Option<String>) -> Rc<String> {
|
|
174
|
-
alias
|
|
175
|
-
.as_ref()
|
|
176
|
-
.and_then(|alias| metadata.get_expression_by_alias(alias))
|
|
177
|
-
.unwrap_or_default()
|
|
178
|
-
.into()
|
|
179
|
-
}
|