♻️ [html] Cambia tipos Option... por Attr...

Renombra los tipos para atributos HTML `Id`, `Name`, `Value` (`String`),
`L10n` (`Translate`) y `Classes`. Y mueve `OptionComponent` al *core* de
componentes como `TypedSlot`.
This commit is contained in:
Manuel Cillero 2025-08-23 18:52:45 +02:00
parent 75eec8bebc
commit bb34ba5887
17 changed files with 367 additions and 311 deletions

View file

@ -1,6 +1,6 @@
use crate::{builder_fn, AutoDefault};
/// Operaciones disponibles sobre la lista de clases en [`OptionClasses`].
/// Operaciones disponibles sobre la lista de clases en [`AttrClasses`].
pub enum ClassesOp {
/// Añade al final (si no existe).
Add,
@ -33,7 +33,7 @@ pub enum ClassesOp {
/// ```rust
/// use pagetop::prelude::*;
///
/// let classes = OptionClasses::new("Btn btn-primary")
/// let classes = AttrClasses::new("Btn btn-primary")
/// .with_value(ClassesOp::Add, "Active")
/// .with_value(ClassesOp::Remove, "btn-primary");
///
@ -41,14 +41,14 @@ pub enum ClassesOp {
/// assert!(classes.contains("active"));
/// ```
#[derive(AutoDefault, Clone, Debug)]
pub struct OptionClasses(Vec<String>);
pub struct AttrClasses(Vec<String>);
impl OptionClasses {
impl AttrClasses {
pub fn new(classes: impl AsRef<str>) -> Self {
OptionClasses::default().with_value(ClassesOp::Prepend, classes)
AttrClasses::default().with_value(ClassesOp::Prepend, classes)
}
// OptionClasses BUILDER ***********************************************************************
// AttrClasses BUILDER *************************************************************************
#[builder_fn]
pub fn with_value(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
@ -114,7 +114,7 @@ impl OptionClasses {
}
}
// OptionClasses GETTERS ***********************************************************************
// AttrClasses GETTERS *************************************************************************
/// Devuele la cadena de clases, si existe.
pub fn get(&self) -> Option<String> {

63
src/html/attr_id.rs Normal file
View file

@ -0,0 +1,63 @@
use crate::{builder_fn, AutoDefault};
/// Identificador normalizado para el atributo `id` o similar de HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso:
///
/// - Se eliminan los espacios al principio y al final.
/// - Se convierte a minúsculas.
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let id = AttrId::new(" main Section ");
/// assert_eq!(id.as_str(), Some("main_section"));
///
/// let empty = AttrId::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct AttrId(Option<String>);
impl AttrId {
/// Crea un nuevo `AttrId` normalizando el valor.
pub fn new(value: impl AsRef<str>) -> Self {
AttrId::default().with_value(value)
}
// AttrId BUILDER ******************************************************************************
/// Establece un identificador nuevo normalizando el valor.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
self.0 = if value.is_empty() { None } else { Some(value) };
self
}
// AttrId GETTERS ******************************************************************************
/// Devuelve el identificador normalizado, si existe.
pub fn get(&self) -> Option<String> {
self.0.as_ref().cloned()
}
/// Devuelve el identificador normalizado (sin clonar), si existe.
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
}
/// Devuelve el identificador normalizado (propiedad), si existe.
pub fn into_inner(self) -> Option<String> {
self.0
}
/// `true` si no hay valor.
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
}

View file

@ -2,7 +2,7 @@ use crate::html::Markup;
use crate::locale::{L10n, LangId};
use crate::{builder_fn, AutoDefault};
/// Cadena para traducir al renderizar ([`locale`](crate::locale)).
/// Texto para [traducir](crate::locale) en atributos HTML.
///
/// Encapsula un tipo [`L10n`] para manejar traducciones de forma segura.
///
@ -12,7 +12,7 @@ use crate::{builder_fn, AutoDefault};
/// use pagetop::prelude::*;
///
/// // Traducción por clave en las locales por defecto de PageTop.
/// let hello = OptionTranslated::new(L10n::l("test-hello-world"));
/// let hello = AttrL10n::new(L10n::l("test-hello-world"));
///
/// // Español disponible.
/// assert_eq!(
@ -31,15 +31,15 @@ use crate::{builder_fn, AutoDefault};
/// assert_eq!(markup.into_string(), "¡Hola mundo!");
/// ```
#[derive(AutoDefault, Clone, Debug)]
pub struct OptionTranslated(L10n);
pub struct AttrL10n(L10n);
impl OptionTranslated {
/// Crea una nueva instancia [`OptionTranslated`].
impl AttrL10n {
/// Crea una nueva instancia `AttrL10n`.
pub fn new(value: L10n) -> Self {
OptionTranslated(value)
AttrL10n(value)
}
// OptionTranslated BUILDER ********************************************************************
// AttrL10n BUILDER ****************************************************************************
/// Establece una traducción nueva.
#[builder_fn]
@ -48,7 +48,7 @@ impl OptionTranslated {
self
}
// OptionTranslated GETTERS ********************************************************************
// AttrL10n GETTERS ****************************************************************************
/// Devuelve la traducción para `language`, si existe.
pub fn using(&self, language: &impl LangId) -> Option<String> {

63
src/html/attr_name.rs Normal file
View file

@ -0,0 +1,63 @@
use crate::{builder_fn, AutoDefault};
/// Nombre normalizado para el atributo `name` o similar de HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso:
///
/// - Se eliminan los espacios al principio y al final.
/// - Se convierte a minúsculas.
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let name = AttrName::new(" DISplay name ");
/// assert_eq!(name.as_str(), Some("display_name"));
///
/// let empty = AttrName::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct AttrName(Option<String>);
impl AttrName {
/// Crea un nuevo `AttrName` normalizando el valor.
pub fn new(value: impl AsRef<str>) -> Self {
AttrName::default().with_value(value)
}
// AttrName BUILDER ****************************************************************************
/// Establece un nombre nuevo normalizando el valor.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
self.0 = if value.is_empty() { None } else { Some(value) };
self
}
// AttrName GETTERS ****************************************************************************
/// Devuelve el nombre normalizado, si existe.
pub fn get(&self) -> Option<String> {
self.0.as_ref().cloned()
}
/// Devuelve el nombre normalizado (sin clonar), si existe.
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
}
/// Devuelve el nombre normalizado (propiedad), si existe.
pub fn into_inner(self) -> Option<String> {
self.0
}
/// `true` si no hay valor.
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
}

65
src/html/attr_value.rs Normal file
View file

@ -0,0 +1,65 @@
use crate::{builder_fn, AutoDefault};
/// Cadena normalizada para renderizar en atributos HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso:
///
/// - Se eliminan los espacios al principio y al final.
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let s = AttrValue::new(" a new string ");
/// assert_eq!(s.as_str(), Some("a new string"));
///
/// let empty = AttrValue::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct AttrValue(Option<String>);
impl AttrValue {
/// Crea un nuevo `AttrValue` normalizando el valor.
pub fn new(value: impl AsRef<str>) -> Self {
AttrValue::default().with_value(value)
}
// AttrValue BUILDER ***************************************************************************
/// Establece una cadena nueva normalizando el valor.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim();
self.0 = if value.is_empty() {
None
} else {
Some(value.to_owned())
};
self
}
// AttrValue GETTERS ***************************************************************************
/// Devuelve la cadena normalizada, si existe.
pub fn get(&self) -> Option<String> {
self.0.as_ref().cloned()
}
/// Devuelve la cadena normalizada (sin clonar), si existe.
pub fn as_str(&self) -> Option<&str> {
self.0.as_deref()
}
/// Devuelve la cadena normalizada (propiedad), si existe.
pub fn into_inner(self) -> Option<String> {
self.0
}
/// `true` si no hay valor.
pub fn is_empty(&self) -> bool {
self.0.is_none()
}
}

View file

@ -1,68 +0,0 @@
use crate::builder_fn;
use crate::core::component::{Component, Typed};
use crate::html::{html, Context, Markup};
/// Contenedor de componente para incluir en otros componentes.
///
/// Este tipo encapsula `Option<Typed<C>>` para incluir un componente de manera segura en otros
/// componentes, útil para representar estructuras complejas.
///
/// # Ejemplo
///
/// ```rust,ignore
/// use pagetop::prelude::*;
///
/// let comp = MyComponent::new();
/// let opt = OptionComponent::new(comp);
/// assert!(opt.get().is_some());
/// ```
pub struct OptionComponent<C: Component>(Option<Typed<C>>);
impl<C: Component> Default for OptionComponent<C> {
fn default() -> Self {
OptionComponent(None)
}
}
impl<C: Component> OptionComponent<C> {
/// Crea un nuevo [`OptionComponent`].
///
/// El componente se envuelve automáticamente en un [`Typed`] y se almacena.
pub fn new(component: C) -> Self {
OptionComponent::default().with_value(Some(component))
}
// OptionComponent BUILDER *********************************************************************
/// Establece un componente nuevo, o lo vacía.
///
/// Si se proporciona `Some(component)`, se guarda en [`Typed`]; y si es `None`, se limpia.
#[builder_fn]
pub fn with_value(mut self, component: Option<C>) -> Self {
if let Some(component) = component {
self.0 = Some(Typed::with(component));
} else {
self.0 = None;
}
self
}
// OptionComponent GETTERS *********************************************************************
/// Devuelve el componente, si existe.
pub fn get(&self) -> Option<Typed<C>> {
if let Some(value) = &self.0 {
return Some(value.clone());
}
None
}
/// Renderiza el componente, si existe.
pub fn render(&self, cx: &mut Context) -> Markup {
if let Some(component) = &self.0 {
component.render(cx)
} else {
html! {}
}
}
}

View file

@ -1,59 +0,0 @@
use crate::{builder_fn, AutoDefault};
/// Identificador normalizado para el atributo `id` o similar de HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso.
///
/// # Normalización
///
/// - Se eliminan los espacios al principio y al final.
/// - Se convierte a minúsculas.
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let id = OptionId::new(" main Section ");
/// assert_eq!(id.get(), Some(String::from("main_section")));
///
/// let empty = OptionId::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct OptionId(Option<String>);
impl OptionId {
/// Crea un nuevo [`OptionId`].
///
/// El valor se normaliza automáticamente.
pub fn new(value: impl AsRef<str>) -> Self {
OptionId::default().with_value(value)
}
// OptionId BUILDER ****************************************************************************
/// Establece un identificador nuevo.
///
/// El valor se normaliza automáticamente.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
self.0 = (!value.is_empty()).then_some(value);
self
}
// OptionId GETTERS ****************************************************************************
/// Devuelve el identificador, si existe.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}

View file

@ -1,59 +0,0 @@
use crate::{builder_fn, AutoDefault};
/// Nombre normalizado para el atributo `name` o similar de HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso.
///
/// # Normalización
///
/// - Se eliminan los espacios al principio y al final.
/// - Se convierte a minúsculas.
/// - Se sustituyen los espacios intermedios por guiones bajos (`_`).
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let name = OptionName::new(" DISplay name ");
/// assert_eq!(name.get(), Some(String::from("display_name")));
///
/// let empty = OptionName::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct OptionName(Option<String>);
impl OptionName {
/// Crea un nuevo [`OptionName`].
///
/// El valor se normaliza automáticamente.
pub fn new(value: impl AsRef<str>) -> Self {
OptionName::default().with_value(value)
}
// OptionName BUILDER **************************************************************************
/// Establece un nombre nuevo.
///
/// El valor se normaliza automáticamente.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_");
self.0 = (!value.is_empty()).then_some(value);
self
}
// OptionName GETTERS **************************************************************************
/// Devuelve el nombre, si existe.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}

View file

@ -1,57 +0,0 @@
use crate::{builder_fn, AutoDefault};
/// Cadena normalizada para renderizar en atributos HTML.
///
/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso.
///
/// # Normalización
///
/// - Se eliminan los espacios al principio y al final.
/// - Si el resultado es una cadena vacía, se guarda `None`.
///
/// # Ejemplo
///
/// ```rust
/// use pagetop::prelude::*;
///
/// let s = OptionString::new(" a new string ");
/// assert_eq!(s.get(), Some(String::from("a new string")));
///
/// let empty = OptionString::default();
/// assert_eq!(empty.get(), None);
/// ```
#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)]
pub struct OptionString(Option<String>);
impl OptionString {
/// Crea un nuevo [`OptionString`].
///
/// El valor se normaliza automáticamente.
pub fn new(value: impl AsRef<str>) -> Self {
OptionString::default().with_value(value)
}
// OptionString BUILDER ************************************************************************
/// Establece una cadena nueva.
///
/// El valor se normaliza automáticamente.
#[builder_fn]
pub fn with_value(mut self, value: impl AsRef<str>) -> Self {
let value = value.as_ref().trim().to_owned();
self.0 = (!value.is_empty()).then_some(value);
self
}
// OptionString GETTERS ************************************************************************
/// Devuelve la cadena, si existe.
pub fn get(&self) -> Option<String> {
if let Some(value) = &self.0 {
if !value.is_empty() {
return Some(value.to_owned());
}
}
None
}
}