Container y Navbar lo adoptan vía `with_flex()`/`PropsOp::flex_item()`. Y se elimina `ButtonSet` porque su funcionalidad queda cubierta por este mecanismo más general. Incluye el ejemplo `examples/intro-flex.rs` con los patrones de uso.
167 lines
6.5 KiB
Rust
167 lines
6.5 KiB
Rust
use crate::html::flex::props_item::{
|
|
ItemAlign, ItemGrow, ItemOffset, ItemOrder, ItemShrink, ItemSize,
|
|
};
|
|
use crate::html::props::{Props, PropsOp};
|
|
use crate::{AutoDefault, Getters, builder_impl};
|
|
|
|
// **< FlexItem >***********************************************************************************
|
|
|
|
/// Configuración de un elemento como ítem de un contenedor Flexbox.
|
|
///
|
|
/// A diferencia de [`Flex`](crate::html::flex::Flex), que configura el comportamiento Flexbox
|
|
/// global de un contenedor y sus hijos como grupo, `FlexItem` configura un único elemento en
|
|
/// relación con el contenedor flex padre: crecimiento ([`ItemGrow`]), reducción ([`ItemShrink`]),
|
|
/// alineación individual ([`ItemAlign`]), orden visual ([`ItemOrder`]), ancho ([`ItemSize`]) y
|
|
/// desplazamiento ([`ItemOffset`]).
|
|
///
|
|
/// 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.
|
|
///
|
|
/// Con [`ItemSize`] y [`ItemOffset`] se pueden modelar rejillas de columnas fijas sobre Flexbox,
|
|
/// combinando un ancho en fracción del contenedor con un desplazamiento lateral cuando se necesite.
|
|
///
|
|
/// # Ejemplo
|
|
///
|
|
/// ```rust,no_run
|
|
/// use pagetop::prelude::*;
|
|
///
|
|
/// // Crece para ocupar el espacio sobrante, partiendo de ancho cero.
|
|
/// let title = Button::plain(Lc::n("Panel")).with_prop(PropsOp::flex_item(
|
|
/// 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(
|
|
/// FlexItem::new()
|
|
/// .with_size(flex::ItemSize::Percent33)
|
|
/// .with_offset(flex::ItemOffset::Percent33),
|
|
/// ));
|
|
/// ```
|
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq, Getters)]
|
|
pub struct FlexItem {
|
|
/// Devuelve el factor de crecimiento.
|
|
#[getters(copy)]
|
|
grow: ItemGrow,
|
|
/// Devuelve el factor de reducción.
|
|
#[getters(copy)]
|
|
shrink: ItemShrink,
|
|
/// Devuelve la alineación individual en el eje transversal.
|
|
#[getters(copy)]
|
|
align_self: ItemAlign,
|
|
/// Devuelve la posición en el orden visual.
|
|
#[getters(copy)]
|
|
order: ItemOrder,
|
|
/// Devuelve el ancho como fracción del contenedor.
|
|
#[getters(copy)]
|
|
size: ItemSize,
|
|
/// Devuelve el desplazamiento respecto al inicio del contenedor.
|
|
#[getters(copy)]
|
|
offset: ItemOffset,
|
|
}
|
|
|
|
#[builder_impl]
|
|
impl FlexItem {
|
|
/// Crea una configuración de ítem con todos los valores por defecto.
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
// **< FlexItem BUILDER >***********************************************************************
|
|
|
|
/// Establece el factor de crecimiento.
|
|
pub fn with_grow(mut self, grow: ItemGrow) -> Self {
|
|
self.grow = grow;
|
|
self
|
|
}
|
|
|
|
/// Establece el factor de reducción.
|
|
pub fn with_shrink(mut self, shrink: ItemShrink) -> Self {
|
|
self.shrink = shrink;
|
|
self
|
|
}
|
|
|
|
/// Establece la alineación individual en el eje transversal.
|
|
pub fn with_align_self(mut self, align_self: ItemAlign) -> Self {
|
|
self.align_self = align_self;
|
|
self
|
|
}
|
|
|
|
/// Establece la posición en el orden visual.
|
|
pub fn with_order(mut self, order: ItemOrder) -> Self {
|
|
self.order = order;
|
|
self
|
|
}
|
|
|
|
/// Establece el ancho como una fracción del contenedor (`flex-basis`). No fuerza
|
|
/// [`ItemShrink::Is0`](super::ItemShrink::Is0) por sí solo (consulta la documentación de
|
|
/// [`ItemSize`] antes de combinarlo con [`with_shrink()`](Self::with_shrink) porque con un
|
|
/// tamaño en porcentaje, forzar `ItemShrink::Is0` sólo es seguro si el contenedor no tiene
|
|
/// [`Gap`](super::Gap)).
|
|
pub fn with_size(mut self, size: ItemSize) -> Self {
|
|
self.size = size;
|
|
self
|
|
}
|
|
|
|
/// Establece el desplazamiento respecto al inicio del contenedor (`margin-inline-start`). No
|
|
/// tiene relación con [`push_end()`](Self::push_end) aunque aplican la misma propiedad CSS para
|
|
/// casos de uso distintos.
|
|
pub fn with_offset(mut self, offset: ItemOffset) -> Self {
|
|
self.offset = offset;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl FlexItem {
|
|
// Aplica esta configuración a un Props como declaraciones de estilo en línea.
|
|
pub(crate) fn apply_to(self, props: &mut Props) {
|
|
for (property, value) in [
|
|
("flex-grow", self.grow.value()),
|
|
("flex-shrink", self.shrink.value()),
|
|
("align-self", self.align_self.value()),
|
|
("order", self.order.value()),
|
|
("flex-basis", self.size.value()),
|
|
("margin-inline-start", self.offset.value()),
|
|
] {
|
|
props.alter_prop(PropsOp::add_style(property, value));
|
|
}
|
|
}
|
|
|
|
/// Separa un elemento (y los que le sigan en el mismo eje principal) del resto, empujándolo
|
|
/// hacia el extremo final de un contenedor flex.
|
|
///
|
|
/// Se resuelve siempre como margen inicial automático (`margin-inline-start: auto`) en línea,
|
|
/// igual que el resto de facetas de `FlexItem`. Es el mecanismo estándar de Flexbox para, por
|
|
/// ejemplo, separar dos menús dentro de una misma [`Navbar`](crate::base::component::Navbar)
|
|
/// -- uno pegado al inicio, el siguiente empujado al final -- sin que el contenedor necesite
|
|
/// conocer ninguna distinción entre sus elementos.
|
|
///
|
|
/// No forma parte de los campos de `FlexItem` (no se combina con `grow`/`shrink`/`align_self`/
|
|
/// `order`/`size`/`offset` en una misma llamada): es una función asociada independiente porque
|
|
/// resuelve un caso de uso completo por sí sola, con una sola línea, y vive aquí -- en vez de
|
|
/// como función suelta del módulo `flex` -- para dejar claro que es una operación de **ítem**,
|
|
/// no de contenedor.
|
|
///
|
|
/// # Ejemplo
|
|
///
|
|
/// ```rust,no_run
|
|
/// use pagetop::prelude::*;
|
|
///
|
|
/// let user_menu = Nav::new()
|
|
/// .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"));
|
|
/// ```
|
|
pub fn push_end() -> PropsOp {
|
|
PropsOp::add_style("margin-inline-start", "auto")
|
|
}
|
|
}
|
|
|
|
impl From<FlexItem> for PropsOp {
|
|
fn from(item: FlexItem) -> Self {
|
|
Self::flex_item(item)
|
|
}
|
|
}
|