diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..48fe96bf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,130 @@ +# Guía de contribución a PageTop + +Gracias por tu interés en contribuir a **PageTop** 🎉 + +Este documento describe **cómo participar en el desarrollo del proyecto**, el flujo de trabajo y las +normas que permitan garantizar un historial limpio, trazable y sostenible a largo plazo. + +Por favor, léelo completo antes de abrir una *issue* o una *pull request*. + + +## 1. Repositorios + +PageTop mantiene **un único repositorio oficial**: + + * **Repositorio oficial:** https://git.cillero.es/manuelcillero/pagetop + * **Repositorio espejo:** https://github.com/manuelcillero/pagetop + +> ⚠️ **Importante** +> Aunque GitHub permite abrir *issues* y *pull requests*, **la integración del código se realiza +> únicamente en el repositorio oficial**. GitHub actúa como repositorio espejo que se sincroniza +> automáticamente para reflejar el mismo estado. + + +## 2. Issues (incidencias, propuestas, preguntas) + +Antes de abrir una *issue* **en GitHub**: + + * comprueba que no exista ya una similar, + * describe claramente el problema o propuesta, + * incluye pasos de reproducción si se trata de un *bug*, + * indica versión, entorno y contexto cuando sea relevante. + +Las *issues* se usan para: + + * informar de errores, + * propuestas de mejora, + * discusión técnica previa a cambios relevantes. + + +## 3. Pull Requests (PRs) + +### 3.1 Dónde abrirlas + +Las *pull requests* se abren **en GitHub**, normalmente contra la rama `main`. GitHub es el punto de +entrada recomendado para contribuciones externas. + +### 3.2 Reglas generales para PRs + + * Cada PR debe abordar **un único objetivo claro**. + * Mantén el alcance lo más acotado posible. + * Incluye descripción clara del cambio. + * Si el PR corrige una *issue*, enlázala explícitamente. + * Asegúrate de que el código compila y pasa las pruebas. + +### 3.3 Revisión y aceptación + +Todas las PRs son **revisadas manualmente** y pueden recibir comentarios o solicitudes de cambios. + +Las PRs aceptadas se integran en el repositorio oficial, nunca directamente en GitHub, preservando +siempre la **autoría original** del contribuidor. + + +### 3.4. Cierre de Pull Requests y sincronización + +Una vez que el cambio ha sido integrado en el repositorio oficial: + + * La PR en GitHub se **cierra manualmente**. + * Se añade un **mensaje estándar de cierre** indicando que el cambio ha sido integrado. + * El repositorio de GitHub **se sincroniza automáticamente** como espejo. + + +## 4. Estilo de código y calidad + + * Sigue el estilo existente del proyecto. + * Mantén los comentarios claros y precisos. + * La documentación es parte del código: actualízala cuando sea necesario. + * Cambios públicos o estructurales deben ir acompañados de documentación. + + +## 5. Commits + +PageTop usa la especificación **gitmoji** para los mensajes de *commit*. El formato recomendado es: + + ```text + [(ámbito opcional):] + «LÍNEA EN BLANCO» + Cuerpo opcional + «LÍNEA EN BLANCO» + Nota(s) al pie opcional(es) para referencias, incidencias o cambios incompatibles + ``` + +Ejemplos (no más de 50 caracteres en la primera línea, y no más de 80 en el resto): + + * `📝 Actualiza la guía de contribución` + * `♻️ (locale): Refactoriza sistema de localización` + * Un mensaje completo: + ``` + 🎨 (bootsier): Mejora la asignación de clases + + - Simplifica la generación de clases CSS para componentes Bootstrap. + - Elimina duplicidades en enums de estilos y centraliza la lógica de composición + para reducir errores y facilitar mantenimiento. + - Alinea los nombres de variantes con la documentación pública. + + Refs: PR #123 + ``` + +El emoji puede usarse en formato Unicode o como *shortcode*, por ejemplo `:sparkles:` en vez de ✨. + +Consulta la especificación oficial en https://gitmoji.dev/specification + +Durante la integración, los *commits* pueden ajustarse para adaptarse al historial del proyecto. + +Un *commit* debe representar una unidad lógica de cambio. Usa mensajes claros y descriptivos. + + +## 6. Comunicación y respeto + +PageTop sigue un enfoque profesional y colaborativo: + + * Sé respetuoso en revisiones y discusiones. + * Acepta sugerencias técnicas como parte del proceso. + * Recuerda que todas las contribuciones son revisadas con el objetivo de mejorar el proyecto. + +Si tienes dudas sobre el proceso, abre una *issue* de tipo pregunta para tratar la cuestión en +comunidad. + +--- + +Gracias por contribuir a **PageTop** 🚀 Cada aportación contribuye a mejorar el proyecto. diff --git a/Cargo.lock b/Cargo.lock index 2f9fa42e..16b0fd66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,6 +982,17 @@ dependencies = [ "wasi 0.14.7+wasi-0.2.4", ] +[[package]] +name = "getter-methods" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43d815f896a3c730f0d76b8348a1700dc8d8fd6c377e4590d531bdd646574d8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ghash" version = "0.5.1" @@ -1073,9 +1084,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hkdf" @@ -1279,19 +1290,22 @@ checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inout" @@ -1554,19 +1568,19 @@ dependencies = [ "actix-web", "chrono", "colored", - "concat-string", "config", "figlet-rs", "fluent-templates", - "indoc", + "getter-methods", + "indexmap", "itoa", "pagetop-aliner", "pagetop-bootsier", "pagetop-build", "pagetop-macros", + "pagetop-minimal", "pagetop-statics", "parking_lot", - "pastey", "serde", "serde_json", "substring", @@ -1614,6 +1628,15 @@ dependencies = [ "syn", ] +[[package]] +name = "pagetop-minimal" +version = "0.0.10" +dependencies = [ + "concat-string", + "indoc", + "pastey", +] + [[package]] name = "pagetop-statics" version = "0.1.2" @@ -1651,9 +1674,9 @@ dependencies = [ [[package]] name = "pastey" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" +checksum = "57d6c094ee800037dff99e02cab0eaf3142826586742a270ab3d7a62656bd27a" [[package]] name = "path-matchers" diff --git a/Cargo.toml b/Cargo.toml index b1bae0a6..a801b786 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,12 @@ authors.workspace = true [dependencies] chrono = "0.4" colored = "3.0" -concat-string = "1.0" config = { version = "0.15", default-features = false, features = ["toml"] } figlet-rs = "0.1" -indoc = "2.0" +getter-methods = "2.0" itoa = "1.0" +indexmap = "2.12" parking_lot = "0.12" -paste = { package = "pastey", version = "0.1" } substring = "1.4" terminal_size = "0.4" @@ -42,6 +41,7 @@ actix-web-files = { package = "actix-files", version = "0.6" } serde.workspace = true pagetop-macros.workspace = true +pagetop-minimal.workspace = true pagetop-statics.workspace = true [features] @@ -64,6 +64,7 @@ members = [ # Helpers "helpers/pagetop-build", "helpers/pagetop-macros", + "helpers/pagetop-minimal", "helpers/pagetop-statics", # Extensions "extensions/pagetop-aliner", @@ -82,6 +83,7 @@ serde = { version = "1.0", features = ["derive"] } # Helpers pagetop-build = { version = "0.3", path = "helpers/pagetop-build" } pagetop-macros = { version = "0.2", path = "helpers/pagetop-macros" } +pagetop-minimal = { version = "0.0", path = "helpers/pagetop-minimal" } pagetop-statics = { version = "0.1", path = "helpers/pagetop-statics" } # Extensions pagetop-aliner = { version = "0.0", path = "extensions/pagetop-aliner" } diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000..25559841 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,156 @@ +# MAINTAINERS.md + +## Guía para mantenedores de PageTop + +Este documento describe **el flujo técnico interno de revisión e integración de contribuciones** en +**PageTop**. + +Está dirigido a **mantenedores del proyecto** y **no forma parte de la guía de contribución para +usuarios externos**. Su objetivo es servir como **referencia operativa**, garantizando coherencia, +trazabilidad y preservación de la autoría en un entorno con repositorios espejo. + + +## 1. Repositorios y principios + +PageTop mantiene **un único repositorio oficial**: + + * **Repositorio oficial:** https://git.cillero.es/manuelcillero/pagetop + * **Repositorio espejo:** https://github.com/manuelcillero/pagetop + +### Principios clave + + * El repositorio oficial **es la única fuente de verdad** del historial. + * **Nunca se realizan *merges* en GitHub**. + * Toda integración definitiva se realiza en el repositorio oficial. + * La autoría original debe preservarse siempre. + + +## 2. Configuración local recomendada + +El remoto `github` debe configurarse únicamente para operaciones de lectura (*fetch*), con la URL de +*push* deshabilitada para evitar publicaciones accidentales en el repositorio espejo. + +Estado esperado de `git remote -v`: + +```text +origin git@git.cillero.es:manuelcillero/pagetop.git (fetch) +origin git@git.cillero.es:manuelcillero/pagetop.git (push) +github git@github.com:manuelcillero/pagetop.git (fetch) +github DISABLED (push) +``` + +Convenciones usadas en este documento: + + * `origin` -> Repositorio oficial + * `github` -> Repositorio espejo + + +## 3. Recepción y revisión de Pull Requests + +Las PRs externas llegan por GitHub, normalmente contra la rama `main`. + +Se asume que el repositorio local está configurado para recuperar PRs de GitHub como referencias +remotas (`refs/pull//head`): + +```bash +git fetch github --prune +git checkout -b pr-123 github/pr/123 +``` + +Antes de integrar: + + * Revisar el código manualmente. + * Verificar formato, análisis y pruebas: + + ```bash + cargo fmt + cargo clippy + cargo test + ``` + + * Comprobar impacto en documentación. + * Evaluar coherencia con la arquitectura y el estilo del proyecto. + +Los cambios adicionales se solicitan o se aplican explicando claramente el motivo. + + +## 4. Estrategia de integración + +La integración **se realiza siempre en el repositorio oficial** (`origin`). + +### 4.1 Estrategia por defecto: *rebase* + *fast-forward* + +Esta es la **estrategia estándar y recomendada** en PageTop. Ventajas: + + * conserva los commits originales, + * preserva la autoría real de cada cambio, + * mantiene un historial lineal y trazable, + * facilita auditoría y depuración. + +Procedimiento típico: + +```bash +git checkout pr-123 +git rebase main + +# Resolver conflictos si los hay + +git checkout main +git merge --ff-only pr-123 +``` + +Si `merge --ff-only` falla, **no se debe continuar**, indica divergencias que deben resolverse antes +de integrar la PR. + +### 4.2 Estrategia excepcional: *Squash* + +Sólo debe usarse cuando esté justificado: + + * la PR contiene múltiples commits de prueba o ruido, + * el historial aportado no es significativo, + * el cambio es pequeño y autocontenido. + +En este caso, se debe **preservar explícitamente la autoría**: + +```bash +git checkout main +git merge --squash pr-123 +git commit --author="Nombre Apellido " +``` + + +### 4.3. Publicación en el repositorio oficial + +```bash +git push origin main +``` + +Este *push* representa la **integración definitiva** del cambio en la rama `main`. + + +## 5. Cierre de la PR y sincronización + +Tras integrar el cambio en el repositorio oficial, se cierra manualmente la PR en GitHub con un +mensaje estándar: + +```text +Gracias por tu contribución. + +Este cambio ha sido integrado en el repositorio oficial en `main` (``). +GitHub actúa como repositorio espejo sincronizado. +``` + + +## 6. Principios de mantenimiento + + * Priorizar **claridad y trazabilidad** frente a rapidez. + * Mantener un historial legible y significativo. + * Documentar cambios estructurales o públicos. + * Tratar las contribuciones externas con respeto y transparencia. + +--- + +Este documento puede evolucionar con el proyecto. + +No se trata de imponer rigidez, sino de **capturar el conocimiento operativo real** de PageTop como +guía práctica para el mantenimiento. diff --git a/README.md b/README.md index 9a12c845..4c421edd 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ según las necesidades de cada proyecto, incluyendo: componentes sin comprometer su funcionalidad. -# ⚡️ Guía rápida +## ⚡️ Guía rápida La aplicación más sencilla de PageTop se ve así: @@ -74,7 +74,7 @@ Este programa implementa una extensión llamada `HelloWorld` que sirve una pági (`/`) mostrando el texto "Hello world!" dentro de un elemento HTML `

