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 for PropsOp { fn from(item: FlexItem) -> Self { Self::flex_item(item) } }