Skip to content

Commit 1117f61

Browse files
committed
Fix scroll panel height calc, add debug mode warning
1 parent 335a0ce commit 1117f61

4 files changed

Lines changed: 127 additions & 32 deletions

File tree

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use super::containers::scroll_panel::*;
3636
use super::containers::split_panel::{Orientation, SplitPanel};
3737
use super::style::LocalStyle;
3838
use super::viewer::ColumnLocator;
39+
use crate::components::containers::scroll_panel_item::ScrollPanelItem;
3940
use crate::custom_elements::ColumnDropDownElement;
4041
use crate::dragdrop::*;
4142
use crate::model::*;
@@ -240,16 +241,16 @@ impl Component for ColumnSelector {
240241
active_classes.push("is-aggregated");
241242
}
242243

243-
let size = 28.0f64.mul_add(
244+
let size_hint = 28.0f64.mul_add(
244245
(config.group_by.len()
245246
+ config.split_by.len()
246247
+ config.filter.len()
247248
+ config.sort.len()) as f64,
248-
224.0,
249+
220.0,
249250
);
250251

251252
let config_selector = html_nested! {
252-
<ScrollPanelItem key={ "config_selector" } { size }>
253+
<ScrollPanelItem key={ "config_selector" } { size_hint }>
253254
<ConfigSelector
254255
dragdrop={ &ctx.props().dragdrop }
255256
session={ &ctx.props().session }
@@ -266,13 +267,13 @@ impl Component for ColumnSelector {
266267
.enumerate()
267268
.map(|(idx, name)| {
268269
let ondragenter = ondragenter.reform(move |_| Some(idx));
269-
let size = if named_count > 0 { 48.0 } else { 28.0 };
270+
let size_hint = if named_count > 0 { 50.0 } else { 28.0 };
270271
named_count = named_count.saturating_sub(1);
271272
let key = name.get_name().map(|x| x.to_owned()).unwrap_or_else(|| format!("__auto_{}__", idx));
272273
let column_dropdown = self.column_dropdown.clone();
273274
let is_editing = matches!(&ctx.props().selected_column, Some(ColumnLocator::Plain(x)) if x == &key);
274275
html_nested! {
275-
<ScrollPanelItem key={ key } { size }>
276+
<ScrollPanelItem key={ key } { size_hint }>
276277
<ActiveColumn
277278
{ idx }
278279
{ name }
@@ -300,7 +301,7 @@ impl Component for ColumnSelector {
300301
html_nested! {
301302
<ScrollPanelItem
302303
key={ vc.name }
303-
size={ 28.0 }>
304+
size_hint={ 28.0 }>
304305
<InactiveColumn
305306
{ idx }
306307
visible={ vc.is_visible }
@@ -324,7 +325,7 @@ impl Component for ColumnSelector {
324325
};
325326

326327
let add_column = html_nested! {
327-
<ScrollPanelItem key={ "__add_expression__" } size={ size }>
328+
<ScrollPanelItem key={ "__add_expression__" } size_hint={ size }>
328329
<AddExpressionButton
329330
on_open_expr_panel={ &ctx.props().on_open_expr_panel }
330331
selected_column={ ctx.props().selected_column.clone() }>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod kvpair;
1919
pub mod radio_list;
2020
pub mod radio_list_item;
2121
pub mod scroll_panel;
22+
pub mod scroll_panel_item;
2223
pub mod select;
2324
pub mod sidebar;
2425
pub mod split_panel;

rust/perspective-viewer/src/rust/components/containers/scroll_panel.rs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,11 @@ use web_sys::Element;
2121
use yew::prelude::*;
2222
use yew::virtual_dom::VChild;
2323

24+
use super::scroll_panel_item::ScrollPanelItem;
2425
use crate::components::style::LocalStyle;
2526
use crate::utils::*;
2627
use crate::*;
2728

28-
#[derive(PartialEq, Properties)]
29-
pub struct ScrollPanelItemProps {
30-
pub size: f64,
31-
pub children: Children,
32-
}
33-
34-
pub struct ScrollPanelItem {}
35-
36-
impl Component for ScrollPanelItem {
37-
type Message = ();
38-
type Properties = ScrollPanelItemProps;
39-
40-
fn create(_ctx: &Context<Self>) -> Self {
41-
Self {}
42-
}
43-
44-
fn view(&self, ctx: &Context<Self>) -> Html {
45-
ctx.props().children.iter().collect()
46-
}
47-
}
48-
4929
pub struct ScrollPanel {
5030
viewport_ref: NodeRef,
5131
viewport_height: f64,
@@ -96,7 +76,7 @@ impl ScrollPanelProps {
9676
fn total_height(&self) -> f64 {
9777
self.children
9878
.iter()
99-
.map(|x| x.props.size)
79+
.map(|x| x.props.get_size())
10080
.reduce(|x, y| x + y)
10181
.unwrap_or_default()
10282
}
@@ -149,12 +129,12 @@ impl ScrollPanel {
149129
.iter()
150130
.enumerate()
151131
.find_or_last(|(i, x)| {
152-
if offset + x.props.size < scroll_top {
132+
if offset + x.props.get_size() < scroll_top {
153133
start_node = *i + 1;
154-
start_y = offset + x.props.size;
134+
start_y = offset + x.props.get_size();
155135
}
156136

157-
offset += x.props.size;
137+
offset += x.props.get_size();
158138
offset > scroll_top + self.viewport_height
159139
})
160140
.map(|x| x.0)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
13+
// Forked from https://github.com/AircastDev/yew-virtual-scroller (Apache 2.0)
14+
// Adds support for Yew 0.19, auto-width and a simplified message structure.
15+
16+
#[cfg(debug_assertions)]
17+
use std::cell::Cell;
18+
#[cfg(debug_assertions)]
19+
use std::rc::Rc;
20+
21+
use yew::prelude::*;
22+
23+
#[derive(PartialEq, Properties)]
24+
pub struct ScrollPanelItemProps {
25+
/// The expected size of this component in pixels. Calculating this value
26+
/// ahead of time makes it easier to implement a high-performance virtual
27+
/// renderer without resorting to weird tricks, _but_ checking that this
28+
/// hint is correct is nearly as expensive. So, we only generate the
29+
/// validation code in debug builds.
30+
pub size_hint: f64,
31+
pub children: Children,
32+
33+
#[cfg(debug_assertions)]
34+
#[prop_or_default]
35+
measured_size: Rc<Cell<Option<f64>>>,
36+
}
37+
38+
impl ScrollPanelItemProps {
39+
#[cfg(debug_assertions)]
40+
pub fn get_size(&self) -> f64 {
41+
self.measured_size.get().unwrap_or(self.size_hint)
42+
}
43+
44+
#[cfg(not(debug_assertions))]
45+
pub fn get_size(&self) -> f64 {
46+
self.size_hint
47+
}
48+
}
49+
50+
#[cfg(debug_assertions)]
51+
pub struct ScrollPanelItem {
52+
node: NodeRef,
53+
}
54+
55+
#[cfg(not(debug_assertions))]
56+
pub struct ScrollPanelItem {}
57+
58+
impl Component for ScrollPanelItem {
59+
type Message = ();
60+
type Properties = ScrollPanelItemProps;
61+
62+
#[cfg(debug_assertions)]
63+
fn create(ctx: &Context<Self>) -> Self {
64+
ctx.props().measured_size.set(Some(ctx.props().size_hint));
65+
Self {
66+
node: NodeRef::default(),
67+
}
68+
}
69+
70+
#[cfg(not(debug_assertions))]
71+
fn create(_ctx: &Context<Self>) -> Self {
72+
Self {}
73+
}
74+
75+
#[cfg(debug_assertions)]
76+
fn view(&self, ctx: &Context<Self>) -> Html {
77+
html! {
78+
<div class="debug-size-wrapper" style="display:grid" ref={ self.node.clone() }>
79+
{ for ctx.props().children.iter() }
80+
</div>
81+
}
82+
}
83+
84+
#[cfg(not(debug_assertions))]
85+
fn view(&self, ctx: &Context<Self>) -> Html {
86+
html! {
87+
{ for ctx.props().children.iter() }
88+
}
89+
}
90+
91+
#[cfg(debug_assertions)]
92+
fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
93+
ctx.props().measured_size.set(Some(ctx.props().size_hint));
94+
true
95+
}
96+
97+
#[cfg(debug_assertions)]
98+
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
99+
if first_render {
100+
let elem = self.node.cast::<web_sys::HtmlElement>().unwrap();
101+
let new_height = elem.get_bounding_client_rect().height();
102+
if ctx.props().measured_size.get() != Some(new_height) {
103+
tracing::warn!(
104+
"ScrollPanel size_hint does not match element size: {} != {}",
105+
ctx.props().size_hint,
106+
new_height
107+
);
108+
web_sys::console::warn_1(&elem);
109+
ctx.props().measured_size.set(Some(new_height));
110+
}
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)