use crate::{Button, ButtonAppearance};
use leptos::{either::Either, prelude::*};
use std::cmp::min;
use thaw_utils::{class_list, mount_style, Model};
#[component]
pub fn Pagination(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(default = 1.into(), into)]
page: Model<usize>,
#[prop(into)]
page_count: Signal<usize>,
#[prop(default = 1.into(), into)]
sibling_count: Signal<usize>,
) -> impl IntoView {
mount_style("pagination", include_str!("./pagination.css"));
let no_next = Memo::new(move |_| page.get() == page_count.get());
let no_previous = Memo::new(move |_| page.get() == 1);
let on_click_previous = move |_| {
page.update(|val| *val -= 1);
};
let on_click_next = move |_| {
page.update(|val| *val += 1);
};
view! {
<div class=class_list!["thaw-pagination", class]>
<Button
class="thaw-pagination-item"
on_click=on_click_previous
icon=icondata_ai::AiLeftOutlined
disabled=no_previous
/>
{move || {
use_pagination(page.get(), page_count.get(), sibling_count.get())
.into_iter()
.map(|item| {
if let PaginationItem::Number(nb) = item {
Either::Left(
view! {
<Button
class="thaw-pagination-item"
appearance=Memo::new(move |_| {
if page.get() == nb {
ButtonAppearance::Primary
} else {
ButtonAppearance::Secondary
}
})
on_click=move |_| {
if page.get() != nb {
page.set(nb)
}
}
>
{nb}
</Button>
},
)
} else {
Either::Right(view! { <div class="thaw-pagination-item">"..."</div> })
}
})
.collect_view()
}}
<Button
class="thaw-pagination-item"
on_click=on_click_next
icon=icondata_ai::AiRightOutlined
disabled=no_next
/>
</div>
}
}
fn range(start: usize, end: usize) -> Vec<PaginationItem> {
let mut ret = vec![];
for idx in start..=end {
ret.push(PaginationItem::Number(idx));
}
ret
}
enum PaginationItem {
DotLeft,
DotRight,
Number(usize),
}
fn use_pagination(page: usize, count: usize, sibling_count: usize) -> Vec<PaginationItem> {
let total_page_numbers = sibling_count + 5;
if total_page_numbers >= count {
return range(1, count);
}
let current_page = page;
let left_sibling_index = if current_page > sibling_count + 1 {
current_page - sibling_count
} else {
1
};
let right_sibling_index = min(current_page + sibling_count, count);
let should_show_left_dots = left_sibling_index > 2;
let should_show_right_dots = right_sibling_index < count - 2;
let first_page_index = 1;
let last_page_index = count;
if !should_show_left_dots && should_show_right_dots {
let left_item_count = 3 + 2 * sibling_count;
let mut left_range = range(1, left_item_count);
left_range.push(PaginationItem::DotRight);
left_range.push(PaginationItem::Number(count));
left_range
} else if should_show_left_dots && !should_show_right_dots {
let right_item_count = 3 + 2 * sibling_count;
let mut right_range = range(count - right_item_count + 1, count);
let mut ret = vec![
PaginationItem::Number(first_page_index),
PaginationItem::DotLeft,
];
ret.append(&mut right_range);
ret
} else {
let mut middle_range = range(left_sibling_index, right_sibling_index);
let mut range = vec![
PaginationItem::Number(first_page_index),
PaginationItem::DotLeft,
];
range.append(&mut middle_range);
range.append(&mut vec![
PaginationItem::DotRight,
PaginationItem::Number(last_page_index),
]);
range
}
}