dioxus_tw_components/components/molecules/pagination/
props.rs

1use 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}