zino_dioxus/navigation/
pagination.rs

1use crate::{class::Class, icon::SvgIcon};
2use dioxus::prelude::*;
3use dioxus_free_icons::icons::fa_solid_icons::{FaArrowLeft, FaArrowRight};
4use zino_core::SharedString;
5
6/// A vertical menu used in the navigation aside.
7pub fn Pagination(props: PaginationProps) -> Element {
8    let total = props.total;
9    let page_size = props.page_size.max(1);
10    let current_page = props.current_page.max(1);
11    let page_count = total.div_ceil(page_size);
12    if total == 0 || page_count <= 1 {
13        return rsx! {};
14    }
15    rsx! {
16        nav {
17            class: "{props.class} is-centered",
18            a {
19                class: "pagination-previous",
20                class: if current_page == 1 || page_count <= 5 { "is-invisible" },
21                onclick: move |_| {
22                    if let Some(handler) = props.on_change.as_ref() {
23                        handler.call(current_page - 1);
24                    }
25                },
26                if let Some(prev) = props.prev {
27                    { prev }
28                } else {
29                    SvgIcon {
30                        shape: FaArrowLeft,
31                        width: 16,
32                    }
33                    span {
34                        class: "ml-1",
35                        { props.prev_text }
36                    }
37                }
38            }
39            ul {
40                class: "pagination-list",
41                if current_page > 2 {
42                    li {
43                        a {
44                            class: "pagination-link",
45                            onclick: move |_| {
46                                if let Some(handler) = props.on_change.as_ref() {
47                                    handler.call(1);
48                                }
49                            },
50                            "1"
51                        }
52                    }
53                }
54                if current_page > 3 && page_count > 5 {
55                    li {
56                        span {
57                            class: "pagination-ellipsis",
58                            "…"
59                        }
60                    }
61                }
62                if current_page > 4 && page_count < current_page + 1 {
63                    li {
64                        a {
65                            class: "pagination-link",
66                            onclick: move |_| {
67                                if let Some(handler) = props.on_change.as_ref() {
68                                    handler.call(current_page - 3);
69                                }
70                            },
71                            "{current_page - 3}"
72                        }
73                    }
74                }
75                if current_page > 3 && page_count < current_page + 2 {
76                    li {
77                        a {
78                            class: "pagination-link",
79                            onclick: move |_| {
80                                if let Some(handler) = props.on_change.as_ref() {
81                                    handler.call(current_page - 2);
82                                }
83                            },
84                            "{current_page - 2}"
85                        }
86                    }
87                }
88                if current_page > 1 {
89                    li {
90                        a {
91                            class: "pagination-link",
92                            onclick: move |_| {
93                                if let Some(handler) = props.on_change.as_ref() {
94                                    handler.call(current_page - 1);
95                                }
96                            },
97                            "{current_page - 1}"
98                        }
99                    }
100                }
101                li {
102                    a {
103                        class: "pagination-link is-current",
104                        onclick: move |_| {
105                            if let Some(handler) = props.on_change.as_ref() {
106                                handler.call(current_page);
107                            }
108                        },
109                        "{current_page}"
110                    }
111                }
112                if current_page < page_count {
113                    li {
114                        a {
115                            class: "pagination-link",
116                            onclick: move |_| {
117                                if let Some(handler) = props.on_change.as_ref() {
118                                    handler.call(current_page + 1);
119                                }
120                            },
121                            "{current_page + 1}"
122                        }
123                    }
124                }
125                if current_page < 3 && page_count > current_page + 2 {
126                    li {
127                        a {
128                            class: "pagination-link",
129                            onclick: move |_| {
130                                if let Some(handler) = props.on_change.as_ref() {
131                                    handler.call(current_page + 2);
132                                }
133                            },
134                            "{current_page + 2}"
135                        }
136                    }
137                }
138                if current_page < 2 && page_count > current_page + 3 {
139                    li {
140                        a {
141                            class: "pagination-link",
142                            onclick: move |_| {
143                                if let Some(handler) = props.on_change.as_ref() {
144                                    handler.call(current_page + 3);
145                                }
146                            },
147                            "{current_page + 3}"
148                        }
149                    }
150                }
151                if page_count > current_page + 2 && page_count > 5 {
152                    li {
153                        span {
154                            class: "pagination-ellipsis",
155                            "…"
156                        }
157                    }
158                }
159                if page_count > current_page + 1 {
160                    li {
161                        a {
162                            class: "pagination-link",
163                            onclick: move |_| {
164                                if let Some(handler) = props.on_change.as_ref() {
165                                    handler.call(page_count);
166                                }
167                            },
168                            "{page_count}"
169                        }
170                    }
171                }
172            }
173            a {
174                class: "pagination-next",
175                class: if total <= current_page * page_size || page_count <= 5 { "is-invisible" },
176                onclick: move |_| {
177                    if let Some(handler) = props.on_change.as_ref() {
178                        handler.call(current_page + 1);
179                    }
180                },
181                if let Some(next) = props.next {
182                    { next }
183                } else {
184                    span {
185                        class: "mr-1",
186                        { props.next_text }
187                    }
188                    SvgIcon {
189                        shape: FaArrowRight,
190                        width: 16,
191                    }
192                }
193            }
194        }
195    }
196}
197
198/// The [`Pagination`] properties struct for the configuration of the component.
199#[derive(Clone, PartialEq, Props)]
200pub struct PaginationProps {
201    /// The class attribute for the component.
202    #[props(into, default = "pagination")]
203    pub class: Class,
204    /// Total number of data items.
205    pub total: usize,
206    /// Number of data items per page.
207    #[props(default = 10)]
208    pub page_size: usize,
209    /// The current page number.
210    pub current_page: usize,
211    /// The element for the previous button.
212    #[props(into, default = "Previous")]
213    pub prev_text: SharedString,
214    /// The text for the next button.
215    #[props(into, default = "Next")]
216    pub next_text: SharedString,
217    /// The element for the previous button.
218    pub prev: Option<Element>,
219    /// The element for the next button.
220    pub next: Option<Element>,
221    /// An event handler to be called when the page number is changed.
222    pub on_change: Option<EventHandler<usize>>,
223}
OSZAR »