dioxus_tw_components/components/
pagination.rs1use crate::components::{button::*, icon::*};
2use dioxus::dioxus_core::AttributeValue;
3use dioxus::prelude::*;
4
5#[derive(Clone, PartialEq, Props)]
6pub struct PaginationProps {
7 #[props(extends = div, extends = GlobalAttributes)]
8 attributes: Vec<Attribute>,
9
10 #[props(optional, default)]
11 pub class: ReadSignal<String>,
12 #[props(into)]
13 pub data_size: ReadSignal<usize>,
14 #[props(into)]
15 pub page_size: ReadSignal<usize>,
16 pub page_number: Signal<usize>,
17}
18
19#[component]
20pub fn Pagination(mut props: PaginationProps) -> Element {
21 let default_classes = "pagination";
22 crate::setup_class_attribute(&mut props.attributes, default_classes);
23
24 let max_pages = use_memo(move || (*props.data_size.read() / *props.page_size.read()) + 1);
25
26 let data_variant_attribute = match props
27 .attributes
28 .iter()
29 .find(|attr| attr.name == "data-variant")
30 {
31 Some(attribute) => {
32 if let AttributeValue::Text(ref value) = attribute.value {
33 value.clone()
34 } else {
35 "none".to_string()
36 }
37 }
38 _ => "none".to_string(),
39 };
40 let data_color_attribute = match props
41 .attributes
42 .iter()
43 .find(|attr| attr.name == "data-style")
44 {
45 Some(attribute) => {
46 if let AttributeValue::Text(ref value) = attribute.value {
47 value.clone()
48 } else {
49 "none".to_string()
50 }
51 }
52 _ => "none".to_string(),
53 };
54
55 let prev_dots = use_memo(move || *props.page_number.read() > 2);
56 let next_dots =
57 use_memo(move || *props.page_number.read() <= max_pages.read().checked_sub(2).unwrap_or(0));
58
59 rsx! {
60 div { class: format!("{} {}", default_classes, props.class),
61 ..props.attributes,
62 Button {
63 class: format!("pagination-nav-button {}", props.class),
64 "data-variant": data_variant_attribute.clone(),
65 "data-style": data_color_attribute.clone(),
66 disabled: *props.page_number.read() == 1,
67 onclick: move |_event: MouseEvent| {
68 let value = *props.page_number.peek();
69 props.page_number.set(value - 1);
70 },
71 Icon { style: "font-family: 'Material Symbols Rounded'; user-select: none; font-size: 1.5rem;", icon: Icons::ArrowLeft }
72 }
73 if *prev_dots.read() {
74 Button {
75 class: props.class,
76 disabled: *props.page_number.read() == 1,
77 "data-variant": data_variant_attribute.clone(),
78 "data-style": data_color_attribute.clone(),
79 onclick: move |_event: MouseEvent| {
80 props.page_number.set(1);
81 },
82 "1"
83 }
84 p { class: "pagination-dots", "..." }
85 }
86 for page in (std::cmp::max(1_isize, *props.page_number.read() as isize - 1)
87 as usize)..=std::cmp::min(*max_pages.read(), *props.page_number.read() + 1)
88 {
89 Button {
90 class: props.class,
91 "data-variant": data_variant_attribute.clone(),
92 "data-style": data_color_attribute.clone(),
93 disabled: *props.page_number.read() == page,
94 onclick: move |_event: MouseEvent| {
95 props.page_number.set(page);
96 },
97 "{page}"
98 }
99 }
100 if *next_dots.read() {
101 p { class: "pagination-dots", "..." }
102 Button {
103 class: props.class,
104 "data-variant": data_variant_attribute.clone(),
105 "data-style": data_color_attribute.clone(),
106 disabled: *props.page_number.read() == *max_pages.read(),
107 onclick: move |_event: MouseEvent| {
108 props.page_number.set(*max_pages.peek());
109 },
110 "{*max_pages.read()}"
111 }
112 }
113 Button {
114 class: format!("pagination-nav-button {}", props.class),
115 "data-variant": data_variant_attribute.clone(),
116 "data-style": data_color_attribute.clone(),
117 disabled: *props.page_number.read() == *max_pages.read(),
118 onclick: move |_event: MouseEvent| {
119 let value = *props.page_number.peek();
120 props.page_number.set(value + 1);
121 },
122 Icon { style: "font-family: 'Material Symbols Rounded'; user-select: none; font-size: 1.5rem;", icon: Icons::ArrowRight }
123 }
124 }
125 }
126}