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}