use crate::*;
#[component]
pub fn euv_virtual_list(node: VirtualNode<EuvVirtualListProps>) -> VirtualNode {
let EuvVirtualListProps {
config,
item_renderer,
on_scroll,
on_visible_range_change,
} = node.try_get_props().unwrap_or_default();
let state: UseVirtualList = UseVirtualList::use_scroll_state();
let container_id: String = config.id.clone();
let total_count: usize = config.total_count;
let item_height: i32 = config.item_height;
let overscan_count: usize = config.overscan_count;
let viewport_height: i32 = state.get_viewport_height().get();
if viewport_height == 0 {
state.schedule_measure_by_id(&container_id);
}
let scroll_handler: Option<Rc<dyn Fn(Event)>> = {
let state: UseVirtualList = state;
let container_id: String = container_id.clone();
let on_scroll: Option<VirtualListScrollHandler> = on_scroll;
Some(Rc::new(move |_: Event| {
if let Some(element) = UseVirtualList::try_get_container_by_id(&container_id) {
let html_element: HtmlElement = element.unchecked_into();
let scroll_offset: i32 = html_element.scroll_top();
state.get_scroll_offset().set(scroll_offset);
if let Some(ref callback) = on_scroll {
callback(scroll_offset);
}
}
}))
};
let scroll_offset: i32 = state.get_scroll_offset().get();
let (_, _, render_start, render_end): (usize, usize, usize, usize) =
UseVirtualList::compute_visible_range(
scroll_offset,
viewport_height,
total_count,
item_height,
overscan_count,
);
let range_callback: Option<VirtualListRangeHandler> = on_visible_range_change.clone();
let range_watch_initialized: Signal<bool> = App::use_signal(|| false);
if !range_watch_initialized.get() {
let scroll_offset_signal: Signal<i32> = state.get_scroll_offset();
let viewport_height_signal: Signal<i32> = state.get_viewport_height();
let fire_addr: usize = Box::leak(Box::new(Box::new(move || {
let scroll_offset: i32 = scroll_offset_signal.get();
let viewport_height: i32 = viewport_height_signal.get();
if let Some(ref callback) = range_callback {
let (visible_start, visible_end, _, _): (usize, usize, usize, usize) =
UseVirtualList::compute_visible_range(
scroll_offset,
viewport_height,
total_count,
item_height,
overscan_count,
);
callback((visible_start, visible_end));
}
}) as Box<dyn FnMut()>)) as *mut Box<dyn FnMut()> as usize;
App::batch(|| {
scroll_offset_signal.subscribe(move || {
App::batch(|| unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() });
});
viewport_height_signal.subscribe(move || {
App::batch(|| unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() });
});
unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() }
range_watch_initialized.set(true);
});
}
let total_height: i32 = total_count as i32 * item_height;
let top_padding: i32 = render_start as i32 * item_height;
let children: Vec<VirtualNode> = (render_start..render_end)
.map(|index: usize| {
let item_node: VirtualNode = (item_renderer)(index);
html! {
div {
key: index.to_string()
style: format!("height: {item_height}px; box-sizing: border-box;")
item_node
}
}
})
.collect();
html! {
div {
class: c_virtual_list_container()
id: container_id
onscroll: scroll_handler
div {
style: format!("position: relative; height: {total_height}px;")
div {
style: format!("position: absolute; top: {top_padding}px; left: 0; right: 0;")
children
}
}
}
}
}