Skip to content

Commit 8253dc5

Browse files
committed
Use adoptedStyleSheets
Signed-off-by: Andrew Stein <steinlink@gmail.com>
1 parent b9257c8 commit 8253dc5

8 files changed

Lines changed: 71 additions & 64 deletions

File tree

rust/perspective-viewer/src/rust/components/copy_dropdown.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
2828
pub struct CopyDropDownMenuProps {
2929
pub renderer: Renderer,
3030
pub callback: Callback<ExportFile>,
31+
pub root: web_sys::HtmlElement,
3132

3233
#[prop_or_default]
3334
weak_link: WeakScope<CopyDropDownMenu>,
@@ -54,7 +55,7 @@ impl Component for CopyDropDownMenu {
5455
let is_chart = plugin.name().as_str() != "Datagrid";
5556
let has_selection = ctx.props().renderer.get_selection().is_some();
5657
html! {
57-
<StyleProvider>
58+
<StyleProvider root={ctx.props().root.clone()}>
5859
<DropDownMenu<ExportFile>
5960
values={Rc::new(get_menu_items(is_chart, has_selection))}
6061
callback={&ctx.props().callback}

rust/perspective-viewer/src/rust/components/export_dropdown.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct ExportDropDownMenuProps {
3030
pub renderer: Renderer,
3131
pub presentation: Presentation,
3232
pub callback: Callback<ExportFile>,
33+
pub root: web_sys::HtmlElement,
3334

3435
#[prop_or_default]
3536
weak_link: WeakScope<ExportDropDownMenu>,
@@ -99,7 +100,7 @@ impl Component for ExportDropDownMenu {
99100
// js_intern::js_intern!("render")).unwrap();
100101
let is_chart = plugin.name().as_str() != "Datagrid";
101102
html! {
102-
<StyleProvider>
103+
<StyleProvider root={ctx.props().root.clone()}>
103104
<span class="dropdown-group-label">{ "Save as" }</span>
104105
<input
105106
class={if self.invalid { "invalid" } else { "" }}

rust/perspective-viewer/src/rust/components/number_column_style.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ impl NumberColumnStyleProps {
8989
let view = session.get_view().unwrap();
9090
let min_max = view.get_min_max(column_name).await.unwrap();
9191
let abs_max = max!(
92-
min_max.0.parse::<f64>().unwrap().abs(),
93-
min_max.1.parse::<f64>().unwrap().abs()
92+
min_max.0.parse::<f64>().unwrap_or_default().abs(),
93+
min_max.1.parse::<f64>().unwrap_or_default().abs()
9494
);
9595
let gradient = (abs_max * 100.).round() / 100.;
9696
NumberColumnStyleMsg::DefaultGradientChanged(gradient)

rust/perspective-viewer/src/rust/components/style/style_cache.rs

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1212

1313
use std::cell::RefCell;
14-
use std::collections::BTreeMap;
14+
use std::collections::{BTreeMap, BTreeSet};
1515
use std::ops::Deref;
1616
use std::rc::Rc;
1717

18-
use perspective_js::utils::global;
1918
use wasm_bindgen::JsCast;
20-
use web_sys::HtmlStyleElement;
2119

2220
use crate::*;
2321

@@ -28,6 +26,17 @@ type CSSResource = (&'static str, &'static str);
2826
/// their styles registered).
2927
static DOM_STYLES: &[CSSResource] = &[css!("dom/checkbox"), css!("dom/select")];
3028

29+
thread_local! {
30+
/// Cache of `CssStyleSheet` objects, which can safely be re-used across
31+
/// `HTMLPerspectiveViewerElement` instances
32+
static STYLE_SHEET_CACHE: RefCell<BTreeMap<&'static str, web_sys::CssStyleSheet>> = RefCell::new(
33+
DOM_STYLES
34+
.iter()
35+
.map(|x| (x.0, StyleCache::into_style(x.1)))
36+
.collect(),
37+
);
38+
}
39+
3140
/// A state object for `<style>` snippets used by a yew `Component` with a
3241
/// `<StyleProvider>` at the root.
3342
#[derive(Clone)]
@@ -48,8 +57,8 @@ impl PartialEq for StyleCache {
4857
}
4958

5059
impl StyleCache {
51-
pub fn new(is_shadow: bool) -> StyleCache {
52-
StyleCache(Rc::new(StyleCacheData::new(is_shadow)))
60+
pub fn new(is_shadow: bool, elem: &web_sys::HtmlElement) -> StyleCache {
61+
StyleCache(Rc::new(StyleCacheData::new(is_shadow, elem)))
5362
}
5463

5564
/// Insert a new stylesheet into this manager, _immediately_ inserting it
@@ -63,63 +72,61 @@ impl StyleCache {
6372
/// attached _before_ the style's target nodes are attached.
6473
pub fn add_style(&self, name: &'static str, css: &'static str) {
6574
let mut map = self.0.styles.borrow_mut();
66-
if !map.contains_key(name) {
67-
let style = Self::into_style(name, css, self.0.is_shadow);
68-
let first = map.values().next().cloned();
69-
map.insert(name, style.clone());
70-
let mut values = map.values();
71-
if let Some(mut x) = first {
72-
while let Some(y) = values.next()
73-
&& y.get_attribute("name").as_deref() < Some(name)
74-
{
75-
x = y.clone();
75+
STYLE_SHEET_CACHE.with_borrow_mut(|cache| {
76+
if !map.contains(name) {
77+
map.insert(name);
78+
if !cache.contains_key(name) {
79+
let style = Self::into_style(css);
80+
cache.insert(name, style);
7681
}
7782

78-
x.parent_node()
79-
.unwrap_or_else(|| x.get_root_node())
80-
.insert_before(&style, x.next_sibling().as_ref())
81-
.unwrap();
83+
self.adopted_style_sheets.splice(
84+
map.iter().position(|x| x == &name).unwrap() as u32,
85+
0,
86+
cache.get(name).unwrap(),
87+
);
8288
}
83-
}
89+
});
8490
}
8591

8692
/// Concert a CSS string to an `HtmlStyleElement`, which are memoized due
8793
/// to their size and DOM performance impact.
88-
fn into_style(name: &str, css: &str, is_shadow: bool) -> web_sys::HtmlStyleElement {
89-
let elem = global::document().create_element("style").unwrap();
90-
if is_shadow {
91-
elem.set_text_content(Some(css));
92-
} else {
93-
let new_css = css.replace(":host", ":root");
94-
elem.set_text_content(Some(&new_css));
95-
}
96-
97-
elem.set_attribute("name", name).unwrap();
98-
elem.unchecked_into()
99-
}
100-
101-
pub fn iter_styles(&self) -> impl Iterator<Item = (&'static str, HtmlStyleElement)> {
102-
self.styles.borrow().clone().into_iter()
94+
fn into_style(css: &str) -> web_sys::CssStyleSheet {
95+
let sheet = web_sys::CssStyleSheet::new().unwrap();
96+
sheet.replace_sync(css).unwrap();
97+
sheet
10398
}
10499
}
105100

106101
/// Using a `BTreeMap` so the resulting `<style>` elements have a stable order
107102
/// when rendered to the DOM.
108103
pub struct StyleCacheData {
109-
styles: RefCell<BTreeMap<&'static str, web_sys::HtmlStyleElement>>,
110-
is_shadow: bool,
104+
styles: RefCell<BTreeSet<&'static str>>,
105+
adopted_style_sheets: js_sys::Array,
111106
}
112107

113108
impl StyleCacheData {
114-
fn new(is_shadow: bool) -> Self {
115-
let styles = DOM_STYLES
116-
.iter()
117-
.map(|x| (x.0, StyleCache::into_style(x.0, x.1, is_shadow)))
118-
.collect();
109+
fn new(is_shadow: bool, elem: &web_sys::HtmlElement) -> Self {
110+
let styles: RefCell<BTreeSet<&'static str>> =
111+
RefCell::new(DOM_STYLES.iter().map(|x| x.0).collect());
112+
113+
let root: &JsValue = if is_shadow {
114+
&elem.shadow_root().unwrap()
115+
} else {
116+
&web_sys::window().unwrap().document().unwrap()
117+
};
118+
119+
let adopted_style_sheets = js_sys::Reflect::get(root, &"adoptedStyleSheets".into())
120+
.unwrap()
121+
.unchecked_into::<js_sys::Array>();
122+
123+
for name in styles.borrow().iter() {
124+
STYLE_SHEET_CACHE.with_borrow(|x| adopted_style_sheets.push(x.get(name).unwrap()));
125+
}
119126

120127
Self {
121-
styles: RefCell::new(styles),
122-
is_shadow,
128+
styles,
129+
adopted_style_sheets,
123130
}
124131
}
125132
}

rust/perspective-viewer/src/rust/components/style/style_provider.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::style_cache::StyleCache;
1818

1919
#[derive(Properties, PartialEq)]
2020
pub struct StyleProviderProps {
21+
pub root: web_sys::HtmlElement,
2122
#[prop_or(true)]
2223
pub is_shadow: bool,
2324
pub children: Children,
@@ -37,24 +38,15 @@ impl Component for StyleProvider {
3738
type Properties = StyleProviderProps;
3839

3940
fn create(ctx: &Context<Self>) -> Self {
40-
let cache = StyleCache::new(ctx.props().is_shadow);
41+
let cache = StyleCache::new(ctx.props().is_shadow, &ctx.props().root);
4142
Self { cache }
4243
}
4344

4445
fn view(&self, ctx: &Context<Self>) -> Html {
45-
let styles = self.cache.iter_styles();
46-
4746
html! {
48-
<>
49-
{ for styles.map(|x| {
50-
html! {
51-
<StyleKeyed key={ x.0 } elem={ x.1 } />
52-
}
53-
}) }
54-
<ContextProvider<StyleCache> context={self.cache.clone()}>
55-
{ for ctx.props().children.iter() }
56-
</ContextProvider<StyleCache>>
57-
</>
47+
<ContextProvider<StyleCache> context={self.cache.clone()}>
48+
{ for ctx.props().children.iter() }
49+
</ContextProvider<StyleCache>>
5850
}
5951
}
6052
}

rust/perspective-viewer/src/rust/components/viewer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ impl Component for PerspectiveViewer {
544544

545545
html! {
546546
<>
547-
<StyleProvider>
547+
<StyleProvider root={ctx.props().elem.clone()}>
548548
<LocalStyle href={css!("viewer")} />
549549
if self.settings_open && ctx.props().session.has_table() {
550550
if self.debug_open {

rust/perspective-viewer/src/rust/custom_elements/copy_dropdown.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ impl CopyDropDownMenuElement {
100100
});
101101

102102
let renderer = model.renderer().clone();
103-
let props = props!(CopyDropDownMenuProps { renderer, callback });
103+
let props = props!(CopyDropDownMenuProps {
104+
renderer,
105+
callback,
106+
root: self.elem.clone()
107+
});
108+
104109
let modal = ModalElement::new(self.elem.clone(), props, true, None);
105110
*self.modal.borrow_mut() = Some(modal);
106111
}

rust/perspective-viewer/src/rust/custom_elements/export_dropdown.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ impl ExportDropDownMenuElement {
102102
let props = props!(ExportDropDownMenuProps {
103103
renderer,
104104
presentation,
105-
callback
105+
callback,
106+
root: self.elem.clone()
106107
});
107108

108109
let modal = ModalElement::new(self.elem.clone(), props, true, None);

0 commit comments

Comments
 (0)