Compare commits
3 commits
47c47ba9a0
...
4bf2c18b24
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bf2c18b24 | |||
| fa32833ffa | |||
| 25d32ec5de |
30 changed files with 467 additions and 394 deletions
|
|
@ -11,7 +11,7 @@ pub struct Container {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al contenedor.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el tipo semántico del contenedor.
|
||||
container_kind: container::Kind,
|
||||
/// Devuelve el comportamiento para el ancho del contenedor.
|
||||
|
|
@ -22,7 +22,7 @@ pub struct Container {
|
|||
|
||||
impl Component for Container {
|
||||
fn new() -> Self {
|
||||
Container::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -82,7 +82,7 @@ impl Component for Container {
|
|||
impl Container {
|
||||
/// Crea un contenedor de tipo `Main` (`<main>`).
|
||||
pub fn main() -> Self {
|
||||
Container {
|
||||
Self {
|
||||
container_kind: container::Kind::Main,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ impl Container {
|
|||
|
||||
/// Crea un contenedor de tipo `Header` (`<header>`).
|
||||
pub fn header() -> Self {
|
||||
Container {
|
||||
Self {
|
||||
container_kind: container::Kind::Header,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ impl Container {
|
|||
|
||||
/// Crea un contenedor de tipo `Footer` (`<footer>`).
|
||||
pub fn footer() -> Self {
|
||||
Container {
|
||||
Self {
|
||||
container_kind: container::Kind::Footer,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ impl Container {
|
|||
|
||||
/// Crea un contenedor de tipo `Section` (`<section>`).
|
||||
pub fn section() -> Self {
|
||||
Container {
|
||||
Self {
|
||||
container_kind: container::Kind::Section,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -114,7 +114,7 @@ impl Container {
|
|||
|
||||
/// Crea un contenedor de tipo `Article` (`<article>`).
|
||||
pub fn article() -> Self {
|
||||
Container {
|
||||
Self {
|
||||
container_kind: container::Kind::Article,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ impl Container {
|
|||
/// Establece el identificador único (`id`) del contenedor.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ impl Container {
|
|||
/// - Redondear las esquinas ([`classes::Rounded`]).
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub struct Dropdown {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al menú desplegable.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el título del menú desplegable.
|
||||
title: L10n,
|
||||
/// Devuelve el tamaño configurado del botón.
|
||||
|
|
@ -49,7 +49,7 @@ pub struct Dropdown {
|
|||
|
||||
impl Component for Dropdown {
|
||||
fn new() -> Self {
|
||||
Dropdown::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -76,7 +76,7 @@ impl Component for Dropdown {
|
|||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class=[self.classes().get()] {
|
||||
@if !title.is_empty() {
|
||||
@let mut btn_classes = AttrClasses::new({
|
||||
@let mut btn_classes = Classes::new({
|
||||
let mut classes = "btn".to_string();
|
||||
self.button_size().push_class(&mut classes);
|
||||
self.button_color().push_class(&mut classes);
|
||||
|
|
@ -86,7 +86,7 @@ impl Component for Dropdown {
|
|||
@let offset = pos.data_offset();
|
||||
@let reference = pos.data_reference();
|
||||
@let auto_close = self.auto_close.as_str();
|
||||
@let menu_classes = AttrClasses::new({
|
||||
@let menu_classes = Classes::new({
|
||||
let mut classes = "dropdown-menu".to_string();
|
||||
self.menu_align().push_class(&mut classes);
|
||||
classes
|
||||
|
|
@ -107,7 +107,7 @@ impl Component for Dropdown {
|
|||
@let btn_toggle = html! {
|
||||
button
|
||||
type="button"
|
||||
class=[btn_classes.alter_value(
|
||||
class=[btn_classes.alter_classes(
|
||||
ClassesOp::Add, "dropdown-toggle dropdown-toggle-split"
|
||||
).get()]
|
||||
data-bs-toggle="dropdown"
|
||||
|
|
@ -138,7 +138,7 @@ impl Component for Dropdown {
|
|||
// Botón único con funcionalidad de *toggle*.
|
||||
button
|
||||
type="button"
|
||||
class=[btn_classes.alter_value(
|
||||
class=[btn_classes.alter_classes(
|
||||
ClassesOp::Add, "dropdown-toggle"
|
||||
).get()]
|
||||
data-bs-toggle="dropdown"
|
||||
|
|
@ -166,14 +166,14 @@ impl Dropdown {
|
|||
/// Establece el identificador único (`id`) del menú desplegable.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al menú desplegable.
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,14 +48,14 @@ pub struct Item {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al elemento.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el tipo de elemento representado.
|
||||
item_kind: ItemKind,
|
||||
}
|
||||
|
||||
impl Component for Item {
|
||||
fn new() -> Self {
|
||||
Item::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -158,7 +158,7 @@ impl Component for Item {
|
|||
impl Item {
|
||||
/// Crea un elemento de tipo texto, mostrado sin interacción.
|
||||
pub fn label(label: L10n) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Label(label),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -170,7 +170,7 @@ impl Item {
|
|||
/// [`RoutePath`] en función del [`Context`]. El enlace se marca como `active` si la ruta actual
|
||||
/// del *request* coincide con la ruta de destino (devuelta por `RoutePath::path`).
|
||||
pub fn link(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -183,7 +183,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace deshabilitado que no permite la interacción.
|
||||
pub fn link_disabled(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -196,7 +196,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace que se abre en una nueva ventana o pestaña.
|
||||
pub fn link_blank(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -209,7 +209,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace inicialmente deshabilitado que se abriría en una nueva ventana.
|
||||
pub fn link_blank_disabled(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -222,7 +222,7 @@ impl Item {
|
|||
|
||||
/// Crea un botón de acción local, sin navegación asociada.
|
||||
pub fn button(label: L10n) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Button {
|
||||
label,
|
||||
disabled: false,
|
||||
|
|
@ -233,7 +233,7 @@ impl Item {
|
|||
|
||||
/// Crea un botón deshabilitado.
|
||||
pub fn button_disabled(label: L10n) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Button {
|
||||
label,
|
||||
disabled: true,
|
||||
|
|
@ -244,7 +244,7 @@ impl Item {
|
|||
|
||||
/// Crea un encabezado para un grupo de elementos dentro del menú.
|
||||
pub fn header(label: L10n) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Header(label),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ impl Item {
|
|||
|
||||
/// Crea un separador visual entre bloques de elementos.
|
||||
pub fn divider() -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Divider,
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -263,14 +263,14 @@ impl Item {
|
|||
/// Establece el identificador único (`id`) del elemento.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al elemento.
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ pub enum IconKind {
|
|||
#[derive(AutoDefault, Getters)]
|
||||
pub struct Icon {
|
||||
/// Devuelve las clases CSS asociadas al icono.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
icon_kind: IconKind,
|
||||
aria_label: AttrL10n,
|
||||
}
|
||||
|
||||
impl Component for Icon {
|
||||
fn new() -> Self {
|
||||
Icon::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
|
|
@ -75,22 +75,22 @@ impl Component for Icon {
|
|||
|
||||
impl Icon {
|
||||
pub fn font() -> Self {
|
||||
Icon::default().with_icon_kind(IconKind::Font(FontSize::default()))
|
||||
Self::default().with_icon_kind(IconKind::Font(FontSize::default()))
|
||||
}
|
||||
|
||||
pub fn font_sized(font_size: FontSize) -> Self {
|
||||
Icon::default().with_icon_kind(IconKind::Font(font_size))
|
||||
Self::default().with_icon_kind(IconKind::Font(font_size))
|
||||
}
|
||||
|
||||
pub fn svg(shapes: Markup) -> Self {
|
||||
Icon::default().with_icon_kind(IconKind::Svg {
|
||||
Self::default().with_icon_kind(IconKind::Svg {
|
||||
shapes,
|
||||
viewbox: AttrValue::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn svg_with_viewbox(shapes: Markup, viewbox: impl AsRef<str>) -> Self {
|
||||
Icon::default().with_icon_kind(IconKind::Svg {
|
||||
Self::default().with_icon_kind(IconKind::Svg {
|
||||
shapes,
|
||||
viewbox: AttrValue::new(viewbox),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,18 +14,18 @@ pub struct Image {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas a la imagen.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve las dimensiones de la imagen.
|
||||
size: image::Size,
|
||||
/// Devuelve el origen de la imagen.
|
||||
source: image::Source,
|
||||
/// Devuelve el texto alternativo localizado.
|
||||
alternative: AttrL10n,
|
||||
alternative: Attr<L10n>,
|
||||
}
|
||||
|
||||
impl Component for Image {
|
||||
fn new() -> Self {
|
||||
Image::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -73,7 +73,7 @@ impl Component for Image {
|
|||
impl Image {
|
||||
/// Crea rápidamente una imagen especificando su origen.
|
||||
pub fn with(source: image::Source) -> Self {
|
||||
Image::default().with_source(source)
|
||||
Self::default().with_source(source)
|
||||
}
|
||||
|
||||
// **< Image BUILDER >**************************************************************************
|
||||
|
|
@ -81,7 +81,7 @@ impl Image {
|
|||
/// Establece el identificador único (`id`) de la imagen.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ impl Image {
|
|||
/// - Redondear las esquinas ([`classes::Rounded`]).
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub struct Nav {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al menú.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el estilo visual seleccionado.
|
||||
nav_kind: nav::Kind,
|
||||
/// Devuelve la distribución y orientación seleccionada.
|
||||
|
|
@ -26,7 +26,7 @@ pub struct Nav {
|
|||
|
||||
impl Component for Nav {
|
||||
fn new() -> Self {
|
||||
Nav::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -59,17 +59,17 @@ impl Component for Nav {
|
|||
impl Nav {
|
||||
/// Crea un `Nav` usando pestañas para los elementos (*Tabs*).
|
||||
pub fn tabs() -> Self {
|
||||
Nav::default().with_kind(nav::Kind::Tabs)
|
||||
Self::default().with_kind(nav::Kind::Tabs)
|
||||
}
|
||||
|
||||
/// Crea un `Nav` usando botones para los elementos (*Pills*).
|
||||
pub fn pills() -> Self {
|
||||
Nav::default().with_kind(nav::Kind::Pills)
|
||||
Self::default().with_kind(nav::Kind::Pills)
|
||||
}
|
||||
|
||||
/// Crea un `Nav` usando elementos subrayados (*Underline*).
|
||||
pub fn underline() -> Self {
|
||||
Nav::default().with_kind(nav::Kind::Underline)
|
||||
Self::default().with_kind(nav::Kind::Underline)
|
||||
}
|
||||
|
||||
// **< Nav BUILDER >****************************************************************************
|
||||
|
|
@ -77,14 +77,14 @@ impl Nav {
|
|||
/// Establece el identificador único (`id`) del menú.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al menú.
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,14 +81,14 @@ pub struct Item {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al elemento.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el tipo de elemento representado.
|
||||
item_kind: ItemKind,
|
||||
}
|
||||
|
||||
impl Component for Item {
|
||||
fn new() -> Self {
|
||||
Item::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -196,7 +196,7 @@ impl Component for Item {
|
|||
impl Item {
|
||||
/// Crea un elemento de tipo texto, mostrado sin interacción.
|
||||
pub fn label(label: L10n) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Label(label),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ impl Item {
|
|||
/// [`RoutePath`] en función del [`Context`]. El enlace se marca como `active` si la ruta actual
|
||||
/// del *request* coincide con la ruta de destino (devuelta por `RoutePath::path`).
|
||||
pub fn link(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -221,7 +221,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace deshabilitado que no permite la interacción.
|
||||
pub fn link_disabled(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -234,7 +234,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace que se abre en una nueva ventana o pestaña.
|
||||
pub fn link_blank(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -247,7 +247,7 @@ impl Item {
|
|||
|
||||
/// Crea un enlace inicialmente deshabilitado que se abriría en una nueva ventana.
|
||||
pub fn link_blank_disabled(label: L10n, route: FnPathByContext) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Link {
|
||||
label,
|
||||
route,
|
||||
|
|
@ -263,7 +263,7 @@ impl Item {
|
|||
/// El contenido se renderiza tal cual lo devuelve el componente [`Html`], dentro de un `<li>`
|
||||
/// con las clases de navegación asociadas a [`Item`].
|
||||
pub fn html(html: Html) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Html(Typed::with(html)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -275,7 +275,7 @@ impl Item {
|
|||
/// lista de elementos** del [`Dropdown`]; el resto de propiedades del componente no afectarán
|
||||
/// a su representación en [`Nav`].
|
||||
pub fn dropdown(menu: Dropdown) -> Self {
|
||||
Item {
|
||||
Self {
|
||||
item_kind: ItemKind::Dropdown(Typed::with(menu)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -286,14 +286,14 @@ impl Item {
|
|||
/// Establece el identificador único (`id`) del elemento.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al elemento.
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pub struct Brand {
|
|||
|
||||
impl Component for Brand {
|
||||
fn new() -> Self {
|
||||
Brand::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -59,7 +59,7 @@ impl Brand {
|
|||
/// Establece el identificador único (`id`) de la marca.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub struct Navbar {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas a la barra de navegación.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el punto de ruptura configurado.
|
||||
expand: BreakPoint,
|
||||
/// Devuelve la disposición configurada para la barra de navegación.
|
||||
|
|
@ -32,7 +32,7 @@ pub struct Navbar {
|
|||
|
||||
impl Component for Navbar {
|
||||
fn new() -> Self {
|
||||
Navbar::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -169,37 +169,37 @@ impl Component for Navbar {
|
|||
impl Navbar {
|
||||
/// Crea una barra de navegación **simple**, sin marca y sin botón.
|
||||
pub fn simple() -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::Simple)
|
||||
Self::default().with_layout(navbar::Layout::Simple)
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación **simple pero colapsable**, con botón a la izquierda.
|
||||
pub fn simple_toggle() -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::SimpleToggle)
|
||||
Self::default().with_layout(navbar::Layout::SimpleToggle)
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación **con marca a la izquierda**, siempre visible.
|
||||
pub fn simple_brand_left(brand: navbar::Brand) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::SimpleBrandLeft(Typed::with(brand)))
|
||||
Self::default().with_layout(navbar::Layout::SimpleBrandLeft(Typed::with(brand)))
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación con **marca a la izquierda** y **botón a la derecha**.
|
||||
pub fn brand_left(brand: navbar::Brand) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::BrandLeft(Typed::with(brand)))
|
||||
Self::default().with_layout(navbar::Layout::BrandLeft(Typed::with(brand)))
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación con **botón a la izquierda** y **marca a la derecha**.
|
||||
pub fn brand_right(brand: navbar::Brand) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::BrandRight(Typed::with(brand)))
|
||||
Self::default().with_layout(navbar::Layout::BrandRight(Typed::with(brand)))
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación cuyo contenido se muestra en un **offcanvas**.
|
||||
pub fn offcanvas(oc: Offcanvas) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::Offcanvas(Typed::with(oc)))
|
||||
Self::default().with_layout(navbar::Layout::Offcanvas(Typed::with(oc)))
|
||||
}
|
||||
|
||||
/// Crea una barra de navegación con **marca a la izquierda** y contenido en **offcanvas**.
|
||||
pub fn offcanvas_brand_left(brand: navbar::Brand, oc: Offcanvas) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::OffcanvasBrandLeft(
|
||||
Self::default().with_layout(navbar::Layout::OffcanvasBrandLeft(
|
||||
Typed::with(brand),
|
||||
Typed::with(oc),
|
||||
))
|
||||
|
|
@ -207,7 +207,7 @@ impl Navbar {
|
|||
|
||||
/// Crea una barra de navegación con **marca a la derecha** y contenido en **offcanvas**.
|
||||
pub fn offcanvas_brand_right(brand: navbar::Brand, oc: Offcanvas) -> Self {
|
||||
Navbar::default().with_layout(navbar::Layout::OffcanvasBrandRight(
|
||||
Self::default().with_layout(navbar::Layout::OffcanvasBrandRight(
|
||||
Typed::with(brand),
|
||||
Typed::with(oc),
|
||||
))
|
||||
|
|
@ -218,7 +218,7 @@ impl Navbar {
|
|||
/// Establece el identificador único (`id`) de la barra de navegación.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ impl Navbar {
|
|||
/// - Definir la apariencia del texto ([`classes::Text`]).
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub enum Item {
|
|||
|
||||
impl Component for Item {
|
||||
fn new() -> Self {
|
||||
Item::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub struct Offcanvas {
|
|||
#[getters(skip)]
|
||||
id: AttrId,
|
||||
/// Devuelve las clases CSS asociadas al panel.
|
||||
classes: AttrClasses,
|
||||
classes: Classes,
|
||||
/// Devuelve el título del panel.
|
||||
title: L10n,
|
||||
/// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel.
|
||||
|
|
@ -45,7 +45,7 @@ pub struct Offcanvas {
|
|||
|
||||
impl Component for Offcanvas {
|
||||
fn new() -> Self {
|
||||
Offcanvas::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -73,14 +73,14 @@ impl Offcanvas {
|
|||
/// Establece el identificador único (`id`) del panel.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al panel.
|
||||
#[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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl<C: Component> AfterRender<C> {
|
|||
/// Afina el registro para ejecutar la acción [`FnActionWithComponent`] sólo para el componente
|
||||
/// `C` con identificador `id`.
|
||||
pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.referer_id.alter_value(id);
|
||||
self.referer_id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl<C: Component> BeforeRender<C> {
|
|||
/// Afina el registro para ejecutar la acción [`FnActionWithComponent`] sólo para el componente
|
||||
/// `C` con identificador `id`.
|
||||
pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.referer_id.alter_value(id);
|
||||
self.referer_id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -18,7 +18,7 @@ pub struct Block {
|
|||
|
||||
impl Component for Block {
|
||||
fn new() -> Self {
|
||||
Block::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
|
|
@ -55,14 +55,14 @@ impl Block {
|
|||
/// Establece el identificador único (`id`) del bloque.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self.id.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,13 @@ pub struct Html(Box<dyn Fn(&mut Context) -> Markup + Send + Sync>);
|
|||
|
||||
impl Default for Html {
|
||||
fn default() -> Self {
|
||||
Html::with(|_| html! {})
|
||||
Self::with(|_| html! {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Html {
|
||||
fn new() -> Self {
|
||||
Html::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl Default for Intro {
|
|||
|
||||
impl Component for Intro {
|
||||
fn new() -> Self {
|
||||
Intro::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, cx: &mut Context) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub struct ActionsList(RwLock<Vec<ActionBox>>);
|
|||
|
||||
impl ActionsList {
|
||||
pub fn new() -> Self {
|
||||
ActionsList::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn add(&mut self, action: ActionBox) {
|
||||
|
|
|
|||
|
|
@ -208,12 +208,12 @@ pub struct Children(Vec<Child>);
|
|||
impl Children {
|
||||
/// Crea una lista vacía.
|
||||
pub fn new() -> Self {
|
||||
Children::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Crea una lista con un componente hijo inicial.
|
||||
pub fn with(child: Child) -> Self {
|
||||
Children::default().with_child(ChildOp::Add(child))
|
||||
Self::default().with_child(ChildOp::Add(child))
|
||||
}
|
||||
|
||||
/// Fusiona varias listas de `Children` en una sola.
|
||||
|
|
|
|||
17
src/html.rs
17
src/html.rs
|
|
@ -21,20 +21,11 @@ pub use logo::PageTopSvg;
|
|||
|
||||
// **< HTML ATTRIBUTES >****************************************************************************
|
||||
|
||||
mod attr_id;
|
||||
pub use attr_id::AttrId;
|
||||
mod attr;
|
||||
pub use attr::{Attr, AttrId, AttrName, AttrValue};
|
||||
|
||||
mod attr_name;
|
||||
pub use attr_name::AttrName;
|
||||
|
||||
mod attr_value;
|
||||
pub use attr_value::AttrValue;
|
||||
|
||||
mod attr_l10n;
|
||||
pub use attr_l10n::AttrL10n;
|
||||
|
||||
mod attr_classes;
|
||||
pub use attr_classes::{AttrClasses, ClassesOp};
|
||||
mod classes;
|
||||
pub use classes::{Classes, ClassesOp};
|
||||
|
||||
mod unit;
|
||||
pub use unit::UnitValue;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl Favicon {
|
|||
/// Equivalente a `Favicon::default()`. Se recomienda iniciar la secuencia de configuración
|
||||
/// desde aquí.
|
||||
pub fn new() -> Self {
|
||||
Favicon::default()
|
||||
Self::default()
|
||||
}
|
||||
|
||||
// **< Favicon BUILDER >************************************************************************
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ impl JavaScript {
|
|||
///
|
||||
/// Equivale a `<script src="...">`.
|
||||
pub fn from(path: impl Into<String>) -> Self {
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::From(path.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ impl JavaScript {
|
|||
/// Equivale a `<script src="..." defer>`. Suele ser la opción recomendada para scripts no
|
||||
/// críticos.
|
||||
pub fn defer(path: impl Into<String>) -> Self {
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::Defer(path.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ impl JavaScript {
|
|||
///
|
||||
/// Equivale a `<script src="..." async>`. **No garantiza** el orden relativo con otros scripts.
|
||||
pub fn asynchronous(path: impl Into<String>) -> Self {
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::Async(path.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ impl JavaScript {
|
|||
where
|
||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||
{
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::Inline(name.into(), Box::new(f)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ impl JavaScript {
|
|||
where
|
||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||
{
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::OnLoad(name.into(), Box::new(f)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ impl JavaScript {
|
|||
where
|
||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||
{
|
||||
JavaScript {
|
||||
Self {
|
||||
source: Source::OnLoadAsync(name.into(), Box::new(f)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ impl StyleSheet {
|
|||
///
|
||||
/// Equivale a `<link rel="stylesheet" href="...">`.
|
||||
pub fn from(path: impl Into<String>) -> Self {
|
||||
StyleSheet {
|
||||
Self {
|
||||
source: Source::From(path.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ impl StyleSheet {
|
|||
where
|
||||
F: Fn(&mut Context) -> String + Send + Sync + 'static,
|
||||
{
|
||||
StyleSheet {
|
||||
Self {
|
||||
source: Source::Inline(name.into(), Box::new(f)),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
|
|||
321
src/html/attr.rs
Normal file
321
src/html/attr.rs
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
use crate::locale::{L10n, LangId};
|
||||
use crate::{builder_fn, AutoDefault};
|
||||
|
||||
/// Valor opcional para atributos HTML.
|
||||
///
|
||||
/// `Attr<T>` encapsula un `Option<T>` y sirve como tipo base para representar atributos HTML
|
||||
/// opcionales, uniformes y tipados.
|
||||
///
|
||||
/// Este tipo **no impone ninguna normalización ni semántica concreta**; dichas reglas se definen en
|
||||
/// implementaciones concretas como `Attr<L10n>` y `Attr<String>`, o en tipos específicos como
|
||||
/// [`AttrId`] y [`AttrName`].
|
||||
#[derive(AutoDefault, Clone, Debug)]
|
||||
pub struct Attr<T>(Option<T>);
|
||||
|
||||
impl<T> Attr<T> {
|
||||
/// Crea un atributo vacío.
|
||||
pub fn empty() -> Self {
|
||||
Self(None)
|
||||
}
|
||||
|
||||
/// Crea un atributo con valor.
|
||||
pub fn some(value: T) -> Self {
|
||||
Self(Some(value))
|
||||
}
|
||||
|
||||
// **< Attr<T> BUILDER >************************************************************************
|
||||
|
||||
/// Establece un valor para el atributo.
|
||||
#[builder_fn]
|
||||
pub fn with_value(mut self, value: T) -> Self {
|
||||
self.0 = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Elimina el valor del atributo.
|
||||
#[builder_fn]
|
||||
pub fn with_none(mut self) -> Self {
|
||||
self.0 = None;
|
||||
self
|
||||
}
|
||||
|
||||
// **< Attr<T> GETTERS >************************************************************************
|
||||
|
||||
/// Devuelve el valor (clonado), si existe.
|
||||
pub fn get(&self) -> Option<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// Devuelve una referencia al valor, si existe.
|
||||
pub fn as_ref(&self) -> Option<&T> {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
/// Devuelve el valor (propiedad), si existe.
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// `true` si no hay valor.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
// **< Attr<L10n> >*********************************************************************************
|
||||
|
||||
/// Extiende [`Attr`] para [texto localizado](crate::locale) en atributos HTML.
|
||||
///
|
||||
/// Encapsula un [`L10n`] para manejar traducciones de forma segura en atributos.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// // Traducción por clave en las locales por defecto de PageTop.
|
||||
/// let hello = Attr::<L10n>::new(L10n::l("test_hello_world"));
|
||||
///
|
||||
/// // Español disponible.
|
||||
/// assert_eq!(
|
||||
/// hello.lookup(&Locale::resolve("es-ES")),
|
||||
/// Some("¡Hola mundo!".to_string())
|
||||
/// );
|
||||
///
|
||||
/// // Japonés no disponible, traduce al idioma de respaldo (`"en-US"`).
|
||||
/// assert_eq!(
|
||||
/// hello.lookup(&Locale::resolve("ja-JP")),
|
||||
/// Some("Hello world!".to_string())
|
||||
/// );
|
||||
///
|
||||
/// // Uso típico en un atributo:
|
||||
/// let title = hello.value(&Locale::resolve("es-ES"));
|
||||
/// // Ejemplo: html! { a title=(title) { "Link" } }
|
||||
/// ```
|
||||
impl Attr<L10n> {
|
||||
/// Crea una nueva instancia `Attr<L10n>`.
|
||||
pub fn new(value: L10n) -> Self {
|
||||
Self::some(value)
|
||||
}
|
||||
|
||||
/// Devuelve la traducción para `language` si puede resolverse.
|
||||
pub fn lookup(&self, language: &impl LangId) -> Option<String> {
|
||||
self.0.as_ref()?.lookup(language)
|
||||
}
|
||||
|
||||
/// Devuelve la traducción para `language` o una cadena vacía si no existe.
|
||||
pub fn value(&self, language: &impl LangId) -> String {
|
||||
self.lookup(language).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
// **< Attr<String> >*******************************************************************************
|
||||
|
||||
/// Extiende [`Attr`] para cadenas de texto.
|
||||
impl Attr<String> {
|
||||
/// Devuelve el texto como `&str` si existe.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.0.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
// **< AttrId >*************************************************************************************
|
||||
|
||||
/// 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)]
|
||||
pub struct AttrId(Attr<String>);
|
||||
|
||||
impl AttrId {
|
||||
/// Crea un nuevo `AttrId` normalizando el valor.
|
||||
pub fn new(id: impl AsRef<str>) -> Self {
|
||||
Self::default().with_id(id)
|
||||
}
|
||||
|
||||
// **< AttrId BUILDER >*************************************************************************
|
||||
|
||||
/// Establece un identificador nuevo normalizando el valor.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
let id = id.as_ref().trim();
|
||||
if id.is_empty() {
|
||||
self.0 = Attr::default();
|
||||
} else {
|
||||
self.0 = Attr::some(id.to_ascii_lowercase().replace(' ', "_"));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// **< AttrId GETTERS >*************************************************************************
|
||||
|
||||
/// Devuelve el identificador normalizado, si existe.
|
||||
pub fn get(&self) -> Option<String> {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Devuelve el identificador normalizado (sin clonar), si existe.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.0.as_str()
|
||||
}
|
||||
|
||||
/// Devuelve el identificador normalizado (propiedad), si existe.
|
||||
pub fn into_inner(self) -> Option<String> {
|
||||
self.0.into_inner()
|
||||
}
|
||||
|
||||
/// `true` si no hay valor.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
// **< AttrName >***********************************************************************************
|
||||
|
||||
/// 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)]
|
||||
pub struct AttrName(Attr<String>);
|
||||
|
||||
impl AttrName {
|
||||
/// Crea un nuevo `AttrName` normalizando el valor.
|
||||
pub fn new(name: impl AsRef<str>) -> Self {
|
||||
Self::default().with_name(name)
|
||||
}
|
||||
|
||||
// **< AttrName BUILDER >***********************************************************************
|
||||
|
||||
/// Establece un nombre nuevo normalizando el valor.
|
||||
#[builder_fn]
|
||||
pub fn with_name(mut self, name: impl AsRef<str>) -> Self {
|
||||
let name = name.as_ref().trim();
|
||||
if name.is_empty() {
|
||||
self.0 = Attr::default();
|
||||
} else {
|
||||
self.0 = Attr::some(name.to_ascii_lowercase().replace(' ', "_"));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// **< AttrName GETTERS >***********************************************************************
|
||||
|
||||
/// Devuelve el nombre normalizado, si existe.
|
||||
pub fn get(&self) -> Option<String> {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Devuelve el nombre normalizado (sin clonar), si existe.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.0.as_str()
|
||||
}
|
||||
|
||||
/// Devuelve el nombre normalizado (propiedad), si existe.
|
||||
pub fn into_inner(self) -> Option<String> {
|
||||
self.0.into_inner()
|
||||
}
|
||||
|
||||
/// `true` si no hay valor.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
// **< AttrValue >**********************************************************************************
|
||||
|
||||
/// 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)]
|
||||
pub struct AttrValue(Attr<String>);
|
||||
|
||||
impl AttrValue {
|
||||
/// Crea un nuevo `AttrValue` normalizando el valor.
|
||||
pub fn new(value: impl AsRef<str>) -> Self {
|
||||
Self::default().with_str(value)
|
||||
}
|
||||
|
||||
// **< AttrValue BUILDER >**********************************************************************
|
||||
|
||||
/// Establece una cadena nueva normalizando el valor.
|
||||
#[builder_fn]
|
||||
pub fn with_str(mut self, value: impl AsRef<str>) -> Self {
|
||||
let value = value.as_ref().trim();
|
||||
if value.is_empty() {
|
||||
self.0 = Attr::default();
|
||||
} else {
|
||||
self.0 = Attr::some(value.to_string());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// **< AttrValue GETTERS >**********************************************************************
|
||||
|
||||
/// Devuelve la cadena normalizada, si existe.
|
||||
pub fn get(&self) -> Option<String> {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Devuelve la cadena normalizada (sin clonar), si existe.
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.0.as_str()
|
||||
}
|
||||
|
||||
/// Devuelve la cadena normalizada (propiedad), si existe.
|
||||
pub fn into_inner(self) -> Option<String> {
|
||||
self.0.into_inner()
|
||||
}
|
||||
|
||||
/// `true` si no hay valor.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +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:
|
||||
///
|
||||
/// - 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
use crate::locale::{L10n, LangId};
|
||||
use crate::{builder_fn, AutoDefault};
|
||||
|
||||
/// Texto para [traducir](crate::locale) en atributos HTML.
|
||||
///
|
||||
/// Encapsula un [`L10n`] para manejar traducciones de forma segura en atributos.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// // Traducción por clave en las locales por defecto de PageTop.
|
||||
/// let hello = AttrL10n::new(L10n::l("test_hello_world"));
|
||||
///
|
||||
/// // Español disponible.
|
||||
/// assert_eq!(
|
||||
/// hello.lookup(&Locale::resolve("es-ES")),
|
||||
/// Some("¡Hola mundo!".to_string())
|
||||
/// );
|
||||
///
|
||||
/// // Japonés no disponible, traduce al idioma de respaldo (`"en-US"`).
|
||||
/// assert_eq!(
|
||||
/// hello.lookup(&Locale::resolve("ja-JP")),
|
||||
/// Some("Hello world!".to_string())
|
||||
/// );
|
||||
///
|
||||
/// // Uso típico en un atributo:
|
||||
/// let title = hello.value(&Locale::resolve("es-ES"));
|
||||
/// // Ejemplo: html! { a title=(title) { "Link" } }
|
||||
/// ```
|
||||
#[derive(AutoDefault, Clone, Debug)]
|
||||
pub struct AttrL10n(L10n);
|
||||
|
||||
impl AttrL10n {
|
||||
/// Crea una nueva instancia `AttrL10n`.
|
||||
pub fn new(value: L10n) -> Self {
|
||||
AttrL10n(value)
|
||||
}
|
||||
|
||||
// **< AttrL10n BUILDER >***********************************************************************
|
||||
|
||||
/// Establece una traducción nueva.
|
||||
#[builder_fn]
|
||||
pub fn with_value(mut self, value: L10n) -> Self {
|
||||
self.0 = value;
|
||||
self
|
||||
}
|
||||
|
||||
// **< AttrL10n GETTERS >***********************************************************************
|
||||
|
||||
/// Devuelve la traducción para `language`, si existe.
|
||||
pub fn lookup(&self, language: &impl LangId) -> Option<String> {
|
||||
self.0.lookup(language)
|
||||
}
|
||||
|
||||
/// Devuelve la traducción para `language` o una cadena vacía si no existe.
|
||||
pub fn value(&self, language: &impl LangId) -> String {
|
||||
self.0.lookup(language).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +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:
|
||||
///
|
||||
/// - 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +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:
|
||||
///
|
||||
/// - 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_string())
|
||||
};
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
AttrClasses::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)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ pub struct L10n {
|
|||
impl L10n {
|
||||
/// **n** = *“native”*. Crea una instancia con una cadena literal sin traducción.
|
||||
pub fn n(text: impl Into<Cow<'static, str>>) -> Self {
|
||||
L10n {
|
||||
Self {
|
||||
op: L10nOp::Text(text.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ impl L10n {
|
|||
/// **l** = *“lookup”*. Crea una instancia para traducir usando una clave del conjunto de
|
||||
/// traducciones predefinidas.
|
||||
pub fn l(key: impl Into<Cow<'static, str>>) -> Self {
|
||||
L10n {
|
||||
Self {
|
||||
op: L10nOp::Translate(key.into()),
|
||||
..Default::default()
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ impl L10n {
|
|||
/// **t** = *“translate”*. Crea una instancia para traducir usando una clave de un conjunto de
|
||||
/// traducciones específico.
|
||||
pub fn t(key: impl Into<Cow<'static, str>>, locales: &'static Locales) -> Self {
|
||||
L10n {
|
||||
Self {
|
||||
op: L10nOp::Translate(key.into()),
|
||||
locales,
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ use crate::core::component::{Context, ContextOp, Contextual};
|
|||
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::{AttrClasses, ClassesOp};
|
||||
use crate::html::{AttrId, AttrL10n};
|
||||
use crate::html::{Attr, AttrId};
|
||||
use crate::html::{Classes, ClassesOp};
|
||||
use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier};
|
||||
use crate::service::HttpRequest;
|
||||
use crate::{builder_fn, AutoDefault};
|
||||
|
|
@ -89,12 +89,12 @@ impl Region for ReservedRegion {
|
|||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Page {
|
||||
title : AttrL10n,
|
||||
description : AttrL10n,
|
||||
title : Attr<L10n>,
|
||||
description : Attr<L10n>,
|
||||
metadata : Vec<(&'static str, &'static str)>,
|
||||
properties : Vec<(&'static str, &'static str)>,
|
||||
body_id : AttrId,
|
||||
body_classes: AttrClasses,
|
||||
body_classes: Classes,
|
||||
context : Context,
|
||||
}
|
||||
|
||||
|
|
@ -106,12 +106,12 @@ impl Page {
|
|||
#[rustfmt::skip]
|
||||
pub fn new(request: HttpRequest) -> Self {
|
||||
Page {
|
||||
title : AttrL10n::default(),
|
||||
description : AttrL10n::default(),
|
||||
title : Attr::<L10n>::default(),
|
||||
description : Attr::<L10n>::default(),
|
||||
metadata : Vec::default(),
|
||||
properties : Vec::default(),
|
||||
body_id : AttrId::default(),
|
||||
body_classes: AttrClasses::default(),
|
||||
body_classes: Classes::default(),
|
||||
context : Context::new(Some(request)),
|
||||
}
|
||||
}
|
||||
|
|
@ -149,14 +149,14 @@ impl Page {
|
|||
/// Establece el atributo `id` del elemento `<body>`.
|
||||
#[builder_fn]
|
||||
pub fn with_body_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.body_id.alter_value(id);
|
||||
self.body_id.alter_id(id);
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue