Skip to content

Commit 85997ce

Browse files
committed
Add copy dropdown menu
1 parent d0a1823 commit 85997ce

File tree

24 files changed

+383
-147
lines changed

24 files changed

+383
-147
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (c) 2018, the Perspective Authors.
4+
//
5+
// This file is part of the Perspective library, distributed under the terms
6+
// of the Apache License 2.0. The full license can be found in the LICENSE
7+
// file.
8+
9+
use super::containers::dropdown_menu::*;
10+
use crate::model::*;
11+
12+
pub fn get_menu_items(has_render: bool) -> Vec<CopyDropDownMenuItem> {
13+
vec![
14+
CopyDropDownMenuItem::OptGroup(
15+
"Current View",
16+
if has_render {
17+
vec![ExportMethod::Csv, ExportMethod::Json, ExportMethod::Png]
18+
} else {
19+
vec![ExportMethod::Csv, ExportMethod::Json]
20+
},
21+
),
22+
CopyDropDownMenuItem::OptGroup(
23+
"All",
24+
vec![ExportMethod::CsvAll, ExportMethod::JsonAll],
25+
),
26+
CopyDropDownMenuItem::OptGroup("Config", vec![ExportMethod::JsonConfig]),
27+
]
28+
}
29+
30+
pub type CopyDropDownMenu = DropDownMenu<ExportMethod>;
31+
pub type CopyDropDownMenuProps = DropDownMenuProps<ExportMethod>;
32+
pub type CopyDropDownMenuMsg = DropDownMenuMsg;
33+
pub type CopyDropDownMenuItem = DropDownMenuItem<ExportMethod>;

rust/perspective-viewer/src/rust/components/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//! `components` contains all Yew `Component` types, but only exports the 4 necessary
1010
//! for public Custom Elements. The rest are internal components of these 4.
1111
12+
pub mod copy_dropdown;
1213
pub mod export_dropdown;
1314
pub mod expression_editor;
1415
pub mod filter_dropdown;

rust/perspective-viewer/src/rust/components/status_bar.rs

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88

99
use crate::components::containers::select::*;
1010
use crate::components::status_bar_counter::StatusBarRowsCounter;
11+
use crate::custom_elements::copy_dropdown::*;
1112
use crate::custom_elements::export_dropdown::*;
1213
use crate::renderer::*;
1314
use crate::session::*;
1415
use crate::utils::*;
1516
use crate::*;
16-
use wasm_bindgen_futures::spawn_local;
17+
1718
use web_sys::*;
1819
use yew::prelude::*;
1920

