Commit graph

127 commits

Author SHA1 Message Date
58d1fef4f1 ⚡️ (core): Registro de acciones inmutable 2026-09-26 00:56:30 +02:00
16eec316c4 ♻️ (base): Reorganiza el componente Intro 2026-09-20 23:53:18 +02:00
307ca6f62c ✨ (pagetop): Añade Grid y extrae Flex de Container 2026-09-18 16:49:09 +02:00
64113aff09 ♻️ (pagetop): Sustituye StyleSheet::inline
- `StyleSheet::inline` se resuelve ahora con `ResponsiveStyles`.
- `StyleSheet` se simplifica a sólo hojas de estilo externas.
- `ResponsiveStyles` añade `add_styles()` para declarar varias
  propiedades en una sola llamada.
- `AssetsOp` gana un constructor por variante y conversiones
  "From<Favicon/Preload/StyleSheet/JavaScript>" para pasarlos
  directamente a `with_assets()`.
2026-09-12 20:44:17 +02:00
0b8f3f3000 ✨ (pagetop): Añade puntos de corte a Flex/FlexItem
- `Breakpoint` gana variante `Xxxl` y métodos `name()`/`min_width()`/
  `resolved()`, para consultarse contra el tema activo.
- `Theme::breakpoint_entry()` sustituye a `breakpoint_min_width()`:
  devuelve un `BreakpointEntry` con nombre y ancho mínimo.
- Nuevo `Responsive<T>`, valor en cascada mobile-first por punto de
  corte.
- Ejemplo `examples/intro-responsive.rs` con los patrones de uso.
2026-09-12 11:13:19 +02:00
28f1eee391 ♻️ (pagetop): Separa handle_component() en 2 métodos
`Theme::handle_component()` se divide en `setup_component()` (síncrono,
muta por tipo con la macro homónima) y `render_component()` (async,
decide el renderizado con su propia macro). Aplica mismo criterio que ya
separa `Component::setup()` de `prepare()`.
2026-09-06 13:07:01 +02:00
1e2d171805 ♻️ (pagetop): setup() recibe &mut Context
Antes sólo mutaba el propio componente; ahora también puede aplicar
ajustes síncronos sobre el Context, visibles para las extensiones que
intercepten BeforeRender. La frontera con prepare() pasa a ser de
sincronía, no de qué puede mutar cada uno.
2026-09-06 09:04:44 +02:00
f2a7507317 ✨ (theme): Añade Breakpoint y ResponsiveStyles
- Nuevo `Breakpoint` (`Xs`/`Sm`/`Md`/`Lg`/`Xl`/`Xxl`) y
  `Theme::breakpoint_min_width()`, que traduce cada variante al ancho
  mínimo CSS del tema activo (mismo patrón que `Theme::intent_color()`).
- `ResponsiveStyles` acumula declaraciones `property: value` agrupadas
  por punto de corte (opcional: `None` para reglas siempre activas, sin
  depender del tema) y por clase o clases. Aplica first-write-wins para
  clases utilitarias repetidas por muchos componentes. `render()` las
  agrupa en bloques `@media (min-width: ...)` mobile-first, sin saltos
  de línea.
2026-09-06 01:46:06 +02:00
1be3d88888 ♻️ Migra #[builder_fn] a #[builder_impl]
Sustituye las ~400 anotaciones `#[builder_fn]` método a método por un
único `#[builder_impl]` por `impl`/`trait`, en 74 ficheros de pagetop y
sus extensiones (admin, bootsier, menu, user).
2026-08-29 19:25:58 +02:00
56a6691c87 🚧 (theme): Renombra variantes de Intent
Renombra `Intent::Secondary`/`Danger` a `Neutral`/`Severe` y actualiza
Badge y su CSS por defecto (basic.css) al nuevo vocabulario.

Introduce el punto de extensión `Theme::intent_color()`, que traduce una
`Intent` al nombre de color de la paleta propia de cada tema, con
`Intent::color(cx)` como atajo de uso desde los componentes.
2026-08-28 01:10:45 +02:00
c8654a5742 ✨ (core): Añade TypedOp<C> para restringir Children
Mismo repertorio de operaciones que `ChildOp`, pero cada variante exige
un componente de tipo `C`, evitando que un componente ajeno a ese tipo
acabe en una lista pensada para un único tipo de elemento.
2026-08-24 20:00:12 +02:00
e24b9ab9de ♻️ (pagetop): Logotipo embebido y Badge en Intent
- `PageTopSvg::markup_with()` fusiona `Props` y controla la etiqueta
  accesible directamente en el `<svg>`.
