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.
186 lines
5.7 KiB
Rust
186 lines
5.7 KiB
Rust
/*!
|
|
<div align="center">
|
|
|
|
<h1>PageTop Bootsier</h1>
|
|
|
|
<p>Tema de <strong>PageTop</strong> basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.</p>
|
|
|
|
[](https://docs.rs/pagetop-bootsier)
|
|
[](https://crates.io/crates/pagetop-bootsier)
|
|
[](https://crates.io/crates/pagetop-bootsier)
|
|
[](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-bootsier#licencia)
|
|
|
|
</div>
|
|
|
|
## 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
|
|
|
|
Igual que con otras extensiones, **añade la dependencia** a tu `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
pagetop-bootsier = { ... }
|
|
```
|
|
|
|
**Declara la extensión** en tu aplicación (o extensión que la requiera). Recuerda que el orden en
|
|
`dependencies()` determina la prioridad relativa frente a las otras extensiones:
|
|
|
|
```rust,no_run
|
|
use pagetop::prelude::*;
|
|
|
|
struct MyApp;
|
|
|
|
#[async_trait]
|
|
impl Extension for MyApp {
|
|
fn dependencies(&self) -> Vec<ExtensionRef> {
|
|
vec![
|
|
// ...
|
|
&pagetop_bootsier::Bootsier,
|
|
// ...
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
Y **selecciona el tema en la configuración** de la aplicación:
|
|
|
|
```toml
|
|
[app]
|
|
theme = "Bootsier"
|
|
```
|
|
|
|
o **fuerza el tema por código** en una página concreta:
|
|
|
|
```rust,no_run
|
|
use pagetop::prelude::*;
|
|
use pagetop_bootsier::Bootsier;
|
|
|
|
async fn homepage(request: HttpRequest) -> Result<Markup, ErrorPage> {
|
|
Page::new(request)
|
|
.with_theme(&Bootsier)
|
|
.with_child(
|
|
Block::new()
|
|
.with_title(L10n::l("sample_title"))
|
|
.with_child(Html::with(|cx| html! {
|
|
p { (L10n::l("sample_content").using(cx)) }
|
|
})),
|
|
)
|
|
.render().await
|
|
}
|
|
```
|
|
*/
|
|
|
|
#![doc(
|
|
html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/assets/favicon.ico"
|
|
)]
|
|
|
|
use pagetop::prelude::*;
|
|
|
|
include_locales!(LOCALES_BOOTSIER);
|
|
|
|
// Versión de la librería Bootstrap.
|
|
const BOOTSTRAP_VERSION: &str = "5.3.8";
|
|
|
|
pub mod config;
|
|
|
|
pub mod theme;
|
|
|
|
mod handlers {
|
|
pub mod button;
|
|
pub mod input;
|
|
pub mod select;
|
|
pub mod textarea;
|
|
}
|
|
|
|
/// Implementa el tema.
|
|
pub struct Bootsier;
|
|
|
|
#[async_trait]
|
|
impl Extension for Bootsier {
|
|
fn name(&self) -> L10n {
|
|
L10n::t("extension_name", &LOCALES_BOOTSIER)
|
|
}
|
|
|
|
fn description(&self) -> L10n {
|
|
L10n::t("extension_description", &LOCALES_BOOTSIER)
|
|
}
|
|
|
|
fn theme(&self) -> Option<ThemeRef> {
|
|
Some(&Self)
|
|
}
|
|
|
|
fn configure_router(&self, router: Router) -> Router {
|
|
let base = &config::SETTINGS.dev.bootsier_static_dir;
|
|
let subdir = |s: &str| {
|
|
if base.is_empty() {
|
|
String::new()
|
|
} else {
|
|
format!("{base}/{s}")
|
|
}
|
|
};
|
|
serve_static_files!(router, [subdir("css"), bootsier_css] => "/bootsier/css");
|
|
serve_static_files!(router, [subdir("js"), bootsier_js] => "/bootsier/js");
|
|
serve_static_files!(router, [subdir("fonts"), bootsier_fonts] => "/bootsier/fonts");
|
|
router
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Theme for Bootsier {
|
|
#[inline]
|
|
fn default_template(&self) -> TemplateRef {
|
|
&BootsierTemplate::Standard
|
|
}
|
|
|
|
async fn handle_component(
|
|
&self,
|
|
component: &mut dyn Component,
|
|
cx: &mut Context,
|
|
) -> Option<Result<Markup, ComponentError>> {
|
|
setup_component!(component, {
|
|
Button => |c| handlers::button::setup(c),
|
|
form::select::Field => |c| handlers::select::setup(c),
|
|
form::Textarea => |c| handlers::textarea::setup(c),
|
|
});
|
|
render_component!(component, {
|
|
form::input::Field => |c| handlers::input::render(c, cx),
|
|
form::select::Field => |c| handlers::select::render(c, cx),
|
|
form::Textarea => |c| handlers::textarea::render(c, cx),
|
|
})
|
|
}
|
|
|
|
fn before_render_page_body(&self, page: &mut Page) {
|
|
// Las URLs de las fuentes deben coincidir exactamente con las declaradas en @font-face de
|
|
// _bootsier-custom.scss; cualquier discrepancia hace que el navegador descargue dos veces.
|
|
page.alter_assets(AssetsOp::AddPreload(
|
|
Preload::font("/bootsier/fonts/bootsier.font.woff2").with_weight(-99),
|
|
))
|
|
.alter_assets(AssetsOp::AddPreload(
|
|
Preload::font("/bootsier/fonts/bootsier.font.italic.woff2").with_weight(-99),
|
|
))
|
|
.alter_assets(AssetsOp::AddStyleSheet(
|
|
StyleSheet::from("/bootsier/css/bootsier.min.css")
|
|
.with_version(ADMINLTE_VERSION)
|
|
.with_weight(-90),
|
|
))
|
|
.alter_assets(AssetsOp::AddJavaScript(
|
|
JavaScript::defer("/bootsier/js/bootsier.bundle.min.js")
|
|
.with_version(BOOTSTRAP_VERSION)
|
|
.with_weight(-90),
|
|
))
|
|
.alter_assets(AssetsOp::AddJavaScript(
|
|
JavaScript::defer("/bootsier/js/bootsier.extended.min.js")
|
|
.with_version(ADMINLTE_VERSION)
|
|
.with_weight(-90),
|
|
))
|
|
.alter_child_in(
|
|
&DefaultRegion::Footer,
|
|
ChildOp::AddIfEmpty(PoweredBy::new().into()),
|
|
);
|
|
}
|
|
}
|