@perspective-dev/viewer 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -0
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/pro-dark.css +1 -1
  17. package/dist/css/pro.css +1 -1
  18. package/dist/css/solarized-dark.css +1 -1
  19. package/dist/css/solarized.css +1 -1
  20. package/dist/css/themes.css +1 -1
  21. package/dist/css/vaporwave.css +1 -1
  22. package/dist/esm/extensions.d.ts +32 -1
  23. package/dist/esm/perspective-viewer.d.ts +1 -0
  24. package/dist/esm/perspective-viewer.inline.js +2 -2
  25. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  26. package/dist/esm/perspective-viewer.js +2 -2
  27. package/dist/esm/perspective-viewer.js.map +4 -4
  28. package/dist/esm/ts-rs/GroupRollupMode.d.ts +1 -0
  29. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +2 -0
  30. package/dist/wasm/perspective-viewer.d.ts +57 -53
  31. package/dist/wasm/perspective-viewer.js +197 -164
  32. package/dist/wasm/perspective-viewer.wasm +0 -0
  33. package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
  34. package/package.json +9 -6
  35. package/src/{less/aggregate-selector.less → css/aggregate-selector.css} +23 -20
  36. package/src/css/column-dropdown.css +109 -0
  37. package/src/{less/column-selector.less → css/column-selector.css} +161 -159
  38. package/src/{less/column-settings-panel.less → css/column-settings-panel.css} +69 -59
  39. package/src/{less/column-style.less → css/column-style.css} +52 -66
  40. package/src/{less/column-symbol-attributes.less → css/column-symbol-attributes.css} +15 -14
  41. package/src/css/config-selector.css +441 -0
  42. package/src/{less/containers/dropdown-menu.less → css/containers/dropdown-menu.css} +20 -19
  43. package/src/{less/containers/pairs-list.less → css/containers/pairs-list.css} +13 -12
  44. package/src/{themes/variables.less → css/containers/scroll-panel.css} +25 -22
  45. package/src/{less/containers/split-panel.less → css/containers/split-panel.css} +15 -14
  46. package/src/{less/containers/tabs.less → css/containers/tabs.css} +17 -19
  47. package/src/css/dom/checkbox.css +102 -0
  48. package/src/css/dom/scrollbar.css +35 -0
  49. package/src/{less/dom/select.less → css/dom/select.css} +17 -18
  50. package/src/{less/empty-column.less → css/empty-column.css} +19 -18
  51. package/src/{less/expression-editor.less → css/expression-editor.css} +19 -18
  52. package/src/{less/filter-dropdown.less → css/filter-dropdown.css} +12 -11
  53. package/src/{less/filter-item.less → css/filter-item.css} +16 -15
  54. package/src/{less/form/code-editor.less → css/form/code-editor.css} +26 -30
  55. package/src/{less/form/debug.less → css/form/debug.css} +19 -18
  56. package/src/{less/function-dropdown.less → css/function-dropdown.css} +12 -11
  57. package/src/css/plugin-selector.css +261 -0
  58. package/src/{less/render-warning.less → css/render-warning.css} +18 -17
  59. package/src/{less/status-bar.less → css/status-bar.css} +156 -144
  60. package/src/css/type-icon.css +116 -0
  61. package/src/{less/viewer.less → css/viewer.css} +112 -146
  62. package/src/rust/components/column_dropdown.rs +229 -119
  63. package/src/rust/components/column_selector/active_column.rs +81 -62
  64. package/src/rust/components/column_selector/add_expression_button.rs +1 -0
  65. package/src/rust/components/column_selector/aggregate_selector.rs +25 -15
  66. package/src/rust/components/column_selector/config_selector.rs +374 -185
  67. package/src/rust/components/column_selector/empty_column.rs +2 -2
  68. package/src/rust/components/column_selector/expr_edit_button.rs +8 -2
  69. package/src/rust/components/column_selector/filter_column.rs +37 -26
  70. package/src/rust/components/column_selector/inactive_column.rs +41 -29
  71. package/src/rust/components/column_selector/invalid_column.rs +7 -18
  72. package/src/rust/components/column_selector/pivot_column.rs +21 -10
  73. package/src/rust/components/column_selector/sort_column.rs +23 -13
  74. package/src/rust/components/column_selector.rs +189 -100
  75. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +1 -1
  76. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +3 -2
  77. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +3 -2
  78. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +2 -3
  79. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +7 -1
  80. package/src/rust/components/column_settings_sidebar/style_tab.rs +153 -112
  81. package/src/rust/components/column_settings_sidebar.rs +91 -53
  82. package/src/rust/components/containers/dragdrop_list.rs +29 -7
  83. package/src/rust/components/containers/scroll_panel.rs +8 -1
  84. package/src/rust/components/containers/select.rs +3 -3
  85. package/src/rust/components/containers/sidebar_close_button.rs +1 -1
  86. package/src/rust/components/containers/split_panel.rs +3 -2
  87. package/src/rust/components/containers/tab_list.rs +1 -1
  88. package/src/rust/components/copy_dropdown.rs +7 -28
  89. package/src/rust/components/datetime_column_style/custom.rs +2 -2
  90. package/src/rust/components/datetime_column_style/simple.rs +2 -2
  91. package/src/rust/components/datetime_column_style.rs +4 -2
  92. package/src/rust/components/editable_header.rs +7 -4
  93. package/src/rust/components/empty_row.rs +1 -1
  94. package/src/rust/components/export_dropdown.rs +4 -30
  95. package/src/rust/components/expression_editor.rs +19 -10
  96. package/src/rust/components/filter_dropdown.rs +246 -102
  97. package/src/rust/components/font_loader.rs +11 -28
  98. package/src/rust/components/form/code_editor.rs +17 -2
  99. package/src/rust/components/form/color_range_selector.rs +19 -6
  100. package/src/rust/components/form/debug.rs +30 -13
  101. package/src/rust/components/function_dropdown.rs +186 -113
  102. package/src/rust/components/main_panel.rs +71 -89
  103. package/src/rust/components/mod.rs +1 -1
  104. package/src/rust/components/modal.rs +7 -1
  105. package/src/rust/components/number_column_style.rs +22 -7
  106. package/src/rust/components/plugin_selector.rs +34 -92
  107. package/src/rust/components/portal.rs +274 -0
  108. package/src/rust/components/render_warning.rs +72 -123
  109. package/src/rust/components/settings_panel.rs +115 -11
  110. package/src/rust/components/status_bar.rs +222 -98
  111. package/src/rust/components/status_bar_counter.rs +8 -20
  112. package/src/rust/components/status_indicator.rs +64 -111
  113. package/src/rust/components/string_column_style.rs +2 -2
  114. package/src/rust/components/style/style_cache.rs +5 -1
  115. package/src/rust/components/viewer.rs +391 -39
  116. package/src/rust/custom_elements/copy_dropdown.rs +102 -21
  117. package/src/rust/custom_elements/export_dropdown.rs +102 -20
  118. package/src/rust/custom_elements/mod.rs +0 -7
  119. package/src/rust/custom_elements/modal.rs +7 -103
  120. package/src/rust/custom_elements/viewer.rs +99 -35
  121. package/src/rust/custom_events.rs +23 -2
  122. package/src/rust/dragdrop.rs +149 -10
  123. package/src/{less/containers/scroll-panel.less → rust/engines.rs} +15 -13
  124. package/src/rust/js/plugin.rs +20 -1
  125. package/src/rust/lib.rs +5 -4
  126. package/src/rust/presentation/props.rs +39 -0
  127. package/src/rust/presentation/sheets.rs +3 -3
  128. package/src/rust/presentation.rs +44 -8
  129. package/src/rust/renderer/limits.rs +32 -3
  130. package/src/{less/dom/scrollbar.less → rust/renderer/props.rs} +18 -19
  131. package/src/rust/renderer/registry.rs +8 -1
  132. package/src/rust/renderer.rs +83 -9
  133. package/src/rust/session/column_defaults_update.rs +18 -0
  134. package/src/rust/session/metadata.rs +23 -2
  135. package/src/rust/session/props.rs +178 -0
  136. package/src/rust/session/replace_expression_update.rs +1 -0
  137. package/src/rust/session.rs +124 -117
  138. package/src/rust/tasks/column_locator.rs +133 -0
  139. package/src/rust/{model → tasks}/columns_iter_set.rs +14 -23
  140. package/src/rust/{model → tasks}/edit_expression.rs +34 -10
  141. package/src/rust/{model → tasks}/eject.rs +2 -2
  142. package/src/rust/{model → tasks}/get_viewer_config.rs +0 -11
  143. package/src/rust/{model → tasks}/intersection_observer.rs +22 -4
  144. package/src/{less/containers/radio-list.less → rust/tasks/is_invalid_drop.rs} +21 -14
  145. package/src/rust/tasks/mod.rs +52 -0
  146. package/src/rust/{model → tasks}/plugin_column_styles.rs +69 -46
  147. package/src/rust/{model → tasks}/resize_observer.rs +39 -6
  148. package/src/rust/{model → tasks}/send_plugin_config.rs +1 -1
  149. package/src/rust/tasks/structural.rs +53 -0
  150. package/src/rust/utils/mod.rs +4 -0
  151. package/src/rust/utils/modal_position.rs +110 -0
  152. package/src/rust/utils/ptr_eq_rc.rs +74 -0
  153. package/src/rust/utils/pubsub.rs +11 -1
  154. package/src/svg/bg-pattern.png +0 -0
  155. package/src/svg/close-icon.svg +1 -1
  156. package/src/svg/expression.svg +1 -1
  157. package/src/svg/mega-menu-icons-candlestick.svg +1 -1
  158. package/src/svg/mega-menu-icons-datagrid.svg +1 -2
  159. package/src/svg/mega-menu-icons-heatmap.svg +1 -1
  160. package/src/svg/mega-menu-icons-map-scatter.svg +1 -1
  161. package/src/svg/mega-menu-icons-ohlc.svg +1 -1
  162. package/src/svg/mega-menu-icons-sunburst.svg +1 -1
  163. package/src/svg/mega-menu-icons-treemap.svg +1 -1
  164. package/src/svg/mega-menu-icons-x-bar.svg +1 -1
  165. package/src/svg/mega-menu-icons-x-y-line.svg +1 -1
  166. package/src/svg/mega-menu-icons-x-y-scatter.svg +1 -1
  167. package/src/svg/mega-menu-icons-y-area.svg +1 -1
  168. package/src/svg/mega-menu-icons-y-bar.svg +1 -1
  169. package/src/svg/mega-menu-icons-y-line.svg +1 -1
  170. package/src/svg/mega-menu-icons-y-scatter.svg +1 -1
  171. package/src/svg/radio-hover.svg +1 -1
  172. package/src/svg/radio-off.svg +1 -1
  173. package/src/svg/radio-on.svg +1 -1
  174. package/src/themes/botanical.css +157 -0
  175. package/src/themes/defaults.css +139 -0
  176. package/src/themes/dracula.css +233 -0
  177. package/src/themes/gruvbox-dark.css +255 -0
  178. package/src/themes/gruvbox.css +134 -0
  179. package/src/themes/icons.css +124 -0
  180. package/src/themes/intl/de.css +102 -0
  181. package/src/themes/intl/es.css +102 -0
  182. package/src/themes/intl/fr.css +102 -0
  183. package/src/themes/intl/ja.css +102 -0
  184. package/src/themes/intl/pt.css +102 -0
  185. package/src/themes/intl/zh.css +102 -0
  186. package/src/themes/intl.css +102 -0
  187. package/src/themes/monokai.css +233 -0
  188. package/src/themes/pro-dark.css +158 -0
  189. package/src/themes/{themes.less → pro.css} +17 -20
  190. package/src/themes/solarized-dark.css +135 -0
  191. package/src/themes/solarized.css +95 -0
  192. package/src/themes/themes.css +22 -0
  193. package/src/themes/vaporwave.css +256 -0
  194. package/src/ts/extensions.ts +73 -2
  195. package/src/ts/perspective-viewer.ts +1 -0
  196. package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
  197. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -1
  198. package/tsconfig.json +1 -0
  199. package/dist/css/variables.css +0 -0
  200. package/src/less/column-dropdown.less +0 -95
  201. package/src/less/config-selector.less +0 -363
  202. package/src/less/dom/checkbox.less +0 -100
  203. package/src/less/plugin-selector.less +0 -183
  204. package/src/less/type-icon.less +0 -68
  205. package/src/rust/components/error_message.rs +0 -56
  206. package/src/rust/custom_elements/column_dropdown.rs +0 -123
  207. package/src/rust/custom_elements/filter_dropdown.rs +0 -179
  208. package/src/rust/custom_elements/function_dropdown.rs +0 -115
  209. package/src/rust/model/column_locator.rs +0 -82
  210. package/src/rust/model/is_invalid_drop.rs +0 -36
  211. package/src/rust/model/mod.rs +0 -100
  212. package/src/rust/model/reset_all.rs +0 -38
  213. package/src/rust/model/structural.rs +0 -244
  214. package/src/themes/dracula.less +0 -101
  215. package/src/themes/gruvbox-dark.less +0 -116
  216. package/src/themes/gruvbox.less +0 -152
  217. package/src/themes/icons.less +0 -130
  218. package/src/themes/intl/de.less +0 -102
  219. package/src/themes/intl/es.less +0 -102
  220. package/src/themes/intl/fr.less +0 -102
  221. package/src/themes/intl/ja.less +0 -102
  222. package/src/themes/intl/pt.less +0 -102
  223. package/src/themes/intl/zh.less +0 -102
  224. package/src/themes/intl.less +0 -102
  225. package/src/themes/monokai.less +0 -107
  226. package/src/themes/pro-dark.less +0 -147
  227. package/src/themes/pro.less +0 -186
  228. package/src/themes/solarized-dark.less +0 -78
  229. package/src/themes/solarized.less +0 -102
  230. package/src/themes/vaporwave.less +0 -145
  231. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
  232. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
  233. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
  234. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
  235. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline4.js +0 -0
  236. /package/src/rust/{model → tasks}/copy_export.rs +0 -0
  237. /package/src/rust/{model → tasks}/export_app.rs +0 -0
  238. /package/src/rust/{model → tasks}/export_method.rs +0 -0
  239. /package/src/rust/{model → tasks}/restore_and_render.rs +0 -0
  240. /package/src/rust/{model → tasks}/update_and_render.rs +0 -0
