ui/components/
pagination.rs1use std::rc::Rc;
2
3use crate::prelude::*;
4
5#[derive(IntoElement, RegisterComponent)]
11pub struct Pagination {
12 id: ElementId,
13 current_page: usize,
14 total_pages: usize,
15 on_change: Option<Rc<dyn Fn(usize, &mut Window, &mut App) + 'static>>,
16}
17
18impl Pagination {
19 pub fn new(id: impl Into<ElementId>, current_page: usize, total_pages: usize) -> Self {
21 Self {
22 id: id.into(),
23 current_page: current_page.max(1),
24 total_pages: total_pages.max(1),
25 on_change: None,
26 }
27 }
28
29 pub fn on_change(mut self, handler: impl Fn(usize, &mut Window, &mut App) + 'static) -> Self {
32 self.on_change = Some(Rc::new(handler));
33 self
34 }
35}
36
37impl RenderOnce for Pagination {
38 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
39 let total = self.total_pages;
40 let current = self.current_page.min(total);
41 let on_change = self.on_change;
42
43 let prev_disabled = current <= 1;
44 let next_disabled = current >= total;
45
46 let prev_handler = on_change.clone();
47 let next_handler = on_change.clone();
48
49 h_flex()
50 .id(self.id)
51 .items_center()
52 .gap_1()
53 .child(
54 IconButton::new("pagination-prev", IconName::ChevronLeft)
55 .disabled(prev_disabled)
56 .when(!prev_disabled, |this| {
57 this.on_click(move |_, window, cx| {
58 if let Some(handler) = prev_handler.as_ref() {
59 handler(current - 1, window, cx);
60 }
61 })
62 }),
63 )
64 .children((1..=total).map(|page| {
65 let handler = on_change.clone();
66 let is_active = page == current;
67
68 let button = Button::new(("pagination-page", page), page.to_string())
69 .label_size(LabelSize::Small);
70 let button = if is_active {
71 button.primary()
72 } else {
73 button.style(ButtonStyle::Transparent)
74 };
75
76 button.when(!is_active, |this| {
77 this.on_click(move |_, window, cx| {
78 if let Some(handler) = handler.as_ref() {
79 handler(page, window, cx);
80 }
81 })
82 })
83 }))
84 .child(
85 IconButton::new("pagination-next", IconName::ChevronRight)
86 .disabled(next_disabled)
87 .when(!next_disabled, |this| {
88 this.on_click(move |_, window, cx| {
89 if let Some(handler) = next_handler.as_ref() {
90 handler(current + 1, window, cx);
91 }
92 })
93 }),
94 )
95 }
96}
97
98impl Component for Pagination {
99 fn scope() -> ComponentScope {
100 ComponentScope::Navigation
101 }
102
103 fn description() -> Option<&'static str> {
104 Some("Prev/next and numbered page navigation; caller owns the current page.")
105 }
106
107 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
108 Some(
109 v_flex()
110 .gap_6()
111 .children(vec![
112 example_group_with_title(
113 "Middle Page",
114 vec![single_example(
115 "Page 3 of 5",
116 Pagination::new("pagination_middle", 3, 5).into_any_element(),
117 )],
118 ),
119 example_group_with_title(
120 "Bounds",
121 vec![
122 single_example(
123 "First Page",
124 Pagination::new("pagination_first", 1, 5).into_any_element(),
125 ),
126 single_example(
127 "Last Page",
128 Pagination::new("pagination_last", 5, 5).into_any_element(),
129 ),
130 ],
131 ),
132 ])
133 .into_any_element(),
134 )
135 }
136}