diff --git a/assets/css/basic.css b/assets/css/basic.css index 1f92da1f..2fe4bb7c 100644 --- a/assets/css/basic.css +++ b/assets/css/basic.css @@ -44,7 +44,7 @@ body { -webkit-tap-highlight-color: transparent; } -/* Header, Aside, Content y Footer se apilan en el mismo orden ocupando todo el ancho disponible. */ +/* Header, Aside, Content and Footer are stacked in that order, using the full available width. */ .wrapper { display: flex; flex-direction: column; @@ -125,10 +125,10 @@ body { text-decoration: underline; } -/* Geometría base de .dropdown-button/.dropdown-toggle (ver "Dropdown component" más abajo). Se - * declara aquí, antes de "Buttons", para que las clases .button-sm/.button-lg/.button-* ganen el - * empate de especificidad frente a ella cuando se combinan en el mismo elemento (CSS resuelve un - * empate por orden de aparición en el fichero, no por el orden de las clases en el HTML). */ +/* Base geometry of .dropdown-button/.dropdown-toggle (see "Dropdown component" below). It is + * declared here, before "Buttons", so that the .button-sm/.button-lg/.button-* classes win the + * specificity tie against it when combined on the same element (CSS resolves a tie by order of + * appearance in the file, not by the order of the classes in the HTML). */ .dropdown-button, .dropdown-toggle { padding: 0.5rem 0.75rem; @@ -247,6 +247,79 @@ body { gap: 0.5rem; } +/* + * Container component + */ + +/* Classes added by the `Container` component from `Width`. All of them are centered and fill the + * available width; `.container` and `.container-{name}` also get a maximum width from the + * breakpoint they name (`.container` from the first one). The minimum widths match the default + * `Theme::breakpoint_entry()` table, so a child theme that renames or resizes the breakpoints must + * also redefine these classes. */ +.container, +.container-fluid, +.container-sm, +.container-md, +.container-lg, +.container-xl, +.container-xxl, +.container-xxxl { + width: 100%; + margin-inline: auto; + padding-inline: 1rem; +} +@media (min-width: 576px) { + .container, + .container-sm { + max-width: 540px; + } +} +@media (min-width: 768px) { + .container, + .container-sm, + .container-md { + max-width: 720px; + } +} +@media (min-width: 992px) { + .container, + .container-sm, + .container-md, + .container-lg { + max-width: 960px; + } +} +@media (min-width: 1200px) { + .container, + .container-sm, + .container-md, + .container-lg, + .container-xl { + max-width: 1140px; + } +} +@media (min-width: 1400px) { + .container, + .container-sm, + .container-md, + .container-lg, + .container-xl, + .container-xxl { + max-width: 1320px; + } +} +@media (min-width: 1920px) { + .container, + .container-sm, + .container-md, + .container-lg, + .container-xl, + .container-xxl, + .container-xxxl { + max-width: 1800px; + } +} + /* * Dialog component */ @@ -318,10 +391,10 @@ body { display: inline-block; } -/* La geometría base de .dropdown-button/.dropdown-toggle vive junto a "Buttons" (antes de esta - * sección), para que las clases .button-sm/.button-lg/.button-* ganen el empate de especificidad - * frente a ella (CSS resuelve un empate por orden de aparición en el fichero, no por el orden de - * las clases en el HTML). */ +/* The base geometry of .dropdown-button/.dropdown-toggle lives next to "Buttons" (before this + * section), so that the .button-sm/.button-lg/.button-* classes win the specificity tie against it + * (CSS resolves a tie by order of appearance in the file, not by the order of the classes in the + * HTML). */ .dropdown-toggle::after { content: "\25BC"; display: inline-block; @@ -928,9 +1001,9 @@ input:disabled + label { color: var(--val-color--text--muted); } -/* .page-link-icon muestra el texto de navegación de los botones anterior/siguiente. El texto - accesible completo va aparte, en el aria-label del enlace. Por eso sustituir el texto visible por - otro contenido no afecta a la accesibilidad, p. ej. para usar "‹"/"›" se puede aplicar: +/* .page-link-icon shows the navigation text of the previous/next buttons. The full accessible text + goes separately, in the link's aria-label. So replacing the visible text with other content does + not affect accessibility, e.g. to use "‹"/"›" you can apply: .page-link-icon { display: inline-block; diff --git a/examples/locale/es-ES/navbar-menus.ftl b/examples/locale/es-ES/navbar-menus.ftl index af0b1ec3..a87e407d 100644 --- a/examples/locale/es-ES/navbar-menus.ftl +++ b/examples/locale/es-ES/navbar-menus.ftl @@ -1,7 +1,7 @@ menus_item_label = Etiqueta menus_item_link = Enlace -menus_item_blank = Enlace externo -menus_item_disabled = Enlace deshabilitado +menus_item_blank = Externo +menus_item_disabled = Deshabilitado menus_test_title = Desplegable diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs index eb2fecaf..54cb37b7 100644 --- a/examples/navbar-menus.rs +++ b/examples/navbar-menus.rs @@ -1,5 +1,4 @@ use pagetop::prelude::*; -use pagetop_bootsier::theme::*; include_locales!(LOC from "examples/locale"); @@ -16,87 +15,84 @@ impl Extension for SuperMenu { } async fn initialize(&self) { - let navbar_menu = bs::Navbar::brand_left(bs::navbar::Brand::new()) - .with_expand(BreakPoint::LG) - .with_item(bs::navbar::Item::nav( - bs::Nav::new() - .with_item(bs::nav::Item::link(Lc::t("menus_item_link", &LOC), "/")) - .with_item(bs::nav::Item::link_blank( + let navbar_menu = Navbar::brand_left(Brand::new()) + .with_expand(Breakpoint::Lg) + .with_item(navbar::Item::nav( + Nav::new() + .with_item(nav::Item::link(Lc::t("menus_item_link", &LOC), "/")) + .with_item(nav::Item::link_blank( Lc::t("menus_item_blank", &LOC), "https://docs.rs/pagetop", )) - .with_item(bs::nav::Item::dropdown( - bs::Dropdown::new() + .with_item(nav::Item::dropdown( + Dropdown::new() .with_title(Lc::t("menus_test_title", &LOC)) - .with_item(bs::dropdown::Item::header(Lc::t("menus_dev_header", &LOC))) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::header(Lc::t("menus_dev_header", &LOC))) + .with_item(dropdown::Item::link( Lc::t("menus_dev_getting_started", &LOC), "/dev/getting-started", )) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::link( Lc::t("menus_dev_guides", &LOC), "/dev/guides", )) - .with_item(bs::dropdown::Item::link_blank( + .with_item(dropdown::Item::link_blank( Lc::t("menus_dev_forum", &LOC), "https://forum.example.dev", )) - .with_item(bs::dropdown::Item::divider()) - .with_item(bs::dropdown::Item::header(Lc::t("menus_sdk_header", &LOC))) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::divider()) + .with_item(dropdown::Item::header(Lc::t("menus_sdk_header", &LOC))) + .with_item(dropdown::Item::link( Lc::t("menus_sdk_rust", &LOC), "/dev/sdks/rust", )) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::link( Lc::t("menus_sdk_js", &LOC), "/dev/sdks/js", )) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::link( Lc::t("menus_sdk_python", &LOC), "/dev/sdks/python", )) - .with_item(bs::dropdown::Item::divider()) - .with_item(bs::dropdown::Item::header(Lc::t( - "menus_plugin_header", - &LOC, - ))) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::divider()) + .with_item(dropdown::Item::header(Lc::t("menus_plugin_header", &LOC))) + .with_item(dropdown::Item::link( Lc::t("menus_plugin_auth", &LOC), "/dev/sdks/rust/plugins/auth", )) - .with_item(bs::dropdown::Item::link( + .with_item(dropdown::Item::link( Lc::t("menus_plugin_cache", &LOC), "/dev/sdks/rust/plugins/cache", )) - .with_item(bs::dropdown::Item::divider()) - .with_item(bs::dropdown::Item::label(Lc::t("menus_item_label", &LOC))) - .with_item(bs::dropdown::Item::link_disabled( + .with_item(dropdown::Item::divider()) + .with_item(dropdown::Item::label(Lc::t("menus_item_label", &LOC))) + .with_item(dropdown::Item::link_disabled( Lc::t("menus_item_disabled", &LOC), "#", )), )) - .with_item(bs::nav::Item::link_disabled( + .with_item(nav::Item::link_disabled( Lc::t("menus_item_disabled", &LOC), "#", )), )) - .with_item(bs::navbar::Item::nav( - bs::Nav::new() + .with_item(navbar::Item::nav( + Nav::new() // Empuja este menú (y lo que le siga) al extremo final de la barra. .with_prop(FlexItem::push_end()) - .with_item(bs::nav::Item::link( + .with_item(nav::Item::link( Lc::t("menus_item_sign_up", &LOC), "/auth/sign-up", )) - .with_item(bs::nav::Item::link( + .with_item(nav::Item::link( Lc::t("menus_item_login", &LOC), "/auth/login", )), )); InRegion::Global(&CoreRegions::Header).add( - bs::Container::new() - .with_width(bs::container::Width::FluidMax(UnitValue::RelRem(75.0))) + Container::new() + .with_width(container::Width::FluidMax(UnitValue::RelRem(75.0))) .with_child(navbar_menu), ); } diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index 54528768..949913ee 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -146,12 +146,12 @@ impl Theme for Bootsier { theme::BootsierColors::from(intent).as_str() } - fn setup_component(&self, component: &mut dyn Component, cx: &mut Context) { + fn setup_component(&self, component: &mut dyn Component, _cx: &mut Context) { setup_component!(component, { Badge => |c| theme::bs::badge::setup(c), Brand => |c| theme::bs::brand::setup(c), Button => |c| theme::bs::button::setup(c), - Container => |c| theme::bs::container::setup(c, cx), + Container => |c| theme::bs::container::setup(c), Dialog => |c| theme::bs::dialog::setup(c), Dropdown => |c| theme::bs::dropdown::setup(c), Image => |c| theme::bs::image::setup(c), diff --git a/extensions/pagetop-bootsier/src/theme.rs b/extensions/pagetop-bootsier/src/theme.rs index f093ac42..6879c222 100644 --- a/extensions/pagetop-bootsier/src/theme.rs +++ b/extensions/pagetop-bootsier/src/theme.rs @@ -103,8 +103,6 @@ pub use bs::badge::BadgeBootsier; #[doc(hidden)] pub use bs::button::ButtonBootsier; #[doc(hidden)] -pub use bs::container::ContainerBootsier; -#[doc(hidden)] pub use bs::dropdown::DropdownBootsier; #[doc(hidden)] pub use bs::form::input::InputBootsier; diff --git a/extensions/pagetop-bootsier/src/theme/bs.rs b/extensions/pagetop-bootsier/src/theme/bs.rs index 0ce50d05..7bc36b71 100644 --- a/extensions/pagetop-bootsier/src/theme/bs.rs +++ b/extensions/pagetop-bootsier/src/theme/bs.rs @@ -27,8 +27,6 @@ pub use button::{Button, ButtonBootsier}; pub mod container; #[doc(inline)] pub use container::Container; -#[doc(inline)] -pub use container::ContainerBootsier; // Dialog. pub mod dialog; diff --git a/extensions/pagetop-bootsier/src/theme/bs/container.rs b/extensions/pagetop-bootsier/src/theme/bs/container.rs index 2bb08e79..00afd2ff 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/container.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/container.rs @@ -1,108 +1,37 @@ //! Definiciones para crear contenedores de componentes ([`Container`]). +//! +//! El comportamiento del ancho del contenedor se establece con [`Container::with_width()`], usando +//! [`Width`]. Sin él, Bootsier aplica los anchos máximos predefinidos de Bootstrap para cada punto +//! de corte ([`Width::Responsive`]). +//! +//! También se pueden aplicar al componente clases predefinidas para: +//! +//! - Modificar el color de fondo ([`Bg`](crate::theme::class::Bg)). +//! - 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(Breakpoint::Lg)) +//! .with_prop(PropsOp::add_classes(class::Bg::with(BootsierColors::Light))) +//! .with_prop(PropsOp::add_classes(class::Text::with(BootsierColors::Dark))) +//! .with_prop(PropsOp::add_classes(class::Border::with(ScaleSize::One))) +//! .with_prop(PropsOp::add_classes(class::Rounded::new())); +//! ``` use pagetop::prelude::*; -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 establecer el comportamiento del ancho del contenedor usando el método -/// [`with_width()`](Self::with_width). -/// -/// También habilita al componente para aceptar clases predefinidas para: -/// -/// - Modificar el color de fondo ([`Bg`](crate::theme::class::Bg)). -/// - 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(Breakpoint::Lg)) -/// .with_prop(PropsOp::add_classes(class::Bg::with(BootsierColors::Light))) -/// .with_prop(PropsOp::add_classes(class::Text::with(BootsierColors::Dark))) -/// .with_prop(PropsOp::add_classes(class::Border::with(ScaleSize::One))) -/// .with_prop(PropsOp::add_classes(class::Rounded::new())); -/// ``` -#[builder_impl] -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 corte, - /// 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; -} - -#[builder_impl] -impl ContainerBootsier for Container { - fn with_width(mut self, width: Width) -> Self { - self.alter_prop(PropsOp::set_extra(EXTRA_WIDTH, width)); - self - } -} - -// **< 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 corte. - /// Por debajo del menor punto de corte ocupa el 100% del ancho disponible. - #[default] - Default, - /// Aplica los anchos máximos predefinidos a partir del punto de corte indicado. Por debajo de - /// ese punto de corte 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), -} - -impl Width { - const CONTAINER: &str = "container"; - - /// Añade la clase asociada al ancho del contenedor a la cadena de clases. - /// - /// El nombre del punto de corte se resuelve en el tema activo de `cx`. - #[inline] - pub fn push_to(self, cx: &Context, classes: &mut String) { - match self { - Self::Default => { - push_breakpoint_class(cx, Breakpoint::Xs, classes, Self::CONTAINER, "") - } - Self::From(bp) => push_breakpoint_class(cx, bp, classes, Self::CONTAINER, ""), - Self::Fluid | Self::FluidMax(_) => { - push_breakpoint_class(cx, Breakpoint::Xs, classes, Self::CONTAINER, "fluid") - } - } - } - - /// Devuelve la clase asociada al ancho del contenedor. - pub fn to_class(self, cx: &Context) -> String { - let mut class = String::new(); - self.push_to(cx, &mut class); - class - } -} +pub use pagetop::base::component::container::{Container, Kind, Width}; // **< Container SETUP >**************************************************************************** -pub(crate) fn setup(container: &mut Container, cx: &Context) { - let width = container.props().extra_or(EXTRA_WIDTH, Width::default()); - container.alter_prop(PropsOp::prepend_classes(width.to_class(cx))); - if let Width::FluidMax(w) = width - && w.is_measurable() - { - container.alter_prop(PropsOp::add_style("max-width", w.to_string())); +pub(crate) fn setup(container: &mut Container) { + if container.width().is_none() { + container.alter_prop(PropsOp::prepend_classes("container")); } } diff --git a/extensions/pagetop-bootsier/src/theme/bs/layout/template.rs b/extensions/pagetop-bootsier/src/theme/bs/layout/template.rs index 8b80c84b..90ba79c9 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/layout/template.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/layout/template.rs @@ -1,7 +1,7 @@ use pagetop::prelude::*; use crate::config; -use crate::theme::{ContainerBootsier, bs}; +use crate::theme::bs; use crate::{ADMINLTE_VERSION, LOCALES_BOOTSIER}; // Regiones de Bootsier: se renderiza sin el `