♻️ Refactoriza AttrClasses como Classes

This commit is contained in:
Manuel Cillero 2025-12-28 14:57:21 +01:00
parent fa32833ffa
commit 4bf2c18b24
13 changed files with 57 additions and 49 deletions

View file

@ -9,7 +9,7 @@ pub struct Block {
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al bloque.
classes: AttrClasses,
classes: Classes,
/// Devuelve el título del bloque.
title: L10n,
/// Devuelve la lista de componentes hijo del bloque.
@ -62,7 +62,7 @@ impl Block {
/// Modifica la lista de clases CSS aplicadas al bloque.
#[builder_fn]
pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
self.classes.alter_value(op, classes);
self.classes.alter_classes(op, classes);
self
}

View file

@ -24,8 +24,8 @@ pub use logo::PageTopSvg;
mod attr;
pub use attr::{Attr, AttrId, AttrName, AttrValue};
mod attr_classes;
pub use attr_classes::{AttrClasses, ClassesOp};
mod classes;
pub use classes::{Classes, ClassesOp};
mod unit;
pub use unit::UnitValue;

View file

@ -1,16 +1,18 @@
use crate::{builder_fn, AutoDefault};
/// Operaciones disponibles sobre la lista de clases en [`AttrClasses`].
use std::borrow::Cow;
/// Operaciones disponibles sobre la lista de clases en [`Classes`].
pub enum ClassesOp {
/// Añade al final (si no existe).
Add,
/// Añade al principio.
Prepend,
/// Elimina coincidencias.
/// Elimina la(s) clase(s) indicada(s).
Remove,
/// Sustituye una o varias por las nuevas (`Replace("old other")`).
Replace(String),
/// Alterna presencia/ausencia.
/// Sustituye una o varias clases por otras nuevas (`Replace("old other".into())`).
Replace(Cow<'static, str>),
/// Alterna presencia/ausencia de una o más clases.
Toggle,
/// Sustituye toda la lista.
Set,
@ -23,34 +25,40 @@ pub enum ClassesOp {
///
/// # Normalización
///
/// - El [orden de las clases no es relevante](https://stackoverflow.com/a/1321712) en CSS.
/// - No se permiten clases duplicadas.
/// - El [orden de las clases no es relevante](https://stackoverflow.com/a/1321712) en CSS, pero
/// [`ClassesOp`] ofrece operaciones para controlar su orden de aparición.
/// - Las clases se convierten a minúsculas.
/// - No se permiten clases duplicadas.
/// - Las clases vacías se ignoran.
///
/// # Ejemplo
///
/// ```rust
/// # use pagetop::prelude::*;
/// let classes = AttrClasses::new("Btn btn-primary")
/// .with_value(ClassesOp::Add, "Active")
/// .with_value(ClassesOp::Remove, "btn-primary");
/// let classes = Classes::new("Btn btn-primary")
/// .with_classes(ClassesOp::Add, "Active")
/// .with_classes(ClassesOp::Remove, "btn-primary");
///
/// assert_eq!(classes.get(), Some("btn active".to_string()));
/// assert!(classes.contains("active"));
/// ```
#[derive(AutoDefault, Clone, Debug)]
pub struct AttrClasses(Vec<String>);
pub struct Classes(Vec<String>);
impl AttrClasses {
impl Classes {
/// Crea una nueva lista de clases a partir de la clase o clases proporcionadas en `classes`.
pub fn new(classes: impl AsRef<str>) -> Self {
Self::default().with_value(ClassesOp::Prepend, classes)
Self::default().with_classes(ClassesOp::Prepend, classes)
}
// **< AttrClasses BUILDER >********************************************************************
// **< Classes BUILDER >************************************************************************
/// Modifica la lista de clases según la operación indicada.
///
/// Realiza la operación indicada en `op` para las clases proporcionadas en `classes` sobre la
/// lista de clases actual.
#[builder_fn]
pub fn with_value(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
let classes = classes.as_ref().to_ascii_lowercase();
let classes: Vec<&str> = classes.split_ascii_whitespace().collect();
@ -114,7 +122,7 @@ impl AttrClasses {
}
}
// **< AttrClasses GETTERS >********************************************************************
// **< Classes GETTERS >************************************************************************
/// Devuelve la cadena de clases, si existe.
pub fn get(&self) -> Option<String> {
@ -127,7 +135,7 @@ impl AttrClasses {
/// Devuelve `true` si la clase está presente.
pub fn contains(&self, class: impl AsRef<str>) -> bool {
let class = class.as_ref();
self.0.iter().any(|c| c == class)
let class = class.as_ref().to_ascii_lowercase();
self.0.iter().any(|c| c == &class)
}
}

View file

@ -25,7 +25,7 @@ use crate::core::theme::{DefaultRegion, Region, RegionRef, TemplateRef, ThemeRef
use crate::html::{html, Markup, DOCTYPE};
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
use crate::html::{Attr, AttrId};
use crate::html::{AttrClasses, ClassesOp};
use crate::html::{Classes, ClassesOp};
use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier};
use crate::service::HttpRequest;
use crate::{builder_fn, AutoDefault};
@ -94,7 +94,7 @@ pub struct Page {
metadata : Vec<(&'static str, &'static str)>,
properties : Vec<(&'static str, &'static str)>,
body_id : AttrId,
body_classes: AttrClasses,
body_classes: Classes,
context : Context,
}
@ -111,7 +111,7 @@ impl Page {
metadata : Vec::default(),
properties : Vec::default(),
body_id : AttrId::default(),
body_classes: AttrClasses::default(),
body_classes: Classes::default(),
context : Context::new(Some(request)),
}
}
@ -153,10 +153,10 @@ impl Page {
self
}
/// Modifica las clases CSS del elemento `<body>` con una operación sobre [`AttrClasses`].
/// Modifica las clases CSS del elemento `<body>` con una operación sobre [`Classes`].
#[builder_fn]
pub fn with_body_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
self.body_classes.alter_value(op, classes);
self.body_classes.alter_classes(op, classes);
self
}
@ -204,7 +204,7 @@ impl Page {
}
/// Devuelve las clases CSS del elemento `<body>`.
pub fn body_classes(&self) -> &AttrClasses {
pub fn body_classes(&self) -> &Classes {
&self.body_classes
}