Skip to content

Commit 8d65778

Browse files
Add pagination component (#197)
* Add pagination component * stylelint fix * fix pagination module missing * feat button event handler for pagination * fix merge * a bit tighter spacing * fill ellipses * slightly rounder --------- Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
1 parent cc61648 commit 8d65778

File tree

8 files changed

+426
-0
lines changed

8 files changed

+426
-0
lines changed

component.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"preview/src/components/skeleton",
3939
"preview/src/components/card",
4040
"preview/src/components/sheet",
41+
"preview/src/components/pagination",
4142
"preview/src/components/sidebar",
4243
"preview/src/components/badge"
4344
]

preview/src/components/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ examples!(
137137
label,
138138
menubar,
139139
navbar,
140+
pagination,
140141
popover,
141142
progress,
142143
radio_group,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "pagination",
3+
"description": "Navigation controls for paged content.",
4+
"authors": [
5+
"zhiyanzhaijie"
6+
],
7+
"exclude": [
8+
"variants",
9+
"docs.md",
10+
"component.json"
11+
],
12+
"cargoDependencies": [
13+
{
14+
"name": "dioxus-primitives",
15+
"git": "https://github.com/DioxusLabs/components"
16+
}
17+
],
18+
"globalAssets": [
19+
"../../../assets/dx-components-theme.css"
20+
]
21+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
use dioxus::prelude::*;
2+
3+
#[derive(Copy, Clone, PartialEq, Default)]
4+
#[non_exhaustive]
5+
pub enum PaginationLinkSize {
6+
#[default]
7+
Icon,
8+
Default,
9+
}
10+
11+
impl PaginationLinkSize {
12+
pub fn class(&self) -> &'static str {
13+
match self {
14+
PaginationLinkSize::Icon => "icon",
15+
PaginationLinkSize::Default => "default",
16+
}
17+
}
18+
}
19+
20+
#[derive(Copy, Clone, PartialEq)]
21+
#[non_exhaustive]
22+
pub enum PaginationLinkKind {
23+
Previous,
24+
Next,
25+
}
26+
27+
impl PaginationLinkKind {
28+
pub fn attr(&self) -> &'static str {
29+
match self {
30+
PaginationLinkKind::Previous => "previous",
31+
PaginationLinkKind::Next => "next",
32+
}
33+
}
34+
}
35+
36+
#[component]
37+
pub fn Pagination(
38+
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
39+
children: Element,
40+
) -> Element {
41+
rsx! {
42+
document::Link { rel: "stylesheet", href: asset!("./style.css") }
43+
nav {
44+
class: "pagination",
45+
"data-slot": "pagination",
46+
role: "navigation",
47+
aria_label: "pagination",
48+
..attributes,
49+
{children}
50+
}
51+
}
52+
}
53+
54+
#[component]
55+
pub fn PaginationContent(
56+
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
57+
children: Element,
58+
) -> Element {
59+
rsx! {
60+
ul {
61+
class: "pagination-content",
62+
"data-slot": "pagination-content",
63+
..attributes,
64+
{children}
65+
}
66+
}
67+
}
68+
69+
#[component]
70+
pub fn PaginationItem(
71+
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
72+
children: Element,
73+
) -> Element {
74+
rsx! {
75+
li {
76+
class: "pagination-item",
77+
"data-slot": "pagination-item",
78+
..attributes,
79+
{children}
80+
}
81+
}
82+
}
83+
84+
#[derive(Props, Clone, PartialEq)]
85+
pub struct PaginationLinkProps {
86+
#[props(default)]
87+
pub is_active: bool,
88+
#[props(default)]
89+
pub size: PaginationLinkSize,
90+
#[props(default)]
91+
pub data_kind: Option<PaginationLinkKind>,
92+
onclick: Option<EventHandler<MouseEvent>>,
93+
onmousedown: Option<EventHandler<MouseEvent>>,
94+
onmouseup: Option<EventHandler<MouseEvent>>,
95+
#[props(extends = GlobalAttributes)]
96+
#[props(extends = a)]
97+
pub attributes: Vec<Attribute>,
98+
pub children: Element,
99+
}
100+
101+
#[component]
102+
pub fn PaginationLink(props: PaginationLinkProps) -> Element {
103+
let aria_current = if props.is_active { Some("page") } else { None };
104+
let data_kind = props.data_kind.map(|kind| kind.attr());
105+
rsx! {
106+
a {
107+
class: "pagination-link",
108+
"data-slot": "pagination-link",
109+
"data-active": props.is_active,
110+
"data-size": props.size.class(),
111+
"data-kind": data_kind,
112+
aria_current: aria_current,
113+
onclick: move |event| {
114+
if let Some(f) = &props.onclick {
115+
f.call(event);
116+
}
117+
},
118+
onmousedown: move |event| {
119+
if let Some(f) = &props.onmousedown {
120+
f.call(event);
121+
}
122+
},
123+
onmouseup: move |event| {
124+
if let Some(f) = &props.onmouseup {
125+
f.call(event);
126+
}
127+
},
128+
..props.attributes,
129+
{props.children}
130+
}
131+
}
132+
}
133+
134+
#[component]
135+
pub fn PaginationPrevious(
136+
onclick: Option<EventHandler<MouseEvent>>,
137+
onmousedown: Option<EventHandler<MouseEvent>>,
138+
onmouseup: Option<EventHandler<MouseEvent>>,
139+
#[props(extends = GlobalAttributes)]
140+
#[props(extends = a)]
141+
attributes: Vec<Attribute>,
142+
) -> Element {
143+
rsx! {
144+
PaginationLink {
145+
size: PaginationLinkSize::Default,
146+
aria_label: "Go to previous page",
147+
data_kind: Some(PaginationLinkKind::Previous),
148+
onclick,
149+
onmousedown,
150+
onmouseup,
151+
attributes,
152+
// ChevronLeft icon from lucide https://lucide.dev/icons/chevron-left
153+
svg {
154+
class: "pagination-icon",
155+
view_box: "0 0 24 24",
156+
xmlns: "http://www.w3.org/2000/svg",
157+
polyline { points: "15 6 9 12 15 18" }
158+
}
159+
span { class: "pagination-label", "Previous" }
160+
}
161+
}
162+
}
163+
164+
#[component]
165+
pub fn PaginationNext(
166+
onclick: Option<EventHandler<MouseEvent>>,
167+
onmousedown: Option<EventHandler<MouseEvent>>,
168+
onmouseup: Option<EventHandler<MouseEvent>>,
169+
#[props(extends = GlobalAttributes)]
170+
#[props(extends = a)]
171+
attributes: Vec<Attribute>,
172+
) -> Element {
173+
rsx! {
174+
PaginationLink {
175+
size: PaginationLinkSize::Default,
176+
aria_label: "Go to next page",
177+
data_kind: Some(PaginationLinkKind::Next),
178+
onclick,
179+
onmousedown,
180+
onmouseup,
181+
attributes,
182+
span { class: "pagination-label", "Next" }
183+
// ChevronRight icon from lucide https://lucide.dev/icons/chevron-right
184+
svg {
185+
class: "pagination-icon",
186+
view_box: "0 0 24 24",
187+
xmlns: "http://www.w3.org/2000/svg",
188+
polyline { points: "9 6 15 12 9 18" }
189+
}
190+
}
191+
}
192+
}
193+
194+
#[component]
195+
pub fn PaginationEllipsis(
196+
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
197+
) -> Element {
198+
rsx! {
199+
span {
200+
class: "pagination-ellipsis",
201+
"data-slot": "pagination-ellipsis",
202+
aria_hidden: "true",
203+
..attributes,
204+
// MoreHorizontal icon from lucide https://lucide.dev/icons/more-horizontal
205+
svg {
206+
class: "pagination-icon",
207+
view_box: "0 0 24 24",
208+
xmlns: "http://www.w3.org/2000/svg",
209+
circle { cx: "5", cy: "12", r: "1.5" }
210+
circle { cx: "12", cy: "12", r: "1.5" }
211+
circle { cx: "19", cy: "12", r: "1.5" }
212+
}
213+
span { class: "sr-only", "More pages" }
214+
}
215+
}
216+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
The pagination component provides navigational controls for paged content. It exposes a consistent structure for previous/next actions, individual page links, and an optional ellipsis for truncated ranges.
2+
3+
## Component Structure
4+
5+
```rust
6+
// The Pagination component wraps the entire control.
7+
Pagination {
8+
// PaginationContent groups all items in a horizontal list.
9+
PaginationContent {
10+
// PaginationItem is the container for a single pagination element.
11+
// Use one item at a time and swap the inner component as needed.
12+
PaginationItem {
13+
// PaginationPrevious renders a previous-page link.
14+
// - Set href to your previous page url.
15+
PaginationPrevious { href: "#" }
16+
17+
// PaginationLink renders a numbered page link.
18+
// - is_active marks the current page.
19+
// - href sets the target page.
20+
PaginationLink { href: "#", is_active: true, "2" }
21+
22+
// PaginationEllipsis indicates truncated pages.
23+
PaginationEllipsis {}
24+
25+
// PaginationNext renders a next-page link.
26+
// - Set href to your next page url.
27+
PaginationNext { href: "#" }
28+
}
29+
}
30+
}
31+
```
32+
33+
## Notes
34+
35+
- `PaginationLink` uses `is_active` to indicate the current page.
36+
- `PaginationPrevious` and `PaginationNext` show labels on larger (non-mobile) screens; labels are hidden on smaller screens to keep the control compact.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod component;
2+
pub use component::*;

0 commit comments

Comments
 (0)