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}