use yew::prelude::*;
use super::containers::select::*;
use super::LocalStyle;
use crate::config::*;
use crate::js::*;
use crate::model::*;
use crate::renderer::*;
use crate::session::*;
use crate::utils::*;
use crate::*;
#[derive(Properties, PartialEq)]
pub struct PluginSelectorProps {
pub renderer: Renderer,
pub session: Session,
#[cfg(test)]
#[prop_or_default]
pub weak_link: WeakScope<PluginSelector>,
}
derive_model!(Renderer, Session for PluginSelectorProps);
#[derive(Debug)]
pub enum PluginSelectorMsg {
ComponentSelectPlugin(String),
RendererSelectPlugin(String),
OpenMenu,
}
use PluginSelectorMsg::*;
pub struct PluginSelector {
options: Vec<SelectItem<String>>,
is_open: bool,
_plugin_sub: Subscription,
}
impl Component for PluginSelector {
type Message = PluginSelectorMsg;
type Properties = PluginSelectorProps;
fn create(ctx: &Context<Self>) -> Self {
enable_weak_link_test!(ctx.props(), ctx.link());
let options = generate_plugin_optgroups(&ctx.props().renderer);
let _plugin_sub = ctx.props().renderer.plugin_changed.add_listener({
let link = ctx.link().clone();
move |plugin: JsPerspectiveViewerPlugin| {
let name = plugin.name();
link.send_message(PluginSelectorMsg::RendererSelectPlugin(name))
}
});
Self {
options,
is_open: false,
_plugin_sub,
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
RendererSelectPlugin(_plugin_name) => true,
ComponentSelectPlugin(plugin_name) => {
ctx.props()
.renderer
.update_plugin(&PluginUpdate::Update(plugin_name))
.unwrap();
let mut update = ViewConfigUpdate::default();
ctx.props()
.session
.set_update_column_defaults(&mut update, &ctx.props().renderer.metadata());
ApiFuture::spawn(ctx.props().update_and_render(update));
self.is_open = false;
false
}
OpenMenu => {
self.is_open = !self.is_open;
true
}
}
}
fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let callback = ctx.link().callback(|_| OpenMenu);
let plugin_name = ctx.props().renderer.get_active_plugin().unwrap().name();
let plugin_name2 = plugin_name.clone();
let class = if self.is_open { "open" } else { "" };
let items = self.options.iter().map(|item| match item {
SelectItem::OptGroup(_cat, items) => html! {
items.iter().filter(|x| *x != &plugin_name2).map(|x| {
let callback = ctx.link().callback(ComponentSelectPlugin);
html! {
<PluginSelect
name={ x.to_owned() }
on_click={ callback } />
}
}).collect::<Html>()
},
SelectItem::Option(_item) => html! {},
});
html_template! {
<LocalStyle href={ css!("plugin-selector") } />
<div id="plugin_selector_container" { class }>
<PluginSelect
name={ plugin_name }
on_click={ callback } />
<div id="plugin_selector_border"></div>
if self.is_open {
{
items.collect::<Html>()
}
}
</div>
}
}
}
fn generate_plugin_optgroups(renderer: &Renderer) -> Vec<SelectItem<String>> {
let mut options = renderer
.get_all_plugin_categories()
.into_iter()
.map(|(category, value)| SelectItem::OptGroup(category.into(), value))
.collect::<Vec<_>>();
options.sort_by_key(|x| x.name());
options
}
#[derive(Properties, PartialEq)]
struct PluginSelectProps {
name: String,
on_click: Callback<String>,
}
#[function_component]
fn PluginSelect(props: &PluginSelectProps) -> Html {
let name = props.name.clone().tee::<2>();
html! {
<div
class="plugin-select-item"
data-plugin={name.0 }
onclick={ props.on_click.reform(move |_| name.1.clone()) }>
<span class="plugin-select-item-name">
{ &props.name }
</span>
</div>
}
}