Skip to main content

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}