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    /// Panel context menu (right-click a panel).
33    ContextMenu,
34}
35
36#[derive(Properties, PartialEq)]
37pub struct StyleProviderProps {
38    pub root: web_sys::HtmlElement,
39
40    #[prop_or(true)]
41    pub is_shadow: bool,
42
43    #[prop_or_default]
44    pub surface: StyleSurface,
45
46    pub children: Children,
47}
48
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.
52pub struct StyleProvider;
53
54impl Component for StyleProvider {
55    type Message = ();
56    type Properties = StyleProviderProps;
57
58    fn create(ctx: &Context<Self>) -> Self {
59        adopt_surface_sheet(ctx.props());
60        Self
61    }
62
63    fn view(&self, ctx: &Context<Self>) -> Html {
64        html! { <>{ for ctx.props().children.iter() }</> }
65    }
66}
67
68/// Push the surface's shared stylesheet onto the root's `adoptedStyleSheets`,
69/// unless it is already present (a root may host more than one provider).
70fn adopt_surface_sheet(props: &StyleProviderProps) {
71    let root: JsValue = if props.is_shadow {
72        props.root.shadow_root().unwrap().into()
73    } else {
74        web_sys::window().unwrap().document().unwrap().into()
75    };
76
77    let sheets = js_sys::Reflect::get(&root, &"adoptedStyleSheets".into())
78        .unwrap()
79        .unchecked_into::<js_sys::Array>();
80
81    let sheet = surface_sheet(props.surface);
82    let sheet_val: &JsValue = sheet.as_ref();
83    if sheets.index_of(sheet_val, 0) < 0 {
84        sheets.push(sheet_val);
85    }
86}