✨ (htmx): Añade soporte HTMX a tablas ordenables
- Nuevo `SortDir` en `pagetop::html` para representar direcciones de orden (asc/desc) y calcular la siguiente al pulsar una cabecera. - `hx_table::sort_link()` construye el enlace de ordenación con los cuatro atributos `hx-*` fijos, reutilizable en cualquier tabla. - `HtmxResponse` usa `RoutePath` en `location`/`push_url`/`replace_url`/ `redirect` para preservar "lang"; `location_json()` se separa de `location()` para el caso de configuración JSON personalizada. - Añade el módulo `prelude` y una batería de tests para hx, hx_table, request, response y extension.
This commit is contained in:
parent
55159f6d8f
commit
e7f2563967
14 changed files with 1176 additions and 46 deletions
|
|
@ -6,6 +6,9 @@ pub use maud::{DOCTYPE, Escaper, Markup, PreEscaped, Render, display, html, html
|
|||
mod route_path;
|
||||
pub use route_path::RoutePath;
|
||||
|
||||
mod sort_dir;
|
||||
pub use sort_dir::SortDir;
|
||||
|
||||
// **< HTML DOCUMENT ASSETS >***********************************************************************
|
||||
|
||||
mod assets;
|
||||
|
|
|
|||
116
src/html/sort_dir.rs
Normal file
116
src/html/sort_dir.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
/// Representa una dirección de ordenación (ascendente o descendente).
|
||||
///
|
||||
/// Es un tipo definido exclusivamente para trabajar con datos ordenables. No depende de ningún
|
||||
/// componente. Sirve tanto para representar el estado de un listado ordenable en la capa de
|
||||
/// servicio (interpretando el valor de la *query string* con [`from_query()`](Self::from_query) y
|
||||
/// serializándolo con [`as_str()`](Self::as_str)), como para calcular la dirección que debe llevar
|
||||
/// un enlace de ordenación de la interfaz si se vuelve a pulsar ([`toggled()`](Self::toggled),
|
||||
/// [`next_for()`](Self::next_for)).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum SortDir {
|
||||
/// Orden ascendente. Es la dirección por defecto.
|
||||
#[default]
|
||||
Asc,
|
||||
/// Orden descendente.
|
||||
Desc,
|
||||
}
|
||||
|
||||
impl SortDir {
|
||||
/// Interpreta el valor del parámetro de ordenación procedente de una *query string*
|
||||
/// (`"asc"`/`"desc"`).
|
||||
///
|
||||
/// Cualquier valor distinto de `"desc"` (incluido `None`, una cadena vacía o un valor no
|
||||
/// reconocido) se interpreta como [`Asc`](Self::Asc).
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::html::SortDir;
|
||||
///
|
||||
/// assert_eq!(SortDir::from_query(Some("desc")), SortDir::Desc);
|
||||
/// assert_eq!(SortDir::from_query(Some("asc")), SortDir::Asc);
|
||||
/// assert_eq!(SortDir::from_query(Some("")), SortDir::Asc);
|
||||
/// assert_eq!(SortDir::from_query(None), SortDir::Asc);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_query(value: Option<&str>) -> Self {
|
||||
match value {
|
||||
Some("desc") => SortDir::Desc,
|
||||
_ => SortDir::Asc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Devuelve el valor de esta dirección tal como se representa en una *query string*
|
||||
/// (`"asc"`/`"desc"`).
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::html::SortDir;
|
||||
///
|
||||
/// assert_eq!(SortDir::Asc.as_str(), "asc");
|
||||
/// assert_eq!(SortDir::Desc.as_str(), "desc");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
SortDir::Asc => "asc",
|
||||
SortDir::Desc => "desc",
|
||||
}
|
||||
}
|
||||
|
||||
/// Devuelve la dirección contraria a esta.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::html::SortDir;
|
||||
///
|
||||
/// assert_eq!(SortDir::Asc.toggled(), SortDir::Desc);
|
||||
/// assert_eq!(SortDir::Desc.toggled(), SortDir::Asc);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn toggled(self) -> Self {
|
||||
match self {
|
||||
SortDir::Asc => SortDir::Desc,
|
||||
SortDir::Desc => SortDir::Asc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calcula la dirección que debe llevar un enlace de ordenación si se vuelve a pulsar, dado su
|
||||
/// estado actual (`current`) que puede ser `Some(dir)` si el listado ya está ordenado por este
|
||||
/// enlace, o `None` si el orden vigente lo determina otro campo.
|
||||
///
|
||||
/// Si el enlace ya ordena, [alterna la dirección](Self::toggled); si no, siempre empieza en
|
||||
/// [`Asc`](Self::Asc), sea cual sea la dirección vigente del listado en conjunto.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::html::SortDir;
|
||||
///
|
||||
/// // Otro campo determina el orden vigente: el próximo clic aquí empieza en ascendente.
|
||||
/// assert_eq!(SortDir::next_for(None), SortDir::Asc);
|
||||
///
|
||||
/// // Este mismo campo ya ordena en ascendente: el próximo clic debería invertirlo.
|
||||
/// assert_eq!(SortDir::next_for(Some(SortDir::Asc)), SortDir::Desc);
|
||||
/// assert_eq!(SortDir::next_for(Some(SortDir::Desc)), SortDir::Asc);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn next_for(current: Option<SortDir>) -> SortDir {
|
||||
match current {
|
||||
Some(dir) => dir.toggled(),
|
||||
None => SortDir::Asc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Permite pasar un [`SortDir`] allí donde se espere `impl Into<String>`, por ejemplo, en
|
||||
/// [`RoutePath::with_param()`](crate::html::RoutePath::with_param) o su equivalente
|
||||
/// `alter_param()`, sin tener que escribir `as_str().to_owned()` a mano.
|
||||
///
|
||||
/// ```rust
|
||||
/// use pagetop::html::SortDir;
|
||||
///
|
||||
/// assert_eq!(String::from(SortDir::Asc), "asc");
|
||||
/// assert_eq!(String::from(SortDir::Desc), "desc");
|
||||
/// ```
|
||||
impl From<SortDir> for String {
|
||||
fn from(dir: SortDir) -> Self {
|
||||
dir.as_str().to_owned()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue