dioxus_tw_components/components/molecules/pagination/
props.rs1use dioxus::prelude::*;
2use dioxus_tw_components_macro::UiComp;
3
4use std::string::String;
5use tailwind_fuse::tw_merge;
6
7use crate::prelude::ButtonVariant;
8
9use crate::{
10 atoms::{Button, Icon, Icons},
11 attributes::*,
12};
13
14#[derive(Clone, PartialEq, Default, Props, UiComp)]
15pub struct PaginationProps {
16 #[props(optional, default)]
17 pub class: ReadOnlySignal<String>,
18 #[props(optional, default)]
19 pub style: ReadOnlySignal<String>,
20
21 #[props(optional, default)]
22 pub color: ReadOnlySignal<Color>,
23 #[props(optional, default)]
24 pub size: ReadOnlySignal<Size>,
25 #[props(optional, default)]
26 pub variant: ReadOnlySignal<ButtonVariant>,
27
28 #[props(into)]
29 pub data_size: ReadOnlySignal<usize>,
30 #[props(into)]
31 pub page_size: ReadOnlySignal<usize>,
32 pub page_number: Signal<usize>,
33}
34
35#[component]
36pub fn Pagination(mut props: PaginationProps) -> Element {
37 let max_pages = use_memo(move || (*props.data_size.read() / *props.page_size.read()) + 1);
38
39 let page_selector = use_memo(move || {
40 let mut next_dots = false;
41 let mut prev_dots = false;
42 if *props.page_number.read() > 2 {
43 prev_dots = true;
44 }
45 if *props.page_number.read() <= max_pages.read().checked_sub(2).unwrap_or(0) {
46 next_dots = true;
47 }
48 rsx! {
49 if prev_dots {
50 Button {
51 class: props.class,
52 style: props.style,
53 color: props.color,
54 size: props.size,
55 variant: props.variant,
56 disabled: *props.page_number.read() == 1,
57 onclick: move |_event: MouseEvent| {
58 props.page_number.set(1);
59 },
60 p { "1" }
61 }
62 p { class: "text-center h-9 px-2 py-2 select-none", "..." }
63 }
64 for page in (std::cmp::max(1_isize, *props.page_number.read() as isize - 1)
65 as usize)..=std::cmp::min(*max_pages.read(), *props.page_number.read() + 1)
66 {
67 Button {
68 class: props.class,
69 style: props.style,
70 color: props.color,
71 size: props.size,
72 variant: props.variant,
73 disabled: *props.page_number.read() == page,
74 onclick: move |_event: MouseEvent| {
75 props.page_number.set(page);
76 },
77 p { "{page}" }
78 }
79 }
80 if next_dots {
81 p { class: "text-center h-9 px-2 py-2 select-none", "..." }
82 Button {
83 class: props.class,
84 style: props.style,
85 color: props.color,
86 size: props.size,
87 variant: props.variant,
88 disabled: *props.page_number.read() == *max_pages.read(),
89 onclick: move |_event: MouseEvent| {
90 props.page_number.set(*max_pages.peek());
91 },
92 p { "{*max_pages.read()}" }
93 }
94 }
95 }
96 });
97
98 rsx! {
99 div { class: tw_merge!("flex flex-row gap-2 justify-center items-center", props.class.read()),
100 Button {
101 class: tw_merge!("flex justify-center items-center", props.class.read()),
102 style: props.style,
103 color: props.color,
104 size: props.size,
105 variant: props.variant,
106 disabled: *props.page_number.read() == 1,
107 onclick: move |_event: MouseEvent| {
108 let value = *props.page_number.peek();
109 props.page_number.set(value - 1);
110 },
111 Icon { icon: Icons::ArrowLeft }
112 }
113 {page_selector}
114 Button {
115 class: tw_merge!("flex justify-center items-center", props.class.read()),
116 style: props.style,
117 color: props.color,
118 size: props.size,
119 variant: props.variant,
120 disabled: *props.page_number.read() == *max_pages.read(),
121 onclick: move |_event: MouseEvent| {
122 let value = *props.page_number.peek();
123 props.page_number.set(value + 1);
124 },
125 Icon { icon: Icons::ArrowRight }
126 }
127 }
128 }
129}