euv_ui/component/virtual_list/view/
fn.rs1use crate::*;
2
3#[component]
17pub fn euv_virtual_list(node: VirtualNode<EuvVirtualListProps>) -> VirtualNode {
18 let EuvVirtualListProps {
19 config,
20 item_renderer,
21 on_scroll,
22 on_visible_range_change,
23 } = node.try_get_props().unwrap_or_default();
24 let state: UseVirtualList = UseVirtualList::use_scroll_state();
25 let container_id: String = config.id.clone();
26 let total_count: usize = config.total_count;
27 let item_height: i32 = config.item_height;
28 let overscan_count: usize = config.overscan_count;
29 let viewport_height: i32 = state.get_viewport_height().get();
30 if viewport_height == 0 {
31 state.schedule_measure_by_id(&container_id);
32 }
33 let scroll_handler: Option<Rc<dyn Fn(Event)>> = {
34 let state: UseVirtualList = state;
35 let container_id: String = container_id.clone();
36 let on_scroll: Option<VirtualListScrollHandler> = on_scroll;
37 Some(Rc::new(move |_: Event| {
38 if let Some(element) = UseVirtualList::try_get_container_by_id(&container_id) {
39 let html_element: HtmlElement = element.unchecked_into();
40 let scroll_offset: i32 = html_element.scroll_top();
41 state.get_scroll_offset().set(scroll_offset);
42 if let Some(ref callback) = on_scroll {
43 callback(scroll_offset);
44 }
45 }
46 }))
47 };
48 let scroll_offset: i32 = state.get_scroll_offset().get();
49 let (_, _, render_start, render_end): (usize, usize, usize, usize) =
50 UseVirtualList::compute_visible_range(
51 scroll_offset,
52 viewport_height,
53 total_count,
54 item_height,
55 overscan_count,
56 );
57 let range_callback: Option<VirtualListRangeHandler> = on_visible_range_change.clone();
58 let range_watch_initialized: Signal<bool> = App::use_signal(|| false);
59 if !range_watch_initialized.get() {
60 let scroll_offset_signal: Signal<i32> = state.get_scroll_offset();
61 let viewport_height_signal: Signal<i32> = state.get_viewport_height();
62 let fire_addr: usize = Box::leak(Box::new(Box::new(move || {
63 let scroll_offset: i32 = scroll_offset_signal.get();
64 let viewport_height: i32 = viewport_height_signal.get();
65 if let Some(ref callback) = range_callback {
66 let (visible_start, visible_end, _, _): (usize, usize, usize, usize) =
67 UseVirtualList::compute_visible_range(
68 scroll_offset,
69 viewport_height,
70 total_count,
71 item_height,
72 overscan_count,
73 );
74 callback((visible_start, visible_end));
75 }
76 }) as Box<dyn FnMut()>)) as *mut Box<dyn FnMut()> as usize;
77 App::batch(|| {
78 scroll_offset_signal.subscribe(move || {
79 App::batch(|| unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() });
80 });
81 viewport_height_signal.subscribe(move || {
82 App::batch(|| unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() });
83 });
84 unsafe { (&mut *(fire_addr as *mut Box<dyn FnMut()>))() }
85 range_watch_initialized.set(true);
86 });
87 }
88 let total_height: i32 = total_count as i32 * item_height;
89 let top_padding: i32 = render_start as i32 * item_height;
90 let children: Vec<VirtualNode> = (render_start..render_end)
91 .map(|index: usize| {
92 let item_node: VirtualNode = (item_renderer)(index);
93 html! {
94 div {
95 key: index.to_string()
96 style: format!("height: {item_height}px; box-sizing: border-box;")
97 item_node
98 }
99 }
100 })
101 .collect();
102 html! {
103 div {
104 class: c_virtual_list_container()
105 id: container_id
106 onscroll: scroll_handler
107 div {
108 style: format!("position: relative; height: {total_height}px;")
109 div {
110 style: format!("position: absolute; top: {top_padding}px; left: 0; right: 0;")
111 children
112 }
113 }
114 }
115 }
116}