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