@perspective-dev/viewer 4.0.1 → 4.1.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.
- package/dist/cdn/perspective-viewer.js +2 -2
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/dracula.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/esm/extensions.d.ts +23 -2
- package/dist/esm/perspective-viewer.d.ts +2 -7
- package/dist/esm/perspective-viewer.inline.js +2 -2
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +2 -2
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/plugin.d.ts +1 -1
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +1 -0
- package/dist/wasm/perspective-viewer.d.ts +218 -46
- package/dist/wasm/perspective-viewer.js +1250 -761
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +38 -19
- package/package.json +1 -1
- package/src/less/containers/scroll-panel.less +0 -1
- package/src/less/plugin-selector.less +15 -5
- package/src/less/status-bar.less +75 -27
- package/src/less/viewer.less +140 -58
- package/src/rust/components/column_dropdown.rs +21 -21
- package/src/rust/components/column_selector/active_column.rs +131 -120
- package/src/rust/components/column_selector/add_expression_button.rs +5 -0
- package/src/rust/components/column_selector/aggregate_selector.rs +8 -4
- package/src/rust/components/column_selector/config_selector.rs +170 -161
- package/src/rust/components/column_selector/empty_column.rs +16 -11
- package/src/rust/components/column_selector/{expression_toolbar.rs → expr_edit_button.rs} +7 -0
- package/src/rust/components/column_selector/filter_column.rs +195 -194
- package/src/rust/components/column_selector/inactive_column.rs +82 -67
- package/src/rust/components/column_selector/pivot_column.rs +16 -11
- package/src/rust/components/column_selector/sort_column.rs +9 -7
- package/src/rust/components/column_selector.rs +42 -37
- package/src/rust/components/column_settings_sidebar/save_settings.rs +3 -1
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +58 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +6 -6
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +2 -94
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +111 -0
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +3 -3
- package/src/rust/components/column_settings_sidebar/style_tab.rs +23 -83
- package/src/rust/components/{column_settings_sidebar/sidebar.rs → column_settings_sidebar.rs} +198 -171
- package/src/rust/components/containers/dragdrop_list.rs +20 -20
- package/src/rust/components/containers/dropdown_menu.rs +4 -6
- package/src/rust/components/containers/mod.rs +1 -4
- package/src/rust/components/containers/scroll_panel.rs +80 -80
- package/src/rust/components/containers/scroll_panel_item.rs +36 -36
- package/src/rust/components/containers/select.rs +46 -44
- package/src/rust/components/containers/sidebar.rs +3 -19
- package/src/rust/components/{column_settings_sidebar/style_tab/symbol/symbol_config.rs → containers/sidebar_close_button.rs} +15 -9
- package/src/rust/components/containers/split_panel.rs +212 -200
- package/src/rust/components/containers/tab_list.rs +11 -11
- package/src/rust/components/copy_dropdown.rs +22 -25
- package/src/rust/components/datetime_column_style/custom.rs +19 -19
- package/src/rust/components/datetime_column_style/simple.rs +13 -14
- package/src/rust/components/datetime_column_style.rs +75 -76
- package/src/rust/components/editable_header.rs +18 -14
- package/src/rust/components/empty_row.rs +5 -5
- package/src/rust/components/export_dropdown.rs +42 -42
- package/src/rust/components/expression_editor.rs +25 -19
- package/src/rust/components/filter_dropdown.rs +22 -22
- package/src/rust/components/font_loader.rs +11 -9
- package/src/rust/components/form/code_editor.rs +106 -105
- package/src/rust/components/form/color_range_selector.rs +14 -12
- package/src/rust/components/form/color_selector.rs +3 -1
- package/src/rust/components/form/debug.rs +95 -94
- package/src/rust/components/form/highlight.rs +5 -3
- package/src/rust/components/form/mod.rs +3 -2
- package/src/rust/components/form/optional_field.rs +2 -2
- package/src/rust/components/form/{select_field.rs → select_enum_field.rs} +1 -46
- package/src/rust/components/form/select_value_field.rs +64 -0
- package/src/rust/components/function_dropdown.rs +21 -21
- package/src/rust/components/main_panel.rs +219 -0
- package/src/rust/components/mod.rs +6 -6
- package/src/rust/components/modal.rs +42 -42
- package/src/rust/components/number_column_style.rs +34 -88
- package/src/rust/components/plugin_selector.rs +22 -25
- package/src/rust/components/render_warning.rs +9 -6
- package/src/rust/components/settings_panel.rs +82 -0
- package/src/rust/components/status_bar.rs +250 -146
- package/src/rust/components/status_bar_counter.rs +26 -119
- package/src/rust/components/status_indicator.rs +95 -79
- package/src/rust/components/string_column_style.rs +45 -45
- package/src/rust/components/style/style_provider.rs +1 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format.rs +45 -46
- package/src/rust/components/type_icon.rs +14 -11
- package/src/rust/components/viewer.rs +241 -384
- package/src/rust/config/columns_config.rs +2 -2
- package/src/rust/config/datetime_column_style.rs +1 -6
- package/src/rust/config/mod.rs +1 -0
- package/src/rust/config/number_column_style.rs +0 -6
- package/src/rust/config/number_string_format.rs +27 -4
- package/src/rust/config/viewer_config.rs +27 -167
- package/src/rust/custom_elements/copy_dropdown.rs +14 -6
- package/src/rust/custom_elements/export_dropdown.rs +15 -7
- package/src/rust/custom_elements/filter_dropdown.rs +4 -4
- package/src/rust/custom_elements/mod.rs +3 -0
- package/src/rust/custom_elements/viewer.rs +353 -161
- package/src/rust/custom_events.rs +55 -32
- package/src/rust/dragdrop.rs +4 -24
- package/src/rust/exprtk/cursor.rs +10 -1
- package/src/rust/exprtk/mod.rs +2 -0
- package/src/rust/exprtk/tokenize.rs +20 -3
- package/src/rust/js/clipboard.rs +2 -2
- package/src/rust/js/mimetype.rs +2 -7
- package/src/rust/js/mod.rs +0 -1
- package/src/rust/js/plugin.rs +7 -0
- package/src/rust/lib.rs +18 -5
- package/src/rust/model/column_locator.rs +82 -0
- package/src/rust/model/columns_iter_set.rs +1 -0
- package/src/rust/model/copy_export.rs +50 -14
- package/src/rust/model/edit_expression.rs +2 -5
- package/src/rust/model/eject.rs +41 -0
- package/src/rust/model/get_viewer_config.rs +4 -28
- package/src/rust/model/intersection_observer.rs +20 -8
- package/src/rust/model/mod.rs +11 -4
- package/src/rust/model/plugin_column_styles.rs +0 -31
- package/src/rust/model/reset_all.rs +38 -0
- package/src/rust/model/resize_observer.rs +34 -7
- package/src/rust/model/restore_and_render.rs +12 -7
- package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
- package/src/rust/model/structural.rs +194 -23
- package/src/rust/model/update_and_render.rs +14 -4
- package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
- package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
- package/src/rust/presentation.rs +60 -119
- package/src/rust/renderer/activate.rs +20 -5
- package/src/rust/renderer/limits.rs +0 -149
- package/src/rust/renderer/render_timer.rs +1 -1
- package/src/rust/renderer.rs +34 -18
- package/src/rust/root.rs +50 -0
- package/src/rust/session/column_defaults_update.rs +4 -4
- package/src/rust/session/drag_drop_update.rs +1 -1
- package/src/rust/session/metadata.rs +3 -17
- package/src/rust/session/replace_expression_update.rs +1 -2
- package/src/rust/session.rs +162 -82
- package/src/rust/utils/browser/blob.rs +16 -2
- package/src/rust/utils/browser/download.rs +1 -0
- package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
- package/src/rust/utils/browser/mod.rs +8 -4
- package/src/rust/utils/browser/selection.rs +5 -0
- package/src/rust/utils/custom_element.rs +28 -13
- package/src/rust/utils/datetime.rs +5 -0
- package/src/rust/utils/debounce.rs +7 -1
- package/src/rust/utils/hooks/use_async_callback.rs +7 -17
- package/src/rust/utils/mod.rs +28 -40
- package/src/rust/utils/number_format.rs +6 -5
- package/src/rust/utils/pubsub.rs +15 -10
- package/src/rust/utils/weak_scope.rs +11 -1
- package/src/svg/bookmark-icon.svg +4 -0
- package/src/svg/drag-handle copy.svg +10 -0
- package/src/svg/drawer-tab-hover.svg +5 -7
- package/src/svg/drawer-tab-invert-hover.svg +4 -8
- package/src/svg/drawer-tab-invert.svg +4 -7
- package/src/svg/drawer-tab.svg +4 -6
- package/src/svg/status_ok.svg +24 -24
- package/src/ts/extensions.ts +51 -3
- package/src/ts/perspective-viewer.ts +2 -14
- package/src/ts/plugin.ts +1 -1
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
- package/src/rust/components/containers/tests/mod.rs +0 -11
- package/src/rust/components/containers/tests/split_panel.rs +0 -91
- package/src/rust/js/testing.rs +0 -149
- package/src/rust/utils/tee.rs +0 -88
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-0d326a25c1022412}/inline4.js +0 -0
- /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
- /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
use std::cmp::max;
|
|
14
14
|
|
|
15
|
-
use perspective_js::utils::global;
|
|
15
|
+
use perspective_js::utils::{ApiResult, global};
|
|
16
16
|
use wasm_bindgen::JsCast;
|
|
17
17
|
use wasm_bindgen::prelude::*;
|
|
18
18
|
use web_sys::HtmlElement;
|
|
@@ -20,173 +20,7 @@ use yew::html::Scope;
|
|
|
20
20
|
use yew::prelude::*;
|
|
21
21
|
|
|
22
22
|
use crate::components::style::LocalStyle;
|
|
23
|
-
|
|
24
|
-
use crate::utils::*;
|
|
25
|
-
use crate::*;
|
|
26
|
-
|
|
27
|
-
/// The state for the `Resizing` action, including the `MouseEvent` callbacks
|
|
28
|
-
/// and panel starting dimensions.
|
|
29
|
-
struct ResizingState {
|
|
30
|
-
mousemove: Closure<dyn Fn(MouseEvent)>,
|
|
31
|
-
mouseup: Closure<dyn Fn(MouseEvent)>,
|
|
32
|
-
cursor: String,
|
|
33
|
-
index: usize,
|
|
34
|
-
start: i32,
|
|
35
|
-
total: i32,
|
|
36
|
-
alt: i32,
|
|
37
|
-
orientation: Orientation,
|
|
38
|
-
reverse: bool,
|
|
39
|
-
body_style: web_sys::CssStyleDeclaration,
|
|
40
|
-
pointer_id: i32,
|
|
41
|
-
pointer_elem: HtmlElement,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
impl Drop for ResizingState {
|
|
45
|
-
/// On `drop`, we must remove these event listeners from the document
|
|
46
|
-
/// `body`. Without this, the `Closure` objects would not leak, but the
|
|
47
|
-
/// document will continue to call them, causing runtime exceptions.
|
|
48
|
-
fn drop(&mut self) {
|
|
49
|
-
let result: ApiResult<()> = maybe! {
|
|
50
|
-
let mousemove = self.mousemove.as_ref().unchecked_ref();
|
|
51
|
-
global::body().remove_event_listener_with_callback("mousemove", mousemove)?;
|
|
52
|
-
let mouseup = self.mouseup.as_ref().unchecked_ref();
|
|
53
|
-
global::body().remove_event_listener_with_callback("mouseup", mouseup)?;
|
|
54
|
-
self.release_cursor()?;
|
|
55
|
-
Ok(())
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
result.expect("Drop failed")
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/// The minimum size a split panel child can be, including when overridden via
|
|
63
|
-
/// user drag/drop.
|
|
64
|
-
const MINIMUM_SIZE: i32 = 8;
|
|
65
|
-
|
|
66
|
-
/// When the instantiated, capture the initial dimensions and create the
|
|
67
|
-
/// MouseEvent callbacks.
|
|
68
|
-
impl ResizingState {
|
|
69
|
-
pub fn new(
|
|
70
|
-
index: usize,
|
|
71
|
-
client_offset: i32,
|
|
72
|
-
ctx: &Context<SplitPanel>,
|
|
73
|
-
first_elem: &HtmlElement,
|
|
74
|
-
pointer_id: i32,
|
|
75
|
-
pointer_elem: HtmlElement,
|
|
76
|
-
) -> ApiResult<Self> {
|
|
77
|
-
let orientation = ctx.props().orientation;
|
|
78
|
-
let reverse = ctx.props().reverse;
|
|
79
|
-
let split_panel = ctx.link();
|
|
80
|
-
let total = match orientation {
|
|
81
|
-
Orientation::Horizontal => first_elem.offset_width(),
|
|
82
|
-
Orientation::Vertical => first_elem.offset_height(),
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
let alt = match orientation {
|
|
86
|
-
Orientation::Horizontal => first_elem.offset_height(),
|
|
87
|
-
Orientation::Vertical => first_elem.offset_width(),
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
let mouseup = Closure::new({
|
|
91
|
-
let cb = split_panel.callback(|_| SplitPanelMsg::StopResizing);
|
|
92
|
-
move |x| cb.emit(x)
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
let mousemove = Closure::new({
|
|
96
|
-
let cb = split_panel.callback(move |event: MouseEvent| {
|
|
97
|
-
SplitPanelMsg::MoveResizing(match orientation {
|
|
98
|
-
Orientation::Horizontal => event.client_x(),
|
|
99
|
-
Orientation::Vertical => event.client_y(),
|
|
100
|
-
})
|
|
101
|
-
});
|
|
102
|
-
move |x| cb.emit(x)
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
let mut state = Self {
|
|
106
|
-
index,
|
|
107
|
-
cursor: "".to_owned(),
|
|
108
|
-
start: client_offset,
|
|
109
|
-
orientation,
|
|
110
|
-
reverse,
|
|
111
|
-
total,
|
|
112
|
-
alt,
|
|
113
|
-
body_style: global::body().style(),
|
|
114
|
-
mouseup,
|
|
115
|
-
mousemove,
|
|
116
|
-
pointer_id,
|
|
117
|
-
pointer_elem,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
state.capture_cursor()?;
|
|
121
|
-
state.register_listeners()?;
|
|
122
|
-
Ok(state)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
fn get_offset(&self, client_offset: i32) -> i32 {
|
|
126
|
-
let delta = if self.reverse {
|
|
127
|
-
self.start - client_offset
|
|
128
|
-
} else {
|
|
129
|
-
client_offset - self.start
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
max(MINIMUM_SIZE, self.total + delta)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
pub fn get_style(&self, client_offset: i32) -> Option<String> {
|
|
136
|
-
let offset = self.get_offset(client_offset);
|
|
137
|
-
Some(match self.orientation {
|
|
138
|
-
Orientation::Horizontal => {
|
|
139
|
-
format!("max-width:{offset}px;min-width:{offset}px;width:{offset}px")
|
|
140
|
-
},
|
|
141
|
-
Orientation::Vertical => {
|
|
142
|
-
format!("max-height:{offset}px;min-height:{offset}px;height:{offset}px")
|
|
143
|
-
},
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
pub fn get_dimensions(&self, client_offset: i32) -> (i32, i32) {
|
|
148
|
-
let offset = self.get_offset(client_offset);
|
|
149
|
-
match self.orientation {
|
|
150
|
-
Orientation::Horizontal => (std::cmp::max(MINIMUM_SIZE, offset), self.alt),
|
|
151
|
-
Orientation::Vertical => (self.alt, std::cmp::max(MINIMUM_SIZE, offset)),
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/// Adds the event listeners, the corollary of `Drop`.
|
|
156
|
-
fn register_listeners(&self) -> ApiResult<()> {
|
|
157
|
-
let mousemove = self.mousemove.as_ref().unchecked_ref();
|
|
158
|
-
global::body().add_event_listener_with_callback("mousemove", mousemove)?;
|
|
159
|
-
let mouseup = self.mouseup.as_ref().unchecked_ref();
|
|
160
|
-
Ok(global::body().add_event_listener_with_callback("mouseup", mouseup)?)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/// Helper functions capture and release the global cursor while dragging is
|
|
164
|
-
/// occurring.
|
|
165
|
-
fn capture_cursor(&mut self) -> ApiResult<()> {
|
|
166
|
-
self.pointer_elem.set_pointer_capture(self.pointer_id)?;
|
|
167
|
-
self.cursor = self.body_style.get_property_value("cursor")?;
|
|
168
|
-
self.body_style
|
|
169
|
-
.set_property("cursor", match self.orientation {
|
|
170
|
-
Orientation::Horizontal => "col-resize",
|
|
171
|
-
Orientation::Vertical => "row-resize",
|
|
172
|
-
})?;
|
|
173
|
-
|
|
174
|
-
Ok(())
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/// " but for release
|
|
178
|
-
fn release_cursor(&self) -> ApiResult<()> {
|
|
179
|
-
self.pointer_elem.release_pointer_capture(self.pointer_id)?;
|
|
180
|
-
Ok(self.body_style.set_property("cursor", &self.cursor)?)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
|
185
|
-
pub enum Orientation {
|
|
186
|
-
#[default]
|
|
187
|
-
Horizontal,
|
|
188
|
-
Vertical,
|
|
189
|
-
}
|
|
23
|
+
use crate::{css, maybe};
|
|
190
24
|
|
|
191
25
|
#[derive(Properties, Default)]
|
|
192
26
|
pub struct SplitPanelProps {
|
|
@@ -220,10 +54,6 @@ pub struct SplitPanelProps {
|
|
|
220
54
|
#[prop_or_default]
|
|
221
55
|
pub on_resize_finished: Option<Callback<()>>,
|
|
222
56
|
|
|
223
|
-
#[cfg(test)]
|
|
224
|
-
#[prop_or_default]
|
|
225
|
-
pub weak_link: WeakScope<SplitPanel>,
|
|
226
|
-
|
|
227
57
|
#[prop_or_default]
|
|
228
58
|
pub initial_size: Option<i32>,
|
|
229
59
|
}
|
|
@@ -278,7 +108,6 @@ impl Component for SplitPanel {
|
|
|
278
108
|
|
|
279
109
|
fn create(ctx: &Context<Self>) -> Self {
|
|
280
110
|
assert!(ctx.props().validate());
|
|
281
|
-
enable_weak_link_test!(ctx.props(), ctx.link());
|
|
282
111
|
let len = ctx.props().children.len();
|
|
283
112
|
// cant just use vec![Default::default(); len] as it would
|
|
284
113
|
// use the same underlying NodeRef for each element.
|
|
@@ -351,7 +180,14 @@ impl Component for SplitPanel {
|
|
|
351
180
|
}
|
|
352
181
|
|
|
353
182
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
354
|
-
let
|
|
183
|
+
let iter = ctx
|
|
184
|
+
.props()
|
|
185
|
+
.children
|
|
186
|
+
.iter()
|
|
187
|
+
.filter(|x| !ctx.props().skip_empty || x != &html! { <></> })
|
|
188
|
+
.enumerate()
|
|
189
|
+
.collect::<Vec<_>>();
|
|
190
|
+
|
|
355
191
|
let orientation = ctx.props().orientation;
|
|
356
192
|
let mut classes = classes!("split-panel");
|
|
357
193
|
if orientation == Orientation::Vertical {
|
|
@@ -362,38 +198,50 @@ impl Component for SplitPanel {
|
|
|
362
198
|
classes.push("orient-reverse");
|
|
363
199
|
}
|
|
364
200
|
|
|
365
|
-
let
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
link={ctx.link().clone()}
|
|
377
|
-
/>
|
|
378
|
-
if i == ctx.props().children.len() - 2 { { x } } else {
|
|
201
|
+
let count = iter.len();
|
|
202
|
+
let contents = html! {
|
|
203
|
+
<>
|
|
204
|
+
<LocalStyle key=0 href={css!("containers/split-panel")} />
|
|
205
|
+
for (i, x) in iter {
|
|
206
|
+
if i == 0 {
|
|
207
|
+
if count == 1 {
|
|
208
|
+
<key={i}>
|
|
209
|
+
{x}
|
|
210
|
+
</>
|
|
211
|
+
} else {
|
|
379
212
|
<SplitPanelChild
|
|
380
|
-
|
|
381
|
-
|
|
213
|
+
key=0
|
|
214
|
+
style={self.styles[i].clone()}
|
|
215
|
+
ref_={self.refs[i].clone()}
|
|
382
216
|
>
|
|
383
217
|
{ x }
|
|
384
218
|
</SplitPanelChild>
|
|
385
219
|
}
|
|
386
|
-
|
|
220
|
+
} else if i == count - 1 {
|
|
221
|
+
<key={i}>
|
|
222
|
+
<SplitPanelDivider
|
|
223
|
+
i={i - 1}
|
|
224
|
+
orientation={ctx.props().orientation}
|
|
225
|
+
link={ctx.link().clone()}
|
|
226
|
+
/>
|
|
227
|
+
{ x }
|
|
228
|
+
</>
|
|
229
|
+
} else {
|
|
230
|
+
<key={i}>
|
|
231
|
+
<SplitPanelDivider
|
|
232
|
+
i={i - 1}
|
|
233
|
+
orientation={ctx.props().orientation}
|
|
234
|
+
link={ctx.link().clone()}
|
|
235
|
+
/>
|
|
236
|
+
<SplitPanelChild
|
|
237
|
+
style={self.styles[i].clone()}
|
|
238
|
+
ref_={self.refs[i].clone()}
|
|
239
|
+
>
|
|
240
|
+
{ x }
|
|
241
|
+
</SplitPanelChild>
|
|
242
|
+
</>
|
|
243
|
+
}
|
|
387
244
|
}
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
let contents = html! {
|
|
391
|
-
<>
|
|
392
|
-
<LocalStyle key=0 href={css!("containers/split-panel")} />
|
|
393
|
-
<SplitPanelChild key=1 style={self.styles[0].clone()} ref_={self.refs[0].clone()}>
|
|
394
|
-
{ head }
|
|
395
|
-
</SplitPanelChild>
|
|
396
|
-
{ for tail }
|
|
397
245
|
</>
|
|
398
246
|
};
|
|
399
247
|
|
|
@@ -406,6 +254,13 @@ impl Component for SplitPanel {
|
|
|
406
254
|
}
|
|
407
255
|
}
|
|
408
256
|
|
|
257
|
+
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
|
258
|
+
pub enum Orientation {
|
|
259
|
+
#[default]
|
|
260
|
+
Horizontal,
|
|
261
|
+
Vertical,
|
|
262
|
+
}
|
|
263
|
+
|
|
409
264
|
#[derive(Properties)]
|
|
410
265
|
struct SplitPanelDividerProps {
|
|
411
266
|
i: usize,
|
|
@@ -481,3 +336,160 @@ fn split_panel_child(props: &SplitPanelChildProps) -> Html {
|
|
|
481
336
|
</div>
|
|
482
337
|
}
|
|
483
338
|
}
|
|
339
|
+
|
|
340
|
+
/// The state for the `Resizing` action, including the `MouseEvent` callbacks
|
|
341
|
+
/// and panel starting dimensions.
|
|
342
|
+
struct ResizingState {
|
|
343
|
+
mousemove: Closure<dyn Fn(MouseEvent)>,
|
|
344
|
+
mouseup: Closure<dyn Fn(MouseEvent)>,
|
|
345
|
+
cursor: String,
|
|
346
|
+
index: usize,
|
|
347
|
+
start: i32,
|
|
348
|
+
total: i32,
|
|
349
|
+
alt: i32,
|
|
350
|
+
orientation: Orientation,
|
|
351
|
+
reverse: bool,
|
|
352
|
+
body_style: web_sys::CssStyleDeclaration,
|
|
353
|
+
pointer_id: i32,
|
|
354
|
+
pointer_elem: HtmlElement,
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
impl Drop for ResizingState {
|
|
358
|
+
/// On `drop`, we must remove these event listeners from the document
|
|
359
|
+
/// `body`. Without this, the `Closure` objects would not leak, but the
|
|
360
|
+
/// document will continue to call them, causing runtime exceptions.
|
|
361
|
+
fn drop(&mut self) {
|
|
362
|
+
let result: ApiResult<()> = maybe! {
|
|
363
|
+
let mousemove = self.mousemove.as_ref().unchecked_ref();
|
|
364
|
+
global::body().remove_event_listener_with_callback("mousemove", mousemove)?;
|
|
365
|
+
let mouseup = self.mouseup.as_ref().unchecked_ref();
|
|
366
|
+
global::body().remove_event_listener_with_callback("mouseup", mouseup)?;
|
|
367
|
+
self.release_cursor()?;
|
|
368
|
+
Ok(())
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
result.expect("Drop failed")
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// The minimum size a split panel child can be, including when overridden via
|
|
376
|
+
/// user drag/drop.
|
|
377
|
+
const MINIMUM_SIZE: i32 = 8;
|
|
378
|
+
|
|
379
|
+
/// When the instantiated, capture the initial dimensions and create the
|
|
380
|
+
/// MouseEvent callbacks.
|
|
381
|
+
impl ResizingState {
|
|
382
|
+
pub fn new(
|
|
383
|
+
index: usize,
|
|
384
|
+
client_offset: i32,
|
|
385
|
+
ctx: &Context<SplitPanel>,
|
|
386
|
+
first_elem: &HtmlElement,
|
|
387
|
+
pointer_id: i32,
|
|
388
|
+
pointer_elem: HtmlElement,
|
|
389
|
+
) -> ApiResult<Self> {
|
|
390
|
+
let orientation = ctx.props().orientation;
|
|
391
|
+
let reverse = ctx.props().reverse;
|
|
392
|
+
let split_panel = ctx.link();
|
|
393
|
+
let total = match orientation {
|
|
394
|
+
Orientation::Horizontal => first_elem.offset_width(),
|
|
395
|
+
Orientation::Vertical => first_elem.offset_height(),
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
let alt = match orientation {
|
|
399
|
+
Orientation::Horizontal => first_elem.offset_height(),
|
|
400
|
+
Orientation::Vertical => first_elem.offset_width(),
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
let mouseup = Closure::new({
|
|
404
|
+
let cb = split_panel.callback(|_| SplitPanelMsg::StopResizing);
|
|
405
|
+
move |x| cb.emit(x)
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
let mousemove = Closure::new({
|
|
409
|
+
let cb = split_panel.callback(move |event: MouseEvent| {
|
|
410
|
+
SplitPanelMsg::MoveResizing(match orientation {
|
|
411
|
+
Orientation::Horizontal => event.client_x(),
|
|
412
|
+
Orientation::Vertical => event.client_y(),
|
|
413
|
+
})
|
|
414
|
+
});
|
|
415
|
+
move |x| cb.emit(x)
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
let mut state = Self {
|
|
419
|
+
index,
|
|
420
|
+
cursor: "".to_owned(),
|
|
421
|
+
start: client_offset,
|
|
422
|
+
orientation,
|
|
423
|
+
reverse,
|
|
424
|
+
total,
|
|
425
|
+
alt,
|
|
426
|
+
body_style: global::body().style(),
|
|
427
|
+
mouseup,
|
|
428
|
+
mousemove,
|
|
429
|
+
pointer_id,
|
|
430
|
+
pointer_elem,
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
state.capture_cursor()?;
|
|
434
|
+
state.register_listeners()?;
|
|
435
|
+
Ok(state)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
fn get_offset(&self, client_offset: i32) -> i32 {
|
|
439
|
+
let delta = if self.reverse {
|
|
440
|
+
self.start - client_offset
|
|
441
|
+
} else {
|
|
442
|
+
client_offset - self.start
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
max(MINIMUM_SIZE, self.total + delta)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
pub fn get_style(&self, client_offset: i32) -> Option<String> {
|
|
449
|
+
let offset = self.get_offset(client_offset);
|
|
450
|
+
Some(match self.orientation {
|
|
451
|
+
Orientation::Horizontal => {
|
|
452
|
+
format!("max-width:{offset}px;min-width:{offset}px;width:{offset}px")
|
|
453
|
+
},
|
|
454
|
+
Orientation::Vertical => {
|
|
455
|
+
format!("max-height:{offset}px;min-height:{offset}px;height:{offset}px")
|
|
456
|
+
},
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
pub fn get_dimensions(&self, client_offset: i32) -> (i32, i32) {
|
|
461
|
+
let offset = self.get_offset(client_offset);
|
|
462
|
+
match self.orientation {
|
|
463
|
+
Orientation::Horizontal => (std::cmp::max(MINIMUM_SIZE, offset), self.alt),
|
|
464
|
+
Orientation::Vertical => (self.alt, std::cmp::max(MINIMUM_SIZE, offset)),
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/// Adds the event listeners, the corollary of `Drop`.
|
|
469
|
+
fn register_listeners(&self) -> ApiResult<()> {
|
|
470
|
+
let mousemove = self.mousemove.as_ref().unchecked_ref();
|
|
471
|
+
global::body().add_event_listener_with_callback("mousemove", mousemove)?;
|
|
472
|
+
let mouseup = self.mouseup.as_ref().unchecked_ref();
|
|
473
|
+
Ok(global::body().add_event_listener_with_callback("mouseup", mouseup)?)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/// Helper functions capture and release the global cursor while dragging is
|
|
477
|
+
/// occurring.
|
|
478
|
+
fn capture_cursor(&mut self) -> ApiResult<()> {
|
|
479
|
+
self.pointer_elem.set_pointer_capture(self.pointer_id)?;
|
|
480
|
+
self.cursor = self.body_style.get_property_value("cursor")?;
|
|
481
|
+
self.body_style
|
|
482
|
+
.set_property("cursor", match self.orientation {
|
|
483
|
+
Orientation::Horizontal => "col-resize",
|
|
484
|
+
Orientation::Vertical => "row-resize",
|
|
485
|
+
})?;
|
|
486
|
+
|
|
487
|
+
Ok(())
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/// " but for release
|
|
491
|
+
fn release_cursor(&self) -> ApiResult<()> {
|
|
492
|
+
self.pointer_elem.release_pointer_capture(self.pointer_id)?;
|
|
493
|
+
Ok(self.body_style.set_property("cursor", &self.cursor)?)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
@@ -14,20 +14,20 @@ use yew::{Callback, Children, Component, Html, Properties, classes, html};
|
|
|
14
14
|
|
|
15
15
|
use crate::components::style::LocalStyle;
|
|
16
16
|
use crate::css;
|
|
17
|
-
|
|
18
|
-
pub trait Tab: PartialEq + std::fmt::Display + Clone + Default + 'static {}
|
|
19
|
-
|
|
20
|
-
impl Tab for String {}
|
|
21
|
-
|
|
22
|
-
impl Tab for &'static str {}
|
|
17
|
+
use crate::presentation::ColumnTab;
|
|
23
18
|
|
|
24
19
|
#[derive(Properties, Debug, PartialEq)]
|
|
25
|
-
pub struct TabListProps<T:
|
|
26
|
-
|
|
20
|
+
pub struct TabListProps<T: ColumnTab> {
|
|
21
|
+
/// All tabs
|
|
27
22
|
pub tabs: Vec<T>,
|
|
23
|
+
|
|
24
|
+
/// Fires when the selected tab changes in the UI.
|
|
28
25
|
pub on_tab_change: Callback<(usize, T)>,
|
|
26
|
+
|
|
27
|
+
// Which tab is selected.
|
|
29
28
|
pub selected_tab: Option<usize>,
|
|
30
|
-
|
|
29
|
+
|
|
30
|
+
// The currently instantiated tabs.
|
|
31
31
|
pub children: Children,
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -35,12 +35,12 @@ pub enum TabListMsg {
|
|
|
35
35
|
SetSelected(usize),
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
pub struct TabList<T:
|
|
38
|
+
pub struct TabList<T: ColumnTab> {
|
|
39
39
|
t: std::marker::PhantomData<T>,
|
|
40
40
|
selected_idx: usize,
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
impl<T:
|
|
43
|
+
impl<T: ColumnTab> Component for TabList<T> {
|
|
44
44
|
type Message = TabListMsg;
|
|
45
45
|
type Properties = TabListProps<T>;
|
|
46
46
|
|
|
@@ -21,37 +21,49 @@ use crate::model::*;
|
|
|
21
21
|
use crate::renderer::*;
|
|
22
22
|
use crate::utils::*;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
pub type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
|
|
24
|
+
type CopyDropDownMenuItem = DropDownMenuItem<ExportFile>;
|
|
26
25
|
|
|
27
26
|
#[derive(Properties, PartialEq)]
|
|
28
27
|
pub struct CopyDropDownMenuProps {
|
|
29
|
-
pub renderer: Renderer,
|
|
30
28
|
pub callback: Callback<ExportFile>,
|
|
31
29
|
pub root: web_sys::HtmlElement,
|
|
30
|
+
pub renderer: Renderer,
|
|
32
31
|
|
|
33
32
|
#[prop_or_default]
|
|
34
33
|
weak_link: WeakScope<CopyDropDownMenu>,
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
pub struct CopyDropDownMenu {
|
|
38
|
-
_sub: Subscription,
|
|
39
|
-
}
|
|
40
|
-
|
|
41
36
|
impl ModalLink<CopyDropDownMenu> for CopyDropDownMenuProps {
|
|
42
37
|
fn weak_link(&self) -> &'_ WeakScope<CopyDropDownMenu> {
|
|
43
38
|
&self.weak_link
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
|
|
42
|
+
pub struct CopyDropDownMenu {
|
|
43
|
+
_sub: Subscription,
|
|
44
|
+
}
|
|
45
|
+
|
|
47
46
|
impl Component for CopyDropDownMenu {
|
|
48
|
-
type Message =
|
|
47
|
+
type Message = ();
|
|
49
48
|
type Properties = CopyDropDownMenuProps;
|
|
50
49
|
|
|
50
|
+
fn create(ctx: &Context<Self>) -> Self {
|
|
51
|
+
ctx.set_modal_link();
|
|
52
|
+
let _sub = ctx
|
|
53
|
+
.props()
|
|
54
|
+
.renderer
|
|
55
|
+
.plugin_changed
|
|
56
|
+
.add_listener(ctx.link().callback(|_| ()));
|
|
57
|
+
|
|
58
|
+
Self { _sub }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
|
62
|
+
true
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
fn view(&self, ctx: &Context<Self>) -> yew::virtual_dom::VNode {
|
|
52
66
|
let plugin = ctx.props().renderer.get_active_plugin().unwrap();
|
|
53
|
-
// let has_render = js_sys::Reflect::has(&plugin,
|
|
54
|
-
// js_intern::js_intern!("render")).unwrap();
|
|
55
67
|
let is_chart = plugin.name().as_str() != "Datagrid";
|
|
56
68
|
let has_selection = ctx.props().renderer.get_selection().is_some();
|
|
57
69
|
html! {
|
|
@@ -63,21 +75,6 @@ impl Component for CopyDropDownMenu {
|
|
|
63
75
|
</StyleProvider>
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
|
-
|
|
67
|
-
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
|
|
68
|
-
true
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
fn create(ctx: &Context<Self>) -> Self {
|
|
72
|
-
ctx.set_modal_link();
|
|
73
|
-
let _sub = ctx
|
|
74
|
-
.props()
|
|
75
|
-
.renderer
|
|
76
|
-
.plugin_changed
|
|
77
|
-
.add_listener(ctx.link().callback(|_| ()));
|
|
78
|
-
|
|
79
|
-
Self { _sub }
|
|
80
|
-
}
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
fn get_menu_items(is_chart: bool, has_selection: bool) -> Vec<CopyDropDownMenuItem> {
|
|
@@ -15,23 +15,11 @@ use std::rc::Rc;
|
|
|
15
15
|
use yew::prelude::*;
|
|
16
16
|
|
|
17
17
|
use crate::components::form::number_field::NumberField;
|
|
18
|
-
use crate::components::form::
|
|
18
|
+
use crate::components::form::select_value_field::SelectValueField;
|
|
19
19
|
use crate::components::modal::{ModalLink, SetModalLink};
|
|
20
20
|
use crate::config::*;
|
|
21
21
|
use crate::utils::WeakScope;
|
|
22
22
|
|
|
23
|
-
pub enum DatetimeStyleCustomMsg {
|
|
24
|
-
FractionalSeconds(Option<f64>),
|
|
25
|
-
Year(CustomDatetimeFormat),
|
|
26
|
-
Month(CustomDatetimeFormat),
|
|
27
|
-
Day(CustomDatetimeFormat),
|
|
28
|
-
Weekday(CustomDatetimeFormat),
|
|
29
|
-
Hour(CustomDatetimeFormat),
|
|
30
|
-
Minute(CustomDatetimeFormat),
|
|
31
|
-
Second(CustomDatetimeFormat),
|
|
32
|
-
Hour12(bool),
|
|
33
|
-
}
|
|
34
|
-
|
|
35
23
|
#[derive(Properties)]
|
|
36
24
|
pub struct DatetimeStyleCustomProps {
|
|
37
25
|
pub enable_time_config: bool,
|
|
@@ -57,18 +45,24 @@ impl PartialEq for DatetimeStyleCustomProps {
|
|
|
57
45
|
}
|
|
58
46
|
}
|
|
59
47
|
|
|
48
|
+
pub enum DatetimeStyleCustomMsg {
|
|
49
|
+
FractionalSeconds(Option<f64>),
|
|
50
|
+
Year(CustomDatetimeFormat),
|
|
51
|
+
Month(CustomDatetimeFormat),
|
|
52
|
+
Day(CustomDatetimeFormat),
|
|
53
|
+
Weekday(CustomDatetimeFormat),
|
|
54
|
+
Hour(CustomDatetimeFormat),
|
|
55
|
+
Minute(CustomDatetimeFormat),
|
|
56
|
+
Second(CustomDatetimeFormat),
|
|
57
|
+
Hour12(bool),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
60
|
/// The custom variation of the options parameter for `Intl.DatetimeFormat`.
|
|
61
61
|
/// Complement to `DatetimeStyleSimple`.
|
|
62
62
|
pub struct DatetimeStyleCustom {
|
|
63
63
|
config: CustomDatetimeStyleConfig,
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
impl DatetimeStyleCustom {
|
|
67
|
-
fn dispatch_config(&self, ctx: &Context<Self>) {
|
|
68
|
-
ctx.props().on_change.emit(self.config.clone());
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
66
|
impl Component for DatetimeStyleCustom {
|
|
73
67
|
type Message = DatetimeStyleCustomMsg;
|
|
74
68
|
type Properties = DatetimeStyleCustomProps;
|
|
@@ -220,3 +214,9 @@ impl Component for DatetimeStyleCustom {
|
|
|
220
214
|
}
|
|
221
215
|
}
|
|
222
216
|
}
|
|
217
|
+
|
|
218
|
+
impl DatetimeStyleCustom {
|
|
219
|
+
fn dispatch_config(&self, ctx: &Context<Self>) {
|
|
220
|
+
ctx.props().on_change.emit(self.config.clone());
|
|
221
|
+
}
|
|
222
|
+
}
|