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}