perspective_viewer/components/style/style_provider.rs
1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors. ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use wasm_bindgen::{JsCast, JsValue};
14use yew::prelude::*;
15
16use super::surface_sheet;
17
18/// Which prebuilt CSS bundle a [`StyleProvider`] adopts into its root.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
20pub 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}
33
34#[derive(Properties, PartialEq)]
35pub struct StyleProviderProps {
36 pub root: web_sys::HtmlElement,
37
38 #[prop_or(true)]
39 pub is_shadow: bool,
40
41 #[prop_or_default]
42 pub surface: StyleSurface,
43
44 pub children: Children,
45}
46
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.
50pub struct StyleProvider;
51
52impl Component for StyleProvider {
53 type Message = ();
54 type Properties = StyleProviderProps;
55
56 fn create(ctx: &Context<Self>) -> Self {
57 adopt_surface_sheet(ctx.props());
58 Self
59 }
60
61 fn view(&self, ctx: &Context<Self>) -> Html {
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).
68fn 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);
83 }
84}