@@ -13,24 +13,91 @@
13
13
  use std::cell::RefCell;
14
14
  use std::rc::Rc;
15
15
 
16
- use ::perspective_js::utils::{global, *};
16
+ use perspective_js::utils::global;
17
17
  use wasm_bindgen::prelude::*;
18
18
  use wasm_bindgen_futures::spawn_local;
19
19
  use web_sys::*;
20
- use yew::*;
20
+ use yew::prelude::*;
21
21
 
22
- use super::modal::*;
23
22
  use super::viewer::PerspectiveViewerElement;
24
- use crate::components::copy_dropdown::{CopyDropDownMenu, CopyDropDownMenuProps};
23
+ use crate::components::copy_dropdown::CopyDropDownMenu;
24
+ use crate::components::portal::PortalModal;
25
+ use crate::components::style::StyleProvider;
25
26
  use crate::js::*;
26
- use crate::model::*;
27
+ use crate::renderer::*;
28
+ use crate::tasks::*;
27
29
  use crate::utils::*;
30
+ use crate::*;
31
+
32
+ type TargetState = Rc<RefCell<Option<HtmlElement>>>;
33
+
34
+ #[derive(Properties, PartialEq)]
35
+ struct CopyDropDownWrapperProps {
36
+ renderer: Renderer,
37
+ callback: Callback<ExportFile>,
38
+ target: TargetState,
39
+ custom_element: HtmlElement,
40
+ #[prop_or_default]
41
+ theme: String,
42
+ }
43
+
44
+ enum CopyDropDownWrapperMsg {
45
+ Open,
46
+ Close,
47
+ }
48
+
49
+ struct CopyDropDownWrapper {
50
+ target: Option<HtmlElement>,
51
+ }
52
+
53
+ impl Component for CopyDropDownWrapper {
54
+ type Message = CopyDropDownWrapperMsg;
55
+ type Properties = CopyDropDownWrapperProps;
56
+
57
+ fn create(_ctx: &Context<Self>) -> Self {
58
+ Self { target: None }
59
+ }
60
+
61
+ fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
62
+ match msg {
63
+ CopyDropDownWrapperMsg::Open => {
64
+ self.target = ctx.props().target.borrow().clone();
65
+ true
66
+ },
67
+ CopyDropDownWrapperMsg::Close => {
68
+ self.target = None;
69
+ true
70
+ },
71
+ }
72
+ }
73
+
74
+ fn view(&self, ctx: &Context<Self>) -> Html {
75
+ let on_close = ctx.link().callback(|_| CopyDropDownWrapperMsg::Close);
76
+ html! {
77
+ <StyleProvider root={ctx.props().custom_element.clone()}>
78
+ <PortalModal
79
+ tag_name="perspective-copy-menu"
80
+ target={self.target.clone()}
81
+ own_focus=true
82
+ {on_close}
83
+ theme={ctx.props().theme.clone()}
84
+ >
85
+ <CopyDropDownMenu
86
+ renderer={ctx.props().renderer.clone()}
87
+ callback={ctx.props().callback.clone()}
88
+ />
89
+ </PortalModal>
90
+ </StyleProvider>
91
+ }
92
+ }
93
+ }
28
94
 
