From 771f61bf292b62a8fb41286ba217298228643daf Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Thu, 9 Jul 2026 21:44:52 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(bootsier):=20Mueve=20Cont?= =?UTF-8?q?ainer=20al=20core=20de=20PageTop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Container` y `Kind` pasan a `pagetop::base::component` como componente genérico reutilizable por cualquier tema. Bootsier conserva `Width` y añade el *trait* `ContainerBootsier`, que guarda el ancho como valor extra en `Props`. --- extensions/pagetop-bootsier/src/lib.rs | 13 +- extensions/pagetop-bootsier/src/theme/bs.rs | 14 +- .../src/theme/bs/container.rs | 107 +++++++++-- .../src/theme/bs/container/component.rs | 172 ------------------ .../src/theme/bs/container/props.rs | 67 ------- .../src/theme/bs/dropdown/component.rs | 2 +- .../pagetop-bootsier/src/theme/bs/form.rs | 6 + .../src/theme/bs/image/component.rs | 2 +- .../src/theme/bs/nav/component.rs | 2 +- .../src/theme/bs/navbar/component.rs | 2 +- .../src/theme/bs/offcanvas/component.rs | 2 +- src/base/component.rs | 4 + src/base/component/container.rs | 145 +++++++++++++++ src/base/component/form.rs | 2 +- src/base/component/form/component.rs | 2 +- 15 files changed, 268 insertions(+), 274 deletions(-) delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/container/component.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/container/props.rs create mode 100644 src/base/component/container.rs diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index ae44ebbb..1ad71914 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -144,14 +144,15 @@ impl Theme for Bootsier { ) -> Option> { setup_component!(component, { Button => |c| theme::bs::button::setup(c), + Container => |c| theme::bs::container::setup(c), form::input::Field => |c| theme::bs::form::input::setup(c), form::select::Field => |c| theme::bs::form::select::setup(c), form::Textarea => |c| theme::bs::form::textarea::setup(c), }); render_component!(component, { - form::input::Field => |c| theme::bs::form::input::render(c, cx), - form::select::Field => |c| theme::bs::form::select::render(c, cx), - form::Textarea => |c| theme::bs::form::textarea::render(c, cx), + form::input::Field => |c| theme::bs::form::input::render(c, cx), + form::select::Field => |c| theme::bs::form::select::render(c, cx), + form::Textarea => |c| theme::bs::form::textarea::render(c, cx), }) } @@ -167,17 +168,17 @@ impl Theme for Bootsier { .alter_assets(AssetsOp::AddStyleSheet( StyleSheet::from("/bootsier/css/bootsier.min.css") .with_version(ADMINLTE_VERSION) - .with_weight(-90), + .with_weight(-99), )) .alter_assets(AssetsOp::AddJavaScript( JavaScript::defer("/bootsier/js/bootsier.bundle.min.js") .with_version(BOOTSTRAP_VERSION) - .with_weight(-90), + .with_weight(-99), )) .alter_assets(AssetsOp::AddJavaScript( JavaScript::defer("/bootsier/js/bootsier.extended.min.js") .with_version(ADMINLTE_VERSION) - .with_weight(-90), + .with_weight(-99), )) .alter_child_in( &DefaultRegion::Footer, diff --git a/extensions/pagetop-bootsier/src/theme/bs.rs b/extensions/pagetop-bootsier/src/theme/bs.rs index c5dd6e08..ae1657fd 100644 --- a/extensions/pagetop-bootsier/src/theme/bs.rs +++ b/extensions/pagetop-bootsier/src/theme/bs.rs @@ -1,14 +1,4 @@ -//! Tipos y componentes disponibles. -//! -//! A continuación, el apartado **Modules** incluye las definiciones necesarias para los componentes -//! que se muestran en el apartado **Structs**, mientras que en **Enums** se listan los elementos -//! auxiliares del tema utilizados en clases y componentes. - -mod attrs; -pub use attrs::*; - -mod classes; -pub use classes::*; +//! Componentes proporcionados por el tema. // Button. mod button; @@ -18,6 +8,8 @@ pub use button::{Button, ButtonAction}; pub mod container; #[doc(inline)] pub use container::Container; +#[doc(inline)] +pub use container::ContainerBootsier; // Dropdown. pub mod dropdown; diff --git a/extensions/pagetop-bootsier/src/theme/bs/container.rs b/extensions/pagetop-bootsier/src/theme/bs/container.rs index 41a35d60..e780c9a3 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/container.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/container.rs @@ -1,14 +1,99 @@ //! Definiciones para crear contenedores de componentes ([`Container`]). -//! -//! Cada contenedor envuelve contenido usando la etiqueta semántica indicada por -//! [`container::Kind`](crate::theme::bs::container::Kind). -//! -//! Con [`container::Width`](crate::theme::bs::container::Width) se puede definir el ancho y el -//! comportamiento *responsive* del contenedor. También permite aplicar utilidades de estilo para el -//! fondo, texto, borde o esquinas redondeadas. -mod props; -pub use props::{Kind, Width}; +use pagetop::prelude::*; -mod component; -pub use component::Container; +use crate::theme::*; + +pub use pagetop::base::component::container::{Container, Kind}; + +const EXTRA_WIDTH: &str = "bootsier.container.width"; + +/// Extensión de Bootsier para [`Container`]. +/// +/// Permite definir el comportamiento del ancho del contenedor usando el método +/// [`with_width()`](Self::with_width). También acepta clases predefinidas para: +/// +/// - Modificar el color de fondo ([`Background`](crate::theme::class::Background)). +/// - Definir la apariencia del texto ([`Text`](crate::theme::class::Text)). +/// - Establecer bordes ([`Border`](crate::theme::class::Border)). +/// - Redondear las esquinas ([`Rounded`](crate::theme::class::Rounded)). +/// +/// ```rust,no_run +/// use pagetop::prelude::*; +/// use pagetop_bootsier::theme::*; +/// +/// let main = bs::Container::main() +/// .with_id("main-page") +/// .with_width(bs::container::Width::From(token::BreakPoint::LG)) +/// .with_prop(PropsOp::add_classes(class::Background::with(token::Color::Light))) +/// .with_prop(PropsOp::add_classes(class::Text::with(token::Color::Dark))) +/// .with_prop(PropsOp::add_classes(class::Border::with(token::ScaleSize::One))) +/// .with_prop(PropsOp::add_classes(class::Rounded::with(token::RoundedRadius::Default))); +/// ``` +pub trait ContainerBootsier { + /// Establece el comportamiento del ancho para el contenedor. + /// + /// Determina si el contenedor aplica los anchos máximos predefinidos para cada punto de + /// ruptura, o si ocupa siempre el 100% del ancho disponible, o lo hace hasta un ancho máximo + /// explícito. Ver [`Width`] para las variantes disponibles. + fn with_width(self, width: Width) -> Self; +} + +impl ContainerBootsier for Container { + fn with_width(self, width: Width) -> Self { + self.with_prop(PropsOp::set_extra(EXTRA_WIDTH, width)) + } +} + +// **< Width >************************************************************************************** + +/// Define cómo se comporta el ancho de un contenedor ([`Container`]). +#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] +pub enum Width { + /// 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(token::BreakPoint), + /// Ocupa el 100% del ancho disponible siempre. + Fluid, + /// Ocupa el 100% del ancho disponible hasta un ancho máximo explícito. + FluidMax(UnitValue), +} + +impl Width { + const CONTAINER: &str = "container"; + + /// Añade la clase asociada al ancho del contenedor a la cadena de clases. + #[inline] + pub fn push_to(self, classes: &mut String) { + match self { + Self::Default => token::BreakPoint::None.push_to(classes, Self::CONTAINER, ""), + Self::From(bp) => bp.push_to(classes, Self::CONTAINER, ""), + Self::Fluid | Self::FluidMax(_) => { + token::BreakPoint::None.push_to(classes, Self::CONTAINER, "fluid") + } + } + } + + /// Devuelve la clase asociada al ancho del contenedor. + pub fn to_class(self) -> String { + let mut class = String::new(); + self.push_to(&mut class); + class + } +} + +// **< Container SETUP >**************************************************************************** + +pub(crate) fn setup(container: &mut Container) { + let width = container.props().extra_or(EXTRA_WIDTH, Width::default()); + container.alter_prop(PropsOp::prepend_classes(width.to_class())); + if let Width::FluidMax(w) = width + && w.is_measurable() + { + container.alter_prop(PropsOp::add_style("max-width", w.to_string())); + } +} diff --git a/extensions/pagetop-bootsier/src/theme/bs/container/component.rs b/extensions/pagetop-bootsier/src/theme/bs/container/component.rs deleted file mode 100644 index ed9a5f7d..00000000 --- a/extensions/pagetop-bootsier/src/theme/bs/container/component.rs +++ /dev/null @@ -1,172 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::*; - -/// Componente para crear un **contenedor de componentes** -/// ([`container`](crate::theme::bs::container)). -/// -/// Envuelve un conjunto de componentes en un contenedor establecido que se crea aplicando uno de -/// los tipos definidos en [`container::Kind`](crate::theme::bs::container::Kind). -/// -/// Si no contiene elementos, el componente **no se renderiza**. -/// -/// # Ejemplo -/// -/// ```rust,no_run -/// use pagetop_bootsier::theme::*; -/// -/// let main = bs::Container::main() -/// .with_id("main-page") -/// .with_width(bs::container::Width::From(token::BreakPoint::LG)); -/// ``` -#[derive(AutoDefault, Clone, Debug, Getters)] -pub struct Container { - /// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente. - props: Props, - /// Devuelve el tipo semántico del contenedor. - container_kind: bs::container::Kind, - /// Devuelve el comportamiento para el ancho del contenedor. - container_width: bs::container::Width, - /// Devuelve la lista de componentes (`children`) del contenedor. - children: Children, -} - -#[async_trait] -impl Component for Container { - fn new() -> Self { - Self::default() - } - - fn id(&self) -> Option { - self.props.get_id() - } - - fn setup(&mut self, _cx: &Context) { - self.alter_prop(PropsOp::prepend_classes(self.container_width().to_class())); - } - - async fn prepare(&self, cx: &mut Context) -> Result { - let output = self.children().render(cx).await; - if output.is_empty() { - return Ok(html! {}); - } - let style = match self.container_width() { - bs::container::Width::FluidMax(w) if w.is_measurable() => { - Some(util::join!("max-width: ", w.to_string(), ";")) - } - _ => None, - }; - Ok(match self.container_kind() { - bs::container::Kind::Default => html! { - div (self.props()) style=[style] { - (output) - } - }, - bs::container::Kind::Main => html! { - main (self.props()) style=[style] { - (output) - } - }, - bs::container::Kind::Header => html! { - header (self.props()) style=[style] { - (output) - } - }, - bs::container::Kind::Footer => html! { - footer (self.props()) style=[style] { - (output) - } - }, - bs::container::Kind::Section => html! { - section (self.props()) style=[style] { - (output) - } - }, - bs::container::Kind::Article => html! { - article (self.props()) style=[style] { - (output) - } - }, - }) - } -} - -impl Container { - /// Crea un contenedor de tipo `Main` (`
`). - pub fn main() -> Self { - Self { - container_kind: bs::container::Kind::Main, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Header` (`
`). - pub fn header() -> Self { - Self { - container_kind: bs::container::Kind::Header, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Footer` (`