`. -# 📂 Repositorio +## 📂 Proyecto El código se organiza en un *workspace* donde actualmente se incluyen los siguientes subproyectos: @@ -82,21 +82,25 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui fuente de la librería principal. Reúne algunos de los *crates* más estables y populares del ecosistema Rust para proporcionar APIs y recursos para la creación avanzada de soluciones web. -## Auxiliares - - * **[pagetop-statics](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-statics)**, - es la librería que permite incluir archivos estáticos en el ejecutable de las aplicaciones - PageTop para servirlos de forma eficiente, con detección de cambios que optimizan el tiempo de - compilación. +### Auxiliares * **[pagetop-build](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-build)**, prepara los archivos estáticos o archivos SCSS compilados para incluirlos en el binario de las aplicaciones PageTop durante la compilación de los ejecutables. * **[pagetop-macros](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-macros)**, - proporciona una colección de macros que mejoran la experiencia de desarrollo con PageTop. + proporciona una colección de macros procedurales que mejoran la experiencia de desarrollo con + PageTop. -## Extensiones + * **[pagetop-minimal](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-minimal)**, + ofrece macros declarativas esenciales para optimizar tareas comunes como la composición de + texto, la concatenación de cadenas y el manejo de colecciones clave-valor. + + * **[pagetop-statics](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-statics)**, + permite incluir archivos estáticos en el ejecutable de las aplicaciones PageTop para servirlos + de forma eficiente, con detección de cambios que optimizan el tiempo de compilación. + +### Extensiones * **[pagetop-aliner](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner)**, es un tema para demos y pruebas que muestra esquemáticamente la composición de las páginas HTML. @@ -106,7 +110,7 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui componentes flexibles. -# 🧪 Pruebas +## 🧪 Pruebas Para simplificar el flujo de trabajo, el repositorio incluye varios **alias de Cargo** declarados en `.cargo/config.toml`. Basta con ejecutarlos desde la raíz del proyecto: @@ -123,14 +127,14 @@ Para simplificar el flujo de trabajo, el repositorio incluye varios **alias de C > Si quieres **activar** las trazas del registro de eventos entonces usa simplemente `cargo test`. -# 🚧 Advertencia +## 🚧 Advertencia **PageTop** es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos hasta que se libere la versión **1.0.0**. -# 📜 Licencia +## 📜 Licencia El código está disponible bajo una doble licencia: @@ -144,7 +148,28 @@ Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el es el ecosistema Rust. -# ✨ Contribuir +## ✨ Contribuir + +PageTop mantiene **un único repositorio oficial**: + + * **Repositorio oficial:** https://git.cillero.es/manuelcillero/pagetop + * **Repositorio espejo:** https://github.com/manuelcillero/pagetop + +El repositorio de GitHub actúa como espejo y punto de entrada para: + + * dar mayor visibilidad al proyecto, + * facilitar la participación de la comunidad, + * centralizar *issues* y *pull requests* externas. + +Aunque GitHub permite abrir *pull requests*, **la integración del código se realiza únicamente en el +repositorio oficial**. El repositorio de GitHub se sincroniza posteriormente para reflejar el mismo +estado. + +En todos los casos, se respeta la **autoría original** de las contribuciones integradas, tanto en el +historial como en la documentación asociada al cambio. + +Para conocer el proceso completo de participación, revisión e integración de cambios, consulta el +archivo [`CONTRIBUTING.md`](CONTRIBUTING.md). Cualquier contribución para añadir al proyecto se considerará automáticamente bajo la doble licencia indicada arriba (MIT o Apache v2.0), sin términos o condiciones adicionales, tal y como permite la diff --git a/examples/hello-name.rs b/examples/hello-name.rs index c6a82aaf..b6f9c113 100644 --- a/examples/hello-name.rs +++ b/examples/hello-name.rs @@ -14,7 +14,11 @@ async fn hello_name( ) -> ResultPage { let name = path.into_inner(); Page::new(request) - .add_child(Html::with(move |_| html! { h1 { "Hello " (name) "!" } })) + .add_child(Html::with(move |_| { + html! { + h1 style="text-align: center;" { "Hello " (name) "!" } + } + })) .render() } diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 64817466..74727ac2 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -10,7 +10,11 @@ impl Extension for HelloWorld { async fn hello_world(request: HttpRequest) -> ResultPage { Page::new(request) - .add_child(Html::with(|_| html! { h1 { "Hello World!" } })) + .add_child(Html::with(|_| { + html! { + h1 style="text-align: center;" { "Hello World!" } + } + })) .render() } diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs index 341d394a..a2ae76d0 100644 --- a/examples/navbar-menus.rs +++ b/examples/navbar-menus.rs @@ -10,22 +10,16 @@ impl Extension for SuperMenu { } fn initialize(&self) { - let home_path = |cx: &Context| match cx.langid().language.as_str() { - "en" => "/en", - _ => "/", - }; - - let navbar_menu = Navbar::brand_left(navbar::Brand::new().with_path(Some(home_path))) + let navbar_menu = Navbar::brand_left(navbar::Brand::new()) .with_expand(BreakPoint::LG) .add_item(navbar::Item::nav( Nav::new() - .add_item(nav::Item::link( - L10n::l("sample_menus_item_link"), - home_path, - )) + .add_item(nav::Item::link(L10n::l("sample_menus_item_link"), |cx| { + cx.route("/") + })) .add_item(nav::Item::link_blank( L10n::l("sample_menus_item_blank"), - |_| "https://docs.rs/pagetop", + |_| "https://docs.rs/pagetop".into(), )) .add_item(nav::Item::dropdown( Dropdown::new() @@ -33,28 +27,28 @@ impl Extension for SuperMenu { .add_item(dropdown::Item::header(L10n::l("sample_menus_dev_header"))) .add_item(dropdown::Item::link( L10n::l("sample_menus_dev_getting_started"), - |_| "/dev/getting-started", + |cx| cx.route("/dev/getting-started"), )) .add_item(dropdown::Item::link( L10n::l("sample_menus_dev_guides"), - |_| "/dev/guides", + |cx| cx.route("/dev/guides"), )) .add_item(dropdown::Item::link_blank( L10n::l("sample_menus_dev_forum"), - |_| "https://forum.example.dev", + |_| "https://forum.example.dev".into(), )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::header(L10n::l("sample_menus_sdk_header"))) .add_item(dropdown::Item::link( L10n::l("sample_menus_sdk_rust"), - |_| "/dev/sdks/rust", + |cx| cx.route("/dev/sdks/rust"), )) - .add_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |_| { - "/dev/sdks/js" + .add_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |cx| { + cx.route("/dev/sdks/js") })) .add_item(dropdown::Item::link( L10n::l("sample_menus_sdk_python"), - |_| "/dev/sdks/python", + |cx| cx.route("/dev/sdks/python"), )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::header(L10n::l( @@ -62,22 +56,22 @@ impl Extension for SuperMenu { ))) .add_item(dropdown::Item::link( L10n::l("sample_menus_plugin_auth"), - |_| "/dev/sdks/rust/plugins/auth", + |cx| cx.route("/dev/sdks/rust/plugins/auth"), )) .add_item(dropdown::Item::link( L10n::l("sample_menus_plugin_cache"), - |_| "/dev/sdks/rust/plugins/cache", + |cx| cx.route("/dev/sdks/rust/plugins/cache"), )) .add_item(dropdown::Item::divider()) .add_item(dropdown::Item::label(L10n::l("sample_menus_item_label"))) .add_item(dropdown::Item::link_disabled( L10n::l("sample_menus_item_disabled"), - |_| "#", + |cx| cx.route("#"), )), )) .add_item(nav::Item::link_disabled( L10n::l("sample_menus_item_disabled"), - |_| "#", + |cx| cx.route("#"), )), )) .add_item(navbar::Item::nav( @@ -88,14 +82,14 @@ impl Extension for SuperMenu { ) .add_item(nav::Item::link( L10n::l("sample_menus_item_sign_up"), - |_| "/auth/sign-up", + |cx| cx.route("/auth/sign-up"), )) - .add_item(nav::Item::link(L10n::l("sample_menus_item_login"), |_| { - "/auth/login" + .add_item(nav::Item::link(L10n::l("sample_menus_item_login"), |cx| { + cx.route("/auth/login") })), )); - InRegion::Named("header").add(Child::with( + InRegion::Global(&DefaultRegion::Header).add(Child::with( Container::new() .with_width(container::Width::FluidMax(UnitValue::RelRem(75.0))) .add_child(navbar_menu), diff --git a/extensions/pagetop-aliner/README.md b/extensions/pagetop-aliner/README.md index f4670aae..f3849122 100644 --- a/extensions/pagetop-aliner/README.md +++ b/extensions/pagetop-aliner/README.md @@ -12,14 +12,14 @@
-## Sobre PageTop +## 🧭 Sobre PageTop [PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la web clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles y configurables, basadas en HTML, CSS y JavaScript. -# ⚡️ Guía rápida +## ⚡️ Guía rápida Igual que con otras extensiones, **añade la dependencia** a tu `Cargo.toml`: @@ -80,14 +80,14 @@ async fn homepage(request: HttpRequest) -> ResultPage { ``` -# 🚧 Advertencia +## 🚧 Advertencia **PageTop** es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos hasta que se libere la versión **1.0.0**. -# 📜 Licencia +## 📜 Licencia El código está disponible bajo una doble licencia: diff --git a/extensions/pagetop-aliner/src/lib.rs b/extensions/pagetop-aliner/src/lib.rs index 4ae4121e..04b5ad1a 100644 --- a/extensions/pagetop-aliner/src/lib.rs +++ b/extensions/pagetop-aliner/src/lib.rs @@ -104,12 +104,25 @@ impl Extension for Aliner { } impl Theme for Aliner { - fn after_render_page_body(&self, page: &mut Page) { - page.alter_param("include_basic_assets", true) - .alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/aliner/css/styles.css") - .with_version(env!("CARGO_PKG_VERSION")) - .with_weight(-90), - )); + fn before_render_page_body(&self, page: &mut Page) { + page.alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/normalize.css") + .with_version("8.0.1") + .with_weight(-99), + )) + .alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/basic.css") + .with_version(PAGETOP_VERSION) + .with_weight(-99), + )) + .alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/aliner/css/styles.css") + .with_version(env!("CARGO_PKG_VERSION")) + .with_weight(-99), + )) + .alter_child_in( + &DefaultRegion::Footer, + ChildOp::AddIfEmpty(Child::with(PoweredBy::new())), + ); } } diff --git a/extensions/pagetop-bootsier/README.md b/extensions/pagetop-bootsier/README.md index d6e1666a..e7a3ea79 100644 --- a/extensions/pagetop-bootsier/README.md +++ b/extensions/pagetop-bootsier/README.md @@ -12,14 +12,14 @@
-## Sobre PageTop +## 🧭 Sobre PageTop [PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la web clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles y configurables, basadas en HTML, CSS y JavaScript. -# ⚡️ Guía rápida +## ⚡️ Guía rápida Igual que con otras extensiones, **añade la dependencia** a tu `Cargo.toml`: @@ -80,14 +80,14 @@ async fn homepage(request: HttpRequest) -> ResultPage { ``` -# 🚧 Advertencia +## 🚧 Advertencia **PageTop** es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos hasta que se libere la versión **1.0.0**. -# 📜 Licencia +## 📜 Licencia El código está disponible bajo una doble licencia: diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs index 0bf94f47..5c88959a 100644 --- a/extensions/pagetop-bootsier/src/lib.rs +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -102,6 +102,34 @@ pub mod prelude { pub use crate::theme::*; } +/// Plantillas que Bootsier añade. +#[derive(AutoDefault)] +pub enum BootsierTemplate { + /// Plantilla predeterminada de Bootsier. + #[default] + Standard, +} + +impl Template for BootsierTemplate { + fn render(&'static self, cx: &mut Context) -> Markup { + match self { + Self::Standard => theme::Container::new() + .with_classes(ClassesOp::Add, "container-wrapper") + .with_width(theme::container::Width::FluidMax( + config::SETTINGS.bootsier.max_width, + )) + .add_child(Html::with(|cx| { + html! { + (DefaultRegion::Header.render(cx)) + (DefaultRegion::Content.render(cx)) + (DefaultRegion::Footer.render(cx)) + } + })), + } + .render(cx) + } +} + /// Implementa el tema. pub struct Bootsier; @@ -117,7 +145,12 @@ impl Extension for Bootsier { } impl Theme for Bootsier { - fn after_render_page_body(&self, page: &mut Page) { + #[inline] + fn default_template(&self) -> TemplateRef { + &BootsierTemplate::Standard + } + + fn before_render_page_body(&self, page: &mut Page) { page.alter_assets(ContextOp::AddStyleSheet( StyleSheet::from("/bootsier/bs/bootstrap.min.css") .with_version(BOOTSTRAP_VERSION) @@ -127,6 +160,10 @@ impl Theme for Bootsier { JavaScript::defer("/bootsier/js/bootstrap.bundle.min.js") .with_version(BOOTSTRAP_VERSION) .with_weight(-90), - )); + )) + .alter_child_in( + &DefaultRegion::Footer, + ChildOp::AddIfEmpty(Child::with(PoweredBy::new())), + ); } } diff --git a/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl b/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl index 0e8969cd..2454c84e 100644 --- a/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl +++ b/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl @@ -1,5 +1,5 @@ -e404-description = Oops! Page Not Found -e404-message = The page you are looking for may have been removed, had its name changed, or is temporarily unavailable. -e500-description = Oops! Unexpected Error -e500-message = We're having an issue. Please report this error to an administrator. -back-homepage = Back to homepage +e404_description = Oops! Page Not Found +e404_message = The page you are looking for may have been removed, had its name changed, or is temporarily unavailable. +e500_description = Oops! Unexpected Error +e500_message = We're having an issue. Please report this error to an administrator. +back_homepage = Back to homepage diff --git a/extensions/pagetop-bootsier/src/locale/en-US/components.ftl b/extensions/pagetop-bootsier/src/locale/en-US/components.ftl index e3b0d6e6..c73478bf 100644 --- a/extensions/pagetop-bootsier/src/locale/en-US/components.ftl +++ b/extensions/pagetop-bootsier/src/locale/en-US/components.ftl @@ -1,8 +1,11 @@ # Dropdown dropdown_toggle = Toggle Dropdown -# Offcanvas -offcanvas_close = Close +# form::Input +input_required = This field is required # Navbar toggle = Toggle navigation + +# Offcanvas +offcanvas_close = Close diff --git a/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl b/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl index f3b76e22..af830a4e 100644 --- a/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl +++ b/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl @@ -1,9 +1,9 @@ -header = Header -nav_branding = Navigation branding region -nav_main = Main navigation region -nav_additional = Additional navigation region (eg search form, social icons, etc) -breadcrumb = Breadcrumb -content = Main content -sidebar_first = Sidebar first -sidebar_second = Sidebar second -footer = Footer +region_header = Header +region_nav_branding = Navigation branding region +region_nav_main = Main navigation region +region_nav_additional = Additional navigation region (eg search form, social icons, etc) +region_breadcrumb = Breadcrumb +region_content = Main content +region_sidebar_first = Sidebar first +region_sidebar_second = Sidebar second +region_footer = Footer diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl index 998b54f2..bd97c2ed 100644 --- a/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl +++ b/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl @@ -1,5 +1,5 @@ -e404-description = ¡Vaya! Página No Encontrada -e404-message = La página que está buscando puede haber sido eliminada, cambiada de nombre o no está disponible temporalmente. -e500-description = ¡Vaya! Error Inesperado -e500-message = Está ocurriendo una incidencia. Por favor, informe de este error a un administrador. -back-homepage = Volver al inicio +e404_description = ¡Vaya! Página No Encontrada +e404_message = La página que está buscando puede haber sido eliminada, cambiada de nombre o no está disponible temporalmente. +e500_description = ¡Vaya! Error Inesperado +e500_message = Está ocurriendo una incidencia. Por favor, informe de este error a un administrador. +back_homepage = Volver al inicio diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl index ab7ff687..21b52c91 100644 --- a/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl +++ b/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl @@ -1,8 +1,11 @@ # Dropdown dropdown_toggle = Mostrar/ocultar menú -# Offcanvas -offcanvas_close = Cerrar +# form::Input +input_required = Este campo es obligatorio # Navbar toggle = Mostrar/ocultar navegación + +# Offcanvas +offcanvas_close = Cerrar diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl index 674fc4b1..e4665add 100644 --- a/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl +++ b/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl @@ -1,9 +1,9 @@ -header = Cabecera -nav_branding = Navegación y marca -nav_main = Navegación principal -nav_additional = Navegación adicional (p.e. formulario de búsqueda, iconos sociales, etc.) -breadcrumb = Ruta de posicionamiento -content = Contenido principal -sidebar_first = Barra lateral primera -sidebar_second = Barra lateral segunda -footer = Pie +region_header = Cabecera +region_nav_branding = Navegación y marca +region_nav_main = Navegación principal +region_nav_additional = Navegación adicional (p.e. formulario de búsqueda, iconos sociales, etc.) +region_breadcrumb = Ruta de posicionamiento +region_content = Contenido principal +region_sidebar_first = Barra lateral primera +region_sidebar_second = Barra lateral segunda +region_footer = Pie diff --git a/extensions/pagetop-bootsier/src/theme.rs b/extensions/pagetop-bootsier/src/theme.rs index 2c6b5757..7464caf5 100644 --- a/extensions/pagetop-bootsier/src/theme.rs +++ b/extensions/pagetop-bootsier/src/theme.rs @@ -19,6 +19,11 @@ pub mod dropdown; #[doc(inline)] pub use dropdown::Dropdown; +// Form. +pub mod form; +#[doc(inline)] +pub use form::Form; + // Image. pub mod image; #[doc(inline)] diff --git a/extensions/pagetop-bootsier/src/theme/aux/border.rs b/extensions/pagetop-bootsier/src/theme/aux/border.rs index 43882767..44d24b0e 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/border.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/border.rs @@ -22,7 +22,7 @@ impl BorderColor { const BORDER: &str = "border"; const BORDER_PREFIX: &str = "border-"; - // Devuelve el sufijo de la clase `border-*`, o `None` si no define ninguna clase. + /// Devuelve el sufijo de la clase `border-*`, o `None` si no define ninguna clase. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -35,7 +35,7 @@ impl BorderColor { } } - // Añade la clase `border-*` a la cadena de clases. + /// Añade la clase `border-*` a la cadena de clases. #[inline] pub(crate) fn push_class(self, classes: &mut String) { if let Some(suffix) = self.suffix() { @@ -64,7 +64,6 @@ impl BorderColor { /// assert_eq!(BorderColor::Black.to_class(), "border-black"); /// assert_eq!(BorderColor::Default.to_class(), ""); /// ``` - #[inline] pub fn to_class(self) -> String { if let Some(suffix) = self.suffix() { let base_len = match self { diff --git a/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs b/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs index 4d9a7626..992f8525 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs @@ -19,7 +19,7 @@ pub enum BreakPoint { } impl BreakPoint { - // Devuelve la identificación del punto de ruptura. + /// Devuelve la identificación del punto de ruptura. #[rustfmt::skip] #[inline] pub(crate) const fn as_str(self) -> &'static str { @@ -33,11 +33,11 @@ impl BreakPoint { } } - // Añade el punto de ruptura con un prefijo y un sufijo (opcional) separados por un guion `-` a - // la cadena de clases. - // - // - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). - // - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. + /// Añade el punto de ruptura con un prefijo y un sufijo (opcional) separados por un guion `-` a + /// la cadena de clases. + /// + /// - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). + /// - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. #[inline] pub(crate) fn push_class(self, classes: &mut String, prefix: &str, suffix: &str) { if prefix.is_empty() { @@ -60,30 +60,30 @@ impl BreakPoint { } } - // Devuelve la clase para el punto de ruptura, con un prefijo y un sufijo opcional, separados - // por un guion `-`. - // - // - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). - // - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. - // - Si `prefix` está vacío devuelve `""`. - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // let bp = BreakPoint::MD; - // assert_eq!(bp.class_with("col", ""), "col-md"); - // assert_eq!(bp.class_with("col", "6"), "col-md-6"); - // - // let bp = BreakPoint::None; - // assert_eq!(bp.class_with("offcanvas", ""), "offcanvas"); - // assert_eq!(bp.class_with("col", "12"), "col-12"); - // - // let bp = BreakPoint::LG; - // assert_eq!(bp.class_with("", "3"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str, suffix: &str) -> String { + /// Devuelve la clase para el punto de ruptura, con un prefijo y un sufijo opcional, separados + /// por un guion `-`. + /// + /// - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). + /// - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. + /// - Si `prefix` está vacío devuelve `""`. + /// + /// # Ejemplos + /// + /// ```rust + /// # use pagetop_bootsier::prelude::*; + /// let bp = BreakPoint::MD; + /// assert_eq!(bp.class_with("col", ""), "col-md"); + /// assert_eq!(bp.class_with("col", "6"), "col-md-6"); + /// + /// let bp = BreakPoint::None; + /// assert_eq!(bp.class_with("offcanvas", ""), "offcanvas"); + /// assert_eq!(bp.class_with("col", "12"), "col-12"); + /// + /// let bp = BreakPoint::LG; + /// assert_eq!(bp.class_with("", "3"), ""); + /// ``` + #[doc(hidden)] + pub fn class_with(self, prefix: &str, suffix: &str) -> String { if prefix.is_empty() { return String::new(); } diff --git a/extensions/pagetop-bootsier/src/theme/aux/button.rs b/extensions/pagetop-bootsier/src/theme/aux/button.rs index 0d1df87d..b32bd17d 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/button.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/button.rs @@ -23,7 +23,7 @@ impl ButtonColor { const BTN_OUTLINE_PREFIX: &str = "btn-outline-"; const BTN_LINK: &str = "btn-link"; - // Añade la clase `btn-*` a la cadena de clases. + /// Añade la clase `btn-*` a la cadena de clases. #[inline] pub(crate) fn push_class(self, classes: &mut String) { if let Self::Default = self { @@ -65,7 +65,6 @@ impl ButtonColor { /// assert_eq!(ButtonColor::Link.to_class(), "btn-link"); /// assert_eq!(ButtonColor::Default.to_class(), ""); /// ``` - #[inline] pub fn to_class(self) -> String { match self { Self::Default => String::new(), @@ -106,7 +105,7 @@ impl ButtonSize { const BTN_SM: &str = "btn-sm"; const BTN_LG: &str = "btn-lg"; - // Añade la clase de tamaño `btn-sm` o `btn-lg` a la cadena de clases. + /// Añade la clase de tamaño `btn-sm` o `btn-lg` a la cadena de clases. #[inline] pub(crate) fn push_class(self, classes: &mut String) { if let Self::Default = self { @@ -132,7 +131,6 @@ impl ButtonSize { /// assert_eq!(ButtonSize::Large.to_class(), "btn-lg"); /// assert_eq!(ButtonSize::Default.to_class(), ""); /// ``` - #[inline] pub fn to_class(self) -> String { match self { Self::Default => String::new(), diff --git a/extensions/pagetop-bootsier/src/theme/aux/color.rs b/extensions/pagetop-bootsier/src/theme/aux/color.rs index 480ff3d8..f49c36b6 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/color.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/color.rs @@ -23,7 +23,7 @@ pub enum Color { } impl Color { - // Devuelve el nombre del color. + /// Devuelve el nombre del color. #[rustfmt::skip] #[inline] pub(crate) const fn as_str(self) -> &'static str { @@ -94,7 +94,7 @@ impl Opacity { const OPACITY: &str = "opacity"; const OPACITY_PREFIX: &str = "-opacity"; - // Devuelve el sufijo para `*opacity-*`, o `None` si no define ninguna clase. + /// Devuelve el sufijo para `*opacity-*`, o `None` si no define ninguna clase. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -109,8 +109,8 @@ impl Opacity { } } - // Añade la opacidad a la cadena de clases usando el prefijo dado (`bg`, `border`, `text`, o - // vacío para `opacity-*`). + /// Añade la opacidad a la cadena de clases usando el prefijo dado (`bg`, `border`, `text`, o + /// vacío para `opacity-*`). #[inline] pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { if let Some(suffix) = self.suffix() { @@ -127,20 +127,20 @@ impl Opacity { } } - // Devuelve la clase de opacidad con el prefijo dado (`bg`, `border`, `text`, o vacío para - // `opacity-*`). - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(Opacity::Opaque.class_with(""), "opacity-100"); - // assert_eq!(Opacity::Half.class_with("bg"), "bg-opacity-50"); - // assert_eq!(Opacity::SemiTransparent.class_with("text"), "text-opacity-25"); - // assert_eq!(Opacity::Default.class_with("bg"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { + /// Devuelve la clase de opacidad con el prefijo dado (`bg`, `border`, `text`, o vacío para + /// `opacity-*`). + /// + /// # Ejemplos + /// + /// ```rust + /// # use pagetop_bootsier::prelude::*; + /// assert_eq!(Opacity::Opaque.class_with(""), "opacity-100"); + /// assert_eq!(Opacity::Half.class_with("bg"), "bg-opacity-50"); + /// assert_eq!(Opacity::SemiTransparent.class_with("text"), "text-opacity-25"); + /// assert_eq!(Opacity::Default.class_with("bg"), ""); + /// ``` + #[doc(hidden)] + pub fn class_with(self, prefix: &str) -> String { if let Some(suffix) = self.suffix() { let base_len = if prefix.is_empty() { Self::OPACITY.len() @@ -206,7 +206,7 @@ impl ColorBg { const BG: &str = "bg"; const BG_PREFIX: &str = "bg-"; - // Devuelve el sufijo de la clase `bg-*`, o `None` si no define ninguna clase. + /// Devuelve el sufijo de la clase `bg-*`, o `None` si no define ninguna clase. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -223,7 +223,7 @@ impl ColorBg { } } - // Añade la clase de fondo `bg-*` a la cadena de clases. + /// Añade la clase de fondo `bg-*` a la cadena de clases. #[inline] pub(crate) fn push_class(self, classes: &mut String) { if let Some(suffix) = self.suffix() { @@ -253,7 +253,6 @@ impl ColorBg { /// assert_eq!(ColorBg::Transparent.to_class(), "bg-transparent"); /// assert_eq!(ColorBg::Default.to_class(), ""); /// ``` - #[inline] pub fn to_class(self) -> String { if let Some(suffix) = self.suffix() { let base_len = match self { @@ -305,7 +304,7 @@ impl ColorText { const TEXT: &str = "text"; const TEXT_PREFIX: &str = "text-"; - // Devuelve el sufijo de la clase `text-*`, o `None` si no define ninguna clase. + /// Devuelve el sufijo de la clase `text-*`, o `None` si no define ninguna clase. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -322,7 +321,7 @@ impl ColorText { } } - // Añade la clase de texto `text-*` a la cadena de clases. + /// Añade la clase de texto `text-*` a la cadena de clases. #[inline] pub(crate) fn push_class(self, classes: &mut String) { if let Some(suffix) = self.suffix() { @@ -352,7 +351,6 @@ impl ColorText { /// assert_eq!(ColorText::Black.to_class(), "text-black"); /// assert_eq!(ColorText::Default.to_class(), ""); /// ``` - #[inline] pub fn to_class(self) -> String { if let Some(suffix) = self.suffix() { let base_len = match self { diff --git a/extensions/pagetop-bootsier/src/theme/aux/layout.rs b/extensions/pagetop-bootsier/src/theme/aux/layout.rs index 1d351582..a1255dc0 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/layout.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/layout.rs @@ -25,8 +25,8 @@ pub enum ScaleSize { } impl ScaleSize { - // Devuelve el sufijo para el tamaño (`"-0"`, `"-1"`, etc.), o `None` si no define ninguna - // clase, o `""` para el tamaño automático. + /// Devuelve el sufijo para el tamaño (`"-0"`, `"-1"`, etc.), o `None` si no define ninguna + /// clase, o `""` para el tamaño automático. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -42,7 +42,7 @@ impl ScaleSize { } } - // Añade el tamaño a la cadena de clases usando el prefijo dado. + /// Añade el tamaño a la cadena de clases usando el prefijo dado. #[inline] pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { if !prefix.is_empty() { @@ -57,18 +57,18 @@ impl ScaleSize { } /* Devuelve la clase del tamaño para el prefijo, o una cadena vacía si no aplica (reservado). - // - // # Ejemplo - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(ScaleSize::Auto.class_with("border"), "border"); - // assert_eq!(ScaleSize::Zero.class_with("m"), "m-0"); - // assert_eq!(ScaleSize::Three.class_with("p"), "p-3"); - // assert_eq!(ScaleSize::None.class_with("border"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { + /// + /// # Ejemplo + /// + /// ```rust + /// # use pagetop_bootsier::prelude::*; + /// assert_eq!(ScaleSize::Auto.class_with("border"), "border"); + /// assert_eq!(ScaleSize::Zero.class_with("m"), "m-0"); + /// assert_eq!(ScaleSize::Three.class_with("p"), "p-3"); + /// assert_eq!(ScaleSize::None.class_with("border"), ""); + /// ``` + #[doc(hidden)] + pub fn class_with(self, prefix: &str) -> String { if !prefix.is_empty() { if let Some(suffix) = self.suffix() { let mut class = String::with_capacity(prefix.len() + suffix.len()); diff --git a/extensions/pagetop-bootsier/src/theme/aux/rounded.rs b/extensions/pagetop-bootsier/src/theme/aux/rounded.rs index 20e061d6..69976142 100644 --- a/extensions/pagetop-bootsier/src/theme/aux/rounded.rs +++ b/extensions/pagetop-bootsier/src/theme/aux/rounded.rs @@ -29,8 +29,8 @@ pub enum RoundedRadius { impl RoundedRadius { const ROUNDED: &str = "rounded"; - // Devuelve el sufijo para `*rounded-*`, o `None` si no define ninguna clase, o `""` para el - // redondeo por defecto. + /// Devuelve el sufijo para `*rounded-*`, o `None` si no define ninguna clase, o `""` para el + /// redondeo por defecto. #[rustfmt::skip] #[inline] const fn suffix(self) -> Option<&'static str> { @@ -48,8 +48,8 @@ impl RoundedRadius { } } - // Añade el redondeo de esquinas a la cadena de clases usando el prefijo dado (`rounded-top`, - // `rounded-bottom-start`, o vacío para `rounded-*`). + /// Añade el redondeo de esquinas a la cadena de clases usando el prefijo dado (`rounded-top`, + /// `rounded-bottom-start`, o vacío para `rounded-*`). #[inline] pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { if let Some(suffix) = self.suffix() { @@ -65,21 +65,21 @@ impl RoundedRadius { } } - // Devuelve la clase para el redondeo de esquinas con el prefijo dado (`rounded-top`, - // `rounded-bottom-start`, o vacío para `rounded-*`). - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(RoundedRadius::Scale2.class_with(""), "rounded-2"); - // assert_eq!(RoundedRadius::Zero.class_with("rounded-top"), "rounded-top-0"); - // assert_eq!(RoundedRadius::Scale3.class_with("rounded-top-end"), "rounded-top-end-3"); - // assert_eq!(RoundedRadius::Circle.class_with(""), "rounded-circle"); - // assert_eq!(RoundedRadius::None.class_with("rounded-bottom-start"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { + /// Devuelve la clase para el redondeo de esquinas con el prefijo dado (`rounded-top`, + /// `rounded-bottom-start`, o vacío para `rounded-*`). + /// + /// # Ejemplos + /// + /// ```rust + /// # use pagetop_bootsier::prelude::*; + /// assert_eq!(RoundedRadius::Scale2.class_with(""), "rounded-2"); + /// assert_eq!(RoundedRadius::Zero.class_with("rounded-top"), "rounded-top-0"); + /// assert_eq!(RoundedRadius::Scale3.class_with("rounded-top-end"), "rounded-top-end-3"); + /// assert_eq!(RoundedRadius::Circle.class_with(""), "rounded-circle"); + /// assert_eq!(RoundedRadius::None.class_with("rounded-bottom-start"), ""); + /// ``` + #[doc(hidden)] + pub fn class_with(self, prefix: &str) -> String { if let Some(suffix) = self.suffix() { let base_len = if prefix.is_empty() { Self::ROUNDED.len() diff --git a/extensions/pagetop-bootsier/src/theme/classes/border.rs b/extensions/pagetop-bootsier/src/theme/classes/border.rs index 3095498c..2da7bfbb 100644 --- a/extensions/pagetop-bootsier/src/theme/classes/border.rs +++ b/extensions/pagetop-bootsier/src/theme/classes/border.rs @@ -145,7 +145,6 @@ impl Border { /// `"border border-top-0 border-end-3 border-primary border-opacity-50"`, etc.). /// /// Si no se define ningún tamaño, color ni opacidad, devuelve `""`. - #[inline] pub fn to_class(self) -> String { let mut classes = String::new(); self.push_class(&mut classes); diff --git a/extensions/pagetop-bootsier/src/theme/classes/color.rs b/extensions/pagetop-bootsier/src/theme/classes/color.rs index 162b7849..4f5b4650 100644 --- a/extensions/pagetop-bootsier/src/theme/classes/color.rs +++ b/extensions/pagetop-bootsier/src/theme/classes/color.rs @@ -76,7 +76,6 @@ impl Background { /// Devuelve las clases de fondo como cadena (`"bg-primary"`, `"bg-body-secondary bg-opacity-50"`, etc.). /// /// Si no se define ni color ni opacidad, devuelve `""`. - #[inline] pub fn to_class(self) -> String { let mut classes = String::new(); self.push_class(&mut classes); @@ -189,7 +188,6 @@ impl Text { /// etc.). /// /// Si no se define ni color ni opacidad, devuelve `""`. - #[inline] pub fn to_class(self) -> String { let mut classes = String::new(); self.push_class(&mut classes); diff --git a/extensions/pagetop-bootsier/src/theme/classes/layout.rs b/extensions/pagetop-bootsier/src/theme/classes/layout.rs index e9d7e248..1f388450 100644 --- a/extensions/pagetop-bootsier/src/theme/classes/layout.rs +++ b/extensions/pagetop-bootsier/src/theme/classes/layout.rs @@ -48,7 +48,7 @@ impl Margin { // **< Margin HELPERS >************************************************************************* - // Devuelve el prefijo `m*` según el lado. + /// Devuelve el prefijo `m*` según el lado. #[rustfmt::skip] #[inline] const fn side_prefix(&self) -> &'static str { @@ -63,7 +63,7 @@ impl Margin { } } - // Devuelve el sufijo del tamaño (`auto`, `0`..`5`), o `None` si no define clase. + /// Devuelve el sufijo del tamaño (`auto`, `0`..`5`), o `None` si no define clase. #[rustfmt::skip] #[inline] const fn size_suffix(&self) -> Option<&'static str> { @@ -80,8 +80,8 @@ impl Margin { } /* Añade la clase de **margin** a la cadena de clases (reservado). - // - // No añade nada si `size` es `ScaleSize::None`. + /// + /// No añade nada si `size` es `ScaleSize::None`. #[inline] pub(crate) fn push_class(self, classes: &mut String) { let Some(size) = self.size_suffix() else { @@ -94,7 +94,6 @@ impl Margin { /// Devuelve la clase de **margin** como cadena (`"mt-3"`, `"ms-lg-auto"`, etc.). /// /// Si `size` es `ScaleSize::None`, devuelve `""`. - #[inline] pub fn to_class(self) -> String { let Some(size) = self.size_suffix() else { return String::new(); @@ -148,7 +147,7 @@ impl Padding { // **< Padding HELPERS >************************************************************************ - // Devuelve el prefijo `p*` según el lado. + /// Devuelve el prefijo `p*` según el lado. #[rustfmt::skip] #[inline] const fn prefix(&self) -> &'static str { @@ -163,9 +162,9 @@ impl Padding { } } - // Devuelve el sufijo del tamaño (`0`..`5`), o `None` si no define clase. - // - // Nota: `ScaleSize::Auto` **no aplica** a padding ⇒ devuelve `None`. + /// Devuelve el sufijo del tamaño (`0`..`5`), o `None` si no define clase. + /// + /// Nota: `ScaleSize::Auto` **no aplica** a padding ⇒ devuelve `None`. #[rustfmt::skip] #[inline] const fn suffix(&self) -> Option<&'static str> { @@ -182,8 +181,8 @@ impl Padding { } /* Añade la clase de **padding** a la cadena de clases (reservado). - // - // No añade nada si `size` es `ScaleSize::None` o `ScaleSize::Auto`. + /// + /// No añade nada si `size` es `ScaleSize::None` o `ScaleSize::Auto`. #[inline] pub(crate) fn push_class(self, classes: &mut String) { let Some(size) = self.suffix() else { @@ -192,10 +191,9 @@ impl Padding { self.breakpoint.push_class(classes, self.prefix(), size); } */ - // Devuelve la clase de **padding** como cadena (`"px-2"`, `"pe-sm-4"`, etc.). - // - // Si `size` es `ScaleSize::None` o `ScaleSize::Auto`, devuelve `""`. - #[inline] + /// Devuelve la clase de **padding** como cadena (`"px-2"`, `"pe-sm-4"`, etc.). + /// + /// Si `size` es `ScaleSize::None` o `ScaleSize::Auto`, devuelve `""`. pub fn to_class(self) -> String { let Some(size) = self.suffix() else { return String::new(); diff --git a/extensions/pagetop-bootsier/src/theme/classes/rounded.rs b/extensions/pagetop-bootsier/src/theme/classes/rounded.rs index 58d50b86..077740e1 100644 --- a/extensions/pagetop-bootsier/src/theme/classes/rounded.rs +++ b/extensions/pagetop-bootsier/src/theme/classes/rounded.rs @@ -160,7 +160,6 @@ impl Rounded { /// `"rounded-top rounded-bottom-start-4 rounded-bottom-end-circle"`, etc.). /// /// Si no se define ningún radio, devuelve `""`. - #[inline] pub fn to_class(self) -> String { let mut classes = String::new(); self.push_class(&mut classes); diff --git a/extensions/pagetop-bootsier/src/theme/container/component.rs b/extensions/pagetop-bootsier/src/theme/container/component.rs index 068d24a3..b105abb1 100644 --- a/extensions/pagetop-bootsier/src/theme/container/component.rs +++ b/extensions/pagetop-bootsier/src/theme/container/component.rs @@ -6,19 +6,23 @@ use crate::prelude::*; /// /// Envuelve un contenido con la etiqueta HTML indicada por [`container::Kind`]. Sólo se renderiza /// si existen componentes hijos (*children*). -#[rustfmt::skip] -#[derive(AutoDefault)] +#[derive(AutoDefault, Getters)] pub struct Container { - id : AttrId, - classes : AttrClasses, - container_kind : container::Kind, + #[getters(skip)] + id: AttrId, + /// Devuelve las clases CSS asociadas al contenedor. + classes: Classes, + /// Devuelve el tipo semántico del contenedor. + container_kind: container::Kind, + /// Devuelve el comportamiento para el ancho del contenedor. container_width: container::Width, - children : Children, + /// Devuelve la lista de componentes (`children`) del contenedor. + children: Children, } impl Component for Container { fn new() -> Self { - Container::default() + Self::default() } fn id(&self) -> Option { @@ -26,7 +30,7 @@ impl Component for Container { } fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, self.width().to_class()); + self.alter_classes(ClassesOp::Prepend, self.container_width().to_class()); } fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { @@ -34,9 +38,9 @@ impl Component for Container { if output.is_empty() { return PrepareMarkup::None; } - let style = match self.width() { + let style = match self.container_width() { container::Width::FluidMax(w) if w.is_measurable() => { - Some(join!("max-width: ", w.to_string(), ";")) + Some(util::join!("max-width: ", w.to_string(), ";")) } _ => None, }; @@ -78,7 +82,7 @@ impl Component for Container { impl Container { /// Crea un contenedor de tipo `Main` (`
`). pub fn main() -> Self { - Container { + Self { container_kind: container::Kind::Main, ..Default::default() } @@ -86,7 +90,7 @@ impl Container { /// Crea un contenedor de tipo `Header` (`
`). pub fn header() -> Self { - Container { + Self { container_kind: container::Kind::Header, ..Default::default() } @@ -94,7 +98,7 @@ impl Container { /// Crea un contenedor de tipo `Footer` (`