perspective_viewer/components/
export_dropdown.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 std::rc::Rc;
14
15use presentation::Presentation;
16use yew::prelude::*;
17
18use super::containers::dropdown_menu::*;
19use super::modal::{ModalLink, SetModalLink};
20use super::style::StyleProvider;
21use crate::model::*;
22use crate::renderer::*;
23use crate::utils::*;
24use crate::*;
25
26pub type ExportDropDownMenuItem = DropDownMenuItem<ExportFile>;
27
28#[derive(Properties, PartialEq)]
29pub struct ExportDropDownMenuProps {
30    pub renderer: Renderer,
31    pub presentation: Presentation,
32    pub callback: Callback<ExportFile>,
33
34    #[prop_or_default]
35    weak_link: WeakScope<ExportDropDownMenu>,
36}
37
38impl ModalLink<ExportDropDownMenu> for ExportDropDownMenuProps {
39    fn weak_link(&self) -> &'_ utils::WeakScope<ExportDropDownMenu> {
40        &self.weak_link
41    }
42}
43
44#[derive(Default)]
45pub struct ExportDropDownMenu {
46    title: String,
47    _sub: Option<Subscription>,
48    input_ref: NodeRef,
49    invalid: bool,
50}
51
52pub enum ExportDropDownMenuMsg {
53    TitleChange,
54}
55
56fn get_menu_items(name: &str, has_render: bool) -> Vec<ExportDropDownMenuItem> {
57    vec![
58        ExportDropDownMenuItem::OptGroup(
59            "Current View".into(),
60            if has_render {
61                vec![
62                    ExportMethod::Csv.new_file(name),
63                    ExportMethod::Json.new_file(name),
64                    ExportMethod::Ndjson.new_file(name),
65                    ExportMethod::Arrow.new_file(name),
66                    ExportMethod::Html.new_file(name),
67                    ExportMethod::Png.new_file(name),
68                ]
69            } else {
70                vec![
71                    ExportMethod::Csv.new_file(name),
72                    ExportMethod::Json.new_file(name),
73                    ExportMethod::Ndjson.new_file(name),
74                    ExportMethod::Arrow.new_file(name),
75                    ExportMethod::Html.new_file(name),
76                ]
77            },
78        ),
79        ExportDropDownMenuItem::OptGroup("All".into(), vec![
80            ExportMethod::CsvAll.new_file(name),
81            ExportMethod::JsonAll.new_file(name),
82            ExportMethod::NdjsonAll.new_file(name),
83            ExportMethod::ArrowAll.new_file(name),
84        ]),
85        ExportDropDownMenuItem::OptGroup("Config".into(), vec![
86            ExportMethod::JsonConfig.new_file(name),
87        ]),
88    ]
89}
90
91impl Component for ExportDropDownMenu {
92    type Message = ExportDropDownMenuMsg;
93    type Properties = ExportDropDownMenuProps;
94
95    fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
96        let callback = ctx.link().callback(|_| ExportDropDownMenuMsg::TitleChange);
97        let plugin = ctx.props().renderer.get_active_plugin().unwrap();
98        let has_render = js_sys::Reflect::has(&plugin, js_intern::js_intern!("render")).unwrap();
99        html! {
100            <StyleProvider>
101                <span class="dropdown-group-label">{ "Save as" }</span>
102                <input
103                    class={if self.invalid { "invalid" } else { "" }}
104                    oninput={callback}
105                    ref={&self.input_ref}
106                    value={self.title.to_owned()}
107                />
108                <DropDownMenu<ExportFile>
109                    values={Rc::new(get_menu_items(&self.title, has_render))}
110                    callback={&ctx.props().callback}
111                />
112            </StyleProvider>
113        }
114    }
115
116    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
117        match msg {
118            ExportDropDownMenuMsg::TitleChange => {
119                self.title = self
120                    .input_ref
121                    .cast::<web_sys::HtmlInputElement>()
122                    .unwrap()
123                    .value();
124
125                self.invalid = self.title.is_empty();
126                true
127            },
128        }
129    }
130
131    fn create(ctx: &Context<Self>) -> Self {
132        ctx.set_modal_link();
133        let _sub = Some(
134            ctx.props()
135                .renderer
136                .plugin_changed
137                .add_listener(ctx.link().callback(|_| ExportDropDownMenuMsg::TitleChange)),
138        );
139
140        Self {
141            title: ctx
142                .props()
143                .presentation
144                .get_title()
145                .unwrap_or_else(|| "untitled".to_owned()),
146            _sub,
147            ..Default::default()
148        }
149    }
150}