- `Image` ya no duplica accesibilidad para el logotipo y corrige el
  `alt` vacío en el resto de fuentes (Responsive/Thumbnail/Plain).
- Sustituye `BadgeKind` y el `ColorName`/`IntoColor` de `core/theme`
  por un `Intent` compartido.
2026-08-22 09:44:28 +02:00
997017bb75 🎨 (core): Retoques de estilo menores 2026-08-21 08:23:39 +02:00
b50d186fb1 ✨ (theme): Añade IntoColor para colores opcionales
Permite que métodos como `with_color(color: impl IntoColor)` acepten un
color directo o uno opcional (`Some(...)`/`None`) sin la ambigüedad que
produce un `Into<Option<C>>` genérico.
2026-08-21 08:18:28 +02:00
bb6855e41f ✨ (theme): Añade ColorName y CoreColors
Nuevo trait `ColorName` para que cada tema aporte su propia paleta de
colores; `CoreColors` es la paleta por defecto.

Aprovecha para refactorizar `core/theme.rs` en submódulos por concepto.
2026-08-20 21:54:12 +02:00
bf4c635e59 ♻️ (pagetop): Sustituye Attr<T> por Option<T>
`Attr<T>` no aportaba nada sobre `Option<T>` directo: `Getters` ya trata
los campos `Option<T>` como caso especial (`Option<&T>`, o `Option<T>`
con `#[getters(copy)]`) y `#[builder_fn]` funciona igual sobre ellos.
2026-08-17 05:28:12 +02:00
be10dd28a1 ♻️ (locale): Renombra L10n a Lc
Simplifica el nombre del tipo de localización. Actualiza todas las
referencias en el crate principal, extensiones, ejemplos y tests.
2026-08-12 22:56:09 +02:00
8cb5e035dc ✨ (theme): Evita referencias cíclicas de parent()
Como `Theme::parent()` se resuelve en tiempo de ejecución, una
referencia cíclica (en la que un tema acaba siendo padre de sí mismo,
directa o transitivamente) no puede descartarse en compilación. Se
detecta al registrar el tema y aborta el arranque si procede.
2026-08-09 18:45:17 +02:00
1cab0ae9a0 ✨ (theme): Añade región Aside
Introduce `CoreRegions::Aside` como nueva región de plantilla, entre
Header y Content, y Template ahora envuelve el cuerpo en `div.wrapper`,
omitiendo el renderizado si no hay contenido.

`CoreRegion`, `ReservedRegion` y `CoreTemplate` pasan a plural
(`CoreRegions`, `ReservedRegions`, `CoreTemplates`) para no confundirlos
con otras definiciones, y se amplía la documentación del módulo theme.
2026-08-02 10:40:36 +02:00
3e83f8e35e 🐛 (locale): Preserva grafía original de "?lang="
Antes, RequestLocale sólo guardaba el LanguageIdentifier ya resuelto,
así que `Context::route()` propagaba siempre la forma canónica en los
enlaces generados ("?lang=en-US"), aunque el usuario hubiera pedido
"?lang=en". Ahora se conserva el valor original de la query junto al
idioma resuelto, y se reutiliza al construir nuevos enlaces mientras
siga siendo válido.

El idioma efectivo para renderizar la petición no cambia: sigue siendo
siempre el LanguageIdentifier canónico soportado por la aplicación.
2026-08-01 08:05:48 +02:00
8ddc26dae1 📝 (theme): Aclara alcance de la herencia de temas 2026-07-28 09:19:50 +02:00
747e64ce34 ✨ (auth): Añade permisos tipados
Sustituye las claves de permiso `&str` sueltas por el trait `Permission`
(clave, etiqueta y grupo), y añade `require_permission()` para cortar un
handler con `ErrorPage::AccessDenied` antes de construir nada.

