pagetop/examples/intro-spacing.rs
Manuel Cillero 50e4b42fe4 (pagetop): Añade Margin y Padding nativos
- Nuevo módulo `html::spacing` con `Margin`/`Padding`: igual que
  `FlexItem`, se resuelven generando clases CSS dinámicamente y se
  aplican con `PropsOp::margin()`/`PropsOp::padding()` sobre cualquier
  componente.
- Elimina `Margin`/`Padding` de `pagetop-bootsier`.
- Añade el ejemplo `intro-spacing` con los patrones de uso típicos.
2026-09-13 00:48:11 +02:00

255 lines
8.5 KiB
Rust

use pagetop::prelude::*;
include_locales!(LOC from "examples/locale");
struct IntroSpacing;
#[async_trait]
impl Extension for IntroSpacing {
fn dependencies(&self) -> Vec<ExtensionRef> {
vec![&pagetop_bootsier::Bootsier]
}
fn configure_router(&self, router: Router) -> Router {
router.route("/", web::get(intro_spacing))
}
}
async fn intro_spacing(request: HttpRequest) -> Result<Markup, ErrorPage> {
Page::new(request)
.with_assets(demo_box_styles())
.with_assets(demo_row_styles())
.with_assets(demo_code_styles())
.with_child(
Intro::default()
.with_opening(IntroOpening::Custom)
.with_title(Lc::n("PageTop"))
.with_slogan(Lc::t("spacing_slogan", &LOC))
.with_button(None::<(Lc, Route)>)
.with_child(Html::with(|cx| {
html! {
p class="intro-text-lead" {
(Lc::t("spacing_note", &LOC).using(cx))
}
}
}))
.with_child(padding_block())
.with_child(layout_block())
.with_child(responsive_block())
.with_child(combined_block()),
)
.render()
.await
}
fn padding_block() -> Block {
let mut block = Block::new().with_title(Lc::t("spacing_block_title_padding", &LOC));
let padding_variants: [(&str, UnitValue, &str); 3] = [
(
"spacing_title_uniform_small",
UnitValue::RelRem(0.5),
"Padding::new().with_all(UnitValue::RelRem(0.5))",
),
(
"spacing_title_uniform_medium",
UnitValue::RelRem(1.0),
"Padding::new().with_all(UnitValue::RelRem(1.0))",
),
(
"spacing_title_uniform_large",
UnitValue::RelRem(2.0),
"Padding::new().with_all(UnitValue::RelRem(2.0))",
),
];
for (title_key, size, code) in padding_variants {
block = block
.with_child(caption(Lc::t(title_key, &LOC), Lc::n(code)))
.with_child(
demo_row(Flex::new().with_gap(flex::Gap::Both(UnitValue::RelRem(0.5)))).with_child(
demo_box(box_sample()).with_prop(Padding::new().with_all(size).into()),
),
);
}
block
.with_child(caption(
Lc::t("spacing_title_sides", &LOC),
Lc::n(concat!(
"Padding::new()",
".with_top(UnitValue::RelRem(0.25))",
".with_end(UnitValue::RelRem(2.5))",
".with_bottom(UnitValue::RelRem(1.5))",
".with_start(UnitValue::RelRem(0.5))",
)),
))
.with_child(
demo_row(Flex::new()).with_child(
demo_box(box_sample()).with_prop(
Padding::new()
.with_top(UnitValue::RelRem(0.25))
.with_end(UnitValue::RelRem(2.5))
.with_bottom(UnitValue::RelRem(1.5))
.with_start(UnitValue::RelRem(0.5))
.into(),
),
),
)
}
fn layout_block() -> Block {
Block::new()
.with_title(Lc::t("spacing_block_title_layout", &LOC))
.with_child(caption(
Lc::t("spacing_title_margin_gap", &LOC),
Lc::n("Margin::new().with_x(UnitValue::RelRem(1.0))"),
))
.with_child(
demo_row(Flex::new())
.with_child(demo_box(box_sample()))
.with_child(
demo_box(box_sample())
.with_prop(Margin::new().with_x(UnitValue::RelRem(1.0)).into()),
)
.with_child(demo_box(box_sample())),
)
.with_child(caption(
Lc::t("spacing_title_margin_center", &LOC),
Lc::n("Margin::new().with_x(UnitValue::Auto)"),
))
.with_child(
demo_row(Flex::new()).with_child(
demo_box(box_sample())
.with_prop(Margin::new().with_x(UnitValue::Auto).into())
.with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Custom(UnitValue::RelRem(8.0))),
)),
),
)
}
fn responsive_block() -> Block {
Block::new()
.with_title(Lc::t("spacing_block_title_responsive", &LOC))
.with_child(caption(
Lc::t("spacing_title_responsive", &LOC),
Lc::n(concat!(
"Padding::new()",
".with_all(UnitValue::RelRem(0.5))",
".with_all_at(Breakpoint::Md, UnitValue::RelRem(2.0))",
".with_all_at(Breakpoint::Lg, UnitValue::RelRem(4.0))",
)),
))
.with_child(
demo_row(Flex::new()).with_child(
demo_box(box_sample()).with_prop(
Padding::new()
.with_all(UnitValue::RelRem(0.5))
.with_all_at(Breakpoint::Md, UnitValue::RelRem(2.0))
.with_all_at(Breakpoint::Lg, UnitValue::RelRem(4.0))
.into(),
),
),
)
}
fn combined_block() -> Block {
Block::new()
.with_title(Lc::t("spacing_block_title_combined", &LOC))
.with_child(caption(
Lc::t("spacing_title_combined", &LOC),
Lc::n(concat!(
"Container::new()",
".with_prop(Margin::new().with_y(UnitValue::RelRem(1.0)).into())",
".with_prop(Padding::new().with_all(UnitValue::RelRem(1.5)).into())",
)),
))
.with_child(
demo_row(Flex::new()).with_child(
Container::new()
.with_prop(PropsOp::add_classes("spacing-demo-box"))
.with_prop(Margin::new().with_y(UnitValue::RelRem(1.0)).into())
.with_prop(Padding::new().with_all(UnitValue::RelRem(1.5)).into())
.with_child(
Button::plain(Lc::t("spacing_box_card_button", &LOC))
.with_style(button::Style::Solid(Intent::Warning)),
),
),
)
}
// **< HELPERS >************************************************************************************
// Caja de muestra sin relleno ni margen propios, para que sólo se vea el efecto de `Margin`/
// `Padding` aplicado en cada demostración.
fn demo_box_styles() -> AssetsOp {
AssetsOp::add_responsive_styles(
None,
"spacing-demo-box",
[
("background-color", "#0d6efd"),
("color", "#fff"),
("min-width", "3rem"),
("width", "auto"),
("max-width", "none"),
("margin", "0"),
("padding", "0"),
("border-radius", "0.375rem"),
("text-align", "center"),
],
)
}
// Aspecto fijo de las filas de muestra.
fn demo_row_styles() -> AssetsOp {
AssetsOp::add_responsive_styles(
None,
"spacing-demo-row",
[
("background-color", "#f1f3f5"),
("width", "100%"),
("max-width", "none"),
("margin", "0 0 1.5rem"),
("padding", "0.75rem"),
],
)
}
// Evita que los fragmentos de código largos desborden su contenedor.
fn demo_code_styles() -> AssetsOp {
AssetsOp::add_responsive_styles(None, "spacing-demo-code", [("overflow-wrap", "anywhere")])
}
// Caja azul de muestra, sin relleno ni margen propios.
fn demo_box(label: Lc) -> Container {
Container::new()
.with_prop(PropsOp::add_classes("spacing-demo-box"))
.with_child(Html::with(move |cx| html! { (label.using(cx)) }))
}
// Etiqueta genérica reutilizada en la mayoría de cajas de muestra.
fn box_sample() -> Lc {
Lc::t("spacing_box_sample", &LOC)
}
// Fila de demostración con fondo gris para visualizar los límites de cada caja.
fn demo_row(flex: Flex) -> Container {
Container::new()
.with_prop(PropsOp::add_classes("spacing-demo-row"))
.with_flex(flex)
}
// Título y fragmento de código que introducen cada demostración.
fn caption(title: Lc, code: Lc) -> Html {
Html::with(move |cx| {
html! {
h3 { (title.using(cx)) }
p { code class="spacing-demo-code" { (code.using(cx)) } }
}
})
}
#[pagetop::main]
async fn main() -> std::io::Result<()> {
Application::prepare(&IntroSpacing).await.run().await
}