29
95
  #[wasm_bindgen]
30
96
  #[derive(Clone)]
31
97
  pub struct CopyDropDownMenuElement {
32
98
  elem: HtmlElement,
33
- modal: Rc<RefCell<Option<ModalElement<CopyDropDownMenu>>>>,
99
+ target: TargetState,
100
+ root: Rc<RefCell<Option<AppHandle<CopyDropDownWrapper>>>>,
34
101
  }
35
102
 
36
103
  impl CustomElementMetadata for CopyDropDownMenuElement {
@@ -43,24 +110,25 @@ impl CopyDropDownMenuElement {
43
110
  pub fn new(elem: HtmlElement) -> Self {
44
111
  Self {
45
112
  elem,
46
- modal: Default::default(),
113
+ target: Default::default(),
114
+ root: Default::default(),
47
115
  }
48
116
  }
49
117
 
50
118
  pub fn open(&self, target: HtmlElement) {
51
- if let Some(x) = &*self.modal.borrow() {
52
- ApiFuture::spawn(x.clone().open(target, None));
119
+ *self.target.borrow_mut() = Some(target);
120
+ if let Some(root) = self.root.borrow().as_ref() {
121
+ root.send_message(CopyDropDownWrapperMsg::Open);
53
122
  }
54
123
  }
55
124
 
56
125
  pub fn hide(&self) -> ApiResult<()> {
57
- let borrowed = self.modal.borrow();
58
- borrowed.as_ref().into_apierror()?.hide()
126
+ if let Some(root) = self.root.borrow().as_ref() {
127
+ root.send_message(CopyDropDownWrapperMsg::Close);
128
+ }
129
+ Ok(())
59
130
  }
60
131
 
61
- /// Internal Only.
62
- ///
63
- /// Set this custom element model's raw pointer.
64
132
  pub fn __set_model(&self, parent: &PerspectiveViewerElement) {
65
133
  self.set_config_model(parent)
66
134
  }
@@ -91,30 +159,43 @@ impl CopyDropDownMenuElement {
91
159
  {
92
160
  let callback = Callback::from({
93
161
  let model = model.clone_state();
94
- let modal_rc = self.modal.clone();
162
+ let target = self.target.clone();
163
+ let root = self.root.clone();
95
164
  move |x: ExportFile| {
96
165
  let model = model.clone();
97
- let modal = modal_rc.borrow().clone().unwrap();
166
+ let target = target.clone();
167
+ let root = root.clone();
98
168
  spawn_local(async move {
99
169
  let mime = x.method.mimetype(x.is_chart);
100
170
  let task = model.export_method_to_blob(x.method);
101
171
  let result = copy_to_clipboard(task, mime).await;
102
172
  crate::maybe_log!({
103
173
  result?;
104
- modal.hide()?;
174
+ *target.borrow_mut() = None;
175
+ if let Some(root) = root.borrow().as_ref() {
176
+ root.send_message(CopyDropDownWrapperMsg::Close);
177
+ }
105
178
  })
106
179
  })
107
180
  }
108
181
  });
109
182
 
110
183
  let renderer = model.renderer().clone();
111
- let props = props!(CopyDropDownMenuProps {
184
+ let init = ShadowRootInit::new(ShadowRootMode::Open);
185
+ let shadow_root = self
186
+ .elem
187
+ .attach_shadow(&init)
188
+ .unwrap()
189
+ .unchecked_into::<Element>();
190
+
191
+ let props = yew::props!(CopyDropDownWrapperProps {
112
192
  renderer,
113
193
  callback,
114
- root: self.elem.clone()
194
+ target: self.target.clone(),
195
+ custom_element: self.elem.clone()
115
196
  });
116
197
 
117
- let modal = ModalElement::new(self.elem.clone(), props, true, None);
118
- *self.modal.borrow_mut() = Some(modal);
198
+ let handle = yew::Renderer::with_root_and_props(shadow_root, props).render();
199
+ *self.root.borrow_mut() = Some(handle);
119
200
  }
120
201
  }
@@ -17,20 +17,89 @@ use perspective_js::utils::global;
17
17
  use wasm_bindgen::prelude::*;
18
18
  use wasm_bindgen_futures::spawn_local;
19
19
  use web_sys::*;
20
- use yew::*;
20
+ use yew::prelude::*;
21
21
 
22
22
  use super::viewer::PerspectiveViewerElement;
23
- use crate::components::export_dropdown::*;
24
- use crate::custom_elements::modal::*;
25
- use crate::model::*;
23
+ use crate::components::export_dropdown::ExportDropDownMenu;
24
+ use crate::components::portal::PortalModal;
25
+ use crate::components::style::StyleProvider;
26
+ use crate::renderer::*;
27
+ use crate::session::*;
28
+ use crate::tasks::*;
26
29
  use crate::utils::*;
27
30
  use crate::*;
28
31
 
32
+ type TargetState = Rc<RefCell<Option<HtmlElement>>>;
33
+
34
+ #[derive(Properties, PartialEq)]
35
+ struct ExportDropDownWrapperProps {
36
+ renderer: Renderer,
37
+ session: Session,
38
+ callback: Callback<ExportFile>,
39
+ target: TargetState,
40
+ custom_element: HtmlElement,
41
+ #[prop_or_default]
42
+ theme: String,
43
+ }
44
+
45
+ enum ExportDropDownWrapperMsg {
46
+ Open,
47
+ Close,
48
+ }
49
+
50
+ struct ExportDropDownWrapper {
51
+ target: Option<HtmlElement>,
52
+ }
53
+
54
+ impl Component for ExportDropDownWrapper {
55
+ type Message = ExportDropDownWrapperMsg;
56
+ type Properties = ExportDropDownWrapperProps;
57
+
58
+ fn create(_ctx: &Context<Self>) -> Self {
59
+ Self { target: None }
60
+ }
61
+
62
+ fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
63
+ match msg {
64
+ ExportDropDownWrapperMsg::Open => {
65
+ self.target = ctx.props().target.borrow().clone();
66
+ true
67
+ },
68
+ ExportDropDownWrapperMsg::Close => {
69
+ self.target = None;
70
+ true
71
+ },
72
+ }
73
+ }
74
+
75
+ fn view(&self, ctx: &Context<Self>) -> Html {
76
+ let on_close = ctx.link().callback(|_| ExportDropDownWrapperMsg::Close);
77
+ html! {
78
+ <StyleProvider root={ctx.props().custom_element.clone()}>
79
+ <PortalModal
80
+ tag_name="perspective-export-menu"
81
+ target={self.target.clone()}
82
+ own_focus=true
83
+ {on_close}
84
+ theme={ctx.props().theme.clone()}
85
+ >
86
+ <ExportDropDownMenu
87
+ renderer={ctx.props().renderer.clone()}
88
+ session={ctx.props().session.clone()}
89
+ callback={ctx.props().callback.clone()}
90
+ />
91
+ </PortalModal>
92
+ </StyleProvider>
93
+ }
94
+ }
95
+ }
96
+
29
97
  #[wasm_bindgen]
30
98
  #[derive(Clone)]
31
99
  pub struct ExportDropDownMenuElement {
32
100
  elem: HtmlElement,
33
- modal: Rc<RefCell<Option<ModalElement<ExportDropDownMenu>>>>,
101
+ target: TargetState,
102
+ root: Rc<RefCell<Option<AppHandle<ExportDropDownWrapper>>>>,
34
103
  }
35
104
 
36
105
  impl CustomElementMetadata for ExportDropDownMenuElement {
@@ -43,24 +112,25 @@ impl ExportDropDownMenuElement {
43
112
  pub fn new(elem: HtmlElement) -> Self {
44
113
  Self {
45
114
  elem,
46
- modal: Default::default(),
115
+ target: Default::default(),
116
+ root: Default::default(),
47
117
  }
48
118
  }
49
119
 
50
120
  pub fn open(&self, target: HtmlElement) {
51
- if let Some(x) = &*self.modal.borrow() {
52
- ApiFuture::spawn(x.clone().open(target, None));
121
+ *self.target.borrow_mut() = Some(target);
122
+ if let Some(root) = self.root.borrow().as_ref() {
123
+ root.send_message(ExportDropDownWrapperMsg::Open);
53
124
  }
54
125
  }
55
126
 
56
127
  pub fn hide(&self) -> ApiResult<()> {
57
- let borrowed = self.modal.borrow();
58
- borrowed.as_ref().into_apierror()?.hide()
128
+ if let Some(root) = self.root.borrow().as_ref() {
129
+ root.send_message(ExportDropDownWrapperMsg::Close);
130
+ }
131
+ Ok(())
59
132
  }
60
133
 
61
- /// Internal Only.
62
- ///
63
- /// Set this custom element model's raw pointer.
64
134
  pub fn __set_model(&self, parent: &PerspectiveViewerElement) {
65
135
  self.set_config_model(parent)
66
136
  }
@@ -91,15 +161,19 @@ impl ExportDropDownMenuElement {
91
161
  {
92
162
  let callback = Callback::from({
93
163
  let model = model.clone_state();
94
- let modal_rc = self.modal.clone();
164
+ let target = self.target.clone();
165
+ let root = self.root.clone();
95
166
  move |x: ExportFile| {
96
167
  if !x.name.is_empty() {
97
- clone!(modal_rc, model);
168
+ clone!(target, root, model);
98
169
  spawn_local(async move {
99
170
  let val = model.export_method_to_blob(x.method).await.unwrap();
100
171
  let is_chart = model.renderer().is_chart();
101
172
  download(&x.as_filename(is_chart), &val).unwrap();
102
- modal_rc.borrow().clone().unwrap().hide().unwrap();
173
+ *target.borrow_mut() = None;
174
+ if let Some(root) = root.borrow().as_ref() {
175
+ root.send_message(ExportDropDownWrapperMsg::Close);
176
+ }
103
177
  })
104
178
  }
105
179
  }
@@ -107,14 +181,22 @@ impl ExportDropDownMenuElement {
107
181
 
108
182
  let renderer = model.renderer().clone();
109
183
  let session = model.session().clone();
110
- let props = props!(ExportDropDownMenuProps {
184
+ let init = ShadowRootInit::new(ShadowRootMode::Open);
185
+ let shadow_root = self
186
+ .elem
187
+ .attach_shadow(&init)
188
+ .unwrap()
189
+ .unchecked_into::<Element>();
190
+
191
+ let props = yew::props!(ExportDropDownWrapperProps {
111
192
  renderer,
112
193
  session,
113
194
  callback,
114
- root: self.elem.clone()
195
+ target: self.target.clone(),
196
+ custom_element: self.elem.clone(),
115
197
  });
116
198
 
117
- let modal = ModalElement::new(self.elem.clone(), props, true, None);
118
- *self.modal.borrow_mut() = Some(modal);
199
+ let handle = yew::Renderer::with_root_and_props(shadow_root, props).render();
200
+ *self.root.borrow_mut() = Some(handle);
119
201
  }
120
202
  }
@@ -13,15 +13,8 @@
13
13
  //! Each file in `custom_elements` exports a single struct which will be the
14
14
  //! public [`wasm_bindgen`] API to a JavaScript Custom Element.
15
15
 
16
- mod column_dropdown;
17
16
  pub mod copy_dropdown;
18
17
  pub mod debug_plugin;
19
18
  pub mod export_dropdown;
20
- mod filter_dropdown;
21
- mod function_dropdown;
22
19
  pub mod modal;
23
20
  pub mod viewer;
24
-
25
- pub use self::column_dropdown::*;
26
- pub use self::filter_dropdown::*;
27
- pub use self::function_dropdown::*;
@@ -56,81 +56,6 @@ where
56
56
  on_blur: Option<Callback<()>>,
57
57
  }
58
58
 
59
- /// Anchor point enum, `ModalCornerTargetCorner`
60
- #[derive(Clone, Copy, Debug, Default)]
61
- enum ModalAnchor {
62
- BottomRightTopLeft,
63
- BottomRightBottomLeft,
64
- BottomRightTopRight,
65
- BottomLeftTopLeft,
66
- TopRightTopLeft,
67
- TopRightBottomRight,
68
-
69
- #[default]
70
- TopLeftBottomLeft,
71
- }
72
-
73
- impl ModalAnchor {
74
- const fn is_rev_vert(&self) -> bool {
75
- matches!(
76
- self,
77
- Self::BottomLeftTopLeft
78
- | Self::BottomRightBottomLeft
79
- | Self::BottomRightTopLeft
80
- | Self::BottomRightTopRight
81
- )
82
- }
83
- }
84
-
85
- /// Given the bounds of the target element as previous computed, as well as the
86
- /// browser's viewport and the bounds of the already-connected
87
- /// `<perspectuve-style-menu>` element itself, determine a new (top, left)
88
- /// coordinates that keeps the element on-screen.
89
- fn calc_relative_position(
90
- elem: &HtmlElement,
91
- _top: f64,
92
- left: f64,
93
- height: f64,
94
- width: f64,
95
- ) -> ModalAnchor {
96
- let window = global::window();
97
- let rect = elem.get_bounding_client_rect();
98
- let inner_width = window.inner_width().unwrap().as_f64().unwrap();
99
- let inner_height = window.inner_height().unwrap().as_f64().unwrap();
100
- let rect_top = rect.top();
101
- let rect_height = rect.height();
102
- let rect_width = rect.width();
103
- let rect_left = rect.left();
104
-
105
- let elem_over_y = inner_height < rect_top + rect_height;
106
- let elem_over_x = inner_width < rect_left + rect_width;
107
- let target_over_x = inner_width < rect_left + width;
108
- let target_over_y = inner_height < rect_top + height;
109
-
110
- // modal/target
111
- match (elem_over_y, elem_over_x, target_over_x, target_over_y) {
112
- (true, _, true, true) => ModalAnchor::BottomRightTopLeft,
113
- (true, _, true, false) => ModalAnchor::BottomRightBottomLeft,
114
- (true, true, false, _) => {
115
- if left + width - rect_width > 0.0 {
116
- ModalAnchor::BottomRightTopRight
117
- } else {
118
- ModalAnchor::BottomLeftTopLeft
119
- }
120
- },
121
- (true, false, false, _) => ModalAnchor::BottomLeftTopLeft,
122
- (false, true, true, _) => ModalAnchor::TopRightTopLeft,
123
- (false, true, false, _) => {
124
- if left + width - rect_width > 0.0 {
125
- ModalAnchor::TopRightBottomRight
126
- } else {
127
- ModalAnchor::TopLeftBottomLeft
128
- }
129
- },
130
- _ => ModalAnchor::TopLeftBottomLeft,
131
- }
132
- }
133
-
134
59
  impl<T> ModalElement<T>
135
60
  where
136
61
  T: Component,
@@ -172,31 +97,10 @@ where
172
97
  }
173
98
  }
174
99
 
175
- fn calc_anchor_position(&self, target: &HtmlElement) -> (f64, f64) {
176
- let elem = target.unchecked_ref::<HtmlElement>();
177
- let rect = elem.get_bounding_client_rect();
178
- let height = rect.height();
179
- let width = rect.width();
180
- let top = rect.top();
181
- let left = rect.left();
182
-
183
- let self_rect = self.custom_element.get_bounding_client_rect();
184
- let rect_height = self_rect.height();
185
- let rect_width = self_rect.width();
186
-
187
- match self.anchor.get() {
188
- ModalAnchor::BottomRightTopLeft => (top - rect_height, left - rect_width + 1.0),
189
- ModalAnchor::BottomRightBottomLeft => {
190
- (top - rect_height + height, left - rect_width + 1.0)
191
- },
192
- ModalAnchor::BottomRightTopRight => {
193
- (top - rect_height + 1.0, left + width - rect_width)
194
- },
195
- ModalAnchor::BottomLeftTopLeft => (top - rect_height + 1.0, left),
196
- ModalAnchor::TopRightTopLeft => (top, left - rect_width + 1.0),
197
- ModalAnchor::TopRightBottomRight => (top + height - 1.0, left + width - rect_width),
198
- ModalAnchor::TopLeftBottomLeft => ((top + height - 1.0), left),
199
- }
100
+ fn calc_anchor_pos(&self, target: &HtmlElement) -> (f64, f64) {
101
+ let target_rect = target.get_bounding_client_rect();
102
+ let modal_rect = self.custom_element.get_bounding_client_rect();
103
+ calc_anchor_position(self.anchor.get(), &target_rect, &modal_rect)
200
104
  }
201
105
 
202
106
  async fn open_within_viewport(&self, target: HtmlElement) -> ApiResult<()> {
@@ -229,7 +133,7 @@ where
229
133
  width,
230
134
  ));
231
135
 
232
- let (top, left) = self.calc_anchor_position(&target);
136
+ let (top, left) = self.calc_anchor_pos(&target);
233
137
  let msg = ModalMsg::SetPos {
234
138
  top,
235
139
  left,
@@ -310,7 +214,7 @@ where
310
214
  let target = target.clone();
311
215
  let anchor = self.anchor.clone();
312
216
  *self.resize_sub.borrow_mut() = Some(resize.add_listener(move |()| {
313
- let (top, left) = this.calc_anchor_position(&target);
217
+ let (top, left) = this.calc_anchor_pos(&target);
314
218
  let msg = ModalMsg::SetPos {
315
219
  top,
316
220
  left,
@@ -382,7 +286,7 @@ where
382
286
  fn get_theme(elem: &HtmlElement) -> Option<String> {
383
287
  let styles = global::window().get_computed_style(elem).unwrap().unwrap();
384
288
  styles
385
- .get_property_value("--theme-name")
289
+ .get_property_value("--psp-theme-name")
386
290
  .ok()
387
291
  .and_then(|x| {
388
292
  let trimmed = x.trim();