Skip to main content

perspective_viewer/components/
style.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
13//! Associates a single, build-time CSS bundle with a `yew::Component` tree
14//! rendered into a Custom Element's `ShadowRoot`.
15//!
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.
23//!
24//! # Example
25//!
26//! ```ignore
27//! html! {
28//!     <StyleProvider root={host} sheet={StyleSurface::Viewer.sheet()}>
29//!         <h1>{ "I am styled!" }</h1>
30//!     </StyleProvider>
31//! }
32//! ```
33
34/// The CSS bundle a shadow tree adopts; each variant maps to one build-time
35/// `src/css/_*.css` entrypoint.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
37pub enum StyleSurface {
38    /// The `<perspective-viewer>` ShadowRoot.
39    #[default]
40    Viewer,
41    /// Column-name completion dropdown (inverted color scheme).
42    ColumnDropdown,
43    /// Filter-value completion dropdown.
44    FilterDropdown,
45    /// Expression function-completion dropdown.
46    FunctionDropdown,
47    /// Copy / Export menu (non-inverted color scheme).
48    DropdownMenu,
49    /// Panel context menu (right-click a panel).
50    ContextMenu,
51}
52
53impl StyleSurface {
54    /// The shared, lazily constructed `CSSStyleSheet` for this surface.
55    pub fn sheet(self) -> web_sys::CssStyleSheet {
56        match self {
57            StyleSurface::Viewer => VIEWER_SHEET.with(|x| x.clone()),
58            StyleSurface::ColumnDropdown => COLUMN_DROPDOWN_SHEET.with(|x| x.clone()),
59            StyleSurface::FilterDropdown => FILTER_DROPDOWN_SHEET.with(|x| x.clone()),
60            StyleSurface::FunctionDropdown => FUNCTION_DROPDOWN_SHEET.with(|x| x.clone()),
61            StyleSurface::DropdownMenu => DROPDOWN_MENU_SHEET.with(|x| x.clone()),
62            StyleSurface::ContextMenu => CONTEXT_MENU_SHEET.with(|x| x.clone()),
63        }
64    }
65}
66
67/// The viewer-surface bundle: every `src/css` file except the floating-popup
68/// sheets. Adopted into the `<perspective-viewer>` ShadowRoot.
69fn viewer_bundle() -> &'static str {
70    include_str!(concat!(env!("OUT_DIR"), "/css/_viewer_bundle.css"))
71}
72
73/// Per-popup-surface bundles. Each floating popup root adopts exactly its own
74/// self-contained sheet (mirroring the pre-bundle behavior where every popup
75/// instance carried only its own CSS) so the four popup styles never collide —
76/// in particular `column-dropdown.css`'s `!important` inverted rules stay
77/// confined to the column-name completion dropdown.
78fn column_dropdown_bundle() -> &'static str {
79    include_str!(concat!(env!("OUT_DIR"), "/css/_column_dropdown.css"))
80}
81fn filter_dropdown_bundle() -> &'static str {
82    include_str!(concat!(env!("OUT_DIR"), "/css/_filter_dropdown.css"))
83}
84fn function_dropdown_bundle() -> &'static str {
85    include_str!(concat!(env!("OUT_DIR"), "/css/_function_dropdown.css"))
86}
87fn dropdown_menu_bundle() -> &'static str {
88    include_str!(concat!(env!("OUT_DIR"), "/css/_dropdown_menu.css"))
89}
90fn context_menu_bundle() -> &'static str {
91    include_str!(concat!(env!("OUT_DIR"), "/css/_context_menu.css"))
92}
93
94thread_local! {
95    static VIEWER_SHEET: web_sys::CssStyleSheet = make_sheet(viewer_bundle());
96    static COLUMN_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(column_dropdown_bundle());
97    static FILTER_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(filter_dropdown_bundle());
98    static FUNCTION_DROPDOWN_SHEET: web_sys::CssStyleSheet = make_sheet(function_dropdown_bundle());
99    static DROPDOWN_MENU_SHEET: web_sys::CssStyleSheet = make_sheet(dropdown_menu_bundle());
100    static CONTEXT_MENU_SHEET: web_sys::CssStyleSheet = make_sheet(context_menu_bundle());
101}
102
103fn make_sheet(css: &str) -> web_sys::CssStyleSheet {
104    let sheet = web_sys::CssStyleSheet::new().unwrap();
105    sheet.replace_sync(css).unwrap();
106    sheet
107}