From c0a5a8c3ab8313360650197aa94a5d43b822979d Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Fri, 28 Aug 2026 19:46:56 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(bootsier):=20A=C3=B1ade=20componen?= =?UTF-8?q?tes=20esenciales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Añade Dialog como modal de Bootstrap, con el JS de soporte para `htmx:confirm` y el saneado de su posición fija. - Adapta Nav, Navbar y Dropdown al reuso de los tipos movidos al core, y traduce BootsierColors/Intent en Button y Badge, con soporte para Light/Dark vía with_color(). - Reexporta en el tema los componentes de PageTop que faltaban: Block, Breadcrumb, Messages, Pager, Table y form::Number. --- extensions/pagetop-bootsier/README.md | 80 ++- .../assets/_bootsier-custom.scss | 161 +++++- .../assets/_bootsier-variables.scss | 1 + .../assets/bootsier.confirm.js | 62 +++ .../assets/bootsier.dialog.js | 18 + extensions/pagetop-bootsier/build.rs | 12 +- extensions/pagetop-bootsier/src/lib.rs | 56 +- .../src/locale/en-US/components.ftl | 14 +- .../src/locale/en-US/regions.ftl | 12 +- .../src/locale/es-ES/components.ftl | 14 +- .../src/locale/es-ES/regions.ftl | 12 +- extensions/pagetop-bootsier/src/theme.rs | 116 ++++- extensions/pagetop-bootsier/src/theme/bs.rs | 43 +- .../pagetop-bootsier/src/theme/bs/badge.rs | 49 +- .../pagetop-bootsier/src/theme/bs/button.rs | 129 ++++- .../src/theme/bs/container.rs | 4 +- .../pagetop-bootsier/src/theme/bs/dialog.rs | 56 ++ .../pagetop-bootsier/src/theme/bs/dropdown.rs | 240 ++++++++- .../src/theme/bs/dropdown/component.rs | 276 ---------- .../src/theme/bs/dropdown/item.rs | 274 ---------- .../src/theme/bs/dropdown/props.rs | 16 +- .../pagetop-bootsier/src/theme/bs/form.rs | 2 + .../pagetop-bootsier/src/theme/bs/icon.rs | 4 +- .../pagetop-bootsier/src/theme/bs/layout.rs | 4 + .../src/theme/bs/layout/region.rs | 113 +++++ .../src/theme/bs/layout/template.rs | 170 +++++++ .../pagetop-bootsier/src/theme/bs/nav.rs | 193 ++++++- .../src/theme/bs/nav/component.rs | 144 ------ .../pagetop-bootsier/src/theme/bs/nav/item.rs | 279 ---------- .../src/theme/bs/nav/props.rs | 66 --- .../pagetop-bootsier/src/theme/bs/navbar.rs | 11 +- .../src/theme/bs/navbar/component.rs | 478 ++++++++---------- .../src/theme/bs/navbar/item.rs | 110 +--- .../src/theme/bs/navbar/props.rs | 10 +- .../pagetop-bootsier/src/theme/bs/sidebar.rs | 43 ++ .../src/theme/bs/sidebar/item.rs | 104 ++++ .../src/theme/bs/sidebar/section.rs | 57 +++ .../pagetop-bootsier/src/theme/class.rs | 7 +- .../src/theme/class/border.rs | 42 +- .../src/theme/class/button.rs | 221 -------- .../pagetop-bootsier/src/theme/class/color.rs | 110 ++-- .../src/theme/class/layout.rs | 16 +- .../src/theme/class/rounded.rs | 16 +- .../pagetop-bootsier/src/theme/token.rs | 2 +- .../pagetop-bootsier/src/theme/token/color.rs | 31 +- .../pagetop-bootsier/tests/badge_color.rs | 46 ++ .../pagetop-bootsier/tests/intent_color.rs | 29 ++ 47 files changed, 2135 insertions(+), 1818 deletions(-) create mode 100644 extensions/pagetop-bootsier/assets/bootsier.confirm.js create mode 100644 extensions/pagetop-bootsier/assets/bootsier.dialog.js create mode 100644 extensions/pagetop-bootsier/src/theme/bs/dialog.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/dropdown/component.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/dropdown/item.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/layout.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/layout/region.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/layout/template.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/nav/component.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/bs/nav/item.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/sidebar.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/sidebar/item.rs create mode 100644 extensions/pagetop-bootsier/src/theme/bs/sidebar/section.rs delete mode 100644 extensions/pagetop-bootsier/src/theme/class/button.rs create mode 100644 extensions/pagetop-bootsier/tests/badge_color.rs create mode 100644 extensions/pagetop-bootsier/tests/intent_color.rs diff --git a/extensions/pagetop-bootsier/README.md b/extensions/pagetop-bootsier/README.md index 69440ec6..041701f9 100644 --- a/extensions/pagetop-bootsier/README.md +++ b/extensions/pagetop-bootsier/README.md @@ -34,6 +34,7 @@ use pagetop::prelude::*; struct MyApp; +#[async_trait] impl Extension for MyApp { fn dependencies(&self) -> Vec { vec![ @@ -63,15 +64,90 @@ async fn homepage(request: HttpRequest) -> Result { .with_theme(&Bootsier) .with_child( Block::new() - .with_title(L10n::l("sample_title")) + .with_title(Lc::l("sample_title")) .with_child(Html::with(|cx| html! { - p { (L10n::l("sample_content").using(cx)) } + p { (Lc::l("sample_content").using(cx)) } })), ) + .render().await +} +``` + +## Plantillas + +Bootsier ofrece dos plantillas (`BootsierTemplates`): `Standard`, con cabecera, contenido y pie, +que es la plantilla por defecto de cualquier página; y `Admin`, con la shell completa de +AdminLTE 4 (barra superior + barra lateral + área de contenido), que se activa creando la página +con `Page::admin()` en lugar de `Page::new()`. + +```rust,no_run +use pagetop::prelude::*; + +async fn about(request: HttpRequest) -> Result { + Page::new(request) + .with_child(Html::with(|_| html! { + h1 { "Sobre nosotros" } + p { "Texto de presentación." } + })) .render() } ``` +## Barra lateral + +Registra elementos en `BootsierRegion::Sidebar` para poblar la barra lateral de la shell. Los +elementos esperados son `theme::sidebar::Item` y `theme::sidebar::Section`. + +De forma **global** (visibles en todas las páginas de administración): + +```rust,no_run +use pagetop::prelude::*; +use pagetop_bootsier::theme::{BootsierRegion, sidebar}; + +fn register_navigation() { + InRegion::Global(&BootsierRegion::Sidebar) + .add(sidebar::Section::titled(Lc::n("Administración"))) + .add(sidebar::Item::link(Lc::n("Usuarios"), "/users", "people")) + .add(sidebar::Item::link(Lc::n("Roles"), "/roles", "shield-check")); +} +``` + +O de forma **por página**: + +```rust,no_run +use pagetop::prelude::*; +use pagetop_bootsier::theme::{BootsierRegion, sidebar}; + +async fn settings(request: HttpRequest) -> Result { + Page::admin(request) + .with_child_in( + &BootsierRegion::Sidebar, + sidebar::Item::link(Lc::n("Ajustes"), "/settings", "gear"), + ) + .with_child(Html::with(|_| html! { h3 { "Ajustes" } })) + .render() +} +``` + +## Barra de navegación superior + +La barra superior incluye por defecto los controles de pantalla completa y selector +de tema. Para añadir elementos adicionales en el lado derecho (por ejemplo, el +dropdown de usuario de `pagetop-user`), registra componentes en +`BootsierRegion::Navbar`: + +```rust,no_run +use pagetop::prelude::*; +use pagetop_bootsier::theme::BootsierRegion; + +InRegion::Global(&BootsierRegion::Navbar) + .add(Html::with(|_| html! { + li class="nav-item" { + a class="nav-link" href="/logout" { "Cerrar sesión" } + } + })); +``` + ## Créditos Este *crate* integra la biblioteca de estilos [Bootstrap 5.3.8](https://getbootstrap.com/) para diff --git a/extensions/pagetop-bootsier/assets/_bootsier-custom.scss b/extensions/pagetop-bootsier/assets/_bootsier-custom.scss index f4c6d5d0..fb77c8b9 100644 --- a/extensions/pagetop-bootsier/assets/_bootsier-custom.scss +++ b/extensions/pagetop-bootsier/assets/_bootsier-custom.scss @@ -1,4 +1,4 @@ -// Bootsier CSS rules: self-hosted fonts, form components, and regions. +// Bootsier CSS rules: self-hosted fonts, form elements, regions and components. // Self-hosted Source Sans 3 (SIL OFL 1.1), served from /bootsier/fonts. // Required by AdminLTE 4, which declares it as the primary font family in $font-family-sans-serif. @@ -39,6 +39,15 @@ } } +// Button set (button::ButtonSet): spaces its buttons with a gap instead of relying on Bootstrap's +// .btn-group, which merges adjacent buttons into a single joined control. +.button-set { + display: flex; + flex-wrap: wrap; + gap: $spacer * .5; + margin: 1rem 0; +} + // Fieldset with border and floating legend. fieldset { position: relative; @@ -83,3 +92,153 @@ fieldset > legend { padding: 0.75rem 0 3rem; text-align: center; } + +// Messages component. Classes are fixed by pagetop core (`.message`, `.message-info`, +// `.message-warning`, `.message-error`); reusing Bootstrap's own `.alert`/`.alert-*` styles keeps +// the palette in sync with the active Bootstrap theme instead of duplicating it here. +.message { + @extend .alert; +} +.message-info { + @extend .alert-info; +} +.message-warning { + @extend .alert-warning; +} +.message-error { + @extend .alert-danger; +} + +// Sort indicator on sortable table headers (`table::SortLink`), using Bootstrap Icons' +// sort-up/sort-down glyphs instead of the plain triangle from basic.css. +.table-sort::after { + font-family: $bootstrap-icons-font; + content: "\f575"; // sort-down + margin-left: 0.25rem; + font-size: 0.8em; + opacity: 0.35; +} +.table-sort-asc::after { + content: "\f57b"; // sort-up + opacity: 1; +} +.table-sort-desc::after { + content: "\f575"; // sort-down + opacity: 1; +} + +// Pager component. +// `.pagination`, `.page-item`, `.page-link`, `.active` and `.disabled` already come styled by +// Bootstrap, since Pager reuses that same markup (the ellipsis is a disabled `.page-link` span, +// same as any other cell). Only what Pager adds on top needs rules here: the