@perspective-dev/viewer 5.3.0 → 5.4.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 +8 -8
- package/dist/cdn/perspective-viewer.js.map +3 -3
- package/dist/css/blueprint.css +1 -0
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -0
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/ar.css +1 -0
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/ledger.css +1 -0
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -0
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/css/velvet.css +1 -0
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/column-format.d.ts +16 -3
- package/dist/esm/extensions.d.ts +0 -5
- package/dist/esm/perspective-viewer.inline.js +7 -7
- package/dist/esm/perspective-viewer.inline.js.map +3 -3
- package/dist/esm/perspective-viewer.js +8 -8
- package/dist/esm/perspective-viewer.js.map +3 -3
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
- package/dist/wasm/perspective-viewer.d.ts +62 -73
- package/dist/wasm/perspective-viewer.js +230 -238
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +18 -28
- package/package.json +7 -7
- package/src/css/_context_menu.css +1 -1
- package/src/css/_dropdown_menu.css +1 -1
- package/src/css/_viewer_bundle.css +7 -8
- package/src/css/column-dropdown.css +0 -3
- package/src/css/column-selector.css +8 -5
- package/src/css/column-settings-panel.css +0 -206
- package/src/css/column-style.css +95 -0
- package/src/css/config-selector.css +18 -10
- package/src/css/filter-dropdown.css +5 -5
- package/src/css/form/debug.css +28 -4
- package/src/css/function-dropdown.css +5 -5
- package/src/css/panel-tab.css +23 -0
- package/src/css/plugin-selector.css +6 -5
- package/src/css/render-warning.css +1 -1
- package/src/css/status-bar.css +52 -21
- package/src/css/type-icon.css +1 -3
- package/src/css/{containers → ui/containers}/context-menu.css +22 -16
- package/src/css/ui/containers/dropdown-menu-items.css +66 -0
- package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
- package/src/css/{containers → ui/containers}/tabs.css +13 -3
- package/src/css/{dom → ui/dom}/select.css +19 -8
- package/src/css/viewer.css +32 -31
- package/src/rust/agent/tools.rs +5 -2
- package/src/rust/components/chat_panel.rs +1 -1
- package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
- package/src/rust/components/column_dropdown.rs +2 -2
- package/src/rust/components/column_selector/active_column.rs +15 -9
- package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
- package/src/rust/components/column_selector/config_selector.rs +2 -2
- package/src/rust/components/column_selector/filter_column.rs +8 -5
- package/src/rust/components/column_selector/inactive_column.rs +4 -2
- package/src/rust/components/column_selector/pivot_column.rs +4 -3
- package/src/rust/components/column_selector/sort_column.rs +7 -4
- package/src/rust/components/column_selector.rs +3 -3
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +15 -17
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab.rs +296 -238
- package/src/rust/components/column_settings_sidebar.rs +3 -4
- package/src/rust/components/copy_dropdown.rs +1 -1
- package/src/rust/components/datetime_column_style/custom.rs +1 -3
- package/src/rust/components/datetime_column_style/simple.rs +16 -6
- package/src/rust/components/datetime_column_style.rs +51 -12
- package/src/rust/components/{form/debug.rs → debug_panel.rs} +5 -5
- package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
- package/src/rust/components/export_dropdown.rs +1 -1
- package/src/rust/components/expression_editor.rs +1 -1
- package/src/rust/components/filter_dropdown.rs +2 -2
- package/src/rust/components/function_dropdown.rs +2 -2
- package/src/rust/components/main_panel/frame_theme.rs +3 -59
- package/src/rust/components/main_panel/reconcile.rs +9 -4
- package/src/rust/components/main_panel/render.rs +62 -11
- package/src/rust/components/main_panel/update.rs +39 -10
- package/src/rust/components/main_panel.rs +26 -2
- package/src/rust/components/mod.rs +5 -7
- package/src/rust/components/new_panel_menu.rs +97 -0
- package/src/rust/components/number_series_style.rs +2 -3
- package/src/rust/components/panel_menu.rs +61 -87
- package/src/rust/components/panel_tab.rs +100 -43
- package/src/rust/components/plugin_tab.rs +125 -87
- package/src/rust/components/render_warning.rs +2 -2
- package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
- package/src/rust/components/settings_panel.rs +3 -2
- package/src/rust/components/status_bar.rs +113 -20
- package/src/rust/components/status_indicator.rs +5 -14
- package/src/rust/components/string_column_style.rs +1 -2
- package/src/rust/components/{style/mod.rs → style.rs} +32 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +25 -15
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +7 -3
- package/src/rust/components/style_controls/number_string_format/style_section.rs +2 -1
- package/src/rust/components/style_controls/number_string_format/types.rs +24 -0
- package/src/rust/components/style_controls/number_string_format.rs +37 -14
- package/src/rust/components/viewer/msg.rs +43 -30
- package/src/rust/components/viewer/panels.rs +50 -19
- package/src/rust/components/viewer/render.rs +15 -13
- package/src/rust/components/viewer/settings.rs +137 -68
- package/src/rust/components/viewer/snapshots.rs +5 -10
- package/src/rust/components/viewer/wiring.rs +1 -21
- package/src/rust/components/viewer.rs +15 -10
- package/src/rust/components/window_editor.rs +8 -9
- package/src/rust/config/column_config_schema.rs +358 -52
- package/src/rust/config/datetime_column_style.rs +4 -21
- package/src/rust/config/mod.rs +0 -2
- package/src/rust/config/number_string_format.rs +211 -18
- package/src/rust/config/workspace_config.rs +2 -1
- package/src/rust/custom_elements/debug_plugin.rs +1 -10
- package/src/rust/custom_elements/mod.rs +0 -3
- package/src/rust/custom_elements/viewer.rs +0 -18
- package/src/rust/js/plugin.rs +0 -86
- package/src/rust/lib.rs +12 -15
- package/src/rust/presentation/column_locator.rs +3 -7
- package/src/rust/presentation.rs +85 -38
- package/src/rust/queries/hosted_tables.rs +38 -0
- package/src/rust/queries/mod.rs +2 -0
- package/src/rust/queries/palette_set.rs +1 -1
- package/src/rust/queries/plugin_column_styles.rs +2 -2
- package/src/rust/renderer/activate.rs +1 -3
- package/src/rust/renderer/dispatch.rs +117 -30
- package/src/rust/renderer/limits.rs +24 -5
- package/src/rust/renderer/plugin_config.rs +56 -8
- package/src/rust/renderer/props.rs +2 -1
- package/src/rust/renderer.rs +14 -27
- package/src/rust/session/props.rs +4 -3
- package/src/rust/session/view_subscription.rs +19 -6
- package/src/rust/session.rs +38 -70
- package/src/rust/tasks/create_panel.rs +1 -1
- package/src/rust/tasks/dismiss_render_warning.rs +1 -1
- package/src/rust/tasks/eject.rs +1 -0
- package/src/rust/tasks/pipeline.rs +0 -60
- package/src/rust/tasks/presize_panels.rs +24 -1
- package/src/rust/tasks/restore_and_render.rs +0 -17
- package/src/rust/tasks/send_column_config.rs +1 -1
- package/src/rust/tasks/send_plugin_config.rs +1 -1
- package/src/rust/tasks/set_edit_mode.rs +1 -1
- package/src/rust/ui/containers/control_group.rs +113 -0
- package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
- package/src/rust/{components → ui}/containers/mod.rs +1 -4
- package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
- package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
- package/src/rust/{components → ui}/containers/select.rs +8 -8
- package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
- package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
- package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
- package/src/rust/{components → ui}/context_menu.rs +8 -13
- package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
- package/src/rust/{components → ui}/font_loader.rs +0 -26
- package/src/rust/{components → ui}/form/color_selector.rs +4 -2
- package/src/rust/ui/form/intl_label.rs +81 -0
- package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
- package/src/rust/{components → ui}/form/mod.rs +1 -10
- package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
- package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
- package/src/rust/{components → ui}/form/number_field.rs +1 -1
- package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
- package/src/rust/{components → ui}/form/optional_field.rs +3 -1
- package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
- package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
- package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
- package/src/rust/ui/mod.rs +56 -0
- package/src/rust/ui/modal.rs +61 -0
- package/src/rust/{components → ui}/portal.rs +174 -79
- package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
- package/src/rust/utils/completion.rs +0 -6
- package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
- package/src/rust/utils/mod.rs +2 -0
- package/src/rust/workspace.rs +35 -0
- package/src/svg/boolean-type.svg +3 -2
- package/src/svg/checkbox-checked-icon.svg +4 -7
- package/src/svg/checkbox-hover.svg +3 -3
- package/src/svg/checkbox-off.svg +2 -3
- package/src/svg/checkbox-on.svg +3 -3
- package/src/svg/checkbox-unchecked-icon.svg +3 -3
- package/src/svg/datagrid-select-column.svg +8 -15
- package/src/svg/datagrid-select-region.svg +8 -15
- package/src/svg/datagrid-select-row-tree.svg +7 -12
- package/src/svg/datagrid-select-row.svg +8 -13
- package/src/svg/date-type.svg +4 -23
- package/src/svg/drawer-tab-hover.svg +4 -6
- package/src/svg/drawer-tab-invert-hover.svg +5 -5
- package/src/svg/drawer-tab-invert.svg +3 -5
- package/src/svg/drawer-tab.svg +5 -5
- package/src/svg/mega-menu-icons-candlestick.svg +5 -37
- package/src/svg/mega-menu-icons-ohlc.svg +4 -39
- package/src/svg/mega-menu-icons-sunburst.svg +4 -38
- package/src/svg/mega-menu-icons-treemap.svg +3 -43
- package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
- package/src/svg/new-panel-icon.svg +4 -0
- package/src/svg/number-type.svg +3 -3
- package/src/svg/radio-hover.svg +3 -3
- package/src/svg/radio-off.svg +3 -3
- package/src/svg/radio-on.svg +3 -3
- package/src/svg/status_ok.svg +3 -26
- package/src/svg/string-type.svg +4 -8
- package/src/svg/theme-icon.svg +5 -5
- package/src/themes/blueprint.css +133 -0
- package/src/themes/botanical.css +3 -52
- package/src/themes/defaults.css +3 -144
- package/src/themes/dracula.css +1 -71
- package/src/themes/eggplant.css +131 -0
- package/src/themes/gruvbox-dark.css +2 -79
- package/src/themes/gruvbox.css +2 -40
- package/src/themes/icons.css +2 -10
- package/src/themes/intl/ar.css +185 -0
- package/src/themes/intl/de.css +65 -33
- package/src/themes/intl/es.css +60 -28
- package/src/themes/intl/fr.css +60 -28
- package/src/themes/intl/ja.css +62 -30
- package/src/themes/intl/pt.css +58 -26
- package/src/themes/intl/zh.css +68 -36
- package/src/themes/intl.css +42 -7
- package/src/themes/ledger.css +137 -0
- package/src/themes/monokai.css +1 -64
- package/src/themes/nord.css +131 -0
- package/src/themes/phosphor.css +1 -61
- package/src/themes/pro-dark.css +1 -52
- package/src/themes/pro.css +98 -0
- package/src/themes/solarized-dark.css +1 -46
- package/src/themes/solarized.css +1 -24
- package/src/themes/themes.css +10 -5
- package/src/themes/vaporwave.css +1 -57
- package/src/themes/velvet.css +132 -0
- package/src/ts/column-format.ts +52 -2
- package/src/ts/extensions.ts +0 -13
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/css/plugin-settings-panel.css +0 -99
- package/src/rust/components/modal.rs +0 -177
- package/src/rust/custom_elements/copy_dropdown.rs +0 -219
- package/src/rust/custom_elements/export_dropdown.rs +0 -216
- package/src/rust/custom_elements/modal.rs +0 -299
- package/src/svg/dropdown-selector-light.svg +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline5.js +0 -0
- /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
- /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
- /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
- /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
- /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
- /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
- /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
- /package/src/rust/{components → ui}/form/number_input.rs +0 -0
|
@@ -280,28 +280,14 @@ pub(super) fn create_active_subscriptions(
|
|
|
280
280
|
|
|
281
281
|
let sub1 = session.table_loaded.add_notify_listener(&cb);
|
|
282
282
|
let sub2 = session.table_unloaded.add_notify_listener(&cb);
|
|
283
|
-
// Snapshot refresh on every RECONCILED commit (SKIP/REUSE/paused
|
|
284
|
-
// included), not just `View` construction.
|
|
285
283
|
let sub3 = session.commit_reconciled.add_notify_listener(&cb);
|
|
286
284
|
let sub4 = session.view_config_changed.add_notify_listener(&cb);
|
|
287
285
|
let sub5 = session.title_changed.add_notify_listener(&cb);
|
|
288
|
-
|
|
289
|
-
// The `StatusIndicator` spinner count. LEVEL-triggered: the payload
|
|
290
|
-
// is the ABSOLUTE in-flight config-run count (RAII-settled by
|
|
291
|
-
// `ConfigRunToken`), which the handler ASSIGNS — replacing the
|
|
292
|
-
// edge-counted `view_config_changed`+1/`view_created`−1 pair, whose
|
|
293
|
-
// pairing the pipeline does not guarantee (stuck-spinner
|
|
294
|
-
// regression — see `UPDATE_COUNT_REGRESSION_PLAN.md`).
|
|
295
286
|
let sub6 = session
|
|
296
287
|
.run_state_changed
|
|
297
288
|
.add_listener(ctx.link().callback(UpdateInFlight));
|
|
298
289
|
|
|
299
|
-
// Stats fetch resolution (populates session.column_stats) triggers
|
|
300
|
-
// a fresh `SessionProps` so `column_stats` reaches downstream
|
|
301
|
-
// components and the StyleTab re-queries the schema with the
|
|
302
|
-
// new value.
|
|
303
290
|
let sub7 = session.column_stats_changed.add_notify_listener(&cb);
|
|
304
|
-
|
|
305
291
|
vec![sub1, sub2, sub3, sub4, sub5, sub6, sub7]
|
|
306
292
|
};
|
|
307
293
|
|
|
@@ -311,12 +297,6 @@ pub(super) fn create_active_subscriptions(
|
|
|
311
297
|
move |_: JsPerspectiveViewerPlugin| UpdateRenderer(Box::new(renderer.to_props(None)))
|
|
312
298
|
});
|
|
313
299
|
|
|
314
|
-
// Re-snapshot RendererProps when the plugin_config bucket
|
|
315
|
-
// changes (in-tab edit via `send_plugin_config`, JSON paste via
|
|
316
|
-
// `restore_and_render`, full clear via `reset_all` with
|
|
317
|
-
// `all=true`). Without this, `RendererProps.plugin_config`
|
|
318
|
-
// would stay frozen at its construct-time value and `PluginTab`
|
|
319
|
-
// would render stale.
|
|
320
300
|
let cb_plugin_config = ctx.link().callback({
|
|
321
301
|
let renderer = renderer.clone();
|
|
322
302
|
move |_: serde_json::Map<String, serde_json::Value>| {
|
|
@@ -413,7 +393,7 @@ pub(super) fn inject_active_callbacks(
|
|
|
413
393
|
let s = session.clone();
|
|
414
394
|
let cb = ctx
|
|
415
395
|
.link()
|
|
416
|
-
.callback(move |_: ()| UpdateSessionStats(s.
|
|
396
|
+
.callback(move |_: ()| UpdateSessionStats(s.has_table_cells(), s.has_table()));
|
|
417
397
|
|
|
418
398
|
*session.on_stats_changed.borrow_mut() = Some(cb);
|
|
419
399
|
}
|
|
@@ -27,6 +27,7 @@ mod settings;
|
|
|
27
27
|
mod snapshots;
|
|
28
28
|
mod wiring;
|
|
29
29
|
|
|
30
|
+
use std::collections::HashMap;
|
|
30
31
|
use std::rc::Rc;
|
|
31
32
|
|
|
32
33
|
use futures::channel::oneshot::Sender;
|
|
@@ -40,13 +41,13 @@ pub use self::msg::PerspectiveViewerMsg;
|
|
|
40
41
|
use self::msg::PerspectiveViewerMsg::*;
|
|
41
42
|
use self::settings::SettingsGeometry;
|
|
42
43
|
use self::wiring::*;
|
|
43
|
-
use super::font_loader::{FontLoaderProps, FontLoaderStatus};
|
|
44
44
|
use crate::presentation::{Presentation, PresentationProps};
|
|
45
45
|
use crate::renderer::{RendererProps, *};
|
|
46
46
|
use crate::session::{SessionProps, *};
|
|
47
47
|
use crate::tasks::*;
|
|
48
|
+
use crate::ui::{FontLoaderProps, FontLoaderStatus};
|
|
48
49
|
use crate::utils::*;
|
|
49
|
-
use crate::workspace::Workspace;
|
|
50
|
+
use crate::workspace::{PanelId, Workspace};
|
|
50
51
|
|
|
51
52
|
#[derive(Clone, Properties)]
|
|
52
53
|
pub struct PerspectiveViewerProps {
|
|
@@ -99,6 +100,10 @@ pub struct PerspectiveViewer {
|
|
|
99
100
|
on_settings_panel_dimensions_reset: Rc<PubSub<()>>,
|
|
100
101
|
settings_open: bool,
|
|
101
102
|
|
|
103
|
+
/// `removePanel()` completions awaiting their panel's `PanelClosed`
|
|
104
|
+
/// (the deferred eject — see `ClosePanel`).
|
|
105
|
+
pending_closes: HashMap<PanelId, Completion>,
|
|
106
|
+
|
|
102
107
|
/// Render snapshot of the `Workspace`-owned global filter set (see
|
|
103
108
|
/// `Workspace::global_filters`); refreshed by `UpdateGlobalFilters` via
|
|
104
109
|
/// the `filters_changed` subscription. The status-bar chips render from
|
|
@@ -210,6 +215,7 @@ impl Component for PerspectiveViewer {
|
|
|
210
215
|
on_resize: Default::default(),
|
|
211
216
|
on_settings_panel_dimensions_reset: Default::default(),
|
|
212
217
|
settings_open: false,
|
|
218
|
+
pending_closes: HashMap::new(),
|
|
213
219
|
global_filters: Vec::new(),
|
|
214
220
|
settings_geometry: Default::default(),
|
|
215
221
|
session_props,
|
|
@@ -305,6 +311,7 @@ impl Component for PerspectiveViewer {
|
|
|
305
311
|
LayoutChanged => self.on_layout_changed(ctx),
|
|
306
312
|
SetActivePanel(id, completion) => self.on_set_active_panel(ctx, id, completion),
|
|
307
313
|
ClosePanel(id, completion) => self.on_close_panel(ctx, id, completion),
|
|
314
|
+
PanelClosed(id) => self.on_panel_closed(ctx, id),
|
|
308
315
|
CommitWorkspaceRestore(id) => self.on_commit_workspace_restore(ctx, id),
|
|
309
316
|
DuplicatePanel(id) => self.on_duplicate_panel(ctx, id),
|
|
310
317
|
NewPanel(id) => self.on_new_panel(ctx, id),
|
|
@@ -325,20 +332,18 @@ impl Component for PerspectiveViewer {
|
|
|
325
332
|
ToggleSettingsComplete(update, resolve) => {
|
|
326
333
|
self.on_toggle_settings_complete(ctx, update, resolve)
|
|
327
334
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
SettingsDividerFinish => self.on_settings_divider_finish(ctx),
|
|
335
|
+
DividerMove(divider, target) => self.on_divider_move(ctx, divider, target),
|
|
336
|
+
DividerPump(divider) => self.on_divider_pump(ctx, divider),
|
|
337
|
+
DividerCommit(divider, target) => self.on_divider_commit(ctx, divider, target),
|
|
338
|
+
DividerFinish(divider) => self.on_divider_finish(ctx, divider),
|
|
333
339
|
SettingsPanelTabChanged(tab) => self.on_settings_panel_tab_changed(tab),
|
|
334
|
-
SettingsPanelAutoWidth(w) => self.on_settings_panel_auto_width(w),
|
|
340
|
+
SettingsPanelAutoWidth(w) => self.on_settings_panel_auto_width(ctx, w),
|
|
335
341
|
OpenColumnSettings {
|
|
336
342
|
target,
|
|
337
343
|
sender,
|
|
338
344
|
toggle,
|
|
339
345
|
} => self.on_open_column_settings(ctx, target, sender, toggle),
|
|
340
|
-
|
|
341
|
-
ColumnSettingsPanelAutoWidth(w) => self.on_column_settings_panel_auto_width(w),
|
|
346
|
+
ColumnSettingsPanelAutoWidth(w) => self.on_column_settings_panel_auto_width(ctx, w),
|
|
342
347
|
ToggleColumnSettingsPin => self.on_toggle_column_settings_pin(ctx),
|
|
343
348
|
ToggleColumnSettingsPinComplete(resolve) => {
|
|
344
349
|
self.on_toggle_column_settings_pin_complete(resolve)
|
|
@@ -20,12 +20,10 @@ use yew::prelude::*;
|
|
|
20
20
|
|
|
21
21
|
use crate::components::column_dropdown::{ColumnDropDownElement, ColumnDropDownPortal};
|
|
22
22
|
use crate::components::column_selector::{ColumnSelectorColumnRow, InPlaceColumn, PivotColumn};
|
|
23
|
-
use crate::components::
|
|
24
|
-
DragContext, DragDropList, DragDropListItemProps,
|
|
25
|
-
};
|
|
26
|
-
use crate::components::containers::select::{Select, SelectItem};
|
|
23
|
+
use crate::components::dragdrop_list::{DragContext, DragDropList, DragDropListItemProps};
|
|
27
24
|
use crate::presentation::Presentation;
|
|
28
25
|
use crate::session::{Session, SessionMetadataRc};
|
|
26
|
+
use crate::ui::{IntlLabel, Select, SelectItem};
|
|
29
27
|
use crate::utils::{AddListener, DragEffect, DragTarget, Subscription};
|
|
30
28
|
|
|
31
29
|
/// The declared capabilities of one window aggregate, for a `source` column
|
|
@@ -479,8 +477,9 @@ impl Component for WindowSlotColumn {
|
|
|
479
477
|
let presentation = ctx.props().presentation.clone();
|
|
480
478
|
let action = ctx.props().action;
|
|
481
479
|
move |event: DragEvent| {
|
|
482
|
-
presentation.set_drag_image(&event)
|
|
483
|
-
|
|
480
|
+
if presentation.set_drag_image(&event) {
|
|
481
|
+
presentation.notify_drag_start(column.clone(), DragEffect::Move(action))
|
|
482
|
+
}
|
|
484
483
|
}
|
|
485
484
|
});
|
|
486
485
|
|
|
@@ -909,7 +908,7 @@ impl Component for WindowEditor {
|
|
|
909
908
|
<div id="window-editor-slots">{ source_list }{ order_list }{ partition_list }</div>
|
|
910
909
|
<div id="window-editor-container">
|
|
911
910
|
if show_frame {
|
|
912
|
-
<div class="column-style-label"><
|
|
911
|
+
<div class="column-style-label"><IntlLabel name="window-frame" /></div>
|
|
913
912
|
<div class="row">
|
|
914
913
|
<Select<String>
|
|
915
914
|
id="window-frame-type"
|
|
@@ -943,7 +942,7 @@ impl Component for WindowEditor {
|
|
|
943
942
|
}
|
|
944
943
|
if show_offset {
|
|
945
944
|
<div class="column-style-label">
|
|
946
|
-
<
|
|
945
|
+
<IntlLabel name="window-offset" class="indent" />
|
|
947
946
|
</div>
|
|
948
947
|
<div class="row">
|
|
949
948
|
<input
|
|
@@ -957,7 +956,7 @@ impl Component for WindowEditor {
|
|
|
957
956
|
}
|
|
958
957
|
if show_alpha {
|
|
959
958
|
<div class="column-style-label">
|
|
960
|
-
<
|
|
959
|
+
<IntlLabel name="window-alpha" class="indent" />
|
|
961
960
|
</div>
|
|
962
961
|
<div class="row">
|
|
963
962
|
<input
|
|
@@ -15,7 +15,10 @@ use std::collections::HashSet;
|
|
|
15
15
|
use serde::{Deserialize, Serialize};
|
|
16
16
|
use serde_json::Value;
|
|
17
17
|
|
|
18
|
-
use super::{
|
|
18
|
+
use super::{
|
|
19
|
+
CustomNumberFormatConfig, DatetimeFormatType, KeyValueOpts, NumberSeriesStyleDefaultConfig,
|
|
20
|
+
};
|
|
21
|
+
use crate::utils::{CssKind, GradientStopSpec, canonicalize_gradient_stops};
|
|
19
22
|
|
|
20
23
|
/// The full schema for one column at one point in time. Plugins may return
|
|
21
24
|
/// different schemas for the same column based on the column's current
|
|
@@ -41,6 +44,21 @@ impl ColumnConfigSchema {
|
|
|
41
44
|
}
|
|
42
45
|
out
|
|
43
46
|
}
|
|
47
|
+
|
|
48
|
+
pub fn leaf_fields(&self) -> Vec<&ControlSpec> {
|
|
49
|
+
fn collect<'a>(fields: &'a [ControlSpec], out: &mut Vec<&'a ControlSpec>) {
|
|
50
|
+
for spec in fields {
|
|
51
|
+
match spec {
|
|
52
|
+
ControlSpec::Group { fields, .. } => collect(fields, out),
|
|
53
|
+
leaf => out.push(leaf),
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let mut out = vec![];
|
|
59
|
+
collect(&self.fields, &mut out);
|
|
60
|
+
out
|
|
61
|
+
}
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
/// Discriminated union of widget kinds the viewer can render. Composite
|
|
@@ -101,7 +119,12 @@ pub enum ControlSpec {
|
|
|
101
119
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
102
120
|
discrete: bool,
|
|
103
121
|
},
|
|
104
|
-
DatetimeFormat
|
|
122
|
+
DatetimeFormat {
|
|
123
|
+
/// Plugin-declared default `date_format`, shown by the editor in
|
|
124
|
+
/// unedited fields and elided from serialized configs.
|
|
125
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
126
|
+
default: Option<DatetimeFormatType>,
|
|
127
|
+
},
|
|
105
128
|
StringFormat,
|
|
106
129
|
NumberSeriesStyle {
|
|
107
130
|
default: NumberSeriesStyleDefaultConfig,
|
|
@@ -109,8 +132,19 @@ pub enum ControlSpec {
|
|
|
109
132
|
Symbols {
|
|
110
133
|
default: KeyValueOpts,
|
|
111
134
|
},
|
|
112
|
-
NumberFormat
|
|
135
|
+
NumberFormat {
|
|
136
|
+
/// Plugin-declared default format, keyed like `number_format`
|
|
137
|
+
/// itself.
|
|
138
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
139
|
+
default: Option<CustomNumberFormatConfig>,
|
|
140
|
+
},
|
|
113
141
|
AggregateDepth,
|
|
142
|
+
|
|
143
|
+
Group {
|
|
144
|
+
key: String,
|
|
145
|
+
#[serde(default)]
|
|
146
|
+
fields: Vec<ControlSpec>,
|
|
147
|
+
},
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
@@ -120,31 +154,6 @@ pub struct EnumVariant {
|
|
|
120
154
|
pub label: Option<String>,
|
|
121
155
|
}
|
|
122
156
|
|
|
123
|
-
/// One stop of a [`ControlSpec::GradientStops`] value in its in-memory
|
|
124
|
-
/// form: a `#rrggbb` color at `offset` ∈ `[0, 1]`.
|
|
125
|
-
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
126
|
-
pub struct GradientStopSpec {
|
|
127
|
-
pub color: String,
|
|
128
|
-
pub offset: f64,
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// The [`ControlSpec::GradientStops`] canonical stop order: offsets
|
|
132
|
-
/// clamped to `[0, 1]` and rounded to 3 decimals, stops sorted stably
|
|
133
|
-
/// by offset.
|
|
134
|
-
pub fn canonicalize_gradient_stops(mut stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
|
|
135
|
-
for stop in &mut stops {
|
|
136
|
-
stop.offset = (stop.offset.clamp(0.0, 1.0) * 1000.0).round() / 1000.0;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
stops.sort_by(|a, b| {
|
|
140
|
-
a.offset
|
|
141
|
-
.partial_cmp(&b.offset)
|
|
142
|
-
.unwrap_or(std::cmp::Ordering::Equal)
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
stops
|
|
146
|
-
}
|
|
147
|
-
|
|
148
157
|
/// Fit `stops` to a `discrete` field's fixed pair: an over-length value
|
|
149
158
|
/// keeps only its two end colors, pinned to `0`/`1`.
|
|
150
159
|
pub fn discrete_pair(stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
|
|
@@ -165,37 +174,90 @@ pub fn discrete_pair(stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
|
|
|
165
174
|
}
|
|
166
175
|
|
|
167
176
|
impl ColumnConfigSchema {
|
|
177
|
+
pub fn canonicalize(self) -> Self {
|
|
178
|
+
self.canonicalize_defaults().group_format_controls()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub fn group_format_controls(mut self) -> Self {
|
|
182
|
+
fn is_format(spec: &ControlSpec) -> bool {
|
|
183
|
+
matches!(
|
|
184
|
+
spec,
|
|
185
|
+
ControlSpec::NumberFormat { .. }
|
|
186
|
+
| ControlSpec::DatetimeFormat { .. }
|
|
187
|
+
| ControlSpec::StringFormat
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fn walk(fields: &mut Vec<ControlSpec>) {
|
|
192
|
+
for spec in fields.iter_mut() {
|
|
193
|
+
if let ControlSpec::Group { key, fields } = spec
|
|
194
|
+
&& key != "format"
|
|
195
|
+
{
|
|
196
|
+
walk(fields);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let first = fields.iter().position(is_format);
|
|
201
|
+
if let Some(first) = first {
|
|
202
|
+
let mut formats = vec![];
|
|
203
|
+
let mut i = first;
|
|
204
|
+
while i < fields.len() {
|
|
205
|
+
if is_format(&fields[i]) {
|
|
206
|
+
formats.push(fields.remove(i));
|
|
207
|
+
} else {
|
|
208
|
+
i += 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
fields.insert(first, ControlSpec::Group {
|
|
213
|
+
key: "format".to_owned(),
|
|
214
|
+
fields: formats,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
walk(&mut self.fields);
|
|
220
|
+
self
|
|
221
|
+
}
|
|
222
|
+
|
|
168
223
|
/// Canonicalize every CSS-valued default at schema ingest, dropping
|
|
169
224
|
/// (and logging) fields whose default fails its kind's reader.
|
|
170
225
|
pub fn canonicalize_defaults(mut self) -> Self {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
226
|
+
fn canonicalize_specs(fields: &mut Vec<ControlSpec>) {
|
|
227
|
+
fields.retain_mut(|spec| {
|
|
228
|
+
let (kind, key, default) = match spec {
|
|
229
|
+
ControlSpec::Group { fields, .. } => {
|
|
230
|
+
canonicalize_specs(fields);
|
|
231
|
+
return !fields.is_empty();
|
|
232
|
+
},
|
|
233
|
+
ControlSpec::Color { key, default } => (CssKind::Color, key, default),
|
|
234
|
+
ControlSpec::Palette { key, default, .. } => (CssKind::Palette, key, default),
|
|
235
|
+
ControlSpec::GradientStops { key, default, .. } => {
|
|
236
|
+
(CssKind::Gradient, key, default)
|
|
237
|
+
},
|
|
238
|
+
_ => return true,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
match kind.canonicalize(default) {
|
|
242
|
+
Ok(canonical) => {
|
|
243
|
+
*default = canonical;
|
|
244
|
+
true
|
|
245
|
+
},
|
|
246
|
+
Err(error) => {
|
|
247
|
+
tracing::error!("Dropping `{key}` — invalid schema default: {error}");
|
|
248
|
+
false
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
192
253
|
|
|
254
|
+
canonicalize_specs(&mut self.fields);
|
|
193
255
|
self
|
|
194
256
|
}
|
|
195
257
|
|
|
196
258
|
/// The CSS kind of the control owning `key`, if it is CSS-valued.
|
|
197
259
|
pub fn css_kind_of(&self, key: &str) -> Option<CssKind> {
|
|
198
|
-
self.
|
|
260
|
+
self.leaf_fields().into_iter().find_map(|spec| match spec {
|
|
199
261
|
ControlSpec::Color { key: k, .. } if k == key => Some(CssKind::Color),
|
|
200
262
|
ControlSpec::Palette { key: k, .. } if k == key => Some(CssKind::Palette),
|
|
201
263
|
ControlSpec::GradientStops { key: k, .. } if k == key => Some(CssKind::Gradient),
|
|
@@ -212,11 +274,11 @@ impl ControlSpec {
|
|
|
212
274
|
/// `columns_config` blob passed to `plugin.restore()`.
|
|
213
275
|
pub fn serialized_keys(&self) -> Vec<&str> {
|
|
214
276
|
match self {
|
|
215
|
-
ControlSpec::DatetimeFormat => vec!["date_format"],
|
|
277
|
+
ControlSpec::DatetimeFormat { .. } => vec!["date_format"],
|
|
216
278
|
ControlSpec::StringFormat => vec!["format"],
|
|
217
279
|
ControlSpec::NumberSeriesStyle { .. } => vec!["chart_type", "stack"],
|
|
218
280
|
ControlSpec::Symbols { .. } => vec!["symbols"],
|
|
219
|
-
ControlSpec::NumberFormat => vec!["number_format"],
|
|
281
|
+
ControlSpec::NumberFormat { .. } => vec!["number_format"],
|
|
220
282
|
ControlSpec::AggregateDepth => vec!["aggregate_depth"],
|
|
221
283
|
ControlSpec::Enum { key, .. }
|
|
222
284
|
| ControlSpec::Bool { key, .. }
|
|
@@ -225,6 +287,9 @@ impl ControlSpec {
|
|
|
225
287
|
| ControlSpec::Color { key, .. }
|
|
226
288
|
| ControlSpec::Palette { key, .. }
|
|
227
289
|
| ControlSpec::GradientStops { key, .. } => vec![key.as_str()],
|
|
290
|
+
ControlSpec::Group { fields, .. } => {
|
|
291
|
+
fields.iter().flat_map(|f| f.serialized_keys()).collect()
|
|
292
|
+
},
|
|
228
293
|
}
|
|
229
294
|
}
|
|
230
295
|
}
|
|
@@ -252,3 +317,244 @@ pub fn filter_to_schema(
|
|
|
252
317
|
.map(|(k, v)| (k.clone(), v.clone()))
|
|
253
318
|
.collect()
|
|
254
319
|
}
|
|
320
|
+
|
|
321
|
+
#[cfg(test)]
|
|
322
|
+
mod tests {
|
|
323
|
+
use serde_json::json;
|
|
324
|
+
|
|
325
|
+
use super::*;
|
|
326
|
+
|
|
327
|
+
fn color(key: &str, default: &str) -> ControlSpec {
|
|
328
|
+
ControlSpec::Color {
|
|
329
|
+
key: key.to_owned(),
|
|
330
|
+
default: default.to_owned(),
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
fn flag(key: &str) -> ControlSpec {
|
|
335
|
+
ControlSpec::Bool {
|
|
336
|
+
key: key.to_owned(),
|
|
337
|
+
default: false,
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
fn group(key: &str, fields: Vec<ControlSpec>) -> ControlSpec {
|
|
342
|
+
ControlSpec::Group {
|
|
343
|
+
key: key.to_owned(),
|
|
344
|
+
fields,
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#[test]
|
|
349
|
+
fn group_deserializes_recursively() {
|
|
350
|
+
let schema: ColumnConfigSchema = serde_json::from_value(json!({
|
|
351
|
+
"fields": [{
|
|
352
|
+
"kind": "Group",
|
|
353
|
+
"key": "legend",
|
|
354
|
+
"fields": [
|
|
355
|
+
{ "kind": "Bool", "key": "legend_on", "default": false },
|
|
356
|
+
{
|
|
357
|
+
"kind": "Group",
|
|
358
|
+
"key": "inner",
|
|
359
|
+
"fields": [{ "kind": "Color", "key": "color", "default": "#ff0000" }]
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
}]
|
|
363
|
+
}))
|
|
364
|
+
.unwrap();
|
|
365
|
+
|
|
366
|
+
let keys = schema.active_keys();
|
|
367
|
+
assert_eq!(
|
|
368
|
+
keys,
|
|
369
|
+
HashSet::from(["legend_on".to_owned(), "color".to_owned()])
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
let leaves = schema.leaf_fields();
|
|
373
|
+
assert_eq!(leaves.len(), 2);
|
|
374
|
+
assert!(
|
|
375
|
+
leaves
|
|
376
|
+
.iter()
|
|
377
|
+
.all(|s| !matches!(s, ControlSpec::Group { .. }))
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#[test]
|
|
382
|
+
fn grouped_schema_is_equivalent_to_flat() {
|
|
383
|
+
let flat = ColumnConfigSchema {
|
|
384
|
+
fields: vec![flag("stack"), color("color", "#0366d6")],
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
let grouped = ColumnConfigSchema {
|
|
388
|
+
fields: vec![group("series", vec![
|
|
389
|
+
flag("stack"),
|
|
390
|
+
color("color", "#0366d6"),
|
|
391
|
+
])],
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
assert_eq!(flat.active_keys(), grouped.active_keys());
|
|
395
|
+
assert_eq!(flat.css_kind_of("color"), grouped.css_kind_of("color"));
|
|
396
|
+
assert_eq!(flat.css_kind_of("stack"), grouped.css_kind_of("stack"));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#[test]
|
|
400
|
+
fn format_controls_group_and_merge() {
|
|
401
|
+
let schema = ColumnConfigSchema {
|
|
402
|
+
fields: vec![
|
|
403
|
+
ControlSpec::NumberFormat { default: None },
|
|
404
|
+
flag("flag"),
|
|
405
|
+
ControlSpec::StringFormat,
|
|
406
|
+
],
|
|
407
|
+
}
|
|
408
|
+
.group_format_controls();
|
|
409
|
+
|
|
410
|
+
assert_eq!(schema.fields.len(), 2);
|
|
411
|
+
let ControlSpec::Group { key, fields } = &schema.fields[0] else {
|
|
412
|
+
panic!("expected format group first");
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
assert_eq!(key, "format");
|
|
416
|
+
assert!(matches!(fields[0], ControlSpec::NumberFormat { .. }));
|
|
417
|
+
assert!(matches!(fields[1], ControlSpec::StringFormat));
|
|
418
|
+
assert!(matches!(&schema.fields[1], ControlSpec::Bool { .. }));
|
|
419
|
+
|
|
420
|
+
assert_eq!(
|
|
421
|
+
schema.active_keys(),
|
|
422
|
+
HashSet::from([
|
|
423
|
+
"number_format".to_owned(),
|
|
424
|
+
"format".to_owned(),
|
|
425
|
+
"flag".to_owned()
|
|
426
|
+
])
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
#[test]
|
|
431
|
+
fn format_grouping_recurses_but_never_double_wraps() {
|
|
432
|
+
let schema = ColumnConfigSchema {
|
|
433
|
+
fields: vec![
|
|
434
|
+
group("format", vec![ControlSpec::NumberFormat { default: None }]),
|
|
435
|
+
group("styling", vec![flag("x"), ControlSpec::DatetimeFormat {
|
|
436
|
+
default: None,
|
|
437
|
+
}]),
|
|
438
|
+
],
|
|
439
|
+
}
|
|
440
|
+
.group_format_controls();
|
|
441
|
+
|
|
442
|
+
let ControlSpec::Group { key, fields } = &schema.fields[0] else {
|
|
443
|
+
panic!("expected group");
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
assert_eq!(key, "format");
|
|
447
|
+
assert!(matches!(fields[0], ControlSpec::NumberFormat { .. }));
|
|
448
|
+
|
|
449
|
+
let ControlSpec::Group { fields, .. } = &schema.fields[1] else {
|
|
450
|
+
panic!("expected group");
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
assert!(matches!(
|
|
454
|
+
&fields[1],
|
|
455
|
+
ControlSpec::Group { key, fields }
|
|
456
|
+
if key == "format" && matches!(fields[0], ControlSpec::DatetimeFormat { .. })
|
|
457
|
+
));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#[test]
|
|
461
|
+
fn format_controls_deserialize_without_default_payload() {
|
|
462
|
+
let schema: ColumnConfigSchema = serde_json::from_value(json!({
|
|
463
|
+
"fields": [{ "kind": "NumberFormat" }, { "kind": "DatetimeFormat" }]
|
|
464
|
+
}))
|
|
465
|
+
.unwrap();
|
|
466
|
+
|
|
467
|
+
assert!(matches!(&schema.fields[0], ControlSpec::NumberFormat {
|
|
468
|
+
default: None
|
|
469
|
+
}));
|
|
470
|
+
assert!(matches!(&schema.fields[1], ControlSpec::DatetimeFormat {
|
|
471
|
+
default: None
|
|
472
|
+
}));
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
#[test]
|
|
476
|
+
fn number_format_default_payload_deserializes_flattened_families() {
|
|
477
|
+
let schema: ColumnConfigSchema = serde_json::from_value(json!({
|
|
478
|
+
"fields": [{
|
|
479
|
+
"kind": "NumberFormat",
|
|
480
|
+
"default": {
|
|
481
|
+
"notation": "compact",
|
|
482
|
+
"compactDisplay": "short",
|
|
483
|
+
"minimumFractionDigits": 0,
|
|
484
|
+
"maximumFractionDigits": 1
|
|
485
|
+
}
|
|
486
|
+
}]
|
|
487
|
+
}))
|
|
488
|
+
.unwrap();
|
|
489
|
+
|
|
490
|
+
let ControlSpec::NumberFormat {
|
|
491
|
+
default: Some(default),
|
|
492
|
+
} = &schema.fields[0]
|
|
493
|
+
else {
|
|
494
|
+
panic!("expected NumberFormat with default");
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
assert_eq!(
|
|
498
|
+
default._notation,
|
|
499
|
+
Some(crate::config::Notation::Compact(
|
|
500
|
+
crate::config::CompactDisplay::Short
|
|
501
|
+
))
|
|
502
|
+
);
|
|
503
|
+
assert_eq!(default._style, None);
|
|
504
|
+
assert_eq!(default.minimum_fraction_digits, Some(0.));
|
|
505
|
+
assert_eq!(default.maximum_fraction_digits, Some(1.));
|
|
506
|
+
assert_eq!(
|
|
507
|
+
schema.active_keys(),
|
|
508
|
+
HashSet::from(["number_format".to_owned()])
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#[test]
|
|
513
|
+
fn datetime_format_default_payload_deserializes_simple_arm() {
|
|
514
|
+
let schema: ColumnConfigSchema = serde_json::from_value(json!({
|
|
515
|
+
"fields": [{
|
|
516
|
+
"kind": "DatetimeFormat",
|
|
517
|
+
"default": { "dateStyle": "medium", "timeStyle": "disabled" }
|
|
518
|
+
}]
|
|
519
|
+
}))
|
|
520
|
+
.unwrap();
|
|
521
|
+
|
|
522
|
+
let ControlSpec::DatetimeFormat {
|
|
523
|
+
default: Some(DatetimeFormatType::Simple(simple)),
|
|
524
|
+
} = &schema.fields[0]
|
|
525
|
+
else {
|
|
526
|
+
panic!("expected DatetimeFormat with Simple default");
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
assert_eq!(
|
|
530
|
+
simple.date_style,
|
|
531
|
+
crate::config::SimpleDatetimeFormat::Medium
|
|
532
|
+
);
|
|
533
|
+
assert_eq!(
|
|
534
|
+
simple.time_style,
|
|
535
|
+
crate::config::SimpleDatetimeFormat::Disabled
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
#[test]
|
|
540
|
+
fn canonicalize_defaults_recurses_and_drops_empty_groups() {
|
|
541
|
+
let schema = ColumnConfigSchema {
|
|
542
|
+
fields: vec![
|
|
543
|
+
group("ok", vec![color("good", "RGB(255,0,0)"), flag("flag")]),
|
|
544
|
+
group("doomed", vec![color("bad", "not-a-color")]),
|
|
545
|
+
],
|
|
546
|
+
}
|
|
547
|
+
.canonicalize_defaults();
|
|
548
|
+
|
|
549
|
+
assert_eq!(schema.fields.len(), 1);
|
|
550
|
+
let ControlSpec::Group { key, fields } = &schema.fields[0] else {
|
|
551
|
+
panic!("expected group");
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
assert_eq!(key, "ok");
|
|
555
|
+
assert!(matches!(
|
|
556
|
+
&fields[0],
|
|
557
|
+
ControlSpec::Color { default, .. } if default == "#ff0000"
|
|
558
|
+
));
|
|
559
|
+
}
|
|
560
|
+
}
|