diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index 1ad71914..ae44ebbb 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -144,15 +144,14 @@ 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), }) } @@ -168,17 +167,17 @@ impl Theme for Bootsier { .alter_assets(AssetsOp::AddStyleSheet( StyleSheet::from("/bootsier/css/bootsier.min.css") .with_version(ADMINLTE_VERSION) - .with_weight(-99), + .with_weight(-90), )) .alter_assets(AssetsOp::AddJavaScript( JavaScript::defer("/bootsier/js/bootsier.bundle.min.js") .with_version(BOOTSTRAP_VERSION) - .with_weight(-99), + .with_weight(-90), )) .alter_assets(AssetsOp::AddJavaScript( JavaScript::defer("/bootsier/js/bootsier.extended.min.js") .with_version(ADMINLTE_VERSION) - .with_weight(-99), + .with_weight(-90), )) .alter_child_in( &DefaultRegion::Footer, diff --git a/extensions/pagetop-bootsier/src/theme/bs.rs b/extensions/pagetop-bootsier/src/theme/bs.rs index ae1657fd..c5dd6e08 100644 --- a/extensions/pagetop-bootsier/src/theme/bs.rs +++ b/extensions/pagetop-bootsier/src/theme/bs.rs @@ -1,4 +1,14 @@ -//! Componentes proporcionados por el tema. +//! 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::*; // Button. mod button; @@ -8,8 +18,6 @@ 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 e780c9a3..41a35d60 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/container.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/container.rs @@ -1,99 +1,14 @@ //! 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. -use pagetop::prelude::*; +mod props; +pub use props::{Kind, Width}; -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())); - } -} +mod component; +pub use component::Container; diff --git a/extensions/pagetop-bootsier/src/theme/bs/container/component.rs b/extensions/pagetop-bootsier/src/theme/bs/container/component.rs new file mode 100644 index 00000000..ed9a5f7d --- /dev/null +++ b/extensions/pagetop-bootsier/src/theme/bs/container/component.rs @@ -0,0 +1,172 @@ +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` (`