@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
|
@@ -54,7 +54,8 @@ pub async fn get_viewer_config(
|
|
|
54
54
|
let table = session
|
|
55
55
|
.get_table()
|
|
56
56
|
.map(|x| x.get_name().to_owned())
|
|
57
|
-
.or_else(|| session.pending_table())
|
|
57
|
+
.or_else(|| session.pending_table())
|
|
58
|
+
.ok_or_else(|| ApiError::from("Panel has no `table`"))?;
|
|
58
59
|
let columns_config = renderer.all_columns_configs();
|
|
59
60
|
Ok(ViewerConfig {
|
|
60
61
|
settings,
|
package/src/rust/queries/mod.rs
CHANGED
|
@@ -29,6 +29,7 @@ mod exports;
|
|
|
29
29
|
mod fetch_column_stats;
|
|
30
30
|
mod get_viewer_config;
|
|
31
31
|
mod is_invalid_drop;
|
|
32
|
+
mod palette_set;
|
|
32
33
|
mod plugin_column_styles;
|
|
33
34
|
mod validate_expression;
|
|
34
35
|
|
|
@@ -40,5 +41,6 @@ pub use self::exports::*;
|
|
|
40
41
|
pub use self::fetch_column_stats::*;
|
|
41
42
|
pub use self::get_viewer_config::*;
|
|
42
43
|
pub use self::is_invalid_drop::*;
|
|
44
|
+
pub use self::palette_set::*;
|
|
43
45
|
pub use self::plugin_column_styles::*;
|
|
44
46
|
pub use self::validate_expression::*;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
use std::collections::BTreeMap;
|
|
14
|
+
|
|
15
|
+
use crate::config::{CssKind, CssLiteralUse, NamedValue, assign_palette_names};
|
|
16
|
+
use crate::presentation::Presentation;
|
|
17
|
+
use crate::workspace::{PanelId, Workspace};
|
|
18
|
+
|
|
19
|
+
/// Every CSS literal in use across the workspace, as `(panel id, use)`,
|
|
20
|
+
/// in a deterministic order.
|
|
21
|
+
pub fn styles_in_use(workspace: &Workspace) -> Vec<(PanelId, CssLiteralUse)> {
|
|
22
|
+
let mut ids = workspace.panel_ids();
|
|
23
|
+
ids.sort_by(|a, b| a.as_str().cmp(b.as_str()));
|
|
24
|
+
let mut out = vec![];
|
|
25
|
+
for id in ids {
|
|
26
|
+
let Some(panel) = workspace.panel(&id) else {
|
|
27
|
+
continue;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let uses = {
|
|
31
|
+
let view_config = panel.session.get_view_config();
|
|
32
|
+
panel
|
|
33
|
+
.renderer
|
|
34
|
+
.css_literals_in_use(&view_config, &panel.session)
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
out.extend(uses.into_iter().map(|x| (id.clone(), x)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
out
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// The workspace palette set: the restored palette ∪ every literal in
|
|
44
|
+
/// use, named stably (see [`crate::config::assign_palette_names`]).
|
|
45
|
+
pub fn palette_set(workspace: &Workspace, presentation: &Presentation) -> BTreeMap<String, String> {
|
|
46
|
+
let host: Vec<NamedValue> = CssKind::ALL
|
|
47
|
+
.into_iter()
|
|
48
|
+
.flat_map(|kind| presentation.host_named_values(kind))
|
|
49
|
+
.collect();
|
|
50
|
+
|
|
51
|
+
let in_use: Vec<(CssKind, String)> = styles_in_use(workspace)
|
|
52
|
+
.into_iter()
|
|
53
|
+
.map(|(_, x)| (x.kind, x.literal))
|
|
54
|
+
.collect();
|
|
55
|
+
|
|
56
|
+
assign_palette_names(&presentation.palette(), &host, &in_use, &|name| {
|
|
57
|
+
presentation.resolve_css_var(name).is_some()
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// The named values of `kind` a picker offers: the palette set's
|
|
62
|
+
/// entries, then any theme/page-authored host entries not already in
|
|
63
|
+
/// the set by name.
|
|
64
|
+
pub fn named_values(
|
|
65
|
+
workspace: &Workspace,
|
|
66
|
+
presentation: &Presentation,
|
|
67
|
+
kind: CssKind,
|
|
68
|
+
) -> Vec<NamedValue> {
|
|
69
|
+
let mut out: Vec<NamedValue> = palette_set(workspace, presentation)
|
|
70
|
+
.into_iter()
|
|
71
|
+
.filter(|(name, _)| name.starts_with(kind.var_prefix()))
|
|
72
|
+
.map(|(name, value)| NamedValue { name, value })
|
|
73
|
+
.collect();
|
|
74
|
+
|
|
75
|
+
for entry in presentation.host_named_values(kind) {
|
|
76
|
+
if !out.iter().any(|x| x.name == entry.name) {
|
|
77
|
+
out.push(entry);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
out
|
|
82
|
+
}
|
|
@@ -41,7 +41,9 @@ pub fn get_plugin_config_schema(
|
|
|
41
41
|
let view_config_js =
|
|
42
42
|
wasm_bindgen::JsValue::from_serde_ext(view_config).unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
43
43
|
let raw = plugin._plugin_config_schema(&view_config_js)?;
|
|
44
|
-
serde_wasm_bindgen::from_value(raw)
|
|
44
|
+
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
45
|
+
.map(|schema| schema.canonicalize_defaults())
|
|
46
|
+
.map_err(|e| e.into())
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/// Queries the active plugin for the per-column [`ColumnConfigSchema`].
|
|
@@ -97,5 +99,7 @@ pub fn get_column_config_schema(
|
|
|
97
99
|
&stats_js,
|
|
98
100
|
)?;
|
|
99
101
|
|
|
100
|
-
serde_wasm_bindgen::from_value(raw)
|
|
102
|
+
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
103
|
+
.map(|schema| schema.canonicalize_defaults())
|
|
104
|
+
.map_err(|e| e.into())
|
|
101
105
|
}
|
|
@@ -49,7 +49,7 @@ impl Renderer {
|
|
|
49
49
|
let active_plugin = self.active_plugin();
|
|
50
50
|
if let Some(plugin) = plugin.or(active_plugin.as_ref()) {
|
|
51
51
|
let theme_elem = plugin.unchecked_ref::<HtmlElement>();
|
|
52
|
-
match self.
|
|
52
|
+
match self.theme() {
|
|
53
53
|
Some(theme)
|
|
54
54
|
if theme_elem.get_attribute("theme").as_deref() != Some(theme.as_str()) =>
|
|
55
55
|
{
|
|
@@ -73,7 +73,7 @@ impl Renderer {
|
|
|
73
73
|
let _pin = self.pin_context(&guard, ctx.clone());
|
|
74
74
|
let plugin = self.ensure_plugin_selected()?;
|
|
75
75
|
let meta = self.metadata();
|
|
76
|
-
let stamped_theme = self.
|
|
76
|
+
let stamped_theme = self.theme();
|
|
77
77
|
self.stamp_theme(Some(&plugin));
|
|
78
78
|
self.stamp_active(&plugin);
|
|
79
79
|
plugin.restyle();
|
|
@@ -134,7 +134,19 @@ impl Renderer {
|
|
|
134
134
|
|
|
135
135
|
pub async fn resize(&self) -> ApiResult<()> {
|
|
136
136
|
self.0.geometry_cmd.set(Some(GeometryCmd::Measure));
|
|
137
|
-
|
|
137
|
+
|
|
138
|
+
// This caller queued a `Measure`, but the parked run it resolves
|
|
139
|
+
// through executes the LATEST `geometry_cmd` — which a concurrent
|
|
140
|
+
// presize sweep may have swapped to a `Presize` whose sweep
|
|
141
|
+
// coalesced (and so received `None`). Any present closure returned
|
|
142
|
+
// here is therefore held by no one else: invoke it, or the
|
|
143
|
+
// transport's present-hold leaks and the plot freezes on its
|
|
144
|
+
// last-blitted frame.
|
|
145
|
+
if let Some(present) = self.geometry_task().await? {
|
|
146
|
+
present.call0(&JsValue::NULL)?;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Ok(())
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
/// Public one-shot resize to explicit dimensions (`viewer.resize({
|
|
@@ -416,7 +428,7 @@ impl Renderer {
|
|
|
416
428
|
let viewer_elem = self.0.borrow().viewer_elem.clone();
|
|
417
429
|
let slot = self.slot_name();
|
|
418
430
|
let first_paint = !self.0.has_drawn.get();
|
|
419
|
-
let stamped_theme = self.
|
|
431
|
+
let stamped_theme = self.theme();
|
|
420
432
|
self.stamp_active(&plugin);
|
|
421
433
|
self.stamp_theme(Some(&plugin));
|
|
422
434
|
if self.needs_restyle() {
|
|
@@ -18,7 +18,7 @@ use std::collections::HashMap;
|
|
|
18
18
|
|
|
19
19
|
use futures::future::join_all;
|
|
20
20
|
use perspective_client::config::ViewConfig;
|
|
21
|
-
use perspective_js::utils::{ApiResult, JsValueSerdeExt};
|
|
21
|
+
use perspective_js::utils::{ApiError, ApiResult, JsValueSerdeExt};
|
|
22
22
|
use serde_json::Value;
|
|
23
23
|
use wasm_bindgen::prelude::*;
|
|
24
24
|
|
|
@@ -63,6 +63,50 @@ impl Renderer {
|
|
|
63
63
|
.unwrap_or_default()
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
fn resolve_css_refs_in(&self, column: &str, entry: &mut serde_json::Map<String, Value>) {
|
|
67
|
+
let host = self.borrow().viewer_elem.clone();
|
|
68
|
+
let lookup = |name: &str| crate::utils::read_custom_property(&host, name);
|
|
69
|
+
for (key, name) in resolve_css_refs(entry, &lookup) {
|
|
70
|
+
tracing::warn!(
|
|
71
|
+
"Dropping `columns_config[\"{column}\"].{key}`: `var({name})` is undefined or not \
|
|
72
|
+
a valid value of its kind"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Every CSS literal stored in the active plugin's per-column bucket
|
|
78
|
+
/// with its schema kind, sorted by `(column, key)`.
|
|
79
|
+
pub fn css_literals_in_use(
|
|
80
|
+
&self,
|
|
81
|
+
view_config: &ViewConfig,
|
|
82
|
+
session: &Session,
|
|
83
|
+
) -> Vec<CssLiteralUse> {
|
|
84
|
+
let mut out = vec![];
|
|
85
|
+
for (column, entry) in self.all_columns_configs() {
|
|
86
|
+
let Ok(schema) =
|
|
87
|
+
self.query_column_config_schema(view_config, session, &column, Some(&entry))
|
|
88
|
+
else {
|
|
89
|
+
continue;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
for (key, value) in &entry {
|
|
93
|
+
if let (Some(kind), Some(literal)) = (schema.css_kind_of(key), value.as_str())
|
|
94
|
+
&& parse_var_ref(literal).is_none()
|
|
95
|
+
{
|
|
96
|
+
out.push(CssLiteralUse {
|
|
97
|
+
column: column.clone(),
|
|
98
|
+
key: key.clone(),
|
|
99
|
+
kind,
|
|
100
|
+
literal: literal.to_owned(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
out.sort_by(|a, b| (&a.column, &a.key).cmp(&(&b.column, &b.key)));
|
|
107
|
+
out
|
|
108
|
+
}
|
|
109
|
+
|
|
66
110
|
/// Restore-prep snapshot: like [`Self::all_columns_configs`], but
|
|
67
111
|
/// for each column also materializes any `ControlSpec::Number`
|
|
68
112
|
/// fields the schema declares with `include: true` that aren't
|
|
@@ -173,15 +217,15 @@ impl Renderer {
|
|
|
173
217
|
.cloned()
|
|
174
218
|
}
|
|
175
219
|
|
|
176
|
-
/// Wholesale update the active plugin's per-column config map
|
|
220
|
+
/// Wholesale update the active plugin's per-column config map.
|
|
177
221
|
pub fn update_columns_configs(
|
|
178
222
|
&self,
|
|
179
223
|
view_config: &ViewConfig,
|
|
180
224
|
session: &Session,
|
|
181
225
|
update: ColumnConfigUpdate,
|
|
182
|
-
) -> bool {
|
|
226
|
+
) -> ApiResult<bool> {
|
|
183
227
|
let Some(n) = self.active_plugin_name() else {
|
|
184
|
-
return false;
|
|
228
|
+
return Ok(false);
|
|
185
229
|
};
|
|
186
230
|
|
|
187
231
|
match update {
|
|
@@ -190,24 +234,31 @@ impl Renderer {
|
|
|
190
234
|
let bucket = st.plugin_states.entry(n).or_default();
|
|
191
235
|
let was_nonempty = !bucket.columns.is_empty();
|
|
192
236
|
bucket.columns.clear();
|
|
193
|
-
was_nonempty
|
|
237
|
+
Ok(was_nonempty)
|
|
194
238
|
},
|
|
195
|
-
OptionalUpdate::Missing => false,
|
|
239
|
+
OptionalUpdate::Missing => Ok(false),
|
|
196
240
|
OptionalUpdate::Update(map) => {
|
|
197
|
-
let stripped: Vec<(String, serde_json::Map<String, serde_json::Value>)> =
|
|
198
|
-
.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
241
|
+
let mut stripped: Vec<(String, serde_json::Map<String, serde_json::Value>)> =
|
|
242
|
+
Vec::with_capacity(map.len());
|
|
243
|
+
for (col, mut cfg) in map {
|
|
244
|
+
if let Ok(schema) =
|
|
245
|
+
self.query_column_config_schema(view_config, session, &col, Some(&cfg))
|
|
246
|
+
{
|
|
247
|
+
let active = schema.active_keys();
|
|
248
|
+
cfg.retain(|k, _| active.contains(k));
|
|
249
|
+
let errors = normalize_css_values(&schema, &mut cfg);
|
|
250
|
+
if let Some((key, error)) = errors.first() {
|
|
251
|
+
return Err(ApiError::from(JsValue::from_str(&format!(
|
|
252
|
+
"Invalid `columns_config[\"{col}\"].{key}`: {error}"
|
|
253
|
+
))));
|
|
206
254
|
}
|
|
207
255
|
|
|
208
|
-
(col, cfg)
|
|
209
|
-
|
|
210
|
-
|
|
256
|
+
self.resolve_css_refs_in(&col, &mut cfg);
|
|
257
|
+
strip_default_values(&schema, &mut cfg);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
stripped.push((col, cfg));
|
|
261
|
+
}
|
|
211
262
|
|
|
212
263
|
let mut st = self.borrow_mut();
|
|
213
264
|
let bucket = st.plugin_states.entry(n).or_default();
|
|
@@ -226,7 +277,7 @@ impl Renderer {
|
|
|
226
277
|
}
|
|
227
278
|
}
|
|
228
279
|
|
|
229
|
-
changed
|
|
280
|
+
Ok(changed)
|
|
230
281
|
},
|
|
231
282
|
}
|
|
232
283
|
}
|
|
@@ -253,22 +304,45 @@ impl Renderer {
|
|
|
253
304
|
&column_name,
|
|
254
305
|
current_value.as_ref(),
|
|
255
306
|
) {
|
|
307
|
+
for (key, error) in normalize_css_values(&schema, &mut update.value) {
|
|
308
|
+
tracing::error!("Dropping `{column_name}`.`{key}`: {error}");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
self.resolve_css_refs_in(&column_name, &mut update.value);
|
|
256
312
|
strip_default_values(&schema, &mut update.value);
|
|
257
313
|
}
|
|
258
314
|
|
|
315
|
+
let next = {
|
|
316
|
+
let mut st = self.borrow_mut();
|
|
317
|
+
let bucket = st.plugin_states.entry(n.clone()).or_default();
|
|
318
|
+
let entry = bucket.columns.entry(column_name.clone()).or_default();
|
|
319
|
+
for k in &update.keys {
|
|
320
|
+
entry.remove(k);
|
|
321
|
+
}
|
|
322
|
+
for (k, v) in update.value {
|
|
323
|
+
if update.keys.contains(&k) {
|
|
324
|
+
entry.insert(k, v);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
entry.clone()
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
let active = self
|
|
332
|
+
.query_column_config_schema(view_config, session, &column_name, Some(&next))
|
|
333
|
+
.ok()
|
|
334
|
+
.map(|schema| schema.active_keys());
|
|
335
|
+
|
|
259
336
|
let mut st = self.borrow_mut();
|
|
260
337
|
let bucket = st.plugin_states.entry(n).or_default();
|
|
261
|
-
let entry = bucket.columns.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if
|
|
267
|
-
|
|
338
|
+
if let Some(entry) = bucket.columns.get_mut(&column_name) {
|
|
339
|
+
if let Some(active) = &active {
|
|
340
|
+
entry.retain(|k, _| active.contains(k));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if entry.is_empty() {
|
|
344
|
+
bucket.columns.remove(&column_name);
|
|
268
345
|
}
|
|
269
|
-
}
|
|
270
|
-
if entry.is_empty() {
|
|
271
|
-
bucket.columns.remove(&column_name);
|
|
272
346
|
}
|
|
273
347
|
}
|
|
274
348
|
|
|
@@ -305,7 +379,9 @@ impl Renderer {
|
|
|
305
379
|
let plugin = self.ensure_plugin_selected()?;
|
|
306
380
|
let view_config_js = JsValue::from_serde_ext(view_config).unwrap_or(JsValue::NULL);
|
|
307
381
|
let raw = plugin._plugin_config_schema(&view_config_js)?;
|
|
308
|
-
serde_wasm_bindgen::from_value(raw)
|
|
382
|
+
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
383
|
+
.map(|schema| schema.canonicalize_defaults())
|
|
384
|
+
.map_err(|e| e.into())
|
|
309
385
|
}
|
|
310
386
|
|
|
311
387
|
/// Per-column counterpart of [`query_plugin_config_schema`]. Used by
|
|
@@ -352,7 +428,9 @@ impl Renderer {
|
|
|
352
428
|
&stats_js,
|
|
353
429
|
)?;
|
|
354
430
|
|
|
355
|
-
serde_wasm_bindgen::from_value(raw)
|
|
431
|
+
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
432
|
+
.map(|schema| schema.canonicalize_defaults())
|
|
433
|
+
.map_err(|e| e.into())
|
|
356
434
|
}
|
|
357
435
|
|
|
358
436
|
/// Wholesale update the active plugin's plugin-level config map.
|
|
@@ -365,9 +443,9 @@ impl Renderer {
|
|
|
365
443
|
&self,
|
|
366
444
|
view_config: &ViewConfig,
|
|
367
445
|
update: PluginConfigUpdate,
|
|
368
|
-
) -> bool {
|
|
446
|
+
) -> ApiResult<bool> {
|
|
369
447
|
let Some(n) = self.active_plugin_name() else {
|
|
370
|
-
return false;
|
|
448
|
+
return Ok(false);
|
|
371
449
|
};
|
|
372
450
|
|
|
373
451
|
let schema = self.query_plugin_config_schema(view_config).ok();
|
|
@@ -377,14 +455,21 @@ impl Renderer {
|
|
|
377
455
|
OptionalUpdate::SetDefault => {
|
|
378
456
|
let changed = !bucket.plugin.is_empty();
|
|
379
457
|
bucket.plugin.clear();
|
|
380
|
-
changed
|
|
458
|
+
Ok(changed)
|
|
381
459
|
},
|
|
382
|
-
OptionalUpdate::Missing => false,
|
|
460
|
+
OptionalUpdate::Missing => Ok(false),
|
|
383
461
|
OptionalUpdate::Update(mut map) => {
|
|
384
462
|
let mut changed = false;
|
|
385
463
|
if let Some(s) = &schema {
|
|
386
464
|
let active = s.active_keys();
|
|
387
465
|
map.retain(|k, _| active.contains(k));
|
|
466
|
+
let errors = normalize_css_values(s, &mut map);
|
|
467
|
+
if let Some((key, error)) = errors.first() {
|
|
468
|
+
return Err(ApiError::from(JsValue::from_str(&format!(
|
|
469
|
+
"Invalid `plugin_config.{key}`: {error}"
|
|
470
|
+
))));
|
|
471
|
+
}
|
|
472
|
+
|
|
388
473
|
map.retain(|key, value| {
|
|
389
474
|
let is_default = s
|
|
390
475
|
.fields
|
|
@@ -408,7 +493,7 @@ impl Renderer {
|
|
|
408
493
|
}
|
|
409
494
|
}
|
|
410
495
|
|
|
411
|
-
changed
|
|
496
|
+
Ok(changed)
|
|
412
497
|
},
|
|
413
498
|
}
|
|
414
499
|
}
|
|
@@ -424,6 +509,10 @@ impl Renderer {
|
|
|
424
509
|
};
|
|
425
510
|
|
|
426
511
|
if let Ok(schema) = self.query_plugin_config_schema(view_config) {
|
|
512
|
+
for (key, error) in normalize_css_values(&schema, &mut update.value) {
|
|
513
|
+
tracing::error!("Dropping `plugin_config`.`{key}`: {error}");
|
|
514
|
+
}
|
|
515
|
+
|
|
427
516
|
strip_default_values(&schema, &mut update.value);
|
|
428
517
|
}
|
|
429
518
|
|
|
@@ -446,6 +535,34 @@ impl Renderer {
|
|
|
446
535
|
}
|
|
447
536
|
}
|
|
448
537
|
|
|
538
|
+
fn normalize_css_values(
|
|
539
|
+
schema: &ColumnConfigSchema,
|
|
540
|
+
map: &mut serde_json::Map<String, serde_json::Value>,
|
|
541
|
+
) -> Vec<(String, String)> {
|
|
542
|
+
let mut errors = vec![];
|
|
543
|
+
for (key, value) in map.iter_mut() {
|
|
544
|
+
let Some(kind) = schema.css_kind_of(key) else {
|
|
545
|
+
continue;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
let canonical = match value.as_str() {
|
|
549
|
+
Some(src) => kind.canonicalize(src),
|
|
550
|
+
None => Err(format!("expected a CSS string, got `{value}`")),
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
match canonical {
|
|
554
|
+
Ok(canonical) => *value = Value::String(canonical),
|
|
555
|
+
Err(error) => errors.push((key.clone(), error)),
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
for (key, _) in &errors {
|
|
560
|
+
map.remove(key);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
errors
|
|
564
|
+
}
|
|
565
|
+
|
|
449
566
|
fn strip_default_values(
|
|
450
567
|
schema: &ColumnConfigSchema,
|
|
451
568
|
map: &mut serde_json::Map<String, serde_json::Value>,
|
|
@@ -479,17 +596,162 @@ fn matches_declared_default(spec: &ControlSpec, key: &str, value: &Value) -> boo
|
|
|
479
596
|
} if k == key => value.as_str() == Some(default.as_str()),
|
|
480
597
|
ControlSpec::Color {
|
|
481
598
|
key: k, default, ..
|
|
482
|
-
} if k == key =>
|
|
483
|
-
ControlSpec::
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
key_neg,
|
|
490
|
-
default_neg,
|
|
491
|
-
..
|
|
492
|
-
} if key_neg == key => value.as_str() == Some(default_neg.as_str()),
|
|
599
|
+
} if k == key => css_matches(CssKind::Color, value, default),
|
|
600
|
+
ControlSpec::Palette {
|
|
601
|
+
key: k, default, ..
|
|
602
|
+
} if k == key => css_matches(CssKind::Palette, value, default),
|
|
603
|
+
ControlSpec::GradientStops {
|
|
604
|
+
key: k, default, ..
|
|
605
|
+
} if k == key => css_matches(CssKind::Gradient, value, default),
|
|
493
606
|
_ => false,
|
|
494
607
|
}
|
|
495
608
|
}
|
|
609
|
+
|
|
610
|
+
fn css_matches(kind: CssKind, value: &Value, default: &str) -> bool {
|
|
611
|
+
value
|
|
612
|
+
.as_str()
|
|
613
|
+
.and_then(|src| kind.canonicalize(src).ok())
|
|
614
|
+
.is_some_and(|canonical| canonical == default)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
#[cfg(test)]
|
|
618
|
+
mod tests {
|
|
619
|
+
use serde_json::json;
|
|
620
|
+
|
|
621
|
+
use super::*;
|
|
622
|
+
|
|
623
|
+
#[test]
|
|
624
|
+
fn palette_default_matches_canonically() {
|
|
625
|
+
let spec = ControlSpec::Palette {
|
|
626
|
+
key: "palette".to_owned(),
|
|
627
|
+
default: "linear-gradient(to right, #0366d6, #ff7f0e)".to_owned(),
|
|
628
|
+
max: None,
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
assert!(matches_declared_default(
|
|
632
|
+
&spec,
|
|
633
|
+
"palette",
|
|
634
|
+
&json!("linear-gradient(to right, #0366d6, #ff7f0e)")
|
|
635
|
+
));
|
|
636
|
+
|
|
637
|
+
assert!(matches_declared_default(
|
|
638
|
+
&spec,
|
|
639
|
+
"palette",
|
|
640
|
+
&json!("linear-gradient(90deg, RGB(3,102,214), #FF7F0E)")
|
|
641
|
+
));
|
|
642
|
+
|
|
643
|
+
assert!(!matches_declared_default(
|
|
644
|
+
&spec,
|
|
645
|
+
"palette",
|
|
646
|
+
&json!("linear-gradient(to right, #ff7f0e, #0366d6)")
|
|
647
|
+
));
|
|
648
|
+
|
|
649
|
+
assert!(!matches_declared_default(
|
|
650
|
+
&spec,
|
|
651
|
+
"palette",
|
|
652
|
+
&json!("var(--psp-user--palette-1)")
|
|
653
|
+
));
|
|
654
|
+
|
|
655
|
+
assert!(!matches_declared_default(
|
|
656
|
+
&spec,
|
|
657
|
+
"palette",
|
|
658
|
+
&json!(["#0366d6"])
|
|
659
|
+
));
|
|
660
|
+
assert!(!matches_declared_default(
|
|
661
|
+
&spec,
|
|
662
|
+
"other",
|
|
663
|
+
&json!("linear-gradient(to right, #0366d6, #ff7f0e)")
|
|
664
|
+
));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
#[test]
|
|
668
|
+
fn gradient_default_matches_canonical_values() {
|
|
669
|
+
let spec = ControlSpec::GradientStops {
|
|
670
|
+
key: "gradient".to_owned(),
|
|
671
|
+
default: "linear-gradient(to right, #0366d6 0%, #ff7f0e 33.3%)".to_owned(),
|
|
672
|
+
discrete: false,
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
assert!(matches_declared_default(
|
|
676
|
+
&spec,
|
|
677
|
+
"gradient",
|
|
678
|
+
&json!("linear-gradient(#0366d6, #ff7f0e 33.3%)")
|
|
679
|
+
));
|
|
680
|
+
|
|
681
|
+
assert!(!matches_declared_default(
|
|
682
|
+
&spec,
|
|
683
|
+
"gradient",
|
|
684
|
+
&json!("linear-gradient(to right, #0366d6 0%, #ff7f0e 33.4%)")
|
|
685
|
+
));
|
|
686
|
+
|
|
687
|
+
let color = ControlSpec::Color {
|
|
688
|
+
key: "color".to_owned(),
|
|
689
|
+
default: "#ff7f0e".to_owned(),
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
assert!(matches_declared_default(&color, "color", &json!("#FF7F0E")));
|
|
693
|
+
assert!(matches_declared_default(
|
|
694
|
+
&color,
|
|
695
|
+
"color",
|
|
696
|
+
&json!("rgb(255, 127, 14)")
|
|
697
|
+
));
|
|
698
|
+
assert!(!matches_declared_default(
|
|
699
|
+
&color,
|
|
700
|
+
"color",
|
|
701
|
+
&json!("var(--psp-user--color-1)")
|
|
702
|
+
));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
#[test]
|
|
706
|
+
fn normalize_css_values_canonicalizes_and_reports() {
|
|
707
|
+
let schema = ColumnConfigSchema {
|
|
708
|
+
fields: vec![
|
|
709
|
+
ControlSpec::Color {
|
|
710
|
+
key: "color".to_owned(),
|
|
711
|
+
default: "#000000".to_owned(),
|
|
712
|
+
},
|
|
713
|
+
ControlSpec::Palette {
|
|
714
|
+
key: "palette".to_owned(),
|
|
715
|
+
default: "linear-gradient(to right, #000000)".to_owned(),
|
|
716
|
+
max: None,
|
|
717
|
+
},
|
|
718
|
+
ControlSpec::GradientStops {
|
|
719
|
+
key: "gradient".to_owned(),
|
|
720
|
+
default: "linear-gradient(to right, #000000 0%, #ffffff 100%)".to_owned(),
|
|
721
|
+
discrete: false,
|
|
722
|
+
},
|
|
723
|
+
ControlSpec::Bool {
|
|
724
|
+
key: "flag".to_owned(),
|
|
725
|
+
default: false,
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
let mut map = json!({
|
|
731
|
+
"color": "RGB(255,0,0)",
|
|
732
|
+
"palette": "var(--psp-user--palette-warm)",
|
|
733
|
+
"gradient": [{ "color": "#000000", "offset": 0 }],
|
|
734
|
+
"flag": true,
|
|
735
|
+
"foreign": "linear-gradient(red, blue)",
|
|
736
|
+
})
|
|
737
|
+
.as_object()
|
|
738
|
+
.unwrap()
|
|
739
|
+
.clone();
|
|
740
|
+
|
|
741
|
+
let errors = normalize_css_values(&schema, &mut map);
|
|
742
|
+
assert_eq!(errors.len(), 1);
|
|
743
|
+
assert_eq!(errors[0].0, "gradient");
|
|
744
|
+
assert_eq!(
|
|
745
|
+
map,
|
|
746
|
+
json!({
|
|
747
|
+
"color": "#ff0000",
|
|
748
|
+
"palette": "var(--psp-user--palette-warm)",
|
|
749
|
+
"flag": true,
|
|
750
|
+
"foreign": "linear-gradient(red, blue)",
|
|
751
|
+
})
|
|
752
|
+
.as_object()
|
|
753
|
+
.unwrap()
|
|
754
|
+
.clone()
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
}
|