leptos_pagination/components/
controls.rs

1use leptos::prelude::*;
2use reactive_stores::Store;
3
4use crate::{
5    PaginationControls, PaginationState, PaginationStateStoreFields, UsePaginationControlsOptions,
6    use_pagination_controls,
7};
8
9/// A component that renders pagination page controls.
10///
11/// ## Example
12///
13/// ```
14/// ```
15#[component]
16pub fn PaginationPages(
17    /// The current state of the pagination. This is used to communicate with the PaginatedFor component.
18    state: Store<PaginationState>,
19
20    /// How many pages to show around the current page. This number includes the current page.
21    ///
22    /// A value of 3 will display one page before and one page after the current page.
23    /// It's recommended to use odd numbers to ensure symmetry.
24    ///
25    /// Default is 5.
26    #[prop(default = 5)]
27    display_page_count: usize,
28
29    /// How many pages to show at the beginning and end of the pagination.
30    ///
31    /// Default is 1.
32    #[prop(default = 1)]
33    margin_page_count: usize,
34
35    /// The separator to use between page ranges.
36    ///
37    /// Default is "…"
38    #[prop(into, default = "⋯".into())]
39    separator: Signal<String>,
40
41    /// The class of the `<a>` element that represents a page.
42    #[prop(into, optional)]
43    anchor_class: Signal<String>,
44
45    /// The class of the `<li>` element that wraps the `<a>` element that represents a page.
46    #[prop(into, optional)]
47    li_class: Signal<String>,
48
49    /// The class of the `<li>` element that represents an active page.
50    /// This will be used instead of the `li_class` when the page is active.
51    #[prop(into, optional)]
52    active_class: Signal<String>,
53
54    /// Every range is put inside an `<ul>` element.
55    /// The class of this `<ul>` element can be customized using this prop.
56    #[prop(into, optional)]
57    ul_class: Signal<String>,
58
59    /// The class of the `<div>` element that contains the separator.
60    #[prop(into, optional)]
61    separator_class: Signal<String>,
62) -> impl IntoView {
63    let PaginationControls {
64        current_page,
65        start_range,
66        end_range,
67        current_range,
68        show_separator_before,
69        show_separator_after,
70        page_count_error,
71    } = use_pagination_controls(
72        state,
73        UsePaginationControlsOptions::default()
74            .display_page_count(display_page_count)
75            .margin_page_count(margin_page_count),
76    );
77
78    view! {
79        {move || {
80            page_count_error.get().map(|error| view! { <div class="error-message">{error}</div> })
81        }}
82        <PaginationRange
83            state
84            current_page
85            range=start_range
86            ul_class
87            anchor_class
88            li_class
89            active_class
90        />
91        <Show when=move || show_separator_before.get()>
92            <div class=separator_class>{separator}</div>
93        </Show>
94        <PaginationRange
95            state
96            current_page
97            range=current_range
98            ul_class
99            anchor_class
100            li_class
101            active_class
102        />
103        <Show when=move || show_separator_after.get()>
104            <div class=separator_class>{separator}</div>
105        </Show>
106        <PaginationRange
107            state
108            current_page
109            range=end_range
110            ul_class
111            anchor_class
112            li_class
113            active_class
114        />
115    }
116}
117
118/// Used by `PaginationPages` to render the pagination ranges (button groups).
119#[component]
120pub fn PaginationRange(
121    state: Store<PaginationState>,
122    current_page: Signal<usize>,
123    range: Signal<Vec<usize>>,
124    ul_class: Signal<String>,
125    li_class: Signal<String>,
126    anchor_class: Signal<String>,
127    active_class: Signal<String>,
128) -> impl IntoView {
129    view! {
130        <Show when=move || !range.get().is_empty()>
131            <ul class=ul_class>
132                <For
133                    each=move || range.get()
134                    key=|i| *i
135                    children=move |index| {
136                        let class = Signal::derive(move || {
137                            if current_page.get() == index {
138                                active_class.get()
139                            } else {
140                                li_class.get()
141                            }
142                        });
143
144                        view! {
145                            <li class=class>
146                                <a
147                                    class=anchor_class
148                                    on:click=move |evt| {
149                                        evt.prevent_default();
150                                        state.current_page().set(index);
151                                    }
152                                >
153                                    {index + 1}
154                                </a>
155                            </li>
156                        }
157                    }
158                />
159            </ul>
160        </Show>
161    }
162}
163
164#[component]
165/// Button to navigate to the next page.
166pub fn PaginationNext(
167    /// The current state of the pagination. This is used to communicate with the PaginatedFor component.
168    state: Store<PaginationState>,
169    children: Children,
170) -> impl IntoView {
171    view! {
172        <button
173            on:click=move |_| PaginationState::next(state)
174            prop:disabled=move || PaginationState::is_last_page(state)
175        >
176            {children()}
177        </button>
178    }
179}
180
181#[component]
182/// Button to navigate to the previous page.
183pub fn PaginationPrev(
184    /// The current state of the pagination. This is used to communicate with the PaginatedFor component.
185    state: Store<PaginationState>,
186    children: Children,
187) -> impl IntoView {
188    view! {
189        <button
190            on:click=move |_| PaginationState::prev(state)
191            prop:disabled=move || PaginationState::is_first_page(state)
192        >
193            {children()}
194        </button>
195    }
196}