(base): Añade resumen de páginas en Pager

This commit is contained in:
Manuel Cillero 2026-08-07 19:03:59 +02:00
parent 12c14afbf6
commit ee57d6cacf
5 changed files with 85 additions and 0 deletions

View file

@ -158,6 +158,23 @@ input:disabled + label {
.pager-align-end {
justify-content: flex-end;
}
.pager-summary {
color: var(--val-color--text--muted);
font-size: 0.8125rem;
align-self: flex-start;
margin-inline-end: auto;
}
.pager-align-start .pager-summary {
order: 1;
margin-inline-end: 0;
margin-inline-start: auto;
}
.pager-align-center .pager-summary {
order: 1;
flex-basis: 100%;
text-align: center;
margin-inline-end: 0;
}
.pagination {
display: flex;

View file

@ -83,10 +83,14 @@ pub enum PagerAlign {
/// para saltar directamente a una página escribiendo su número, sin depender de JavaScript. Un
/// único campo numérico (`min`/`max` según el total de páginas) y un botón de envío.
///
/// Con [`with_summary()`](Self::with_summary) se puede añadir un texto que resume la vista de las
/// páginas que se muestran en ese momento. Por defecto está oculto.
///
/// # Clases CSS
///
/// - `.pager` - clase base del componente (elemento `<nav>`).
/// - `.pager-align-start` / `.pager-align-center` / `.pager-align-end` - según [`PagerAlign`].
/// - `.pager-summary` - clase del resumen de las páginas mostradas, si está activado.
/// - `.pagination` - clase del elemento `<ul>` que contiene los enlaces de página.
/// - `.page-item` - presente en todos los `<li>` del listado.
/// - `.page-link` - presente en todos los enlaces (`<a>`) del listado, y también en el `<span>` de
@ -142,6 +146,8 @@ pub struct Pager {
window: u64,
/// Devuelve la alineación horizontal del paginador dentro de su contenedor.
align: PagerAlign,
/// Devuelve si se muestra el resumen ("Showing 1-20 of 97") antes del listado de páginas.
summary: bool,
/// Devuelve la visibilidad de los botones de página anterior/siguiente.
prev_next: PagerVisibility,
/// Devuelve la visibilidad del formulario para saltar directamente a una página.
@ -215,6 +221,18 @@ impl Component for Pager {
Ok(html! {
nav (self.props()) aria-label=(self.aria_label().using(cx)) {
@if *self.summary() {
@let items_per_page = self.items_per_page().max(1);
@let first = (page - 1) * items_per_page + 1;
@let last = (page * items_per_page).min(self.total_items());
span.pager-summary {
(L10n::l("pager_summary")
.with_arg("first", first.to_string())
.with_arg("last", last.to_string())
.with_arg("total", self.total_items().to_string())
.using(cx))
}
}
ul.pagination {
@if show_prev_next {
li.page-item.page-previous.disabled[first_disabled] {
@ -375,6 +393,13 @@ impl Pager {
self
}
/// Establece si se muestra el resumen de las páginas que se muestran en ese momento.
#[builder_fn]
pub fn with_summary(mut self, summary: bool) -> Self {
self.summary = summary;
self
}
/// Establece la visibilidad de los botones de página anterior/siguiente. Por defecto es
/// `PagerVisibility::Auto`: sólo se muestran cuando el número total de páginas supera al
/// número de páginas que se muestra en el paginador (con el extremo correspondiente

View file

@ -27,3 +27,4 @@ pager_next_label = Next
pager_next_aria_label = Next page
pager_goto_label = Jump to page
pager_goto_button = Go
pager_summary = Showing { $first }-{ $last } of { $total }

View file

@ -27,3 +27,4 @@ pager_next_label = Siguiente
pager_next_aria_label = Página siguiente
pager_goto_label = Saltar a la página
pager_goto_button = Ir
pager_summary = Mostrando { $first }-{ $last } de { $total }

View file

@ -40,6 +40,47 @@ async fn with_aria_label_overrides_the_default() {
assert!(!html.contains(r#"aria-label="Page navigation""#));
}
#[pagetop::test]
async fn summary_is_hidden_by_default() {
let mut pager = Pager::new()
.with_base_path("/list")
.with_current_page(1)
.with_items_per_page(20)
.with_total_items(97);
let html = pager.render(&mut Context::default()).await.into_string();
assert!(!html.contains("pager-summary"));
}
#[pagetop::test]
async fn summary_shows_the_range_of_the_current_page() {
let mut first_page = Pager::new()
.with_base_path("/list")
.with_current_page(1)
.with_items_per_page(20)
.with_total_items(97)
.with_summary(true);
let html = first_page
.render(&mut Context::default())
.await
.into_string();
assert!(html.contains(r#"<span class="pager-summary">Showing 1-20 of 97</span>"#));
// Last page: fewer items than `items_per_page`, so `last` stops at `total_items`.
let mut last_page = Pager::new()
.with_base_path("/list")
.with_current_page(5)
.with_items_per_page(20)
.with_total_items(97)
.with_summary(true);
let html = last_page
.render(&mut Context::default())
.await
.into_string();
assert!(html.contains(r#"<span class="pager-summary">Showing 81-97 of 97</span>"#));
}
#[pagetop::test]
async fn renders_page_links_and_current_page() {
let mut pager = Pager::new()