@perspective-dev/viewer 4.5.0 → 4.5.2

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.
Files changed (118) hide show
  1. package/dist/cdn/perspective-viewer.js +1 -1
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/perspective-viewer.cdn.d.ts +3 -0
  24. package/dist/esm/perspective-viewer.inline.d.ts +3 -0
  25. package/dist/esm/perspective-viewer.inline.js +1 -1
  26. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  27. package/dist/esm/perspective-viewer.js +1 -1
  28. package/dist/esm/perspective-viewer.js.map +2 -2
  29. package/dist/esm/ts-rs/OnUpdateData.d.ts +6 -0
  30. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +4 -0
  31. package/dist/wasm/perspective-viewer.d.ts +15 -15
  32. package/dist/wasm/perspective-viewer.js +93 -88
  33. package/dist/wasm/perspective-viewer.wasm +0 -0
  34. package/dist/wasm/perspective-viewer.wasm.d.ts +15 -15
  35. package/package.json +1 -1
  36. package/src/{rust/components/style/local_style.rs → css/_column_dropdown.css} +14 -35
  37. package/src/css/_dropdown_menu.css +14 -0
  38. package/src/css/_filter_dropdown.css +14 -0
  39. package/src/css/_function_dropdown.css +14 -0
  40. package/src/css/_viewer_bundle.css +41 -0
  41. package/src/css/column-settings-panel.css +9 -3
  42. package/src/css/column-style.css +9 -1
  43. package/src/css/dom/checkbox.css +2 -2
  44. package/src/css/viewer.css +79 -1
  45. package/src/rust/components/column_dropdown.rs +3 -3
  46. package/src/rust/components/column_selector/active_column.rs +3 -0
  47. package/src/rust/components/column_selector/aggregate_selector.rs +0 -3
  48. package/src/rust/components/column_selector/config_selector.rs +0 -3
  49. package/src/rust/components/column_selector/empty_column.rs +0 -3
  50. package/src/rust/components/column_selector/filter_column.rs +0 -3
  51. package/src/rust/components/column_selector/inactive_column.rs +2 -1
  52. package/src/rust/components/column_selector/invalid_column.rs +0 -4
  53. package/src/rust/components/column_selector.rs +0 -3
  54. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +1 -0
  55. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +0 -3
  56. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +0 -3
  57. package/src/rust/components/column_settings_sidebar.rs +1 -3
  58. package/src/rust/components/containers/dragdrop_list.rs +27 -0
  59. package/src/rust/components/containers/dropdown_menu.rs +1 -3
  60. package/src/rust/components/containers/scroll_panel.rs +1 -3
  61. package/src/rust/components/containers/split_panel.rs +0 -4
  62. package/src/rust/components/containers/tab_list.rs +0 -3
  63. package/src/rust/components/datetime_column_style.rs +0 -3
  64. package/src/rust/components/editable_header.rs +15 -0
  65. package/src/rust/components/empty_row.rs +0 -3
  66. package/src/rust/components/expression_editor.rs +2 -3
  67. package/src/rust/components/filter_dropdown.rs +3 -3
  68. package/src/rust/components/font_loader.rs +2 -2
  69. package/src/rust/components/form/code_editor.rs +0 -3
  70. package/src/rust/components/form/debug.rs +0 -4
  71. package/src/rust/components/function_dropdown.rs +3 -3
  72. package/src/rust/components/number_series_style.rs +0 -3
  73. package/src/rust/components/plugin_selector.rs +0 -3
  74. package/src/rust/components/plugin_tab.rs +0 -5
  75. package/src/rust/components/portal.rs +5 -2
  76. package/src/rust/components/render_warning.rs +0 -3
  77. package/src/rust/components/status_bar.rs +4 -3
  78. package/src/rust/components/string_column_style.rs +0 -3
  79. package/src/rust/components/style/mod.rs +60 -27
  80. package/src/rust/components/style/style_provider.rs +49 -16
  81. package/src/rust/components/style_controls/number_string_format.rs +1 -8
  82. package/src/rust/components/type_icon.rs +1 -4
  83. package/src/rust/components/viewer.rs +79 -5
  84. package/src/rust/custom_elements/copy_dropdown.rs +6 -2
  85. package/src/rust/custom_elements/export_dropdown.rs +6 -2
  86. package/src/rust/lib.rs +0 -1
  87. package/src/rust/renderer.rs +85 -21
  88. package/src/rust/session/metadata.rs +11 -1
  89. package/src/rust/session.rs +14 -1
  90. package/src/rust/tasks/reset_all.rs +38 -18
  91. package/src/rust/tasks/restore_and_render.rs +23 -12
  92. package/src/rust/tasks/send_column_config.rs +3 -2
  93. package/src/rust/tasks/send_plugin_config.rs +3 -2
  94. package/src/rust/tasks/update_and_render.rs +11 -4
  95. package/src/svg/checkbox-checked-icon.svg +1 -1
  96. package/src/svg/checkbox-unchecked-icon.svg +1 -1
  97. package/src/themes/botanical.css +2 -0
  98. package/src/themes/dracula.css +2 -0
  99. package/src/themes/gruvbox-dark.css +2 -0
  100. package/src/themes/icons.css +2 -0
  101. package/src/themes/intl/de.css +1 -0
  102. package/src/themes/intl/es.css +1 -0
  103. package/src/themes/intl/fr.css +1 -0
  104. package/src/themes/intl/ja.css +1 -0
  105. package/src/themes/intl/pt.css +1 -0
  106. package/src/themes/intl/zh.css +1 -0
  107. package/src/themes/intl.css +1 -0
  108. package/src/themes/monokai.css +2 -0
  109. package/src/themes/phosphor.css +2 -0
  110. package/src/themes/pro-dark.css +2 -0
  111. package/src/themes/vaporwave.css +2 -0
  112. package/tsconfig.json +1 -1
  113. package/src/rust/components/style/style_cache.rs +0 -136
  114. /package/dist/wasm/snippets/{perspective-viewer-39ab7da3ca157861 → perspective-viewer-93064d0d79855d97}/inline0.js +0 -0
  115. /package/dist/wasm/snippets/{perspective-viewer-39ab7da3ca157861 → perspective-viewer-93064d0d79855d97}/inline1.js +0 -0
  116. /package/dist/wasm/snippets/{perspective-viewer-39ab7da3ca157861 → perspective-viewer-93064d0d79855d97}/inline2.js +0 -0
  117. /package/dist/wasm/snippets/{perspective-viewer-39ab7da3ca157861 → perspective-viewer-93064d0d79855d97}/inline3.js +0 -0
  118. /package/dist/wasm/snippets/{perspective-viewer-39ab7da3ca157861 → perspective-viewer-93064d0d79855d97}/inline4.js +0 -0