Para que `require_permission()` pueda fallar sin un `Context` previo,
`ErrorPage` pasa a envolver `Option<HttpRequest>`, `Context::new()`
recibe ahora `HttpRequest` sin `Option`, y se añade `Context::admin()`
para la plantilla de administración.
2026-07-27 22:46:41 +02:00
74dbcde5c5 ♻️ (core): Cambia HashMap x Vec en RegionComponents 2026-07-26 13:41:32 +02:00
7e6a9f8557 ♻️ (core): Clave &'static str en RegionComponents 2026-07-26 12:50:33 +02:00
845d91d8c1 ♻️ (core): Evita doble clonado de InRegion en Child
Los prototipos registrados con `InRegion` se clonaban dos veces por
petición: una al ensamblar la región (`ComponentGlobal::as_child`) y
otra al renderizar (`Child::render`). Ahora sólo se clonan al
renderizar.
2026-07-26 12:39:01 +02:00
ce6b4d2581 ♻️ (core): Simplifica Child a Arc<dyn Component>
Elimina el `RwLock<Box<dyn Component>>` interno ya que ningún punto del
ciclo de renderizado tomaba el write-lock.

De paso, `From<Embed<C>> for Child` pasa a una coerción de puntero sin
clonar, gracias al copy-on-write que ya ofrece `Embed::get_mut()`. Los
doctests en `rust,ignore` pasan a `rust,no_run`, verificados por el
compilador.
2026-07-26 11:30:16 +02:00
b9d9cdf601 🚧 Ajustes menores en rutas y localización
- Añade `Route::try_resolve()` y `RoutePath::is_empty()` para omitir
  atributos HTML opcionales cuando la ruta resultante está vacía.
- Renombra `Locale::configured_langid()` a `try_langid()` (convención
  `try_*` para Option).
- Reordena include_locales! y otros cambios menores.
2026-07-26 09:55:27 +02:00
8573aca29e ✨ (theme): Añade componentes Region y Template
Introduce Region y Template (`base::component::layout`) para componer el
`<body>` de una página, con captura explícita vía `handle_component()`
en lugar de maquetado implícito:

- `RegionName`/`TemplateName` (`core/theme.rs`) como interfaces de
  identidad; `CoreRegion` (`Header`/`Content`/`Footer`) y `CoreTemplate`
  (`Standard`/`Admin`) como catálogo por defecto.
- `ReservedRegion` (`PageTop`/`PageBottom`, en `response/page.rs`) queda
  fuera de `CoreRegion`. `Page::render()` las renderiza siempre,
  envolviendo `Theme::render_page_body()`, con independencia de la
  plantilla o el tema activos.
- Elimina `TemplateSource` en `Context`: la plantilla pasa a ser un
  `TemplateRef` fijo, sin resolución por tema (ese mecanismo nunca llegó
  a implementarse).
- La personalización por tema es por captura (`handle_component()` +
  `downcast_ref()`), no por sustituir qué constante se resuelve.
- Corrige referencias obsoletas a `DefaultRegions` por `CoreRegion`.
- Sustituye `tests/theme_template.rs` (API anterior) por
  `tests/component_template.rs`, que cubre el patrón de captura con el
  API nuevo.
2026-07-26 09:51:27 +02:00
af548b03c9 ✨ (core): Añade Route, RoutePath y Waypoint
`Route` resuelve URLs sensibles al `Context`/idioma en propiedades de
componentes; `RoutePath` (renombrado desde html/route.rs) es el valor
concreto de ruta + query. `Waypoint` transporta la URL de retorno entre
pantallas de listado/alta/edición vía query string.

Ajusta `Context::route()`, `Redirect` y los componentes que aceptan
rutas para integrarse con el nuevo tipo `Route`.
2026-07-19 22:16:21 +02:00
6f82da220b 🐛 (web): HttpRequest clona ahora las extensiones
Documenta el nuevo comportamiento y aprovecha para pulir varios
comentarios y restringir visibilidad de símbolos internos.
2026-07-17 00:54:35 +02:00
eb3157c4a4 📝 Retoques menores en documentación 2026-07-14 07:27:51 +02:00
24bd13414c ✨ Añade gestión de errores catastróficos
- Los errores controlados (403, 404, fatales) se renderizan con el tema
  activo; un `panic!` no controlado se intercepta con un HTML mínimo y
  autónomo, sin depender del tema ni de los componentes.
- Corrige además que `Context::template()` no seguía los cambios de tema
  hechos con `with_theme()` después de crear la página.
