@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
|
@@ -19,31 +19,61 @@ use wasm_bindgen::prelude::*;
|
|
|
19
19
|
use web_sys::*;
|
|
20
20
|
use yew::prelude::*;
|
|
21
21
|
|
|
22
|
-
use crate::
|
|
23
|
-
use crate::
|
|
22
|
+
use crate::ui::modal::ModalOrientation;
|
|
23
|
+
use crate::ui::style::StyleProvider;
|
|
24
24
|
use crate::utils::*;
|
|
25
25
|
|
|
26
|
+
/// The `slot` every modal host is assigned in the viewer's light DOM, so it
|
|
27
|
+
/// is in the flat tree.
|
|
28
|
+
pub const MODAL_SLOT: &str = "modal";
|
|
29
|
+
|
|
30
|
+
/// Find the `<perspective-viewer>` that `target` lives in — as a light-DOM
|
|
31
|
+
/// descendant, or through any number of shadow roots.
|
|
32
|
+
fn nearest_viewer(target: &HtmlElement) -> Option<HtmlElement> {
|
|
33
|
+
let is_viewer = |el: &Element| el.tag_name().eq_ignore_ascii_case("perspective-viewer");
|
|
34
|
+
if let Ok(Some(viewer)) = target.closest("perspective-viewer") {
|
|
35
|
+
return Some(viewer.unchecked_into());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let mut node: Node = target.clone().into();
|
|
39
|
+
loop {
|
|
40
|
+
let root = node.get_root_node();
|
|
41
|
+
let host = root.dyn_ref::<ShadowRoot>()?.host();
|
|
42
|
+
if is_viewer(&host) {
|
|
43
|
+
return Some(host.unchecked_into());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if let Ok(Some(viewer)) = host.closest("perspective-viewer") {
|
|
47
|
+
return Some(viewer.unchecked_into());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
node = host.into();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// A modal (menu / completion dropdown) anchored to a `target` element,
|
|
55
|
+
/// hosted as a light-DOM child of the `<perspective-viewer>` in the top layer.
|
|
26
56
|
#[derive(Properties, PartialEq)]
|
|
27
57
|
pub struct PortalModalProps {
|
|
28
58
|
pub children: Children,
|
|
29
|
-
|
|
30
|
-
/// The element to position relative to. `None` means closed.
|
|
31
59
|
pub target: Option<HtmlElement>,
|
|
60
|
+
pub tag_name: &'static str,
|
|
61
|
+
pub theme: String,
|
|
62
|
+
pub sheet: web_sys::CssStyleSheet,
|
|
32
63
|
|
|
33
|
-
///
|
|
64
|
+
/// Adds this component's own light-dismiss (an outside `pointerdown` or
|
|
65
|
+
/// Escape → `on_close`) instead of `popover="auto"`, whose platform
|
|
66
|
+
/// dismissal fires on the opening click's own pointerup.
|
|
34
67
|
#[prop_or(true)]
|
|
35
68
|
pub own_focus: bool,
|
|
36
69
|
|
|
37
|
-
/// Called when the portal closes (blur, etc).
|
|
38
70
|
#[prop_or_default]
|
|
39
71
|
pub on_close: Callback<()>,
|
|
40
72
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/// Which popup CSS surface this portal's ShadowRoot adopts.
|
|
46
|
-
pub surface: StyleSurface,
|
|
73
|
+
/// The `<perspective-viewer>` to mount under, defaulting to the nearest
|
|
74
|
+
/// viewer around `target`.
|
|
75
|
+
#[prop_or_default]
|
|
76
|
+
pub mount: Option<HtmlElement>,
|
|
47
77
|
}
|
|
48
78
|
|
|
49
79
|
pub enum PortalModalMsg {
|
|
@@ -58,28 +88,49 @@ pub struct PortalModal {
|
|
|
58
88
|
visible: bool,
|
|
59
89
|
rev_vert: ModalOrientation,
|
|
60
90
|
anchor: Rc<Cell<ModalAnchor>>,
|
|
61
|
-
|
|
91
|
+
/// The `own_focus` light-dismiss listeners (document-level, capture),
|
|
92
|
+
/// present only while the modal is open.
|
|
93
|
+
_dismiss: Option<DismissHandlers>,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// The dismiss closures of an open `own_focus` modal: outside `pointerdown`
|
|
97
|
+
/// and Escape `keydown` on the document (capture), `blur` on the host.
|
|
98
|
+
struct DismissHandlers {
|
|
99
|
+
pointerdown: Closure<dyn FnMut(PointerEvent)>,
|
|
100
|
+
keydown: Closure<dyn FnMut(KeyboardEvent)>,
|
|
101
|
+
blur: Closure<dyn FnMut(FocusEvent)>,
|
|
62
102
|
}
|
|
63
103
|
|
|
64
104
|
impl PortalModal {
|
|
65
|
-
fn
|
|
66
|
-
if
|
|
67
|
-
|
|
105
|
+
fn attach(&self, ctx: &Context<Self>, target: &HtmlElement) {
|
|
106
|
+
if self.host.is_connected() {
|
|
107
|
+
return;
|
|
68
108
|
}
|
|
109
|
+
|
|
110
|
+
let parent = ctx
|
|
111
|
+
.props()
|
|
112
|
+
.mount
|
|
113
|
+
.clone()
|
|
114
|
+
.or_else(|| nearest_viewer(target))
|
|
115
|
+
.unwrap_or_else(|| {
|
|
116
|
+
tracing::warn!(
|
|
117
|
+
"`{}` target is outside any <perspective-viewer>; mounting on <body> unthemed",
|
|
118
|
+
ctx.props().tag_name
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
global::body()
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
let _ = parent.append_child(&self.host);
|
|
125
|
+
let _ = self.host.show_popover();
|
|
69
126
|
}
|
|
70
127
|
|
|
71
|
-
fn
|
|
128
|
+
fn detach(&mut self) {
|
|
129
|
+
self.remove_dismiss_handlers();
|
|
72
130
|
if self.host.is_connected() {
|
|
73
|
-
let _ =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if let Some(closure) = self._blur_closure.as_ref() {
|
|
77
|
-
self.host
|
|
78
|
-
.remove_event_listener_with_callback("blur", closure.as_ref().unchecked_ref())
|
|
79
|
-
.unwrap()
|
|
131
|
+
let _ = self.host.hide_popover();
|
|
132
|
+
self.host.remove();
|
|
80
133
|
}
|
|
81
|
-
|
|
82
|
-
self._blur_closure = None;
|
|
83
134
|
}
|
|
84
135
|
|
|
85
136
|
fn position_against_target(&mut self, target: &HtmlElement) {
|
|
@@ -90,12 +141,10 @@ impl PortalModal {
|
|
|
90
141
|
let left = target_rect.left();
|
|
91
142
|
|
|
92
143
|
if !self.visible {
|
|
93
|
-
// First pass: position at default anchor, invisible
|
|
94
144
|
self.top = top + height - 1.0;
|
|
95
145
|
self.left = left;
|
|
96
146
|
self.visible = false;
|
|
97
147
|
} else {
|
|
98
|
-
// Second pass: compute actual anchor and reposition
|
|
99
148
|
let anchor = calc_relative_position(&self.host, top, left, height, width);
|
|
100
149
|
self.anchor.set(anchor);
|
|
101
150
|
let modal_rect = self.host.get_bounding_client_rect();
|
|
@@ -104,9 +153,15 @@ impl PortalModal {
|
|
|
104
153
|
self.left = new_left;
|
|
105
154
|
self.rev_vert.set(anchor.is_rev_vert());
|
|
106
155
|
}
|
|
156
|
+
|
|
157
|
+
let style = self.host.style();
|
|
158
|
+
let _ = style.set_property("top", &format!("{}px", self.top));
|
|
159
|
+
let _ = style.set_property("left", &format!("{}px", self.left));
|
|
107
160
|
}
|
|
108
161
|
|
|
109
|
-
|
|
162
|
+
/// This component's light-dismiss for `own_focus` modals: an outside
|
|
163
|
+
/// `pointerdown` (document capture) or Escape.
|
|
164
|
+
fn setup_dismiss_handlers(&mut self, ctx: &Context<Self>) {
|
|
110
165
|
let on_close = {
|
|
111
166
|
let target = ctx.props().target.clone();
|
|
112
167
|
ctx.props().on_close.reform(move |_| {
|
|
@@ -116,15 +171,77 @@ impl PortalModal {
|
|
|
116
171
|
})
|
|
117
172
|
};
|
|
118
173
|
|
|
119
|
-
let
|
|
174
|
+
let host = self.host.clone();
|
|
175
|
+
let pointerdown = Closure::wrap(Box::new({
|
|
176
|
+
let on_close = on_close.clone();
|
|
177
|
+
move |event: PointerEvent| {
|
|
178
|
+
if !event.composed_path().includes(host.as_ref(), 0) {
|
|
179
|
+
on_close.emit(());
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}) as Box<dyn FnMut(PointerEvent)>);
|
|
183
|
+
|
|
184
|
+
let keydown = Closure::wrap(Box::new({
|
|
185
|
+
let on_close = on_close.clone();
|
|
186
|
+
move |event: KeyboardEvent| {
|
|
187
|
+
if event.key() == "Escape" {
|
|
188
|
+
on_close.emit(());
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}) as Box<dyn FnMut(KeyboardEvent)>);
|
|
192
|
+
|
|
193
|
+
let blur = Closure::wrap(Box::new(move |_: FocusEvent| {
|
|
120
194
|
on_close.emit(());
|
|
121
195
|
}) as Box<dyn FnMut(FocusEvent)>);
|
|
122
196
|
|
|
123
197
|
let _ = self
|
|
124
198
|
.host
|
|
125
|
-
.add_event_listener_with_callback("blur",
|
|
199
|
+
.add_event_listener_with_callback("blur", blur.as_ref().unchecked_ref());
|
|
126
200
|
|
|
127
|
-
|
|
201
|
+
let document = global::document();
|
|
202
|
+
let _ = document.add_event_listener_with_callback_and_bool(
|
|
203
|
+
"pointerdown",
|
|
204
|
+
pointerdown.as_ref().unchecked_ref(),
|
|
205
|
+
true,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
let _ = document.add_event_listener_with_callback_and_bool(
|
|
209
|
+
"keydown",
|
|
210
|
+
keydown.as_ref().unchecked_ref(),
|
|
211
|
+
true,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
self._dismiss = Some(DismissHandlers {
|
|
215
|
+
pointerdown,
|
|
216
|
+
keydown,
|
|
217
|
+
blur,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn remove_dismiss_handlers(&mut self) {
|
|
222
|
+
if let Some(DismissHandlers {
|
|
223
|
+
pointerdown,
|
|
224
|
+
keydown,
|
|
225
|
+
blur,
|
|
226
|
+
}) = self._dismiss.take()
|
|
227
|
+
{
|
|
228
|
+
let _ = self
|
|
229
|
+
.host
|
|
230
|
+
.remove_event_listener_with_callback("blur", blur.as_ref().unchecked_ref());
|
|
231
|
+
|
|
232
|
+
let document = global::document();
|
|
233
|
+
let _ = document.remove_event_listener_with_callback_and_bool(
|
|
234
|
+
"pointerdown",
|
|
235
|
+
pointerdown.as_ref().unchecked_ref(),
|
|
236
|
+
true,
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
let _ = document.remove_event_listener_with_callback_and_bool(
|
|
240
|
+
"keydown",
|
|
241
|
+
keydown.as_ref().unchecked_ref(),
|
|
242
|
+
true,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
128
245
|
}
|
|
129
246
|
}
|
|
130
247
|
|
|
@@ -138,8 +255,14 @@ impl Component for PortalModal {
|
|
|
138
255
|
.unwrap()
|
|
139
256
|
.unchecked_into();
|
|
140
257
|
|
|
141
|
-
host.
|
|
142
|
-
host.
|
|
258
|
+
host.set_attribute("popover", "manual").unwrap();
|
|
259
|
+
host.set_attribute("slot", MODAL_SLOT).unwrap();
|
|
260
|
+
|
|
261
|
+
let style = host.style();
|
|
262
|
+
style.set_property("position", "fixed").unwrap();
|
|
263
|
+
style.set_property("right", "auto").unwrap();
|
|
264
|
+
style.set_property("bottom", "auto").unwrap();
|
|
265
|
+
style.set_property("margin", "0").unwrap();
|
|
143
266
|
let init = ShadowRootInit::new(ShadowRootMode::Open);
|
|
144
267
|
let shadow_root = if let Some(elem) = host.shadow_root() {
|
|
145
268
|
elem
|
|
@@ -156,7 +279,7 @@ impl Component for PortalModal {
|
|
|
156
279
|
visible: false,
|
|
157
280
|
rev_vert: Default::default(),
|
|
158
281
|
anchor: Default::default(),
|
|
159
|
-
|
|
282
|
+
_dismiss: None,
|
|
160
283
|
}
|
|
161
284
|
}
|
|
162
285
|
|
|
@@ -170,32 +293,20 @@ impl Component for PortalModal {
|
|
|
170
293
|
}
|
|
171
294
|
|
|
172
295
|
fn changed(&mut self, ctx: &Context<Self>, old_props: &Self::Properties) -> bool {
|
|
173
|
-
// The host element (`tag_name`) and its adopted sheet (`surface`) are
|
|
174
|
-
// fixed at `create` — a swap at the same vdom position must be keyed
|
|
175
|
-
// so Yew recreates the component (see `PanelMenu`'s menu→picker
|
|
176
|
-
// stages).
|
|
177
296
|
debug_assert_eq!(ctx.props().tag_name, old_props.tag_name);
|
|
178
|
-
debug_assert_eq!(ctx.props().
|
|
179
|
-
|
|
180
|
-
let new_target = &ctx.props().target;
|
|
181
|
-
let old_target = &old_props.target;
|
|
297
|
+
debug_assert_eq!(ctx.props().sheet, old_props.sheet);
|
|
182
298
|
|
|
183
|
-
match (
|
|
184
|
-
(None, Some(_)
|
|
299
|
+
match (&old_props.target, &ctx.props().target) {
|
|
300
|
+
(None, Some(_)) => {
|
|
185
301
|
self.visible = false;
|
|
186
|
-
self.
|
|
187
|
-
.remove_event_listener_with_callback("blur", closure.as_ref().unchecked_ref())
|
|
188
|
-
.unwrap();
|
|
189
|
-
|
|
190
|
-
self._blur_closure = None;
|
|
302
|
+
self.remove_dismiss_handlers();
|
|
191
303
|
},
|
|
192
|
-
(
|
|
193
|
-
|
|
194
|
-
self.
|
|
304
|
+
(Some(old), None) => {
|
|
305
|
+
let _ = old.class_list().remove_1("modal-target");
|
|
306
|
+
self.detach();
|
|
195
307
|
},
|
|
196
|
-
(Some(
|
|
197
|
-
|
|
198
|
-
return true;
|
|
308
|
+
(Some(old), Some(new)) if old != new => {
|
|
309
|
+
let _ = old.class_list().remove_1("modal-target");
|
|
199
310
|
},
|
|
200
311
|
_ => {},
|
|
201
312
|
}
|
|
@@ -209,17 +320,13 @@ impl Component for PortalModal {
|
|
|
209
320
|
return html! {};
|
|
210
321
|
}
|
|
211
322
|
|
|
212
|
-
let
|
|
213
|
-
let css = format!(
|
|
214
|
-
":host{{top:{}px;left:{}px{}}}",
|
|
215
|
-
self.top, self.left, opacity
|
|
216
|
-
);
|
|
323
|
+
let css = if self.visible { "" } else { ":host{opacity:0}" };
|
|
217
324
|
|
|
218
325
|
let portal_content = html! {
|
|
219
326
|
<>
|
|
220
327
|
<style>{ css }</style>
|
|
221
328
|
<ContextProvider<ModalOrientation> context={self.rev_vert.clone()}>
|
|
222
|
-
<StyleProvider root={self.host.clone()}
|
|
329
|
+
<StyleProvider root={self.host.clone()} sheet={ctx.props().sheet.clone()}>
|
|
223
330
|
{ for ctx.props().children.iter() }
|
|
224
331
|
</StyleProvider>
|
|
225
332
|
</ContextProvider<ModalOrientation>>
|
|
@@ -234,34 +341,26 @@ impl Component for PortalModal {
|
|
|
234
341
|
if !self.host.is_connected() {
|
|
235
342
|
let theme = ctx.props().theme.as_str();
|
|
236
343
|
self.host.set_attribute("theme", theme).unwrap();
|
|
237
|
-
|
|
238
|
-
// First render with a target: attach to body, position invisible
|
|
239
|
-
self.position_against_target(target);
|
|
240
|
-
self.attach_to_body();
|
|
241
|
-
|
|
242
|
-
// Propagate theme from target
|
|
243
344
|
if let Some(theme) = target.get_attribute("theme") {
|
|
244
345
|
let _ = self.host.set_attribute("theme", &theme);
|
|
245
346
|
}
|
|
246
347
|
|
|
348
|
+
self.position_against_target(target);
|
|
349
|
+
self.attach(ctx, target);
|
|
247
350
|
target.class_list().add_1("modal-target").unwrap();
|
|
248
|
-
|
|
249
351
|
if ctx.props().own_focus {
|
|
250
352
|
self.host.set_attribute("tabindex", "0").unwrap();
|
|
251
|
-
self.
|
|
353
|
+
self.setup_dismiss_handlers(ctx);
|
|
252
354
|
}
|
|
253
355
|
|
|
254
|
-
// Schedule second positioning pass
|
|
255
356
|
let link = ctx.link().clone();
|
|
256
357
|
wasm_bindgen_futures::spawn_local(async move {
|
|
257
358
|
request_animation_frame().await;
|
|
258
359
|
link.send_message(PortalModalMsg::Reposition);
|
|
259
360
|
});
|
|
260
361
|
} else if self.visible {
|
|
261
|
-
// Second pass: reposition with correct anchor
|
|
262
362
|
self.position_against_target(target);
|
|
263
|
-
|
|
264
|
-
if ctx.props().own_focus && self._blur_closure.is_some() {
|
|
363
|
+
if ctx.props().own_focus && self._dismiss.is_some() {
|
|
265
364
|
let _ = self.host.focus();
|
|
266
365
|
}
|
|
267
366
|
}
|
|
@@ -271,14 +370,10 @@ impl Component for PortalModal {
|
|
|
271
370
|
fn destroy(&mut self, ctx: &Context<Self>) {
|
|
272
371
|
if let Some(target) = &ctx.props().target {
|
|
273
372
|
target.class_list().remove_1("modal-target").unwrap();
|
|
274
|
-
if target.get_attribute("theme").is_some() {
|
|
275
|
-
let _ = self.host.remove_attribute("theme");
|
|
276
|
-
}
|
|
277
|
-
|
|
278
373
|
let event = CustomEvent::new("-perspective-close-expression").unwrap();
|
|
279
374
|
let _ = target.dispatch_event(&event);
|
|
280
375
|
}
|
|
281
376
|
|
|
282
|
-
self.
|
|
377
|
+
self.detach();
|
|
283
378
|
}
|
|
284
379
|
}
|
|
@@ -13,42 +13,15 @@
|
|
|
13
13
|
use wasm_bindgen::{JsCast, JsValue};
|
|
14
14
|
use yew::prelude::*;
|
|
15
15
|
|
|
16
|
-
use super::surface_sheet;
|
|
17
|
-
|
|
18
|
-
/// Which prebuilt CSS bundle a [`StyleProvider`] adopts into its root.
|
|
19
|
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
|
20
|
-
pub enum StyleSurface {
|
|
21
|
-
/// The `<perspective-viewer>` ShadowRoot.
|
|
22
|
-
#[default]
|
|
23
|
-
Viewer,
|
|
24
|
-
/// Column-name completion dropdown (inverted color scheme).
|
|
25
|
-
ColumnDropdown,
|
|
26
|
-
/// Filter-value completion dropdown.
|
|
27
|
-
FilterDropdown,
|
|
28
|
-
/// Expression function-completion dropdown.
|
|
29
|
-
FunctionDropdown,
|
|
30
|
-
/// Copy / Export menu (non-inverted color scheme).
|
|
31
|
-
DropdownMenu,
|
|
32
|
-
/// Panel context menu (right-click a panel).
|
|
33
|
-
ContextMenu,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
16
|
#[derive(Properties, PartialEq)]
|
|
37
17
|
pub struct StyleProviderProps {
|
|
38
18
|
pub root: web_sys::HtmlElement,
|
|
39
|
-
|
|
40
19
|
#[prop_or(true)]
|
|
41
20
|
pub is_shadow: bool,
|
|
42
|
-
|
|
43
|
-
#[prop_or_default]
|
|
44
|
-
pub surface: StyleSurface,
|
|
45
|
-
|
|
21
|
+
pub sheet: web_sys::CssStyleSheet,
|
|
46
22
|
pub children: Children,
|
|
47
23
|
}
|
|
48
24
|
|
|
49
|
-
/// Adopts the build-time CSS bundle for its [`StyleSurface`] into the root's
|
|
50
|
-
/// `adoptedStyleSheets` once, on creation. Unlike the previous per-snippet
|
|
51
|
-
/// `LocalStyle` registration, children no longer register CSS individually.
|
|
52
25
|
pub struct StyleProvider;
|
|
53
26
|
|
|
54
27
|
impl Component for StyleProvider {
|
|
@@ -56,7 +29,7 @@ impl Component for StyleProvider {
|
|
|
56
29
|
type Properties = StyleProviderProps;
|
|
57
30
|
|
|
58
31
|
fn create(ctx: &Context<Self>) -> Self {
|
|
59
|
-
|
|
32
|
+
adopt_sheet(ctx.props());
|
|
60
33
|
Self
|
|
61
34
|
}
|
|
62
35
|
|
|
@@ -65,9 +38,7 @@ impl Component for StyleProvider {
|
|
|
65
38
|
}
|
|
66
39
|
}
|
|
67
40
|
|
|
68
|
-
|
|
69
|
-
/// unless it is already present (a root may host more than one provider).
|
|
70
|
-
fn adopt_surface_sheet(props: &StyleProviderProps) {
|
|
41
|
+
fn adopt_sheet(props: &StyleProviderProps) {
|
|
71
42
|
let root: JsValue = if props.is_shadow {
|
|
72
43
|
props.root.shadow_root().unwrap().into()
|
|
73
44
|
} else {
|
|
@@ -78,8 +49,7 @@ fn adopt_surface_sheet(props: &StyleProviderProps) {
|
|
|
78
49
|
.unwrap()
|
|
79
50
|
.unchecked_into::<js_sys::Array>();
|
|
80
51
|
|
|
81
|
-
let
|
|
82
|
-
let sheet_val: &JsValue = sheet.as_ref();
|
|
52
|
+
let sheet_val: &JsValue = props.sheet.as_ref();
|
|
83
53
|
if sheets.index_of(sheet_val, 0) < 0 {
|
|
84
54
|
sheets.push(sheet_val);
|
|
85
55
|
}
|
|
@@ -15,12 +15,6 @@ use std::future::Future;
|
|
|
15
15
|
use futures::channel::oneshot;
|
|
16
16
|
use perspective_js::utils::*;
|
|
17
17
|
|
|
18
|
-
/// A completion handle for message-based public API methods (invariant I6 —
|
|
19
|
-
/// see `SESSION_CONFIG_COHERENCE_PLAN.md`): its ONLY resolution API is
|
|
20
|
-
/// [`Completion::resolve_after`], which takes a run future — so resolving a
|
|
21
|
-
/// public method's promise at message-handling time (before the renders it
|
|
22
|
-
/// caused have drawn) is unwritable, not merely discouraged. Dropping an
|
|
23
|
-
/// unresolved `Completion` rejects the caller as cancelled.
|
|
24
18
|
pub struct Completion(Option<oneshot::Sender<ApiResult<()>>>);
|
|
25
19
|
|
|
26
20
|
impl Completion {
|
|
@@ -12,9 +12,28 @@
|
|
|
12
12
|
|
|
13
13
|
use std::collections::BTreeMap;
|
|
14
14
|
|
|
15
|
+
use serde::{Deserialize, Serialize};
|
|
15
16
|
use serde_json::Value;
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
|
19
|
+
pub struct GradientStopSpec {
|
|
20
|
+
pub color: String,
|
|
21
|
+
pub offset: f64,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn canonicalize_gradient_stops(mut stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
|
|
25
|
+
for stop in &mut stops {
|
|
26
|
+
stop.offset = (stop.offset.clamp(0.0, 1.0) * 1000.0).round() / 1000.0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
stops.sort_by(|a, b| {
|
|
30
|
+
a.offset
|
|
31
|
+
.partial_cmp(&b.offset)
|
|
32
|
+
.unwrap_or(std::cmp::Ordering::Equal)
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
stops
|
|
36
|
+
}
|
|
18
37
|
|
|
19
38
|
pub const USER_VAR_PREFIX: &str = "--psp-user--";
|
|
20
39
|
|
|
@@ -64,8 +83,8 @@ impl CssKind {
|
|
|
64
83
|
}
|
|
65
84
|
}
|
|
66
85
|
|
|
67
|
-
/// The property name a whole-value `var(--psp-user--…)` reference
|
|
68
|
-
///
|
|
86
|
+
/// The property name a whole-value `var(--psp-user--…)` reference names, or
|
|
87
|
+
/// `None` when `src` is not one.
|
|
69
88
|
pub fn parse_var_ref(src: &str) -> Option<&str> {
|
|
70
89
|
let inner = src.trim().strip_prefix("var(")?.strip_suffix(')')?.trim();
|
|
71
90
|
if !inner.starts_with(USER_VAR_PREFIX)
|
|
@@ -129,8 +148,8 @@ fn channel_from_token(token: &str) -> Result<u8, String> {
|
|
|
129
148
|
Ok(value.round().clamp(0.0, 255.0) as u8)
|
|
130
149
|
}
|
|
131
150
|
|
|
132
|
-
/// A color literal — `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()`
|
|
133
|
-
///
|
|
151
|
+
/// A color literal — `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()` or
|
|
152
|
+
/// `rgba()` — normalized to lowercase `#rrggbb`.
|
|
134
153
|
pub fn normalize_css_color(src: &str) -> Result<String, String> {
|
|
135
154
|
let src = src.trim();
|
|
136
155
|
if let Some(hex) = src.strip_prefix('#') {
|
|
@@ -260,8 +279,8 @@ fn format_percent(offset: f64) -> String {
|
|
|
260
279
|
format!("{text}%")
|
|
261
280
|
}
|
|
262
281
|
|
|
263
|
-
/// The canonical gradient string for `stops`: `linear-gradient(to
|
|
264
|
-
///
|
|
282
|
+
/// The canonical gradient string for `stops`: `linear-gradient(to right,
|
|
283
|
+
/// #rrggbb P%, …)` with every position explicit.
|
|
265
284
|
pub fn gradient_to_css(stops: &[GradientStopSpec]) -> String {
|
|
266
285
|
let body = canonicalize_gradient_stops(stops.to_vec())
|
|
267
286
|
.iter()
|
|
@@ -272,26 +291,20 @@ pub fn gradient_to_css(stops: &[GradientStopSpec]) -> String {
|
|
|
272
291
|
format!("linear-gradient(to right, {body})")
|
|
273
292
|
}
|
|
274
293
|
|
|
275
|
-
/// The canonical palette string for `colors`: `linear-gradient(to
|
|
276
|
-
///
|
|
294
|
+
/// The canonical palette string for `colors`: `linear-gradient(to right,
|
|
295
|
+
/// #rrggbb, …)` with no positions.
|
|
277
296
|
pub fn palette_to_css(colors: &[String]) -> String {
|
|
278
297
|
format!("linear-gradient(to right, {})", colors.join(", "))
|
|
279
298
|
}
|
|
280
299
|
|
|
281
|
-
/// A serialized
|
|
282
|
-
///
|
|
283
|
-
/// [`ControlSpec::GradientStops`]: super::ControlSpec::GradientStops
|
|
300
|
+
/// A serialized `ControlSpec::GradientStops` value.
|
|
284
301
|
#[derive(Clone, Debug, PartialEq)]
|
|
285
302
|
pub enum CssGradient {
|
|
286
|
-
/// Canonical stops (sorted, rounded), length ≥ 2.
|
|
287
303
|
Literal(Vec<GradientStopSpec>),
|
|
288
|
-
/// A `--psp-user--gradient-…` property name.
|
|
289
304
|
Var(String),
|
|
290
305
|
}
|
|
291
306
|
|
|
292
307
|
impl CssGradient {
|
|
293
|
-
/// Parse a gradient string of at least 2 stops, with optional
|
|
294
|
-
/// positions filled by the CSS implicit rules.
|
|
295
308
|
pub fn parse(src: &str) -> Result<Self, String> {
|
|
296
309
|
if let Some(name) = parse_kind_var(src, CssKind::Gradient)? {
|
|
297
310
|
return Ok(CssGradient::Var(name));
|
|
@@ -348,20 +361,14 @@ impl CssGradient {
|
|
|
348
361
|
}
|
|
349
362
|
}
|
|
350
363
|
|
|
351
|
-
/// A serialized
|
|
352
|
-
///
|
|
353
|
-
/// [`ControlSpec::Palette`]: super::ControlSpec::Palette
|
|
364
|
+
/// A serialized `ControlSpec::Palette` value.
|
|
354
365
|
#[derive(Clone, Debug, PartialEq)]
|
|
355
366
|
pub enum CssPalette {
|
|
356
|
-
/// Ordered `#rrggbb` entries, length ≥ 1.
|
|
357
367
|
Literal(Vec<String>),
|
|
358
|
-
/// A `--psp-user--palette-…` property name.
|
|
359
368
|
Var(String),
|
|
360
369
|
}
|
|
361
370
|
|
|
362
371
|
impl CssPalette {
|
|
363
|
-
/// Parse a palette string of at least 1 entry, with positions
|
|
364
|
-
/// forbidden.
|
|
365
372
|
pub fn parse(src: &str) -> Result<Self, String> {
|
|
366
373
|
if let Some(name) = parse_kind_var(src, CssKind::Palette)? {
|
|
367
374
|
return Ok(CssPalette::Var(name));
|
|
@@ -395,14 +402,10 @@ impl CssPalette {
|
|
|
395
402
|
}
|
|
396
403
|
}
|
|
397
404
|
|
|
398
|
-
/// A serialized
|
|
399
|
-
///
|
|
400
|
-
/// [`ControlSpec::Color`]: super::ControlSpec::Color
|
|
405
|
+
/// A serialized `ControlSpec::Color` value.
|
|
401
406
|
#[derive(Clone, Debug, PartialEq)]
|
|
402
407
|
pub enum CssColor {
|
|
403
|
-
/// Lowercase `#rrggbb`.
|
|
404
408
|
Literal(String),
|
|
405
|
-
/// A `--psp-user--color-…` property name.
|
|
406
409
|
Var(String),
|
|
407
410
|
}
|
|
408
411
|
|
|
@@ -435,8 +438,8 @@ pub fn canonicalize_css_color(src: &str) -> Result<String, String> {
|
|
|
435
438
|
CssKind::Color.canonicalize(src)
|
|
436
439
|
}
|
|
437
440
|
|
|
438
|
-
/// One CSS literal stored in a panel's `columns_config`: its column,
|
|
439
|
-
///
|
|
441
|
+
/// One CSS literal stored in a panel's `columns_config`: its column, key,
|
|
442
|
+
/// schema kind and canonical value — the unit of "in use".
|
|
440
443
|
#[derive(Clone, Debug, PartialEq)]
|
|
441
444
|
pub struct CssLiteralUse {
|
|
442
445
|
pub column: String,
|
|
@@ -445,8 +448,8 @@ pub struct CssLiteralUse {
|
|
|
445
448
|
pub literal: String,
|
|
446
449
|
}
|
|
447
450
|
|
|
448
|
-
/// The workspace palette: `restored` unioned with every in-use
|
|
449
|
-
///
|
|
451
|
+
/// The workspace palette: `restored` unioned with every in-use literal, each
|
|
452
|
+
/// named deterministically.
|
|
450
453
|
pub fn assign_palette_names(
|
|
451
454
|
restored: &BTreeMap<String, String>,
|
|
452
455
|
host: &[NamedValue],
|
|
@@ -487,9 +490,9 @@ pub fn palette_name_for(
|
|
|
487
490
|
.map(|(name, _)| name.clone())
|
|
488
491
|
}
|
|
489
492
|
|
|
490
|
-
/// Replace every whole-value `var(--psp-user--…)` string in one
|
|
491
|
-
///
|
|
492
|
-
///
|
|
493
|
+
/// Replace every whole-value `var(--psp-user--…)` string in one column's config
|
|
494
|
+
/// entry with the literal `lookup` resolves it to, removing and reporting keys
|
|
495
|
+
/// that do not resolve.
|
|
493
496
|
pub fn resolve_css_refs(
|
|
494
497
|
entry: &mut serde_json::Map<String, Value>,
|
|
495
498
|
lookup: &dyn Fn(&str) -> Option<String>,
|
package/src/rust/utils/mod.rs
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
mod browser;
|
|
20
20
|
mod completion;
|
|
21
|
+
mod css;
|
|
21
22
|
mod css_vars;
|
|
22
23
|
mod custom_element;
|
|
23
24
|
mod datetime;
|
|
@@ -36,6 +37,7 @@ mod tests;
|
|
|
36
37
|
|
|
37
38
|
pub use browser::*;
|
|
38
39
|
pub use completion::*;
|
|
40
|
+
pub use css::*;
|
|
39
41
|
pub use css_vars::*;
|
|
40
42
|
pub use custom_element::*;
|
|
41
43
|
pub use datetime::*;
|