feat: adds Pagination

This commit is contained in:
luoxiao 2024-08-02 15:49:34 +08:00
parent 9e0bf0eaa7
commit 0b8a89caab
8 changed files with 193 additions and 1 deletions

View file

@ -85,13 +85,14 @@ fn TheRouter(is_routing: RwSignal<bool>) -> impl IntoView {
<Route path=StaticSegment("loading-bar") view=LoadingBarMdPage/>
<Route path=StaticSegment("message-bar") view=MessageBarMdPage/>
<Route path=StaticSegment("nav") view=NavMdPage/>
<Route path=StaticSegment("pagination") view=PaginationMdPage/>
<Route path=StaticSegment("popover") view=PopoverMdPage/>
<Route path=StaticSegment("progress-bar") view=ProgressBarMdPage/>
<Route path=StaticSegment("radio") view=RadioMdPage/>
}
}
{
view! {
<Route path=StaticSegment("radio") view=RadioMdPage/>
<Route path=StaticSegment("scrollbar") view=ScrollbarMdPage/>
<Route path=StaticSegment("skeleton") view=SkeletonMdPage/>
<Route path=StaticSegment("slider") view=SliderMdPage/>

View file

@ -230,6 +230,10 @@ pub(crate) fn gen_menu_data() -> Vec<MenuGroupOption> {
value: "/components/nav".into(),
label: "Nav".into(),
},
MenuItemOption {
value: "pagination".into(),
label: "Pagination".into(),
},
MenuItemOption {
value: "/components/popover".into(),
label: "Popover".into(),

View file

@ -54,6 +54,7 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt
"MenuMdPage" => "../docs/menu/mod.md",
"MessageBarMdPage" => "../docs/message_bar/mod.md",
"NavMdPage" => "../docs/nav/mod.md",
"PaginationMdPage" => "../../thaw/src/pagination/docs/mod.md",
"PopoverMdPage" => "../docs/popover/mod.md",
"ProgressBarMdPage" => "../docs/progress_bar/mod.md",
"RadioMdPage" => "../docs/radio/mod.md",

View file

@ -9,6 +9,7 @@ description = "An easy to use leptos component library"
homepage = "https://github.com/thaw-ui/thaw"
repository = "https://github.com/thaw-ui/thaw"
license = "MIT"
exclude = ["src/**/*.md"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -27,6 +27,7 @@ mod loading_bar;
mod menu;
mod message_bar;
mod nav;
mod pagination;
mod popover;
mod progress_bar;
mod radio;
@ -75,6 +76,7 @@ pub use loading_bar::*;
pub use menu::*;
pub use message_bar::*;
pub use nav::*;
pub use pagination::*;
pub use popover::*;
pub use progress_bar::*;
pub use radio::*;

View file

@ -0,0 +1,25 @@
# Pagination
```rust demo
let page = RwSignal::new(1);
view! {
<Space vertical=true>
<div>"Page: " {move || page.get()}</div>
<Pagination page page_count=10 />
</Space>
}
```
### Pagination ranges
```rust demo
view! {
<Space vertical=true>
<Pagination page_count=100 sibling_count=0 />
<Pagination page_count=100 sibling_count=1 />
<Pagination page_count=100 sibling_count=2 />
<Pagination page_count=100 sibling_count=3 />
</Space>
}
```

143
thaw/src/pagination/mod.rs Normal file
View file

@ -0,0 +1,143 @@
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(default = 1.into(), into)] page: Model<usize>,
#[prop(into)] page_count: MaybeSignal<usize>,
#[prop(default = 1.into(), into)] sibling_count: MaybeSignal<usize>,
#[prop(optional, into)] class: MaybeProp<String>,
) -> 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> {
// Pages count is determined as siblingCount + firstPage + lastPage + currentPage + 2*DOTS
let total_page_numbers = sibling_count + 5;
// Case 1:
// If the number of pages is less than the page numbers we want to show in our
// paginationComponent, we return the range [1..totalPageCount]
if total_page_numbers >= count {
return range(1, count);
}
let current_page = page;
// Calculate left and right sibling index and make sure they are within range 1 and totalPageCount
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);
// We do not show dots just when there is just one page number to be inserted between the extremes of sibling and the page limits i.e 1 and totalPageCount.
// Hence we are using leftSiblingIndex > 2 and rightSiblingIndex < totalPageCount - 2
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;
// Case 2: No left dots to show, but rights dots to be shown
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 {
// Case 3: No right dots to show, but left dots to be shown
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 {
// Case 4: Both left and right dots to be shown
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
}
}

View file

@ -0,0 +1,15 @@
.thaw-pagination {
display: flex;
column-gap: 5px;
}
.thaw-pagination-item,
.thaw-button.thaw-pagination-item {
max-width: 32px;
min-width: 32px;
}
div.thaw-pagination-item {
display: flex;
justify-content: center;
}