@perspective-dev/viewer 5.2.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 +2 -2
- package/dist/cdn/perspective-viewer.js.map +2 -2
- 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/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 -1
- package/dist/esm/perspective-viewer.d.ts +1 -0
- package/dist/esm/perspective-viewer.inline.js +9 -9
- package/dist/esm/perspective-viewer.inline.js.map +2 -2
- package/dist/esm/perspective-viewer.js +4 -4
- package/dist/esm/perspective-viewer.js.map +2 -2
- package/dist/esm/plugin.d.ts +10 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +1 -1
- package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +1 -1
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +13 -3
- package/dist/wasm/perspective-viewer.d.ts +43 -36
- package/dist/wasm/perspective-viewer.js +194 -136
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +19 -17
- package/package.json +1 -1
- package/src/css/column-settings-panel.css +9 -5
- package/src/css/column-style.css +209 -31
- package/src/css/dom/select.css +2 -1
- package/src/css/form/code-editor.css +4 -4
- package/src/rust/agent/tools.rs +48 -17
- 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 +8 -0
- 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 +48 -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/editable_header.rs +85 -144
- package/src/rust/components/expression_editor.rs +9 -16
- package/src/rust/components/form/code_editor.rs +16 -6
- package/src/rust/components/form/debug.rs +1 -0
- package/src/rust/components/form/mod.rs +3 -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/plugin_tab.rs +4 -28
- package/src/rust/components/settings_panel.rs +25 -20
- package/src/rust/components/viewer/msg.rs +46 -7
- package/src/rust/components/viewer/panels.rs +11 -9
- package/src/rust/components/viewer/render.rs +8 -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/config/column_config_schema.rs +100 -41
- package/src/rust/config/css_scale.rs +882 -0
- package/src/rust/config/mod.rs +2 -0
- package/src/rust/config/options.rs +10 -0
- package/src/rust/config/viewer_config.rs +49 -55
- package/src/rust/config/workspace_config.rs +132 -5
- package/src/rust/custom_elements/viewer.rs +214 -155
- package/src/rust/custom_events.rs +14 -1
- package/src/rust/js/regular_layout.rs +3 -3
- package/src/rust/lib.rs +2 -0
- package/src/rust/presentation/column_locator.rs +19 -5
- package/src/rust/presentation.rs +192 -36
- package/src/rust/queries/column_locator.rs +74 -44
- package/src/rust/queries/get_viewer_config.rs +0 -3
- 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 +15 -8
- package/src/rust/session/metadata.rs +5 -1
- package/src/rust/session/props.rs +1 -0
- package/src/rust/session.rs +9 -6
- package/src/rust/tasks/create_panel.rs +44 -57
- package/src/rust/tasks/edit_expression.rs +19 -23
- package/src/rust/tasks/edit_window.rs +8 -10
- 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 +11 -13
- package/src/rust/tasks/set_edit_mode.rs +3 -1
- package/src/rust/tasks/sync_update_panels.rs +11 -1
- 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 +435 -272
- 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/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/perspective-viewer.ts +1 -0
- package/src/ts/plugin.ts +14 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +1 -1
- package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
- package/src/ts/ts-rs/ViewerConfig.ts +1 -1
- package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +11 -3
- package/src/rust/components/form/color_range_selector.rs +0 -137
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
package/src/rust/config/mod.rs
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
mod column_config_schema;
|
|
17
17
|
mod columns_config;
|
|
18
|
+
mod css_scale;
|
|
18
19
|
mod datetime_column_style;
|
|
19
20
|
mod export_method;
|
|
20
21
|
mod kvpair;
|
|
@@ -29,6 +30,7 @@ mod workspace_config;
|
|
|
29
30
|
|
|
30
31
|
pub use column_config_schema::*;
|
|
31
32
|
pub use columns_config::*;
|
|
33
|
+
pub use css_scale::*;
|
|
32
34
|
pub use datetime_column_style::*;
|
|
33
35
|
pub use export_method::*;
|
|
34
36
|
pub use number_series_style::*;
|
|
@@ -87,3 +87,13 @@ pub struct GetClientOptions {
|
|
|
87
87
|
#[ts(optional)]
|
|
88
88
|
pub panel: Option<String>,
|
|
89
89
|
}
|
|
90
|
+
|
|
91
|
+
/// Options for the `saveWorkspace()` method.
|
|
92
|
+
#[derive(Deserialize, Default, TS)]
|
|
93
|
+
pub struct SaveWorkspaceOptions {
|
|
94
|
+
/// When `true`, the emitted `palette` is the full set the element
|
|
95
|
+
/// holds rather than only the values the panels reference.
|
|
96
|
+
#[serde(default)]
|
|
97
|
+
#[ts(optional)]
|
|
98
|
+
pub full_palette: Option<bool>,
|
|
99
|
+
}
|
|
@@ -24,7 +24,7 @@ use crate::renderer::ColumnConfigMap;
|
|
|
24
24
|
|
|
25
25
|
/// The state of an entire `custom_elements::PerspectiveViewerElement` component
|
|
26
26
|
/// and its `Plugin`: the element-level `settings` flag plus the per-panel
|
|
27
|
-
/// [`PanelViewerConfig`]. The split exists so the
|
|
27
|
+
/// [`PanelViewerConfig`]. The split exists so the workspace config format
|
|
28
28
|
/// can serialize panel entries *without* a `settings` key (it is element-level
|
|
29
29
|
/// state there, carried by the top-level `active` field instead), while the
|
|
30
30
|
/// single-panel format flattens back to the legacy shape.
|
|
@@ -39,7 +39,7 @@ pub struct ViewerConfig<V: TS = String> {
|
|
|
39
39
|
|
|
40
40
|
/// The per-panel state of a [`ViewerConfig`] — everything except the
|
|
41
41
|
/// element-level `settings` flag. This is the `panels` entry type of the
|
|
42
|
-
///
|
|
42
|
+
/// workspace config format.
|
|
43
43
|
#[derive(Debug, Default, Serialize, PartialEq, TS)]
|
|
44
44
|
pub struct PanelViewerConfig<V: TS = String> {
|
|
45
45
|
/// The `@perspective-dev/viewer` version that wrote this config,
|
|
@@ -315,15 +315,53 @@ impl From<ViewerConfigInitial> for ViewerConfigUpdate {
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
318
|
+
/// The rejection every panel-creating route without a `table` resolves to.
|
|
319
|
+
pub const CREATE_REQUIRES_TABLE: &str = "Cannot create a panel without a `table` — `load()` a \
|
|
320
|
+
`Client` and include `table` in the config, or use \
|
|
321
|
+
`addPanel()`";
|
|
322
|
+
|
|
323
|
+
fn down<T: Clone>(value: OptionalUpdate<T>) -> Option<T> {
|
|
324
|
+
match value {
|
|
325
|
+
OptionalUpdate::Update(value) => Some(value),
|
|
326
|
+
OptionalUpdate::Missing | OptionalUpdate::SetDefault => None,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
impl TryFrom<ViewerConfigUpdate> for ViewerConfigInitial {
|
|
331
|
+
type Error = ApiError;
|
|
332
|
+
|
|
333
|
+
fn try_from(value: ViewerConfigUpdate) -> Result<Self, Self::Error> {
|
|
334
|
+
// Exhaustive (no `..`) on purpose — the same drift alarm as the
|
|
335
|
+
// `From<ViewerConfigInitial>` impl above. `settings` is element-level
|
|
336
|
+
// state, stripped by `restore()` before resolution; discarded here.
|
|
337
|
+
let ViewerConfigUpdate {
|
|
338
|
+
version,
|
|
339
|
+
plugin,
|
|
340
|
+
title,
|
|
341
|
+
table,
|
|
342
|
+
theme,
|
|
343
|
+
settings: _settings,
|
|
344
|
+
plugin_config,
|
|
345
|
+
columns_config,
|
|
346
|
+
view_config,
|
|
347
|
+
} = value;
|
|
348
|
+
|
|
349
|
+
let OptionalUpdate::Update(table) = table else {
|
|
350
|
+
return Err(ApiError::new(CREATE_REQUIRES_TABLE));
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
Ok(Self {
|
|
354
|
+
table,
|
|
355
|
+
version: down(version),
|
|
356
|
+
plugin: down(plugin),
|
|
357
|
+
title: down(title),
|
|
358
|
+
theme: down(theme),
|
|
359
|
+
plugin_config: down(plugin_config),
|
|
360
|
+
columns_config: down(columns_config),
|
|
361
|
+
view_config,
|
|
362
|
+
})
|
|
363
|
+
}
|
|
364
|
+
}
|
|
327
365
|
|
|
328
366
|
impl std::fmt::Display for ViewerConfigUpdate {
|
|
329
367
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
@@ -360,50 +398,6 @@ pub type VersionUpdate = OptionalUpdate<String>;
|
|
|
360
398
|
pub type ColumnConfigUpdate = OptionalUpdate<ColumnConfigMap>;
|
|
361
399
|
pub type PluginConfigUpdate = OptionalUpdate<serde_json::Map<String, Value>>;
|
|
362
400
|
|
|
363
|
-
#[cfg(test)]
|
|
364
|
-
mod tests {
|
|
365
|
-
use super::*;
|
|
366
|
-
|
|
367
|
-
#[test]
|
|
368
|
-
fn initial_requires_a_table() {
|
|
369
|
-
let json = serde_json::json!({ "group_by": ["State"] });
|
|
370
|
-
let err = serde_json::from_value::<ViewerConfigInitial>(json).unwrap_err();
|
|
371
|
-
assert!(format!("{err}").contains("table"));
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/// The creation → patch direction is the only one that exists (see the
|
|
375
|
-
/// note above the `From` impl); a `table` becomes a concrete update and
|
|
376
|
-
/// the element-level `settings` is not carried.
|
|
377
|
-
#[test]
|
|
378
|
-
fn initial_widens_to_an_update() {
|
|
379
|
-
let json = serde_json::json!({
|
|
380
|
-
"table": "superstore",
|
|
381
|
-
"plugin": "Datagrid",
|
|
382
|
-
"group_by": ["State"],
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
let initial: ViewerConfigInitial = serde_json::from_value(json).unwrap();
|
|
386
|
-
let update = ViewerConfigUpdate::from(initial);
|
|
387
|
-
assert!(matches!(&update.table, OptionalUpdate::Update(x) if x == "superstore"));
|
|
388
|
-
assert!(matches!(&update.settings, OptionalUpdate::Missing));
|
|
389
|
-
assert!(matches!(&update.plugin, OptionalUpdate::Update(x) if x == "Datagrid"));
|
|
390
|
-
assert_eq!(
|
|
391
|
-
update.view_config.group_by.as_deref(),
|
|
392
|
-
Some(&["State".to_owned()][..])
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/// A table-less patch is a legitimate creation input now that
|
|
397
|
-
/// `create_panel` takes an update — the deferred panel a `load()`
|
|
398
|
-
/// binds. Nothing in this module may reject it.
|
|
399
|
-
#[test]
|
|
400
|
-
fn a_table_less_update_is_representable() {
|
|
401
|
-
let json = serde_json::json!({ "group_by": ["State"] });
|
|
402
|
-
let update: ViewerConfigUpdate = serde_json::from_value(json).unwrap();
|
|
403
|
-
assert!(matches!(&update.table, OptionalUpdate::Missing));
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
401
|
/// Handles `{}` when included as a field with `#[serde(default)]`.
|
|
408
402
|
impl<T: Clone> Default for OptionalUpdate<T> {
|
|
409
403
|
fn default() -> Self {
|
|
@@ -14,9 +14,9 @@ use std::collections::BTreeMap;
|
|
|
14
14
|
|
|
15
15
|
use perspective_client::config::Filter;
|
|
16
16
|
|
|
17
|
-
use crate::config::{PanelViewerConfig, ViewerConfigInitial};
|
|
17
|
+
use crate::config::{CssKind, PanelViewerConfig, ViewerConfigInitial};
|
|
18
18
|
|
|
19
|
-
/// The
|
|
19
|
+
/// The workspace config format (`{version, active?, layout, panels}`) —
|
|
20
20
|
/// the multi-panel counterpart of the single-panel [`ViewerConfig`] — as
|
|
21
21
|
/// emitted by [`PerspectiveViewerElement::save`].
|
|
22
22
|
///
|
|
@@ -56,9 +56,17 @@ pub struct WorkspaceConfig {
|
|
|
56
56
|
#[ts(as = "Option<_>")]
|
|
57
57
|
#[ts(optional)]
|
|
58
58
|
pub masters: Vec<String>,
|
|
59
|
+
|
|
60
|
+
/// Named color-scale definitions shared by every panel: CSS custom
|
|
61
|
+
/// property name (`--psp-user--<kind>-<name>`) → canonical CSS
|
|
62
|
+
/// value.
|
|
63
|
+
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
|
64
|
+
#[ts(as = "Option<_>")]
|
|
65
|
+
#[ts(optional)]
|
|
66
|
+
pub palette: BTreeMap<String, String>,
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
/// The parse target of a
|
|
69
|
+
/// The parse target of a workspace config in
|
|
62
70
|
/// [`PerspectiveViewerElement::restoreWorkspace`]. Mirrors
|
|
63
71
|
/// [`WorkspaceConfig`], but `panels` entries are [`ViewerConfigInitial`]s —
|
|
64
72
|
/// every entry creates a NEW panel, so `table` is required by type (a
|
|
@@ -79,15 +87,134 @@ pub struct WorkspaceConfigUpdate {
|
|
|
79
87
|
/// The element-level global (master/detail cross-) filters to re-apply as
|
|
80
88
|
/// a transient overlay on every DETAIL panel. Restored as one
|
|
81
89
|
/// unattributed bucket: the next selection on any master replaces it.
|
|
90
|
+
/// `Option` so an explicit `undefined` property deserializes as
|
|
91
|
+
/// `None` like an absent key (also `masters` / `palette` below).
|
|
82
92
|
#[serde(default)]
|
|
83
93
|
#[ts(as = "Option<_>")]
|
|
84
94
|
#[ts(optional)]
|
|
85
|
-
pub global_filters: Vec<Filter
|
|
95
|
+
pub global_filters: Option<Vec<Filter>>,
|
|
86
96
|
|
|
87
97
|
/// The master (filter-source) panels, by saved `panels` key. An id not in
|
|
88
98
|
/// `panels` warns and is dropped.
|
|
89
99
|
#[serde(default)]
|
|
90
100
|
#[ts(as = "Option<_>")]
|
|
91
101
|
#[ts(optional)]
|
|
92
|
-
pub masters: Vec<String
|
|
102
|
+
pub masters: Option<Vec<String>>,
|
|
103
|
+
|
|
104
|
+
/// Named color-scale definitions to apply to the host (see
|
|
105
|
+
/// [`WorkspaceConfig::palette`]), replacing any previously restored
|
|
106
|
+
/// palette.
|
|
107
|
+
#[serde(default)]
|
|
108
|
+
#[ts(as = "Option<_>")]
|
|
109
|
+
#[ts(optional)]
|
|
110
|
+
pub palette: Option<BTreeMap<String, String>>,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Validate a restored palette map: each key's `--psp-user--<kind>-`
|
|
114
|
+
/// prefix selects the reader that canonicalizes its value.
|
|
115
|
+
pub fn validate_palette(
|
|
116
|
+
palette: BTreeMap<String, String>,
|
|
117
|
+
) -> Result<BTreeMap<String, String>, String> {
|
|
118
|
+
palette
|
|
119
|
+
.into_iter()
|
|
120
|
+
.map(|(name, value)| {
|
|
121
|
+
let kind = CssKind::of_var(&name).ok_or_else(|| {
|
|
122
|
+
format!(
|
|
123
|
+
"`palette` key `{name}` must start with `--psp-user--gradient-`, \
|
|
124
|
+
`--psp-user--palette-` or `--psp-user--color-`"
|
|
125
|
+
)
|
|
126
|
+
})?;
|
|
127
|
+
|
|
128
|
+
let canonical = kind
|
|
129
|
+
.canonicalize(&value)
|
|
130
|
+
.map_err(|error| format!("`palette[\"{name}\"]`: {error}"))?;
|
|
131
|
+
|
|
132
|
+
Ok((name, canonical))
|
|
133
|
+
})
|
|
134
|
+
.collect()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#[cfg(test)]
|
|
138
|
+
mod tests {
|
|
139
|
+
use serde_json::json;
|
|
140
|
+
|
|
141
|
+
use super::*;
|
|
142
|
+
|
|
143
|
+
#[test]
|
|
144
|
+
fn palette_serializes_only_when_present() {
|
|
145
|
+
let config = WorkspaceConfig {
|
|
146
|
+
version: "x".to_owned(),
|
|
147
|
+
active: None,
|
|
148
|
+
layout: None,
|
|
149
|
+
panels: BTreeMap::new(),
|
|
150
|
+
global_filters: vec![],
|
|
151
|
+
masters: vec![],
|
|
152
|
+
palette: BTreeMap::new(),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
assert_eq!(
|
|
156
|
+
serde_json::to_value(&config).unwrap(),
|
|
157
|
+
json!({ "version": "x", "layout": null, "panels": {} })
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
let mut palette = BTreeMap::new();
|
|
161
|
+
palette.insert("--psp-user--color-hot".to_owned(), "#ff0000".to_owned());
|
|
162
|
+
|
|
163
|
+
let config = WorkspaceConfig { palette, ..config };
|
|
164
|
+
assert_eq!(
|
|
165
|
+
serde_json::to_value(&config).unwrap(),
|
|
166
|
+
json!({
|
|
167
|
+
"version": "x",
|
|
168
|
+
"layout": null,
|
|
169
|
+
"panels": {},
|
|
170
|
+
"palette": { "--psp-user--color-hot": "#ff0000" },
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
#[test]
|
|
176
|
+
fn update_palette_defaults_empty_and_validates_by_prefix() {
|
|
177
|
+
let update: WorkspaceConfigUpdate =
|
|
178
|
+
serde_json::from_value(json!({ "panels": {} })).unwrap();
|
|
179
|
+
assert!(update.palette.is_none());
|
|
180
|
+
|
|
181
|
+
let update: WorkspaceConfigUpdate = serde_json::from_value(json!({
|
|
182
|
+
"panels": {},
|
|
183
|
+
"palette": {
|
|
184
|
+
"--psp-user--gradient-1": "linear-gradient(#000, #fff)",
|
|
185
|
+
"--psp-user--palette-warm": "linear-gradient(90deg, RGB(255,0,0), #ff0)",
|
|
186
|
+
"--psp-user--color-hot": "#F00",
|
|
187
|
+
},
|
|
188
|
+
}))
|
|
189
|
+
.unwrap();
|
|
190
|
+
|
|
191
|
+
let valid = validate_palette(update.palette.unwrap()).unwrap();
|
|
192
|
+
assert_eq!(
|
|
193
|
+
valid.get("--psp-user--gradient-1").unwrap(),
|
|
194
|
+
"linear-gradient(to right, #000000 0%, #ffffff 100%)"
|
|
195
|
+
);
|
|
196
|
+
assert_eq!(
|
|
197
|
+
valid.get("--psp-user--palette-warm").unwrap(),
|
|
198
|
+
"linear-gradient(to right, #ff0000, #ffff00)"
|
|
199
|
+
);
|
|
200
|
+
assert_eq!(valid.get("--psp-user--color-hot").unwrap(), "#ff0000");
|
|
201
|
+
|
|
202
|
+
let bad = |name: &str, value: &str| {
|
|
203
|
+
let mut map = BTreeMap::new();
|
|
204
|
+
map.insert(name.to_owned(), value.to_owned());
|
|
205
|
+
validate_palette(map).unwrap_err()
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
assert!(bad("--psp-user--other-1", "#ff0000").contains("--psp-user--other-1"));
|
|
209
|
+
assert!(bad("--psp-charts--gradient", "#ff0000").contains("must start with"));
|
|
210
|
+
assert!(
|
|
211
|
+
bad(
|
|
212
|
+
"--psp-user--palette-1",
|
|
213
|
+
"linear-gradient(#000 0%, #fff 100%)"
|
|
214
|
+
)
|
|
215
|
+
.contains("--psp-user--palette-1")
|
|
216
|
+
);
|
|
217
|
+
assert!(bad("--psp-user--gradient-1", "#ff0000").contains("linear-gradient"));
|
|
218
|
+
assert!(bad("--psp-user--color-1", "red").contains("red"));
|
|
219
|
+
}
|
|
93
220
|
}
|