Compare commits

...

2 commits

Author SHA1 Message Date
c2ec7568f0 (pagetop): Añade tests de Margin y Padding 2026-09-13 01:46:21 +02:00
3c3ffc91e6 ♻️ (pagetop): with_prop() acepta impl Into<PropsOp>
`FlexItem`, `Margin` y `Padding` se pasan directamente gracias a `From`
 hacia `PropsOp`, sin `PropsOp::flex_item()`/`margin()`/`padding()` ni
`.into()` explícito. Revisadas todas las llamadas en el código para
simplificarlas.
2026-09-13 01:38:33 +02:00
50 changed files with 324 additions and 165 deletions

View file

@ -217,9 +217,9 @@ fn align_self_block() -> Block {
.with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))),
)
.with_child(sized_box(Lc::t("flex_box_tall", &LOC), "2.5rem 1rem"))
.with_child(demo_box(flex_item("1")).with_prop(PropsOp::flex_item(
FlexItem::new().with_align_self(align_self),
)))
.with_child(
demo_box(flex_item("1")).with_prop(FlexItem::new().with_align_self(align_self)),
)
.with_child(sized_box(Lc::t("flex_box_tall", &LOC), "2.5rem 1rem")),
);
}
@ -238,9 +238,10 @@ fn align_self_block() -> Block {
sized_box(Lc::t("flex_box_tall", &LOC), "2.5rem 1rem")
.with_prop(PropsOp::add_style("font-size", "1.75rem")),
)
.with_child(demo_box(flex_item("1")).with_prop(PropsOp::flex_item(
FlexItem::new().with_align_self(flex::ItemAlign::Baseline),
)))
.with_child(
demo_box(flex_item("1"))
.with_prop(FlexItem::new().with_align_self(flex::ItemAlign::Baseline)),
)
.with_child(sized_box(Lc::t("flex_box_tall", &LOC), "2.5rem 1rem")),
)
}
@ -318,9 +319,8 @@ fn grow_shrink_block() -> Block {
demo_row(Flex::new().with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))))
.with_child(demo_box(Lc::t("flex_box_fixed", &LOC)))
.with_child(
demo_box(Lc::t("flex_box_grows", &LOC)).with_prop(PropsOp::flex_item(
FlexItem::new().with_grow(flex::ItemGrow::Is1),
)),
demo_box(Lc::t("flex_box_grows", &LOC))
.with_prop(FlexItem::new().with_grow(flex::ItemGrow::Is1)),
)
.with_child(demo_box(Lc::t("flex_box_fixed", &LOC))),
)
@ -337,9 +337,7 @@ fn grow_shrink_block() -> Block {
.with_child(
demo_box(flex_item("2"))
.with_prop(PropsOp::add_style("width", "10.5rem"))
.with_prop(PropsOp::flex_item(
FlexItem::new().with_shrink(flex::ItemShrink::Is0),
)),
.with_prop(FlexItem::new().with_shrink(flex::ItemShrink::Is0)),
)
.with_child(
demo_box(flex_item("3")).with_prop(PropsOp::add_style("width", "10.5rem")),
@ -359,9 +357,7 @@ fn other_block() -> Block {
demo_row(Flex::new().with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))))
.with_child(demo_box(Lc::t("flex_box_start_1", &LOC)))
.with_child(demo_box(Lc::t("flex_box_start_2", &LOC)))
.with_child(
demo_box(Lc::t("flex_box_end", &LOC)).with_prop(FlexItem::push_end().into()),
),
.with_child(demo_box(Lc::t("flex_box_end", &LOC)).with_prop(FlexItem::push_end())),
);
let mut wrap_row = demo_row(
@ -394,15 +390,17 @@ fn other_block() -> Block {
))
.with_child(
demo_row(Flex::new().with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))))
.with_child(demo_box(Lc::n("A")).with_prop(PropsOp::flex_item(
FlexItem::new().with_order(flex::ItemOrder::Last),
)))
.with_child(
demo_box(Lc::n("A"))
.with_prop(FlexItem::new().with_order(flex::ItemOrder::Last)),
)
.with_child(demo_box(Lc::n("B")))
.with_child(demo_box(Lc::n("C")))
.with_child(demo_box(Lc::n("D")))
.with_child(demo_box(Lc::n("E")).with_prop(PropsOp::flex_item(
FlexItem::new().with_order(flex::ItemOrder::First),
))),
.with_child(
demo_box(Lc::n("E"))
.with_prop(FlexItem::new().with_order(flex::ItemOrder::First)),
),
)
.with_child(caption(
Lc::t("flex_title_gap_none", &LOC),
@ -430,15 +428,18 @@ fn other_block() -> Block {
))
.with_child(
demo_row(Flex::new())
.with_child(demo_box(Lc::n("1/3")).with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Percent33),
)))
.with_child(demo_box(Lc::n("1/3")).with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Percent33),
)))
.with_child(demo_box(Lc::n("1/3")).with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Percent33),
))),
.with_child(
demo_box(Lc::n("1/3"))
.with_prop(FlexItem::new().with_size(flex::ItemSize::Percent33)),
)
.with_child(
demo_box(Lc::n("1/3"))
.with_prop(FlexItem::new().with_size(flex::ItemSize::Percent33)),
)
.with_child(
demo_box(Lc::n("1/3"))
.with_prop(FlexItem::new().with_size(flex::ItemSize::Percent33)),
),
)
.with_child(caption(
Lc::t("flex_title_grid_offset", &LOC),
@ -450,11 +451,11 @@ fn other_block() -> Block {
))
.with_child(
demo_row(Flex::new()).with_child(
demo_box(Lc::t("flex_box_half_centered", &LOC)).with_prop(PropsOp::flex_item(
demo_box(Lc::t("flex_box_half_centered", &LOC)).with_prop(
FlexItem::new()
.with_size(flex::ItemSize::Percent50)
.with_offset(flex::ItemOffset::Percent25),
)),
),
),
)
.with_child(caption(
@ -470,9 +471,7 @@ fn other_block() -> Block {
.with_child(demo_box(Lc::t("flex_box_file", &LOC)))
.with_child(demo_box(Lc::t("flex_box_edit", &LOC)))
.with_child(demo_box(Lc::t("flex_box_view", &LOC)))
.with_child(
demo_box(Lc::t("flex_box_profile", &LOC)).with_prop(FlexItem::push_end().into()),
)
.with_child(demo_box(Lc::t("flex_box_profile", &LOC)).with_prop(FlexItem::push_end()))
.with_child(demo_box(Lc::t("flex_box_logout", &LOC))),
)
}

View file

@ -92,12 +92,12 @@ fn grid_block() -> Block {
row = row.with_child(
Container::new()
.with_prop(PropsOp::add_style("padding", "0.25rem"))
.with_prop(PropsOp::flex_item(
.with_prop(
FlexItem::new()
.with_size(flex::ItemSize::Percent100)
.with_size_at(Breakpoint::Sm, flex::ItemSize::Percent50)
.with_size_at(Breakpoint::Md, flex::ItemSize::Percent33),
))
)
.with_child(demo_box(card_label(n))),
);
}
@ -154,9 +154,7 @@ fn order_block() -> Block {
demo_row(Flex::at(Breakpoint::Lg).with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))))
.with_child(demo_box(Lc::t("responsive_box_content", &LOC)))
.with_child(demo_box(Lc::t("responsive_box_sidebar", &LOC)).with_prop(
PropsOp::flex_item(
FlexItem::new().with_order_at(Breakpoint::Lg, flex::ItemOrder::First),
),
FlexItem::new().with_order_at(Breakpoint::Lg, flex::ItemOrder::First),
)),
)
}
@ -181,11 +179,10 @@ fn gap_grow_block() -> Block {
)
.with_child(demo_box(Lc::t("responsive_box_file", &LOC)))
.with_child(demo_box(Lc::t("responsive_box_edit", &LOC)))
.with_child(demo_box(Lc::t("responsive_box_search", &LOC)).with_prop(
PropsOp::flex_item(
FlexItem::new().with_grow_at(Breakpoint::Md, flex::ItemGrow::Is1),
),
)),
.with_child(
demo_box(Lc::t("responsive_box_search", &LOC))
.with_prop(FlexItem::new().with_grow_at(Breakpoint::Md, flex::ItemGrow::Is1)),
),
)
}