- Renombra `DefaultRegion(s)`/`DefaultTemplate(s)`, añade
  `Theme::admin_template()` y `Page::admin()`, y documenta la creación
  de temas e `InRegion` en `core/theme.rs`.
2026-07-13 00:38:11 +02:00
0e72d51276 📝 Quita cursivas de algunos anglicismos en docs 2026-07-10 19:33:26 +02:00
9e8cd77140 📝 Retoques de documentación en html y componentes 2026-07-09 00:08:04 +02:00
25713a8a13 ♻️ (html): Añade valores extra a Props
- Reordena campos, variantes y métodos para mayor consistencia interna.
- Añade `extra_or`, `extra_or_default` y `extra_or_else` a `Props`.
- Mejora la documentación y amplía los tests de integración.
2026-07-09 00:05:28 +02:00
c7b680a7f7 ✨ (html): Añade Preload para precarga de recursos
Nuevo tipo `Preload` (junto a `StyleSheet` y `JavaScript`) que emite
`<link rel="preload">` con soporte para fuentes, estilos, scripts e
imágenes (WebP y AVIF incluidos). Bootsier lo usa para precargar las
fuentes Source Sans 3 y eliminar el FOUT en la carga inicial.
2026-07-07 00:03:36 +02:00
9354894b3a 🎨 Convierte el ciclo de renderizado en async
`prepare()` de `Component`, `render()` de `Region` y `Template`, y la
implementación de `ComponentRender` pasan a ser funciones async. Se
actualizan todos los componentes base, helpers, tests y ejemplos.
2026-07-06 00:48:41 +02:00
9acd8cc51a 🐛 Corrige orden de despacho en ActionsList
Las acciones con menor peso deben despacharse primero; `.rev()` lo
invertía. Eliminado de `for_each` y `try_for_each`.
2026-07-04 22:59:56 +02:00
8aa4372bdf ♻️ Convierte initialize() y Application en async
`async_trait` se re-exporta desde `pagetop` y se añade al preludio; ya
no es necesario declararlo como dependencia en las extensiones.
`initialize()` es ahora verdaderamente `async` (con `.await`), y
`Application::new()` y `prepare()` pasan también a ser funciones
`async`.
2026-07-04 22:56:19 +02:00
0e96bcce64 ✨ (auth): Añade CurrentUser y sistema de permisos
- Nuevo módulo `auth` que exporta `CurrentUser`, `CheckPermission` y
  `has_permission()`.
- `Context` y `Page` exponen `current_user()` vía `Contextual`.
- `HttpRequest` accede a extensiones de middleware con `extension<T>()`.
- `Extension` incluye nuevo método `configure_middleware`.
- `try_dispatch_actions` para despachar acciones con control del flujo.
2026-07-02 20:52:13 +02:00
9435678e01 ♻️ (form): Mueve componentes de formulario a base 2026-06-22 02:12:09 +02:00
26f1cda831 ✨ Permite a plantillas cambiar atributos de <body> 2026-06-20 20:31:51 +02:00
62219584b0 ♻️ (html): API para id's en Props y componentes 2026-06-20 15:02:23 +02:00
6376e3e88c 📝 Ajusta formato de comentarios 2026-06-13 19:16:34 +02:00
511149caa7 📝 Añade no_run a doctests ilustrativos 2026-06-13 00:31:36 +02:00
c1afe0e70c ♻️ Migra API pública de actix-web a Axum
- `configure_service` como `configure_router(Router) -> Router`.
- Macro `static_files_service!` como `serve_static_files!`.
- `ResultPage<M, E>` eliminado; handlers devuelven `Result<M, E>`.
- `ErrorPage` implementa `IntoResponse` en lugar de `ResponseError`.
- Registro con `OnceLock`; eliminados `drop_extensions` y `app.welcome`.
- `Redirect` devuelve `Response`; docs y ejemplos actualizados.
2026-05-31 23:38:43 +02:00
7553ed35ec 🎨 Aplica formato Rust 2024 (rustfmt.toml) 2026-05-30 22:50:40 +02:00
c046014d04 🚧 Actualiza dependencias y revisa código 2026-05-02 18:19:45 +02:00
566e024f64 📝 Actualiza créditos y elimina doc obsoleta 2026-05-02 18:19:45 +02:00
412446bb5a 📝 Correcciones menores de doc y comentarios 2026-05-02 18:19:45 +02:00