@@ -19,9 +19,6 @@ use web_sys::HtmlElement;
19
19
  use yew::html::Scope;
20
20
  use yew::prelude::*;
21
21
 
22
- use crate::components::style::LocalStyle;
23
- use crate::css;
24
-
25
22
  #[derive(Properties, Default)]
26
23
  pub struct SplitPanelProps {
27
24
  pub children: Children,
@@ -201,7 +198,6 @@ impl Component for SplitPanel {
201
198
  let count = iter.len();
202
199
  let contents = html! {
203
200
  <>
204
- <LocalStyle href={css!("containers/split-panel")} />
205
201
  for (i, x) in iter {
206
202
  if i == 0 {
207
203
  if count == 1 {
@@ -12,8 +12,6 @@
12
12
 
13
13
  use yew::{Callback, Children, Component, Html, Properties, classes, html};
14
14
 
15
- use crate::components::style::LocalStyle;
16
- use crate::css;
17
15
  use crate::presentation::ColumnTab;
18
16
 
19
17
  #[derive(Properties, Debug, PartialEq)]
@@ -87,7 +85,6 @@ impl<T: ColumnTab> Component for TabList<T> {
87
85
 
88
86
  html! {
89
87
  <>
90
- <LocalStyle href={css!("containers/tabs")} />
91
88
  <div id="settings_tab_bar">{ for gutter_tabs }</div>
92
89
  <div id="format-tab" class="tab-content scrollable">
93
90
  { ctx.props().children.iter().nth(self.selected_idx) }
@@ -23,12 +23,10 @@ use wasm_bindgen::prelude::*;
23
23
  use yew::prelude::*;
24
24
 
25
25
  use super::modal::{ModalLink, SetModalLink};
26
- use super::style::LocalStyle;
27
26
  use crate::components::datetime_column_style::custom::DatetimeStyleCustom;
28
27
  use crate::components::datetime_column_style::simple::DatetimeStyleSimple;
29
28
  use crate::components::form::select_value_field::SelectValueField;
30
29
  use crate::config::*;
31
- use crate::css;
32
30
  use crate::utils::WeakScope;
33
31
 
34
32
  /// Format-only widget for `datetime` columns. Renders the `date_format`
@@ -126,7 +124,6 @@ impl Component for DatetimeColumnStyle {
126
124
  fn view(&self, ctx: &Context<Self>) -> Html {
127
125
  html! {
128
126
  <>
129
- <LocalStyle href={css!("column-style")} />
130
127
  <div id="column-style-container" class="datetime-column-style-container">
131
128
  if ctx.props().enable_time_config {
132
129
  <SelectValueField<String>
@@ -28,9 +28,13 @@ pub struct EditableHeaderProps {
28
28
  pub initial_value: Option<String>,
29
29
  pub placeholder: Rc<String>,
30
30
 
31
+ // TODO remove this pattern
31
32
  #[prop_or_default]
32
33
  pub reset_count: u8,
33
34
 
35
+ #[prop_or_default]
36
+ pub update_on_input: bool,
37
+
34
38
  /// Session metadata snapshot — threaded from `SessionProps`.
35
39
  pub metadata: SessionMetadataRc,
36
40
 
@@ -163,6 +167,16 @@ impl Component for EditableHeader {
163
167
  EditableHeaderMsg::SetNewValue(value)
164
168
  });
165
169
 
170
+ let update_on_input = ctx.props().update_on_input;
171
+ let oninput = ctx.link().batch_callback(move |e: yew::InputEvent| {
172
+ if update_on_input {
173
+ let value = e.target_unchecked_into::<HtmlInputElement>().value();
174
+ vec![EditableHeaderMsg::SetNewValue(value)]
175
+ } else {
176
+ vec![]
177
+ }
178
+ });
179
+
166
180
  html! {
167
181
  <div class={classes} onclick={ctx.link().callback(|_| EditableHeaderMsg::OnClick(()))}>
168
182
  if let Some(icon) = ctx.props().icon_type { <TypeIcon ty={icon} /> }
@@ -173,6 +187,7 @@ impl Component for EditableHeader {
173
187
  disabled={!ctx.props().editable}
174
188
  {onblur}
175
189
  {onkeyup}
190
+ {oninput}
176
191
  value={self.value.clone()}
177
192
  placeholder={self.placeholder.clone()}
178
193
  />
@@ -20,8 +20,6 @@ use web_sys::*;
20
20
  use yew::prelude::*;
21
21
 
22
22
  use crate::components::filter_dropdown::FilterDropDownElement;
23
- use crate::components::style::LocalStyle;
24
- use crate::css;
25
23
 
26
24
  #[derive(Properties, Derivative)]
27
25
  #[derivative(Debug)]
@@ -87,7 +85,6 @@ impl Component for EmptyRow {
87
85
 
88
86
  html! {
89
87
  <div class="pivot-column column-empty">
90
- <LocalStyle href={css!("empty-column")} />
91
88
  <input
92
89
  spellcheck="false"
93
90
  ref={self.input_ref.clone()}
@@ -16,10 +16,8 @@ use perspective_client::{ExprValidationError, clone};
16
16
  use yew::prelude::*;
17
17
 
18
18
  use super::form::code_editor::*;
19
- use super::style::LocalStyle;
20
19
  use crate::session::{Session, SessionMetadata, SessionMetadataRc};
21
20
  use crate::tasks::{ExprValidation, validate_expression};
22
- use crate::*;
23
21
 
24
22
  #[derive(Properties, PartialEq, Clone)]
25
23
  pub struct ExpressionEditorProps {
@@ -54,10 +52,12 @@ pub struct ExpressionEditor {
54
52
  expr: Rc<String>,
55
53
  error: Option<ExprValidationError>,
56
54
  oninput: Callback<Rc<String>>,
55
+
57
56
  /// Monotonically increasing request id used to drop stale
58
57
  /// validation results when the user types faster than the engine
59
58
  /// can validate.
60
59
  validation_req_id: u64,
60
+
61
61
  /// The id of the most recently dispatched validation; the result
62
62
  /// is only applied when its echoed id matches.
63
63
  last_dispatched_req_id: u64,
@@ -131,7 +131,6 @@ impl Component for ExpressionEditor {
131
131
  clone!(ctx.props().disabled);
132
132
  html! {
133
133
  <>
134
- <LocalStyle href={css!("expression-editor")} />
135
134
  <label class="item_title">{ "Expression" }</label>
136
135
  <div id="editor-container" class={disabled_class}>
137
136
  <CodeEditor
@@ -20,12 +20,11 @@ use yew::html::ImplicitClone;
20
20
  use yew::prelude::*;
21
21
 
22
22
  use super::portal::PortalModal;
23
+ use super::style::StyleSurface;
23
24
  use crate::session::Session;
24
25
  use crate::utils::*;
25
26
  use crate::*;
26
27
 
27
- static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/filter-dropdown.css"));
28
-
29
28
  #[derive(Default)]
30
29
  struct FilterDropDownState {
31
30
  values: Vec<String>,
@@ -220,6 +219,7 @@ impl Component for FilterDropDownPortal {
220
219
  html! {
221
220
  <PortalModal
222
221
  tag_name="perspective-dropdown"
222
+ surface={StyleSurface::FilterDropdown}
223
223
  {target}
224
224
  own_focus=false
225
225
  {on_close}
@@ -267,7 +267,7 @@ fn FilterDropDownView(props: &FilterDropDownViewProps) -> Html {
267
267
  }
268
268
  };
269
269
 
270
- html! { <><style>{ CSS }</style>{ body }</> }
270
+ html! { <>{ body }</> }
271
271
  }
272
272
 
273
273
  fn filter_values(
@@ -154,12 +154,12 @@ impl FontLoaderProps {
154
154
  }
155
155
  }
156
156
 
157
- let fut = async { select_all(block_fonts.into_iter()).await.0 };
157
+ let fut = async { select_all(block_fonts).await.0 };
158
158
  block_promises.push(ApiFuture::new(fut))
159
159
  }
160
160
 
161
161
  if block_promises.len() != preload_fonts.len() {
162
- web_sys::console::warn_1(&format!("Missing preload fonts {:?}", &preload_fonts).into());
162
+ web_sys::console::warn_1(&format!("Missing preload fonts {:?}", preload_fonts).into());
163
163
  }
164
164
 
165
165
  let res = join_all(block_promises)
@@ -19,8 +19,6 @@ use yew::prelude::*;
19
19
 
20
20
  use crate::components::form::highlight::highlight;
21
21
  use crate::components::function_dropdown::{FunctionDropDownElement, FunctionDropDownPortal};
22
- use crate::components::style::LocalStyle;
23
- use crate::css;
24
22
  use crate::exprtk::{Cursor, tokenize};
25
23
  use crate::utils::*;
26
24
 
@@ -174,7 +172,6 @@ pub fn code_editor(props: &CodeEditorProps) -> Html {
174
172
  clone!(props.disabled);
175
173
  html! {
176
174
  <>
177
- <LocalStyle href={css!("form/code-editor")} />
178
175
  <div id="editor" {class}>
179
176
  <div id="line_numbers" ref={lineno_ref}>{ line_numbers }</div>
180
177
  <div id="editor-inner" {class}>
@@ -18,8 +18,6 @@ use wasm_bindgen::prelude::*;
18
18
  use yew::prelude::*;
19
19
 
20
20
  use crate::components::form::code_editor::CodeEditor;
21
- use crate::components::style::LocalStyle;
22
- use crate::css;
23
21
  use crate::js::{MimeType, copy_to_clipboard, paste_from_clipboard};
24
22
  use crate::presentation::*;
25
23
  use crate::renderer::*;
@@ -166,8 +164,6 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
166
164
 
167
165
  html! {
168
166
  <>
169
- <LocalStyle href={css!("containers/tabs")} />
170
- <LocalStyle href={css!("form/debug")} />
171
167
  <div id="debug-panel-overflow">
172
168
  <div id="debug-panel" class="sidebar_column" ref={sizer}>
173
169
  <div id="debug-panel-controls">
@@ -20,10 +20,9 @@ use yew::html::ImplicitClone;
20
20
  use yew::prelude::*;
21
21
 
22
22
  use super::portal::PortalModal;
23
+ use super::style::StyleSurface;
23
24
  use crate::utils::*;
24
25
 
25
- static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/function-dropdown.css"));
26
-
27
26
  #[derive(Default)]
28
27
  struct FunctionDropDownState {
29
28
  values: Vec<CompletionItemSuggestion>,
@@ -158,6 +157,7 @@ impl Component for FunctionDropDownPortal {
158
157
  html! {
159
158
  <PortalModal
160
159
  tag_name="perspective-dropdown"
160
+ surface={StyleSurface::FunctionDropdown}
161
161
  {target}
162
162
  own_focus=false
163
163
  {on_close}
@@ -211,7 +211,7 @@ fn FunctionDropDownView(props: &FunctionDropDownViewProps) -> Html {
211
211
  }
212
212
  };
213
213
 
214
- html! { <><style>{ CSS }</style>{ body }</> }
214
+ html! { <>{ body }</> }
215
215
  }
216
216
 
217
217
  fn filter_values(input: &str) -> Vec<CompletionItemSuggestion> {
@@ -14,10 +14,8 @@ use web_sys::{HtmlInputElement, InputEvent};
14
14
  use yew::prelude::*;
15
15
 
16
16
  use super::modal::{ModalLink, SetModalLink};
17
- use super::style::LocalStyle;
18
17
  use crate::components::form::select_enum_field::SelectEnumField;
19
18
  use crate::config::*;
20
- use crate::css;
21
19
  use crate::utils::WeakScope;
22
20
 
23
21
  #[derive(Properties)]
@@ -125,7 +123,6 @@ impl Component for NumberSeriesStyle {
125
123
 
126
124
  html! {
127
125
  <>
128
- <LocalStyle href={css!("column-style")} />
129
126
  <div id="column-style-container" class="number-series-style-container">
130
127
  <SelectEnumField<ChartType>
131
128
  label="chart-type"
@@ -12,8 +12,6 @@
12
12
 
13
13
  use yew::prelude::*;
14
14
 
15
- use super::style::LocalStyle;
16
- use crate::css;
17
15
  use crate::utils::PtrEqRc;
18
16
 
19
17
  /// Pure value props — no engine handles, no PubSub subscriptions.
@@ -85,7 +83,6 @@ impl Component for PluginSelector {
85
83
 
86
84
  html! {
87
85
  <>
88
- <LocalStyle href={css!("plugin-selector")} />
89
86
  <div id="plugin_selector_container" {class}>
90
87
  <PluginSelect name={plugin_name} on_click={callback} />
91
88
  <div id="plugin_selector_border" />
@@ -22,9 +22,7 @@ use yew::prelude::*;
22
22
  use crate::components::column_settings_sidebar::style_tab::primitive_field::{
23
23
  BoolField, ColorField, ColorRangeField, EnumField, NumberFieldPrimitive,
24
24
  };
25
- use crate::components::style::LocalStyle;
26
25
  use crate::config::ControlSpec;
27
- use crate::css;
28
26
  use crate::queries::get_plugin_config_schema;
29
27
  use crate::renderer::Renderer;
30
28
  use crate::session::Session;
@@ -212,9 +210,6 @@ pub fn PluginTab(props: &PluginTabProps) -> Html {
212
210
 
213
211
  html! {
214
212
  <div id="plugin-tab" class="sidebar_column scrollable">
215
- <LocalStyle href={css!("column-style")} />
216
- <LocalStyle href={css!("plugin-settings-panel")} />
217
- <LocalStyle href={css!("containers/tabs")} />
218
213
  <div id="plugin-config-container" class="tab-section">{ components }</div>
219
214
  </div>
220
215
  }
@@ -20,7 +20,7 @@ use web_sys::*;
20
20
  use yew::prelude::*;
21
21
 
22
22
  use crate::components::modal::ModalOrientation;
23
- use crate::components::style::StyleProvider;
23
+ use crate::components::style::{StyleProvider, StyleSurface};
24
24
  use crate::utils::*;
25
25
 
26
26
  #[derive(Properties, PartialEq)]
@@ -41,6 +41,9 @@ pub struct PortalModalProps {
41
41
  pub tag_name: &'static str,
42
42
 
43
43
  pub theme: String,
44
+
45
+ /// Which popup CSS surface this portal's ShadowRoot adopts.
46
+ pub surface: StyleSurface,
44
47
  }
45
48
 
46
49
  pub enum PortalModalMsg {
@@ -209,7 +212,7 @@ impl Component for PortalModal {
209
212
  <>
210
213
  <style>{ css }</style>
211
214
  <ContextProvider<ModalOrientation> context={self.rev_vert.clone()}>
212
- <StyleProvider root={self.host.clone()}>
215
+ <StyleProvider root={self.host.clone()} surface={ctx.props().surface}>
213
216
  { for ctx.props().children.iter() }
214
217
  </StyleProvider>
215
218
  </ContextProvider<ModalOrientation>>
@@ -12,8 +12,6 @@
12
12
 
13
13
  use yew::prelude::*;
14
14
 
15
- use super::style::LocalStyle;
16
- use crate::css;
17
15
  use crate::renderer::limits::RenderLimits;
18
16
 
19
17
  #[derive(Properties, PartialEq)]
@@ -81,7 +79,6 @@ pub fn render_warning(props: &RenderWarningProps) -> Html {
81
79
  let onclick = Callback::from(move |_: MouseEvent| on_dismiss.emit(()));
82
80
  html! {
83
81
  <>
84
- <LocalStyle href={css!("render-warning")} />
85
82
  <div
86
83
  class="plugin_information plugin_information--warning"
87
84
  id="plugin_information--size"
@@ -15,12 +15,12 @@ use web_sys::*;
15
15
  use yew::prelude::*;
16
16
 
17
17
  use super::status_indicator::StatusIndicator;
18
- use super::style::LocalStyle;
19
18
  use crate::components::containers::select::*;
20
19
  use crate::components::copy_dropdown::CopyDropDownMenu;
21
20
  use crate::components::export_dropdown::ExportDropDownMenu;
22
21
  use crate::components::portal::PortalModal;
23
22
  use crate::components::status_bar_counter::StatusBarRowsCounter;
23
+ use crate::components::style::StyleSurface;
24
24
  use crate::config::*;
25
25
  use crate::js::*;
26
26
  use crate::presentation::{Presentation, PresentationProps};
@@ -332,7 +332,6 @@ impl Component for StatusBar {
332
332
  if is_settings {
333
333
  html! {
334
334
  <>
335
- <LocalStyle href={css!("status-bar")} />
336
335
  <div
337
336
  ref={&self.statusbar_ref}
338
337
  id={ctx.props().id.clone()}
@@ -377,7 +376,7 @@ impl Component for StatusBar {
377
376
  <div id="plugin-settings"><slot name="statusbar-extra" /></div>
378
377
  <span class="hover-target">
379
378
  <span id="reset" class="button" onmousedown={&onreset}>
380
- <span class="icon" />
379
+ <span class="icon shift-alt-icon" />
381
380
  <span class="icon-label" />
382
381
  </span>
383
382
  </span>
@@ -418,6 +417,7 @@ impl Component for StatusBar {
418
417
  </div>
419
418
  <PortalModal
420
419
  tag_name="perspective-copy-menu"
420
+ surface={StyleSurface::DropdownMenu}
421
421
  target={self.copy_target.clone()}
422
422
  own_focus=true
423
423
  on_close={on_close_copy}
@@ -427,6 +427,7 @@ impl Component for StatusBar {
427
427
  </PortalModal>
428
428
  <PortalModal
429
429
  tag_name="perspective-export-menu"
430
+ surface={StyleSurface::DropdownMenu}
430
431
  target={self.export_target.clone()}
431
432
  own_focus=true
432
433
  on_close={on_close_export}
@@ -13,10 +13,8 @@
13
13
  use yew::prelude::*;
14
14
 
15
15
  use super::modal::{ModalLink, SetModalLink};
16
- use super::style::LocalStyle;
17
16
  use crate::components::form::select_enum_field::SelectEnumField;
18
17
  use crate::config::*;
19
- use crate::css;
20
18
  use crate::utils::WeakScope;
21
19
 
22
20
  /// Format-only widget for [`String`] columns. Renders the `format` enum
@@ -91,7 +89,6 @@ impl Component for StringColumnStyle {
91
89
  let format_mode_changed = ctx.link().callback(StringColumnStyleMsg::FormatChanged);
92
90
  html! {
93
91
  <>
94
- <LocalStyle href={css!("column-style")} />
95
92
  <div id="column-style-container" class="string-column-style-container">
96
93
  <SelectEnumField<FormatMode>
97
94
  label="format"
@@ -10,43 +10,76 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! A micro-framework for associating local CSS snippets with `yew::Component`s
14
- //! in a Custom Element's `ShadowRoot`.
13
+ //! Associates a single, build-time CSS bundle with a `yew::Component` tree
14
+ //! rendered into a Custom Element's `ShadowRoot`.
15
15
  //!
16
- //! Embedding a `<LocalStyle>` element will only create the underlying `<style>`
17
- //! tag once (when `Component::view()` is called the first time), even if
18
- //! multiple copies of the `Component` exist in the tree.
16
+ //! Each `src/css` file is inlined and minified by `lightningcss` (see
17
+ //! `build.rs` + the `_viewer_bundle.css` and per-popup `_column_dropdown.css`
18
+ //! / `_filter_dropdown.css` / `_function_dropdown.css` / `_dropdown_menu.css`
19
+ //! entrypoints) into one stylesheet per "surface". A `<StyleProvider>` at the
20
+ //! root of a shadow tree adopts its surface's stylesheet exactly once;
21
+ //! constructed `CSSStyleSheet`s are cheap and shared across every viewer
22
+ //! instance.
19
23
  //!
20
24
  //! # Example
21
25
  //!
22
- //! ```
26
+ //! ```ignore
23
27
  //! html! {
24
- //! <StyleProvider>
25
- //! <LocalStyle href={ css!("my-style") } />
28
+ //! <StyleProvider root={host} surface={StyleSurface::Viewer}>
26
29
  //! <h1>{ "I am styled!" }</h1>
27
30
  //! </StyleProvider>
28
31
  //! }
29
32
  //! ```
30
33
 
31
- mod local_style;
32
- mod style_cache;
33
34
  mod style_provider;
34
35
 
35
- pub use local_style::LocalStyle;
36
- pub use style_provider::StyleProvider;
37
-
38
- #[macro_export]
39
- macro_rules! css {
40
- ($name:expr) => {{
41
- (
42
- $name,
43
- include_str!(concat!(env!("OUT_DIR"), "/css/", $name, ".css")),
44
- )
45
- }};
46
- ($path:expr, $name:expr) => {{
47
- (
48
- $name,
49
- include_str!(concat!(env!("OUT_DIR"), "/", $path, "/", $name, ".css")),
50
- )
51
- }};
36
+ pub use style_provider::{StyleProvider, StyleSurface};
37
+
38
+ /// The viewer-surface bundle: every `src/css` file except the floating-popup
39
+ /// sheets. Adopted into the `<perspective-viewer>` ShadowRoot.
40
+ fn viewer_bundle() -> &'static str {
41
+ include_str!(concat!(env!("OUT_DIR"), "/css/_viewer_bundle.css"))
42
+ }
43
+
44
+ /// Per-popup-surface bundles. Each floating popup root adopts exactly its own
45
+ /// self-contained sheet (mirroring the pre-bundle behavior where every popup
46
+ /// instance carried only its own CSS) so the four popup styles never collide —
47
+ /// in particular `column-dropdown.css`'s `!important` inverted rules stay
48
+ /// confined to the column-name completion dropdown.
49
+ fn column_dropdown_bundle() -> &'static str {
50
+ include_str!(concat!(env!("OUT_DIR"), "/css/_column_dropdown.css"))
51
+ }
52
+ fn filter_dropdown_bundle() -> &'static str {
53
+ include_str!(concat!(env!("OUT_DIR"), "/css/_filter_dropdown.css"))
54
+ }
55
+ fn function_dropdown_bundle() -> &'static str {
56
+ include_str!(concat!(env!("OUT_DIR"), "/css/_function_dropdown.css"))
57
+ }
58
+ fn dropdown_menu_bundle() -> &'static str {
59
+ include_str!(concat!(env!("OUT_DIR"), "/css/_dropdown_menu.css"))
60
+ }
61
+
62
+ thread_local! {
63
+ static VIEWER_SHEET: web_sys::CssStyleSheet = make_sheet(viewer_bundle());
64
+ static COLUMN_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(column_dropdown_bundle());
65
+ static FILTER_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(filter_dropdown_bundle());
66
+ static FUNCTION_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(function_dropdown_bundle());
67
+ static DROPDOWN_MENU_SHEET: web_sys::CssStyleSheet = make_sheet(dropdown_menu_bundle());
68
+ }
69
+
70
+ fn make_sheet(css: &str) -> web_sys::CssStyleSheet {
71
+ let sheet = web_sys::CssStyleSheet::new().unwrap();
72
+ sheet.replace_sync(css).unwrap();
73
+ sheet
74
+ }
75
+
76
+ /// The memoized, shared `CSSStyleSheet` for a given [`StyleSurface`].
77
+ pub(crate) fn surface_sheet(surface: StyleSurface) -> web_sys::CssStyleSheet {
78
+ match surface {
79
+ StyleSurface::Viewer => VIEWER_SHEET.with(|x| x.clone()),
80
+ StyleSurface::ColumnDropdown => COLUMN_DROPDOWN_SHEET.with(|x| x.clone()),
81
+ StyleSurface::FilterDropdown => FILTER_DROPDOWN_SHEET.with(|x| x.clone()),
82
+ StyleSurface::FunctionDropdown => FUNCTION_DROPDOWN_SHEET.with(|x| x.clone()),
83
+ StyleSurface::DropdownMenu => DROPDOWN_MENU_SHEET.with(|x| x.clone()),
84
+ }
52
85
  }
@@ -10,9 +10,26 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use wasm_bindgen::{JsCast, JsValue};
13
14
  use yew::prelude::*;
14
15
 
15
- use super::style_cache::StyleCache;
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
+ }
16
33
 
17
34
  #[derive(Properties, PartialEq)]
18
35
  pub struct StyleProviderProps {
@@ -20,32 +37,48 @@ pub struct StyleProviderProps {
20
37
 
21
38
  #[prop_or(true)]
22
39
  pub is_shadow: bool,
40
+
41
+ #[prop_or_default]
42
+ pub surface: StyleSurface,
43
+
23
44
  pub children: Children,
24
45
  }
25
46
 
26
- /// A context which injects any CSS snippet registered within its tree, doing
27
- /// so only once for each unqiue snippet name.
28
- ///
29
- /// CSS can be registered within sub-components via the `<LocalStyle>` component
30
- /// and `css!()` resource inlining macro.
31
- pub struct StyleProvider {
32
- cache: StyleCache,
33
- }
47
+ /// Adopts the build-time CSS bundle for its [`StyleSurface`] into the root's
48
+ /// `adoptedStyleSheets` once, on creation. Unlike the previous per-snippet
49
+ /// `LocalStyle` registration, children no longer register CSS individually.
50
+ pub struct StyleProvider;
34
51
 
35
52
  impl Component for StyleProvider {
36
53
  type Message = ();
37
54
  type Properties = StyleProviderProps;
38
55
 
39
56
  fn create(ctx: &Context<Self>) -> Self {
40
- let cache = StyleCache::new(ctx.props().is_shadow, &ctx.props().root);
41
- Self { cache }
57
+ adopt_surface_sheet(ctx.props());
58
+ Self
42
59
  }
43
60
 
44
61
  fn view(&self, ctx: &Context<Self>) -> Html {
45
- html! {
46
- <ContextProvider<StyleCache> context={self.cache.clone()}>
47
- { for ctx.props().children.iter() }
48
- </ContextProvider<StyleCache>>
49
- }
62
+ html! { <>{ for ctx.props().children.iter() }</> }
63
+ }
64
+ }
65
+
66
+ /// Push the surface's shared stylesheet onto the root's `adoptedStyleSheets`,
67
+ /// unless it is already present (a root may host more than one provider).
68
+ fn adopt_surface_sheet(props: &StyleProviderProps) {
69
+ let root: JsValue = if props.is_shadow {
70
+ props.root.shadow_root().unwrap().into()
71
+ } else {
72
+ web_sys::window().unwrap().document().unwrap().into()
73
+ };
74
+
75
+ let sheets = js_sys::Reflect::get(&root, &"adoptedStyleSheets".into())
76
+ .unwrap()
77
+ .unchecked_into::<js_sys::Array>();
78
+
79
+ let sheet = surface_sheet(props.surface);
80
+ let sheet_val: &JsValue = sheet.as_ref();
81
+ if sheets.index_of(sheet_val, 0) < 0 {
82
+ sheets.push(sheet_val);
50
83
  }
51
84
  }
@@ -19,9 +19,7 @@ use perspective_client::config::ColumnType;
19
19
  pub use types::*;
20
20
  use yew::{Callback, Component, Properties, html};
21
21
 
22
- use crate::components::style::LocalStyle;
23
22
  use crate::config::*;
24
- use crate::css;
25
23
 
26
24
  #[derive(Properties, PartialEq, Clone)]
27
25
  pub struct CustomNumberFormatProps {
@@ -211,12 +209,7 @@ impl Component for CustomNumberFormat {
211
209
 
212
210
  fn view(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
213
211
  html! {
214
- <>
215
- <LocalStyle href={css!("column-style")} />
216
- { self.style_section(ctx) }
217
- { self.digits_section(ctx) }
218
- { self.misc_section(ctx) }
219
- </>
212
+ <>{ self.style_section(ctx) }{ self.digits_section(ctx) }{ self.misc_section(ctx) }</>
220
213
  }
221
214
  }
222
215
  }