Skip to main content

perspective_viewer/config/
plugin_static_config.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 perspective_client::config::GroupRollupMode;
14use serde::Deserialize;
15use ts_rs::TS;
16
17#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, TS)]
18#[serde(rename_all = "camelCase")]
19pub enum ColumnSelectMode {
20    #[default]
21    Toggle,
22    Select,
23}
24
25impl ColumnSelectMode {
26    pub fn css(&self) -> yew::Classes {
27        match self {
28            Self::Toggle => yew::classes!("toggle-mode", "is_column_active"),
29            Self::Select => yew::classes!("select-mode", "is_column_active"),
30        }
31    }
32}
33
34/// Static, immutable configuration for a plugin.
35///
36/// Returned once per plugin from `get_static_config()` at registration
37/// time and cached in [`crate::renderer::PluginRecord`]. Consumers
38/// (renderer, session, queries, components) read these fields off the
39/// renderer's active-plugin metadata rather than calling back into JS.
40///
41/// `<perspective-viewer>` reads this exactly once per plugin (at
42/// `registerPlugin` time) and caches it for the lifetime of the
43/// application. The result must be stable; do not mutate any field
44/// after registration.
45#[derive(Clone, Debug, Default, Deserialize, PartialEq, TS)]
46pub struct PluginStaticConfig {
47    /// The unique key for this plugin. Used as the `plugin` field in a
48    /// `ViewerConfig` and as the display name key in the
49    /// `<perspective-viewer>` UI.
50    pub name: String,
51
52    /// Category in the plugin picker menu.
53    #[serde(default)]
54    #[ts(as = "Option<_>")]
55    #[ts(optional)]
56    pub category: Option<String>,
57
58    /// Soft limit on the number of columns the plugin will render.
59    /// Triggers the "Rendering N of M" warning when the view exceeds
60    /// this value (until dismissed).
61    #[serde(default)]
62    #[ts(as = "Option<_>")]
63    #[ts(optional)]
64    pub max_columns: Option<usize>,
65
66    /// Soft limit on the number of cells (rows × columns) the plugin
67    /// will render. Triggers the "Rendering N of M" warning when the view
68    /// exceeds this value (until dismissed).
69    #[serde(default)]
70    #[ts(as = "Option<_>")]
71    #[ts(optional)]
72    pub max_cells: Option<usize>,
73
74    /// Column add/remove behavior. `"select"` exclusively selects the
75    /// added column, removing other columns. `"toggle"` toggles the
76    /// column on or off based on its current state, leaving other
77    /// columns alone.
78    #[serde(default)]
79    #[ts(as = "Option<_>")]
80    #[ts(optional)]
81    pub select_mode: ColumnSelectMode,
82
83    /// Minimum number of columns the plugin requires to render. Mostly
84    /// affects drag/drop and column-remove button behavior. `undefined`
85    /// is treated identically to `1`.
86    #[serde(default)]
87    #[ts(as = "Option<_>")]
88    #[ts(optional)]
89    pub min_config_columns: Option<usize>,
90
91    /// Named column slots. Named columns have replace/swap behavior in
92    /// drag/drop rather than insert. The length must be at least
93    /// `min_config_columns`.
94    #[serde(default)]
95    #[ts(as = "Option<_>")]
96    #[ts(optional)]
97    pub config_column_names: Vec<String>,
98
99    /// Group-rollup modes the plugin accepts, in preference order.
100    /// The first entry that matches a feature flag becomes the default.
101    #[serde(default)]
102    #[ts(as = "Option<_>")]
103    #[ts(optional)]
104    pub group_rollup_modes: Option<Vec<GroupRollupMode>>,
105
106    /// Plugin load priority. Higher numbers win; ties resolve in
107    /// registration order. The highest-priority plugin is loaded by
108    /// default unless `restore({ plugin })` overrides it.
109    #[serde(default)]
110    #[ts(as = "Option<_>")]
111    #[ts(optional)]
112    pub priority: Option<i32>,
113
114    /// Whether this plugin opts into per-column style controls in the
115    /// settings sidebar. When `true`, the StyleTab is shown for active
116    /// columns and the plugin's `column_config_schema` is queried for
117    /// the per-column field set. When `false` or omitted, no StyleTab
118    /// is shown.
119    #[serde(default)]
120    #[ts(as = "Option<_>")]
121    #[ts(optional)]
122    pub can_render_column_styles: bool,
123}
124
125impl PluginStaticConfig {
126    /// `true` if dropping a column at `index` should swap with the
127    /// column already there rather than insert. Only the named slots
128    /// (`config_column_names[..len()-1]`) participate in swap behaviour;
129    /// the trailing unnamed tail inserts.
130    pub fn is_swap(&self, index: usize) -> bool {
131        !self.config_column_names.is_empty() && index < self.config_column_names.len() - 1
132    }
133
134    pub fn get_group_rollups(&self, rollup_features: &[GroupRollupMode]) -> Vec<GroupRollupMode> {
135        self.group_rollup_modes
136            .clone()
137            .map(|x| {
138                x.into_iter()
139                    .filter(|y| rollup_features.is_empty() || rollup_features.contains(y))
140                    .collect()
141            })
142            .unwrap_or_default()
143    }
144}