use std::sync::Arc;
use crate::div::{div, Div};
use crate::widgets::scroll::ScrollDirection;
pub type ItemBuilder = Arc<dyn Fn(usize) -> Div + Send + Sync>;
pub struct VirtualList {
item_count: usize,
item_builder: ItemBuilder,
inner: Div,
estimated_item_height: f32,
window_size: usize,
item_class: Option<String>,
content_class: Option<String>,
}
pub fn virtual_list<F>(item_count: usize, builder: F) -> VirtualList
where
F: Fn(usize) -> Div + Send + Sync + 'static,
{
VirtualList {
item_count,
item_builder: Arc::new(builder),
inner: div().overflow_clip(),
estimated_item_height: 40.0,
window_size: 50,
item_class: None,
content_class: None,
}
}
impl VirtualList {
pub fn estimated_item_height(mut self, height: f32) -> Self {
self.estimated_item_height = height;
self
}
pub fn window_size(mut self, n: usize) -> Self {
self.window_size = n;
self
}
pub fn w(mut self, v: f32) -> Self {
self.inner = self.inner.w(v);
self
}
pub fn h(mut self, v: f32) -> Self {
self.inner = self.inner.h(v);
self
}
pub fn w_full(mut self) -> Self {
self.inner = self.inner.w_full();
self
}
pub fn h_full(mut self) -> Self {
self.inner = self.inner.h_full();
self
}
pub fn bg(mut self, color: blinc_core::Color) -> Self {
self.inner = self.inner.bg(color);
self
}
pub fn rounded(mut self, r: f32) -> Self {
self.inner = self.inner.rounded(r);
self
}
pub fn border(mut self, width: f32, color: blinc_core::Color) -> Self {
self.inner = self.inner.border(width, color);
self
}
pub fn p(mut self, v: f32) -> Self {
self.inner = self.inner.p(v);
self
}
pub fn gap_px(mut self, v: f32) -> Self {
self.inner = self.inner.gap_px(v);
self
}
pub fn id(mut self, id: &str) -> Self {
self.inner = self.inner.id(id);
self
}
pub fn class(mut self, class: &str) -> Self {
self.inner = self.inner.class(class);
self
}
pub fn item_class(mut self, class: impl Into<String>) -> Self {
self.item_class = Some(class.into());
self
}
pub fn content_class(mut self, class: impl Into<String>) -> Self {
self.content_class = Some(class.into());
self
}
pub fn into_div(self) -> Div {
let viewport_height = {
use crate::div::ElementBuilder as _;
self.inner
.layout_style()
.and_then(|s| match s.size.height {
taffy::Dimension::Length(h) => Some(h),
_ => None,
})
.unwrap_or(400.0)
};
let render_count = self.window_size.min(self.item_count);
let mut content = div().flex_col().w_full();
if let Some(ref cls) = self.content_class {
content = content.class(cls);
}
for i in 0..render_count {
let mut item = (self.item_builder)(i);
if let Some(ref cls) = self.item_class {
item = item.class(cls);
}
content = content.child(item);
}
if render_count < self.item_count {
let remaining = self.item_count - render_count;
let spacer_height = remaining as f32 * self.estimated_item_height;
content = content.child(div().h(spacer_height).w_full());
}
let scroll = crate::widgets::scroll::scroll()
.direction(ScrollDirection::Vertical)
.w_full()
.h(viewport_height)
.child(content);
self.inner.child(scroll)
}
}
impl From<VirtualList> for Div {
fn from(vl: VirtualList) -> Div {
vl.into_div()
}
}