View file

@ -66,9 +66,8 @@ fn padding_block() -> Block {
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()),
),
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))),
);
}
@ -90,8 +89,7 @@ fn padding_block() -> Block {
.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(),
.with_start(UnitValue::RelRem(0.5)),
),
),
)
@ -108,8 +106,7 @@ fn layout_block() -> Block {
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()),
demo_box(box_sample()).with_prop(Margin::new().with_x(UnitValue::RelRem(1.0))),
)
.with_child(demo_box(box_sample())),
)
@ -120,10 +117,10 @@ fn layout_block() -> Block {
.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(
.with_prop(Margin::new().with_x(UnitValue::Auto))
.with_prop(
FlexItem::new().with_size(flex::ItemSize::Custom(UnitValue::RelRem(8.0))),
)),
),
),
)
}
@ -146,8 +143,7 @@ fn responsive_block() -> Block {
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(),
.with_all_at(Breakpoint::Lg, UnitValue::RelRem(4.0)),
),
),
)
@ -160,16 +156,16 @@ fn combined_block() -> Block {
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_prop(Margin::new().with_y(UnitValue::RelRem(1.0)))",
".with_prop(Padding::new().with_all(UnitValue::RelRem(1.5)))",
)),
))
.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_prop(Margin::new().with_y(UnitValue::RelRem(1.0)))
.with_prop(Padding::new().with_all(UnitValue::RelRem(1.5)))
.with_child(
Button::plain(Lc::t("spacing_box_card_button", &LOC))
.with_style(button::Style::Solid(Intent::Warning)),

View file

@ -83,7 +83,7 @@ impl Extension for SuperMenu {
.with_item(bs::navbar::Item::nav(
bs::Nav::new()
// Empuja este menú (y lo que le siga) al extremo final de la barra.
.with_prop(FlexItem::push_end().into())
.with_prop(FlexItem::push_end())
.with_item(bs::nav::Item::link(
Lc::t("menus_item_sign_up", &LOC),
"/auth/sign-up",

View file

@ -111,7 +111,7 @@ impl Icon {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -101,7 +101,7 @@ impl Offcanvas {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -54,7 +54,7 @@
//! pub fn new() -> Self { Self::default() }
//!
//! #[builder_fn]
//! pub fn with_prop(mut self, op: PropsOp) -> Self {
//! pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
//! self.props.alter_prop(op);
//! self
//! }

View file

@ -132,7 +132,7 @@ impl Component for RoleTable {
impl RoleTable {
// **< RoleTable BUILDER >**********************************************************************
pub(crate) fn with_prop(mut self, op: PropsOp) -> Self {
pub(crate) fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -104,7 +104,7 @@ impl Component for UserTable {
impl UserTable {
// **< UserTable BUILDER >**********************************************************************
pub(crate) fn with_prop(mut self, op: PropsOp) -> Self {
pub(crate) fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -122,7 +122,7 @@ impl Badge {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -61,7 +61,7 @@ impl Block {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -88,7 +88,7 @@ impl Brand {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -78,7 +78,7 @@ impl Breadcrumb {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -73,7 +73,7 @@ impl Crumb {
}
/// Modifica identificador, clases CSS o atributos HTML del elemento.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -193,7 +193,7 @@ impl Button {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -133,7 +133,7 @@ impl Container {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -114,7 +114,7 @@ impl Dialog {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -141,7 +141,7 @@ impl Dropdown {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -264,7 +264,7 @@ impl Item {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -189,7 +189,7 @@ impl Field {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -159,7 +159,7 @@ impl Checkbox {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -100,7 +100,7 @@ impl Form {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -78,7 +78,7 @@ impl Fieldset {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -398,7 +398,7 @@ impl Field {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -132,7 +132,7 @@ impl Number {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -206,7 +206,7 @@ impl Field {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -117,7 +117,7 @@ impl Range {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -316,7 +316,7 @@ impl Field {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -146,7 +146,7 @@ impl Textarea {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -110,7 +110,7 @@ impl Image {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -80,7 +80,7 @@ impl Messages {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -80,7 +80,7 @@ impl Nav {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -275,7 +275,7 @@ impl Item {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -209,7 +209,7 @@ impl Navbar {
}
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -346,7 +346,7 @@ impl Pager {
}
/// Modifica identificador, clases CSS o atributos HTML del componente.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -51,7 +51,7 @@ impl Cell {
}
/// Modifica identificador, clases CSS o atributos HTML de la celda.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -46,7 +46,7 @@ impl Column {
}
/// Modifica identificador, clases CSS o atributos HTML de la columna.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -129,7 +129,7 @@ impl Table {
}
/// Modifica identificador, clases CSS o atributos HTML de la tabla.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -66,7 +66,7 @@ impl SortLink {
/// Modifica los atributos HTML del enlace. Es el punto de extensión para añadir atributos de
/// interactividad sin que `Table` necesite conocerlos.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -35,7 +35,7 @@ impl Row {
}
/// Modifica identificador, clases CSS o atributos HTML de la fila.
pub fn with_prop(mut self, op: PropsOp) -> Self {
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
self.props.alter_prop(op);
self
}

View file

@ -7,8 +7,8 @@
//! [`FlexItem`] configura, en cambio, un único elemento en relación con el contenedor flex de su
//! padre (crecimiento, reducción, alineación individual, orden, ancho y desplazamiento). No tiene
//! un builder propio ya que puede acabar aplicándose sobre cualquier componente (no sólo los que
//! ofrecen `with_flex()`). Por eso se aplica con [`PropsOp::flex_item()`] sobre el `with_prop()`
//! que normalmente ya expone cualquier componente.
//! ofrecen `with_flex()`). Por eso se aplica pasándolo directamente al `with_prop()` que
//! normalmente ya expone cualquier componente, gracias a su `From` hacia [`PropsOp`].
//!
//! # Un entorno nativo autosuficiente
//!
@ -22,7 +22,7 @@
//!
//! [Flexbox]: https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout
//! [`AssetsOp::add_responsive_style()`]: crate::core::component::AssetsOp::add_responsive_style
//! [`PropsOp::flex_item()`]: crate::html::props::PropsOp::flex_item
//! [`PropsOp`]: crate::html::props::PropsOp
//! [`Container`]: crate::base::component::Container
//! [`Navbar`]: crate::base::component::Navbar

View file

@ -14,8 +14,8 @@ use crate::{AutoDefault, Getters, builder_impl, util};
///
/// No tiene un builder dedicado en ningún componente. De hecho, no tendría sentido porque cualquier
/// componente puede acabar siendo hijo de un contenedor flex, y ninguno debería necesitar un campo
/// propio para esto. Se aplica con [`PropsOp::flex_item()`] sobre el `with_prop()` que suele
/// exponer cualquier componente.
/// propio para esto. Se aplica sobre el `with_prop()` que suele exponer cualquier componente, que
/// acepta `FlexItem` directamente gracias a su `From` hacia [`PropsOp`].
///
/// Con [`ItemSize`] y [`ItemOffset`] se pueden modelar rejillas de columnas fijas sobre Flexbox,
/// combinando un tamaño en fracción del contenedor con un desplazamiento lateral cuando se
@ -27,18 +27,18 @@ use crate::{AutoDefault, Getters, builder_impl, util};
/// use pagetop::prelude::*;
///
/// // Crece para ocupar el espacio sobrante, partiendo de ancho cero.
/// let panel = Button::plain(Lc::n("Panel")).with_prop(PropsOp::flex_item(
/// let panel = Button::plain(Lc::n("Panel")).with_prop(
/// FlexItem::new()
/// .with_grow(flex::ItemGrow::Is1)
/// .with_size(flex::ItemSize::Custom(UnitValue::Zero)),
/// ));
/// );
///
/// // Ocupa un tercio del ancho del contenedor, desplazado otro tercio desde el inicio.
/// let column = Container::new().with_prop(PropsOp::flex_item(
/// let column = Container::new().with_prop(
/// FlexItem::new()
/// .with_size(flex::ItemSize::Percent33)
/// .with_offset(flex::ItemOffset::Percent33),
/// ));
/// );
/// ```
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq, Getters)]
pub struct FlexItem {
@ -89,7 +89,7 @@ impl FlexItem {
/// use pagetop::prelude::*;
///
/// let user_menu = Nav::new()
/// .with_prop(FlexItem::push_end().into())
/// .with_prop(FlexItem::push_end())
/// .with_item(nav::Item::link(Lc::n("Profile"), "/profile"))
/// .with_item(nav::Item::link(Lc::n("Sign out"), "/sign-out"));
/// ```

View file

@ -177,7 +177,7 @@ use std::panic::Location;
/// #[builder_impl]
/// impl MyButton {
/// /// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
/// pub fn with_prop(mut self, op: PropsOp) -> Self {
/// pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
/// self.props.alter_prop(op);
/// self
/// }
@ -218,9 +218,10 @@ impl Props {
/// Modifica el identificador, las clases, los atributos o los valores extra según la operación
/// indicada, incluido el posicionamiento Flexbox y el espaciado (`FlexItem`, `Margin`,
/// `Padding`). El método recomendado para construir cada operación es usar los constructores de
/// [`PropsOp`].
pub fn with_prop(mut self, op: PropsOp) -> Self {
match op {
/// [`PropsOp`], aunque `FlexItem`, `Margin` y `Padding` pueden pasarse directamente gracias a
/// sus `From` hacia `PropsOp`.
pub fn with_prop(mut self, op: impl Into<PropsOp>) -> Self {
match op.into() {
PropsOp::SetId(value) => {
self.apply_id(value.as_ref());
}

View file

@ -1,9 +1,9 @@
//! Márgenes y relleno nativos aplicados a componentes.
//!
//! [`Margin`] y [`Padding`] configuran márgenes externos y relleno interno por lado lógico y punto
//! de corte. Aplican sobre cualquier componente. Se usan igual que [`FlexItem`], con
//! [`PropsOp::margin()`] y [`PropsOp::padding()`] sobre el `with_prop()` que normalmente ya expone
//! cualquier componente.
//! de corte. Aplican sobre cualquier componente. Se usan igual que [`FlexItem`]: pasándolos
//! directamente al `with_prop()` que normalmente ya expone cualquier componente, gracias a su
//! `From` hacia [`PropsOp`].
//!
//! # Ejemplo
//!
@ -11,17 +11,14 @@
//! use pagetop::prelude::*;
//!
//! // Margen exterior arriba/abajo y relleno interno por los cuatro lados.
//! // `Margin` y `Padding` implementan `From` para `PropsOp`, así que `with_prop()`
//! // acepta `.into()` en vez de `PropsOp::margin()`/`PropsOp::padding()`.
//! let card = 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_prop(Margin::new().with_y(UnitValue::RelRem(1.0)))
//! .with_prop(Padding::new().with_all(UnitValue::RelRem(1.5)))
//! .with_child(Button::plain(Lc::n("Aceptar")));
//! ```
//!
//! [`FlexItem`]: crate::html::flex::FlexItem
//! [`PropsOp::margin()`]: crate::html::props::PropsOp::margin
//! [`PropsOp::padding()`]: crate::html::props::PropsOp::padding
//! [`PropsOp`]: crate::html::props::PropsOp
mod margin;
pub use margin::Margin;

View file

@ -6,8 +6,8 @@ use crate::{AutoDefault, Getters, builder_impl, util};
/// Configuración de márgenes externos por lado lógico y punto de corte.
///
/// No tiene relación con Flexbox. Se aplica sobre cualquier componente, con [`PropsOp::margin()`]
/// desde el `with_prop()` que suele exponer cualquier componente.
/// No tiene relación con Flexbox. Se aplica sobre cualquier componente, pasándolo directamente al
/// `with_prop()` que suele exponer cualquier componente, gracias a su `From` hacia [`PropsOp`].
///
/// Cada lado admite cualquier [`UnitValue`], incluido [`UnitValue::Auto`] (por ejemplo, para
/// centrar un bloque con `margin-inline: auto`). Los lados lógicos `start`/`end` se traducen a
@ -19,11 +19,11 @@ use crate::{AutoDefault, Getters, builder_impl, util};
/// use pagetop::prelude::*;
///
/// // Centra el bloque horizontalmente y añade espacio inferior.
/// let panel = Container::new().with_prop(PropsOp::margin(
/// let panel = Container::new().with_prop(
/// Margin::new()
/// .with_x(UnitValue::Auto)
/// .with_bottom(UnitValue::RelRem(1.5)),
/// ));
/// );
/// ```
///
/// [`Flex`]: crate::html::flex::Flex

View file

@ -7,7 +7,8 @@ use crate::{AutoDefault, Getters, builder_impl, util};
/// Configuración de relleno interno por lado lógico y punto de corte.
///
/// Mismo mecanismo y criterio de uso que [`Margin`](super::Margin): no tiene relación con Flexbox,
/// y se aplica sobre cualquier componente vía [`PropsOp::padding()`] desde su `with_prop()`.
/// y se aplica sobre cualquier componente pasándolo directamente a su `with_prop()`, gracias a su
/// `From` hacia [`PropsOp`].
///
/// A diferencia de `Margin`, [`UnitValue::Auto`] no tiene efecto en ningún lado ya que CSS no
/// admite `padding: auto`, así que un lado establecido a `Auto` se ignora como si no se hubiera
@ -18,11 +19,11 @@ use crate::{AutoDefault, Getters, builder_impl, util};
/// ```rust,no_run
/// use pagetop::prelude::*;
///
/// let card = Container::new().with_prop(PropsOp::padding(
/// let card = Container::new().with_prop(
/// Padding::new()
/// .with_all(UnitValue::RelRem(1.0))
/// .with_bottom_at(Breakpoint::Md, UnitValue::RelRem(2.0)),
/// ));
/// );
/// ```
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq, Getters)]
pub struct Padding {

View file

@ -176,9 +176,7 @@ async fn flex_and_flex_item_classes_keep_their_order() {
let mut container = Container::new()
.with_prop(PropsOp::add_classes("own"))
.with_flex(Flex::new().with_direction(flex::Direction::Column))
.with_prop(PropsOp::flex_item(
FlexItem::new().with_grow(flex::ItemGrow::Is1),
))
.with_prop(FlexItem::new().with_grow(flex::ItemGrow::Is1))
.with_child(Lc::n("x"));
let html = container.render(&mut cx).await.into_string();

View file

@ -69,7 +69,7 @@ async fn flex_gap_adds_a_style_to_the_content_area() {
#[pagetop::test]
async fn push_end_adds_an_automatic_start_margin() {
let mut cx = Context::default();
let mut nav = one_link_nav().with_prop(FlexItem::push_end().into());
let mut nav = one_link_nav().with_prop(FlexItem::push_end());
let html = nav.render(&mut cx).await.into_string();
let assets = cx.render_assets().into_string();

View file

@ -3,7 +3,7 @@ use pagetop::prelude::*;
#[pagetop::test]
async fn default_flex_item_adds_nothing() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(FlexItem::new()));
let props = Props::default().with_prop(FlexItem::new());
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert_eq!(html, "<span></span>");
@ -13,9 +13,7 @@ async fn default_flex_item_adds_nothing() {
#[pagetop::test]
async fn grow_adds_flex_grow_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_grow(flex::ItemGrow::Is1),
));
let props = Props::default().with_prop(FlexItem::new().with_grow(flex::ItemGrow::Is1));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -26,9 +24,7 @@ async fn grow_adds_flex_grow_style() {
#[pagetop::test]
async fn shrink_adds_flex_shrink_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_shrink(flex::ItemShrink::Is0),
));
let props = Props::default().with_prop(FlexItem::new().with_shrink(flex::ItemShrink::Is0));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -39,9 +35,8 @@ async fn shrink_adds_flex_shrink_style() {
#[pagetop::test]
async fn align_self_adds_matching_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_align_self(flex::ItemAlign::Center),
));
let props =
Props::default().with_prop(FlexItem::new().with_align_self(flex::ItemAlign::Center));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -52,9 +47,7 @@ async fn align_self_adds_matching_style() {
#[pagetop::test]
async fn order_adds_matching_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_order(flex::ItemOrder::First),
));
let props = Props::default().with_prop(FlexItem::new().with_order(flex::ItemOrder::First));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -65,9 +58,7 @@ async fn order_adds_matching_style() {
#[pagetop::test]
async fn size_percent_adds_flex_basis_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Percent33),
));
let props = Props::default().with_prop(FlexItem::new().with_size(flex::ItemSize::Percent33));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -78,9 +69,8 @@ async fn size_percent_adds_flex_basis_style() {
#[pagetop::test]
async fn size_custom_adds_flex_basis_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_size(flex::ItemSize::Custom(UnitValue::Zero)),
));
let props = Props::default()
.with_prop(FlexItem::new().with_size(flex::ItemSize::Custom(UnitValue::Zero)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -91,9 +81,8 @@ async fn size_custom_adds_flex_basis_style() {
#[pagetop::test]
async fn offset_percent_adds_margin_inline_start_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_offset(flex::ItemOffset::Percent33),
));
let props =
Props::default().with_prop(FlexItem::new().with_offset(flex::ItemOffset::Percent33));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -104,9 +93,8 @@ async fn offset_percent_adds_margin_inline_start_style() {
#[pagetop::test]
async fn offset_custom_adds_margin_inline_start_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_offset(flex::ItemOffset::Custom(UnitValue::Px(16))),
));
let props = Props::default()
.with_prop(FlexItem::new().with_offset(flex::ItemOffset::Custom(UnitValue::Px(16))));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -117,7 +105,7 @@ async fn offset_custom_adds_margin_inline_start_style() {
#[pagetop::test]
async fn combines_several_facets_in_one_call() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
let props = Props::default().with_prop(
FlexItem::new()
.with_grow(flex::ItemGrow::Is1)
.with_shrink(flex::ItemShrink::Is0)
@ -125,7 +113,7 @@ async fn combines_several_facets_in_one_call() {
.with_order(flex::ItemOrder::Is2)
.with_size(flex::ItemSize::Custom(UnitValue::Zero))
.with_offset(flex::ItemOffset::Percent10),
));
);
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -146,9 +134,8 @@ async fn combines_several_facets_in_one_call() {
#[pagetop::test]
async fn size_at_a_breakpoint_combines_token_and_suffix() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
FlexItem::new().with_size_at(Breakpoint::Md, flex::ItemSize::Percent33),
));
let props = Props::default()
.with_prop(FlexItem::new().with_size_at(Breakpoint::Md, flex::ItemSize::Percent33));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
@ -163,12 +150,12 @@ async fn size_at_a_breakpoint_combines_token_and_suffix() {
#[pagetop::test]
async fn base_and_breakpoint_values_generate_one_class_each() {
let mut cx = Context::default();
let props = Props::default().with_prop(PropsOp::flex_item(
let props = Props::default().with_prop(
FlexItem::new()
.with_grow(flex::ItemGrow::Default)
.with_size(flex::ItemSize::Percent50)
.with_size_at(Breakpoint::Lg, flex::ItemSize::Percent25),
));
);
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
// `ItemGrow::Default` resolves to an empty CSS value, so it adds no class at all.
@ -180,7 +167,7 @@ async fn base_and_breakpoint_values_generate_one_class_each() {
async fn from_flex_item_for_props_op() {
let mut cx = Context::default();
let item = FlexItem::new().with_grow(flex::ItemGrow::Is1);
let props = Props::default().with_prop(item.into());
let props = Props::default().with_prop(item);
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();

183
tests/html_props_spacing.rs Normal file
View file

@ -0,0 +1,183 @@
use pagetop::prelude::*;
#[pagetop::test]
async fn default_margin_adds_nothing() {
let mut cx = Context::default();
let props = Props::default().with_prop(Margin::new());
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert_eq!(html, "<span></span>");
assert!(cx.render_assets().into_string().is_empty());
}
#[pagetop::test]
async fn default_padding_adds_nothing() {
let mut cx = Context::default();
let props = Props::default().with_prop(Padding::new());
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert_eq!(html, "<span></span>");
assert!(cx.render_assets().into_string().is_empty());
}
#[pagetop::test]
async fn margin_top_adds_matching_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(Margin::new().with_top(UnitValue::RelRem(1.5)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_margin-top_1_5rem_""#));
assert!(assets.contains("_margin-top_1_5rem_{margin-top:1.5rem}"));
}
#[pagetop::test]
async fn margin_x_sets_start_and_end() {
let mut cx = Context::default();
let props = Props::default().with_prop(Margin::new().with_x(UnitValue::Auto));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_margin-start_auto_ _margin-end_auto_""#));
assert!(assets.contains("_margin-start_auto_{margin-inline-start:auto}"));
assert!(assets.contains("_margin-end_auto_{margin-inline-end:auto}"));
}
#[pagetop::test]
async fn margin_y_sets_top_and_bottom() {
let mut cx = Context::default();
let props = Props::default().with_prop(Margin::new().with_y(UnitValue::Zero));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_margin-top_0_ _margin-bottom_0_""#));
assert!(assets.contains("_margin-top_0_{margin-top:0}"));
assert!(assets.contains("_margin-bottom_0_{margin-bottom:0}"));
}
#[pagetop::test]
async fn margin_all_sets_every_side() {
let mut cx = Context::default();
let props = Props::default().with_prop(Margin::new().with_all(UnitValue::Px(8)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert!(html.contains(
r#"class="_margin-top_8px_ _margin-bottom_8px_ _margin-start_8px_ _margin-end_8px_""#
));
}
#[pagetop::test]
async fn margin_at_a_breakpoint_combines_token_and_suffix() {
let mut cx = Context::default();
let props =
Props::default().with_prop(Margin::new().with_top_at(Breakpoint::Md, UnitValue::Px(16)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_margin-top_16px_md_""#));
assert!(assets.contains("@media(min-width:768px){._margin-top_16px_md_{margin-top:16px}}"));
}
#[pagetop::test]
async fn successive_with_prop_calls_merge_margin_sides() {
let mut cx = Context::default();
let props = Props::default()
.with_prop(Margin::new().with_top(UnitValue::Px(8)))
.with_prop(Margin::new().with_bottom(UnitValue::Px(16)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
// Each `with_prop()` only sets one side; `Margin::merge()` keeps both instead of letting the
// second call overwrite the first one entirely.
assert!(html.contains(r#"class="_margin-top_8px_ _margin-bottom_16px_""#));
}
#[pagetop::test]
async fn later_with_prop_call_overrides_same_side() {
let mut cx = Context::default();
let props = Props::default()
.with_prop(Margin::new().with_top(UnitValue::Px(8)))
.with_prop(Margin::new().with_top(UnitValue::Px(24)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert!(html.contains(r#"class="_margin-top_24px_""#));
assert!(!html.contains("_margin-top_8px_"));
}
#[pagetop::test]
async fn padding_top_adds_matching_style() {
let mut cx = Context::default();
let props = Props::default().with_prop(Padding::new().with_top(UnitValue::RelRem(1.0)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_padding-top_1rem_""#));
assert!(assets.contains("_padding-top_1rem_{padding-top:1rem}"));
}
#[pagetop::test]
async fn padding_all_sets_every_side() {
let mut cx = Context::default();
let props = Props::default().with_prop(Padding::new().with_all(UnitValue::RelRem(1.0)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert!(html.contains(
r#"class="_padding-top_1rem_ _padding-bottom_1rem_ _padding-start_1rem_ _padding-end_1rem_""#
));
}
#[pagetop::test]
async fn padding_at_a_breakpoint_combines_token_and_suffix() {
let mut cx = Context::default();
let props = Props::default()
.with_prop(Padding::new().with_bottom_at(Breakpoint::Lg, UnitValue::RelRem(2.0)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
let assets = cx.render_assets().into_string();
assert!(html.contains(r#"class="_padding-bottom_2rem_lg_""#));
assert!(
assets.contains("@media(min-width:992px){._padding-bottom_2rem_lg_{padding-bottom:2rem}}")
);
}
#[pagetop::test]
async fn padding_auto_is_ignored() {
let mut cx = Context::default();
// CSS does not support `padding: auto`; unlike `Margin`, a side set to `Auto` adds nothing.
let props = Props::default().with_prop(Padding::new().with_all(UnitValue::Auto));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert_eq!(html, "<span></span>");
assert!(cx.render_assets().into_string().is_empty());
}
#[pagetop::test]
async fn margin_and_padding_combine_on_same_component() {
let mut cx = Context::default();
let props = Props::default()
.with_prop(Margin::new().with_bottom(UnitValue::Px(8)))
.with_prop(Padding::new().with_top(UnitValue::Px(4)));
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
// Class order follows `Props::unpack()`: margin before padding.
assert!(html.contains(r#"class="_margin-bottom_8px_ _padding-top_4px_""#));
}
#[pagetop::test]
async fn from_margin_for_props_op() {
let mut cx = Context::default();
let margin = Margin::new().with_top(UnitValue::Px(8));
let props = Props::default().with_prop(margin);
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert!(html.contains(r#"class="_margin-top_8px_""#));
}
#[pagetop::test]
async fn from_padding_for_props_op() {
let mut cx = Context::default();
let padding = Padding::new().with_top(UnitValue::Px(8));
let props = Props::default().with_prop(padding);
let html = html! { span (props.unpack(&mut cx)) {} }.into_string();
assert!(html.contains(r#"class="_padding-top_8px_""#));
}