perspective_viewer/components/
plugin_selector.rs1use yew::prelude::*;
14
15use crate::utils::PtrEqRc;
16
17#[derive(Properties, PartialEq)]
20pub struct PluginSelectorProps {
21 pub plugin_name: Option<String>,
23
24 pub available_plugins: PtrEqRc<Vec<String>>,
26
27 pub on_select_plugin: Callback<String>,
29}
30
31#[derive(Debug)]
32pub enum PluginSelectorMsg {
33 ComponentSelectPlugin(String),
34 OpenMenu,
35}
36
37use PluginSelectorMsg::*;
38
39pub struct PluginSelector {
40 is_open: bool,
41}
42
43impl Component for PluginSelector {
44 type Message = PluginSelectorMsg;
45 type Properties = PluginSelectorProps;
46
47 fn create(_ctx: &Context<Self>) -> Self {
48 Self { is_open: false }
49 }
50
51 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
52 match msg {
53 ComponentSelectPlugin(plugin_name) => {
54 ctx.props().on_select_plugin.emit(plugin_name);
55 self.is_open = false;
56 false
57 },
58 OpenMenu => {
59 self.is_open = !self.is_open;
60 true
61 },
62 }
63 }
64
65 fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
66 true
67 }
68
69 fn view(&self, ctx: &Context<Self>) -> Html {
70 let callback = ctx.link().callback(|_| OpenMenu);
71 let plugin_name = ctx.props().plugin_name.clone().unwrap_or_default();
72 let plugin_name2 = plugin_name.clone();
73 let class = if self.is_open { "open" } else { "" };
74 let items = ctx
75 .props()
76 .available_plugins
77 .iter()
78 .filter(|x| x.as_str() != plugin_name2.as_str())
79 .map(|x| {
80 let callback = ctx.link().callback(ComponentSelectPlugin);
81 html! { <PluginSelect name={x.to_owned()} on_click={callback} /> }
82 });
83
84 html! {
85 <>
86 <div id="plugin_selector_container" {class}>
87 <PluginSelect name={plugin_name} on_click={callback} />
88 <div id="plugin_selector_border" />
89 if self.is_open {
90 <div class="plugin-selector-options scrollable">
91 { items.collect::<Html>() }
92 </div>
93 }
94 </div>
95 </>
96 }
97 }
98}
99
100#[derive(Properties, PartialEq)]
101struct PluginSelectProps {
102 name: String,
103 on_click: Callback<String>,
104}
105
106#[function_component]
107fn PluginSelect(props: &PluginSelectProps) -> Html {
108 let name = props.name.clone();
109 let path: String = props
110 .name
111 .chars()
112 .map(|x| {
113 if x.is_alphanumeric() {
114 x.to_ascii_lowercase()
115 } else {
116 '-'
117 }
118 })
119 .collect();
120
121 html! {
122 <div
123 class="plugin-select-item"
124 data-plugin={name.clone()}
125 style={format!("--default-column-title:var(--psp-plugin-name--{}--content, \"{}\")", path, props.name)}
126 onclick={props.on_click.reform(move |_| name.clone())}
127 >
128 <span class="icon" />
129 <span class="plugin-select-item-name" />
130 </div>
131 }
132}