@@ -41,7 +42,7 @@ impl PartialEq for StatusBarProps {
4142
pub enum StatusBarMsg {
4243
Reset(bool),
4344
Export,
44-
Copy(bool),
45+
Copy,
4546
SetThemeConfig((Vec<String>, Option<usize>)),
4647
SetTheme(String),
4748
TableStatsChanged,
@@ -54,7 +55,9 @@ pub struct StatusBar {
5455
theme: Option<String>,
5556
themes: Vec<String>,
5657
export_ref: NodeRef,
58+
copy_ref: NodeRef,
5759
export_dropdown: Option<ExportDropDownMenuElement>,
60+
copy_dropdown: Option<CopyDropDownMenuElement>,
5861
_sub: [Subscription; 4],
5962
}
6063

@@ -91,6 +94,8 @@ impl Component for StatusBar {
9194
_sub,
9295
theme: None,
9396
themes: vec![],
97+
copy_dropdown: None,
98+
copy_ref: NodeRef::default(),
9499
export_dropdown: None,
95100
export_ref: NodeRef::default(),
96101
is_updating: 0,
@@ -128,20 +133,25 @@ impl Component for StatusBar {
128133
false
129134
}
130135
StatusBarMsg::Export => {
131-
let session = ctx.props().session.clone();
132-
let renderer = ctx.props().renderer.clone();
133-
let export_dropdown = ExportDropDownMenuElement::new(session, renderer);
134136
let target = self.export_ref.cast::<HtmlElement>().unwrap();
135-
export_dropdown.open(target);
136-
self.export_dropdown = Some(export_dropdown);
137+
self.export_dropdown
138+
.get_or_insert_with(|| {
139+
let session = ctx.props().session.clone();
140+
let renderer = ctx.props().renderer.clone();
141+
ExportDropDownMenuElement::new(session, renderer)
142+
})
143+
.open(target);
137144
false
138145
}
139-
StatusBarMsg::Copy(flat) => {
140-
let session = ctx.props().session.clone();
141-
spawn_local(async move {
142-
session.copy_to_clipboard(flat).await.expect("Copy failed");
143-
});
144-
146+
StatusBarMsg::Copy => {
147+
let target = self.copy_ref.cast::<HtmlElement>().unwrap();
148+
self.copy_dropdown
149+
.get_or_insert_with(|| {
150+
let session = ctx.props().session.clone();
151+
let renderer = ctx.props().renderer.clone();
152+
CopyDropDownMenuElement::new(session, renderer)
153+
})
154+
.open(target);
145155
false
146156
}
147157
}
@@ -161,10 +171,7 @@ impl Component for StatusBar {
161171
.callback(|event: MouseEvent| StatusBarMsg::Reset(event.shift_key()));
162172

163173
let export = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Export);
164-
165-
let copy = ctx
166-
.link()
167-
.callback(|event: MouseEvent| StatusBarMsg::Copy(event.shift_key()));
174+
let copy = ctx.link().callback(|_: MouseEvent| StatusBarMsg::Copy);
168175

169176
let theme_button = match &self.theme {
170177
None => html! {},
@@ -211,7 +218,12 @@ impl Component for StatusBar {
211218

212219
<span>{ "Export" }</span>
213220
</span>
214-
<span id="copy" class="button" onmousedown={ copy }>
221+
<span
222+
ref={ self.copy_ref.clone() }
223+
id="copy"
224+
class="button"
225+
onmousedown={ copy }>
226+
215227
<span>{ "Copy" }</span>
216228
</span>
217229
{ theme_button }

rust/perspective-viewer/src/rust/components/tests/status_bar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn test_callbacks_invoked() {
5555
status_bar.send_message(StatusBarMsg::Export);
5656
assert_eq!(token.get(), 0);
5757
let status_bar = link.borrow().clone().unwrap();
58-
status_bar.send_message(StatusBarMsg::Copy(false));
58+
status_bar.send_message(StatusBarMsg::Copy);
5959
assert_eq!(token.get(), 0);
6060
let status_bar = link.borrow().clone().unwrap();
6161
status_bar.send_message(StatusBarMsg::Reset(false));
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (c) 2018, the Perspective Authors.
4+
//
5+
// This file is part of the Perspective library, distributed under the terms
6+
// of the Apache License 2.0. The full license can be found in the LICENSE
7+
// file.
8+
9+
use crate::components::copy_dropdown::*;
10+
use crate::custom_elements::modal::*;
11+
use crate::model::*;
12+
use crate::renderer::Renderer;
13+
use crate::session::Session;
14+
use crate::utils::*;
15+
16+
use js_intern::*;
17+
use std::cell::RefCell;
18+
use std::rc::Rc;
19+
use wasm_bindgen::prelude::*;
20+
use wasm_bindgen::JsCast;
21+
use wasm_bindgen_futures::spawn_local;
22+
use web_sys::*;
23+
use yew::prelude::*;
24+
25+
#[wasm_bindgen]
26+
#[derive(Clone)]
27+
pub struct CopyDropDownMenuElement {
28+
modal: ModalElement<CopyDropDownMenu>,
29+
target: Rc<RefCell<Option<HtmlElement>>>,
30+
}
31+
32+
impl ResizableMessage for <CopyDropDownMenu as Component>::Message {
33+
fn resize(y: i32, x: i32, _: bool) -> Self {
34+
CopyDropDownMenuMsg::SetPos(y, x)
35+
}
36+
}
37+
38+
impl CopyDropDownMenuElement {
39+
pub fn new(session: Session, renderer: Renderer) -> CopyDropDownMenuElement {
40+
let document = window().unwrap().document().unwrap();
41+
let dropdown = document
42+
.create_element("perspective-copy-dropdown")
43+
.unwrap()
44+
.unchecked_into::<HtmlElement>();
45+
46+
let modal_rc: Rc<RefCell<Option<ModalElement<CopyDropDownMenu>>>> =
47+
Default::default();
48+
49+
let callback = Callback::from({
50+
let modal_rc = modal_rc.clone();
51+
let renderer = renderer.clone();
52+
move |x: ExportMethod| {
53+
let js_task = (&session, &renderer).export_method_to_jsvalue(x);
54+
let copy_task = copy_to_clipboard(js_task, x.mimetype());
55+
let modal = modal_rc.borrow().clone().unwrap();
56+
spawn_local(async move {
57+
let result = copy_task.await;
58+
crate::js_log_maybe! {
59+
result?;
60+
modal.hide()?;
61+
}
62+
})
63+
}
64+
});
65+
66+
let plugin = renderer.get_active_plugin().unwrap();
67+
let has_render = js_sys::Reflect::has(&plugin, js_intern!("render")).unwrap();
68+
let values = Rc::new(get_menu_items(has_render));
69+
let props = CopyDropDownMenuProps { values, callback };
70+
let modal = ModalElement::new(dropdown, props, true);
71+
*modal_rc.borrow_mut() = Some(modal.clone());
72+
CopyDropDownMenuElement {
73+
modal,
74+
target: Default::default(),
75+
}
76+
}
77+
78+
pub fn open(&self, target: HtmlElement) {
79+
self.modal.open(target, None);
80+
}
81+
82+
pub fn hide(&self) -> Result<(), JsValue> {
83+
self.modal.hide()
84+
}
85+
86+
pub fn connected_callback(&self) {}
87+
}

rust/perspective-viewer/src/rust/custom_elements/export_dropdown.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl ExportDropDownMenuElement {
3838
pub fn new(session: Session, renderer: Renderer) -> ExportDropDownMenuElement {
3939
let document = window().unwrap().document().unwrap();
4040
let dropdown = document
41-
.create_element("perspective-filter-dropdown")
41+
.create_element("perspective-export-dropdown")
4242
.unwrap()
4343
.unchecked_into::<HtmlElement>();
4444

rust/perspective-viewer/src/rust/custom_elements/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// of the Apache License 2.0. The full license can be found in the LICENSE
77
// file.
88

9+
pub mod copy_dropdown;
910
pub mod export_dropdown;
1011
pub mod expression_editor;
1112
pub mod filter_dropdown;

rust/perspective-viewer/src/rust/custom_elements/viewer.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,16 @@ impl PerspectiveViewerElement {
415415
/// - `flat` Whether to use the current `ViewConfig` to generate this data, or use
416416
/// the default.
417417
pub fn js_copy(&self, flat: bool) -> js_sys::Promise {
418-
let session = self.session.clone();
418+
let method = if flat {
419+
ExportMethod::CsvAll
420+
} else {
421+
ExportMethod::Csv
422+
};
423+
424+
let js_task = self.export_method_to_jsvalue(method);
425+
let copy_task = copy_to_clipboard(js_task, MimeType::TextPlain);
419426
future_to_promise(async move {
420-
session.copy_to_clipboard(flat).await?;
427+
copy_task.await?;
421428
Ok(JsValue::UNDEFINED)
422429
})
423430
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright (c) 2018, the Perspective Authors.
4+
//
5+
// This file is part of the Perspective library, distributed under the terms
6+
// of the Apache License 2.0. The full license can be found in the LICENSE
7+
// file.
8+
9+
use wasm_bindgen::prelude::*;
10+
11+
#[wasm_bindgen(inline_js = "export const ClipboardItem = window.ClipboardItem")]
12+
extern "C" {
13+
pub type ClipboardItem;
14+
15+
#[wasm_bindgen(constructor, js_class = "ClipboardItem")]
16+
pub fn new(files: &js_sys::Object) -> ClipboardItem;
17+
}

rust/perspective-viewer/src/rust/js/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// of the Apache License 2.0. The full license can be found in the LICENSE
77
// file.
88

9+
pub mod clipboard_item;
910
pub mod monaco;
1011
pub mod perspective;
1112
// pub mod perspective_viewer;

0 commit comments

Comments
 (0)