use crate::*;
pub(crate) fn virtual_list_on_scroll(state: UseVirtualList) -> Option<Rc<dyn Fn(Event)>> {
Some(Rc::new(move |_event: Event| {
if let Some(container) = virtual_list_container() {
let html_element: HtmlElement = container.unchecked_into();
state.get_scroll_offset().set(html_element.scroll_top());
state
.get_viewport_height()
.set(html_element.client_height());
}
}))
}
pub(crate) fn virtual_list_update_viewport_height(state: UseVirtualList) {
if let Some(container) = virtual_list_container() {
let html_element: HtmlElement = container.unchecked_into();
state
.get_viewport_height()
.set(html_element.client_height());
}
}
pub(crate) fn virtual_list_schedule_measure(state: UseVirtualList) {
let callback: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
virtual_list_update_viewport_height(state);
}));
window()
.expect("no global window exists")
.request_animation_frame(callback.as_ref().unchecked_ref())
.expect("failed to request animation frame");
callback.forget();
}
fn virtual_list_container() -> Option<Element> {
window()
.expect("no global window exists")
.document()
.expect("should have a document")
.get_element_by_id(VIRTUAL_LIST_CONTAINER_ID)
}
pub(crate) fn compute_visible_range(
scroll_offset: i32,
viewport_height: i32,
total_count: usize,
item_height: i32,
overscan_count: usize,
) -> (usize, usize, usize, usize) {
let visible_start: usize = (scroll_offset / item_height).max(0) as usize;
let visible_count: usize = if viewport_height > 0 {
let viewport_bottom: i32 = scroll_offset + viewport_height;
let visible_end: usize =
((viewport_bottom + item_height - 1) / item_height).max(0) as usize;
visible_end - visible_start
} else {
VIRTUAL_LIST_DEFAULT_VISIBLE_COUNT
};
let visible_end: usize = (visible_start + visible_count).min(total_count);
let render_start: usize = visible_start.saturating_sub(overscan_count);
let render_end: usize = (visible_end + overscan_count).min(total_count);
(visible_start, visible_end, render_start, render_end)
}