Skip to content

Commit e56cc16

Browse files
committed
feat: optimize Pagination component
1 parent 25ab208 commit e56cc16

File tree

4 files changed

+104
-93
lines changed

4 files changed

+104
-93
lines changed

demo_markdown/docs/pagination/mod.md

+10-24
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
11
# Pagination
22

33
```rust demo
4-
let (page, set_page) = create_signal(1);
4+
let page = create_rw_signal(1);
55

66
view! {
77
<Space vertical=true>
88
<div>"Page: " {move || page.get()}</div>
9-
<Pagination page on_change=move |p| set_page.set(p) count=10 />
10-
</Space>
11-
}
12-
```
13-
14-
### Color
15-
16-
```rust demo
17-
view! {
18-
<Space vertical=true>
19-
<Pagination count=10 color=ButtonColor::Primary />
20-
<Pagination count=10 color=ButtonColor::Success />
21-
<Pagination count=10 color=ButtonColor::Warning />
22-
<Pagination count=10 color=ButtonColor::Error />
9+
<Pagination page count=10 />
2310
</Space>
2411
}
2512
```
@@ -52,12 +39,11 @@ view! {
5239

5340
### Pagination Props
5441

55-
| Name | Type | Default | Description |
56-
| ------------- | ----------------------------------- | ---------------------- | ---------------------------------------------------------- |
57-
| class | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Additional classes. |
58-
| page | `MaybeSignal<i64>` | `1` | The current page starts from 1. |
59-
| count | `MaybeSignal<i64>` | | The total numbers of pages. |
60-
| sibling_count | `MaybeSignal<i64>` | `1` | Number of visible pages after and before the current page. |
61-
| color | `MaybeSignal<ButtonColor>` | `ButtonColor::Primary` | Button's color. |
62-
| size | `MaybeSignal<ButtonSize>` | `ButtonSize::Medium` | Button size. |
63-
| on_change | `Option<Callback<i64>>` | `None` | Callback fired when the page is changed. |
42+
| Name | Type | Default | Description |
43+
| --- | --- | --- | --- |
44+
| class | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Additional classes. |
45+
| page | `Model<usize>` | `1` | The current page starts from 1. |
46+
| count | `MaybeSignal<usize>` | | The total numbers of pages. |
47+
| sibling_count | `MaybeSignal<usize>` | `1` | Number of visible pages after and before the current page. |
48+
| size | `MaybeSignal<ButtonSize>` | `ButtonSize::Medium` | Button size. |
49+
| on_change | `Option<Callback<usize>>` | `None` | Callback fired when the page is changed. |

thaw/src/pagination/mod.rs

+78-60
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
use std::cmp::{max, min};
2-
1+
use crate::{Button, ButtonSize, ButtonVariant};
32
use leptos::*;
4-
use thaw_utils::{class_list, mount_style, OptionalProp};
5-
6-
use crate::{Button, ButtonColor, ButtonSize, ButtonVariant};
3+
use std::cmp::min;
4+
use thaw_utils::{class_list, mount_style, Model, OptionalProp, StoredMaybeSignal};
75

8-
fn range(start: i64, end: i64) -> Vec<PaginationItem> {
6+
fn range(start: usize, end: usize) -> Vec<PaginationItem> {
97
let mut ret = vec![];
108
for idx in start..=end {
119
ret.push(PaginationItem::Number(idx));
@@ -16,10 +14,10 @@ fn range(start: i64, end: i64) -> Vec<PaginationItem> {
1614
enum PaginationItem {
1715
DotLeft,
1816
DotRight,
19-
Number(i64),
17+
Number(usize),
2018
}
2119

22-
fn use_pagination(page: i64, count: i64, sibling_count: i64) -> Vec<PaginationItem> {
20+
fn use_pagination(page: usize, count: usize, sibling_count: usize) -> Vec<PaginationItem> {
2321
// Pages count is determined as siblingCount + firstPage + lastPage + currentPage + 2*DOTS
2422
let total_page_numbers = sibling_count + 5;
2523
// Case 1:
@@ -30,7 +28,11 @@ fn use_pagination(page: i64, count: i64, sibling_count: i64) -> Vec<PaginationIt
3028
}
3129
let current_page = page;
3230
// Calculate left and right sibling index and make sure they are within range 1 and totalPageCount
33-
let left_sibling_index = max(current_page - sibling_count, 1);
31+
let left_sibling_index = if current_page > sibling_count + 1 {
32+
current_page - sibling_count
33+
} else {
34+
1
35+
};
3436
let right_sibling_index = min(current_page + sibling_count, count);
3537
// 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.
3638
// Hence we are using leftSiblingIndex > 2 and rightSiblingIndex < totalPageCount - 2
@@ -75,85 +77,101 @@ fn use_pagination(page: i64, count: i64, sibling_count: i64) -> Vec<PaginationIt
7577

7678
#[component]
7779
pub fn Pagination(
78-
#[prop(default = MaybeSignal::Static(1), into)] page: MaybeSignal<i64>,
79-
#[prop(into)] count: MaybeSignal<i64>,
80-
#[prop(default = MaybeSignal::Static(1), into)] sibling_count: MaybeSignal<i64>,
81-
#[prop(optional, into)] on_change: Option<Callback<i64>>,
80+
#[prop(default = 1.into(), into)] page: Model<usize>,
81+
#[prop(into)] count: MaybeSignal<usize>,
82+
#[prop(default = 1.into(), into)] sibling_count: MaybeSignal<usize>,
83+
#[prop(optional, into)] on_change: Option<Callback<usize>>,
8284
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
83-
#[prop(optional, into)] color: MaybeSignal<ButtonColor>,
8485
#[prop(optional, into)] size: MaybeSignal<ButtonSize>,
8586
) -> impl IntoView {
86-
let (page_signal, set_page_signal) = create_signal(1);
87-
let no_next = MaybeSignal::derive(move || page_signal.get() == count.get());
88-
let no_previous = MaybeSignal::derive(move || page_signal.get() == 1);
87+
mount_style("pagination", include_str!("./pagination.css"));
88+
89+
let size: StoredMaybeSignal<_> = size.into();
90+
let no_next = Memo::new(move |_| page.get() == count.get());
91+
let no_previous = Memo::new(move |_| page.get() == 1);
8992

9093
let on_click_previous = move |_| {
91-
set_page_signal.update(|val| *val -= 1);
94+
page.update(|val| *val -= 1);
9295
if let Some(callback) = on_change.as_ref() {
93-
callback.call(page_signal.get())
96+
callback.call(page.get())
9497
}
9598
};
9699

97100
let on_click_next = move |_| {
98-
set_page_signal.update(|val| *val += 1);
101+
page.update(|val| *val += 1);
99102
if let Some(callback) = on_change.as_ref() {
100-
callback.call(page_signal.get())
103+
callback.call(page.get())
101104
}
102105
};
103106

104-
create_effect(move |_| {
105-
set_page_signal.update(|val| {
106-
let default = page.get();
107-
if *val != default {
108-
*val = default
109-
}
110-
});
111-
});
112-
113-
mount_style("pagination", include_str!("./pagination.css"));
114-
115107
view! {
116108
<nav class=class_list!["thaw-pagination", class.map(| c | move || c.get())]>
117109
<ul>
118110
<li>
119111
<Button
120-
color=color.clone() size=size.clone()
112+
size=size.clone()
121113
on_click=on_click_previous
122114
variant=ButtonVariant::Text
123-
icon=icondata_ai::AiLeftOutlined disabled=no_previous circle=true/>
115+
icon=icondata_ai::AiLeftOutlined
116+
disabled=no_previous
117+
circle=true
118+
/>
124119
</li>
125-
{
126-
let cloned_color = color.clone();
127-
let cloned_size = size.clone();
128-
view! {
129-
<For each=move || use_pagination(page_signal.get(), count.get(), sibling_count.get()) key=|item| match item {
130-
PaginationItem::DotLeft => -2,
131-
PaginationItem::DotRight => -1,
132-
PaginationItem::Number(nb) => nb.clone()
133-
}
134-
children=move |item| match item {
135-
PaginationItem::Number(nb) => view! {<li>
136-
<Button
137-
color=cloned_color.clone() size=cloned_size.clone()
138-
variant=MaybeSignal::derive(move || if page_signal.get() == nb {ButtonVariant::Primary} else {ButtonVariant::Text})
139-
on_click = move |_| {
140-
if page_signal.get() != nb {set_page_signal.set(nb)}
141-
if let Some(callback) = on_change.as_ref() {
142-
callback.call(page_signal.get())
120+
121+
<For
122+
each=move || use_pagination(page.get(), count.get(), sibling_count.get())
123+
key=|item| match item {
124+
PaginationItem::DotLeft => -2,
125+
PaginationItem::DotRight => -1,
126+
PaginationItem::Number(nb) => nb.clone() as i64
127+
}
128+
let:item
129+
>
130+
{
131+
if let PaginationItem::Number(nb) = item {
132+
view! {
133+
<li>
134+
<Button
135+
size=size.clone()
136+
style=Memo::new(move |_| if page.get() == nb {
137+
"color: var(--thaw-font-color-hover); border-color: var(--thaw-border-color-hover);".to_string()
138+
} else {
139+
"".to_string()
140+
})
141+
variant=Memo::new(move |_| if page.get() == nb {
142+
ButtonVariant::Outlined
143+
} else {
144+
ButtonVariant::Text
145+
})
146+
on_click = move |_| {
147+
if page.get() != nb {
148+
page.set(nb)
149+
}
150+
if let Some(callback) = on_change.as_ref() {
151+
callback.call(page.get())
152+
}
143153
}
144-
}
145-
round=true>{nb}</Button>
146-
</li>},
147-
_ => view! {<li>"..."</li>},
148-
}/>
154+
round=true
155+
>
156+
{nb}
157+
</Button>
158+
</li>
159+
}
160+
} else {
161+
view! {
162+
<li>"..."</li>
163+
}
164+
}
149165
}
150-
}
166+
</For>
151167
<li>
152168
<Button
153-
color size
169+
size
154170
on_click=on_click_next
155171
variant=ButtonVariant::Text
156-
icon=icondata_ai::AiRightOutlined disabled=no_next circle=true/>
172+
icon=icondata_ai::AiRightOutlined disabled=no_next
173+
circle=true
174+
/>
157175
</li>
158176
</ul>
159177
</nav>

thaw/src/pagination/pagination.css

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
.thaw-pagination {
2-
ul {
3-
list-style-type: none;
4-
display: flex;
5-
padding: 0;
6-
margin: 0;
7-
column-gap: 5px;
8-
align-items: center;
9-
}
1+
.thaw-pagination > ul {
2+
list-style-type: none;
3+
display: flex;
4+
padding: 0;
5+
margin: 0;
6+
column-gap: 5px;
7+
align-items: center;
108
}

thaw_utils/src/signals/stored_maybe_signal.rs

+9
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,12 @@ impl<T> From<MaybeSignal<T>> for StoredMaybeSignal<T> {
9494
}
9595
}
9696
}
97+
98+
impl<T: Clone> From<StoredMaybeSignal<T>> for MaybeSignal<T> {
99+
fn from(value: StoredMaybeSignal<T>) -> Self {
100+
match value {
101+
StoredMaybeSignal::StoredValue(value) => MaybeSignal::Static(value.get_value()),
102+
StoredMaybeSignal::Signal(singal) => MaybeSignal::Dynamic(singal),
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)