pagetop/extensions/pagetop-bootsier/src/theme/container.rs

299 lines
9.1 KiB
Rust

use pagetop::prelude::*;
use crate::prelude::*;
// **< ContainerType >******************************************************************************
/// Tipo de contenedor ([`Container`]).
///
/// Permite aplicar la etiqueta HTML apropiada (`<main>`, `<header>`, etc.) manteniendo una API
/// común a todos los contenedores.
#[derive(AutoDefault)]
pub enum ContainerType {
/// Contenedor genérico (`<div>`).
#[default]
Default,
/// Contenido principal de la página (`<main>`).
Main,
/// Encabezado de la página o de sección (`<header>`).
Header,
/// Pie de la página o de sección (`<footer>`).
Footer,
/// Sección de contenido (`<section>`).
Section,
/// Artículo de contenido (`<article>`).
Article,
}
// **< ContainerWidth >*****************************************************************************
/// Define el comportamiento para ajustar el ancho de un contenedor ([`Container`]).
#[derive(AutoDefault)]
pub enum ContainerWidth {
/// Comportamiento por defecto, aplica los anchos máximos predefinidos para cada punto de
/// ruptura. Por debajo del menor punto de ruptura ocupa el 100% del ancho disponible.
#[default]
Default,
/// Aplica los anchos máximos predefinidos a partir del punto de ruptura indicado. Por debajo de
/// ese punto de ruptura ocupa el 100% del ancho disponible.
From(BreakPoint),
/// Ocupa el 100% del ancho disponible siempre.
Fluid,
/// Ocupa el 100% del ancho disponible hasta un ancho máximo explícito.
FluidMax(UnitValue),
}
// **< Container >**********************************************************************************
/// Componente para crear un **contenedor de componentes**.
///
/// Envuelve el contenido con la etiqueta HTML indicada por [`ContainerType`]. Sólo se renderiza si
/// existen componentes hijos (*children*).
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Container {
id : AttrId,
classes : AttrClasses,
container_type : ContainerType,
container_width: ContainerWidth,
bg_color : BgColor,
text_color : TextColor,
border : Border,
rounded : Rounded,
children : Children,
}
impl Component for Container {
fn new() -> Self {
Container::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.alter_classes(
ClassesOp::Prepend,
[
join_pair!(
"container",
"-",
match self.width() {
ContainerWidth::Default => String::new(),
ContainerWidth::From(bp) => bp.to_string(),
ContainerWidth::Fluid => "fluid".to_string(),
ContainerWidth::FluidMax(_) => "fluid".to_string(),
}
),
self.bg_color().to_string(),
self.text_color().to_string(),
self.border().to_string(),
self.rounded().to_string(),
]
.join(" "),
);
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let output = self.children().render(cx);
if output.is_empty() {
return PrepareMarkup::None;
}
let style = match self.width() {
ContainerWidth::FluidMax(w) if w.is_measurable() => {
Some(join!("max-width: ", w.to_string(), ";"))
}
_ => None,
};
match self.container_type() {
ContainerType::Default => PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
ContainerType::Main => PrepareMarkup::With(html! {
main id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
ContainerType::Header => PrepareMarkup::With(html! {
header id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
ContainerType::Footer => PrepareMarkup::With(html! {
footer id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
ContainerType::Section => PrepareMarkup::With(html! {
section id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
ContainerType::Article => PrepareMarkup::With(html! {
article id=[self.id()] class=[self.classes().get()] style=[style] {
(output)
}
}),
}
}
}
impl Container {
/// Crea un contenedor de tipo `Main` (`<main>`).
pub fn main() -> Self {
Container {
container_type: ContainerType::Main,
..Default::default()
}
}
/// Crea un contenedor de tipo `Header` (`<header>`).
pub fn header() -> Self {
Container {
container_type: ContainerType::Header,
..Default::default()
}
}
/// Crea un contenedor de tipo `Footer` (`<footer>`).
pub fn footer() -> Self {
Container {
container_type: ContainerType::Footer,
..Default::default()
}
}
/// Crea un contenedor de tipo `Section` (`<section>`).
pub fn section() -> Self {
Container {
container_type: ContainerType::Section,
..Default::default()
}
}
/// Crea un contenedor de tipo `Article` (`<article>`).
pub fn article() -> Self {
Container {
container_type: ContainerType::Article,
..Default::default()
}
}
// **< Container BUILDER >**********************************************************************
/// 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
}
/// Modifica la lista de clases CSS aplicadas al contenedor.
#[builder_fn]
pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
self.classes.alter_value(op, classes);
self
}
/// Establece el comportamiento del ancho para el contenedor.
#[builder_fn]
pub fn with_width(mut self, width: ContainerWidth) -> Self {
self.container_width = width;
self
}
/// Establece el color de fondo ([`BgColor`]).
#[builder_fn]
pub fn with_bg_color(mut self, color: BgColor) -> Self {
self.bg_color = color;
self
}
/// Establece el color del texto ([`TextColor`]).
#[builder_fn]
pub fn with_text_color(mut self, color: TextColor) -> Self {
self.text_color = color;
self
}
/// Atajo para definir los colores de fondo y texto a la vez.
#[builder_fn]
pub fn with_colors(mut self, bg_color: BgColor, text_color: TextColor) -> Self {
self.bg_color = bg_color;
self.text_color = text_color;
self
}
/// Establece el borde del contenedor ([`Border`]).
#[builder_fn]
pub fn with_border(mut self, border: Border) -> Self {
self.border = border;
self
}
/// Establece esquinas redondeadas para el contenedor.
#[builder_fn]
pub fn with_rounded(mut self, rounded: Rounded) -> Self {
self.rounded = rounded;
self
}
/// Añade un nuevo componente hijo al contenedor.
#[inline]
pub fn add_child(mut self, component: impl Component) -> Self {
self.children.add(Child::with(component));
self
}
/// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`].
#[builder_fn]
pub fn with_child(mut self, op: ChildOp) -> Self {
self.children.alter_child(op);
self
}
// **< Container GETTERS >**********************************************************************
/// Devuelve las clases CSS asociadas al contenedor.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el tipo semántico del contenedor.
pub fn container_type(&self) -> &ContainerType {
&self.container_type
}
/// Devuelve el comportamiento para el ancho del contenedor.
pub fn width(&self) -> &ContainerWidth {
&self.container_width
}
/// Devuelve el color de fondo del contenedor.
pub fn bg_color(&self) -> &BgColor {
&self.bg_color
}
/// Devuelve el color del texto del contenedor.
pub fn text_color(&self) -> &TextColor {
&self.text_color
}
/// Devuelve el borde del contenedor.
pub fn border(&self) -> &Border {
&self.border
}
/// Devuelve las esquinas redondeadas del contenedor.
pub fn rounded(&self) -> &Rounded {
&self.rounded
}
/// Devuelve la lista de componentes (`children`) del contenedor.
pub fn children(&self) -> &Children {
&self.children
}
}