✨ (pagetop): Añade Grid y extrae Flex de Container
This commit is contained in:
parent
dfff12881f
commit
307ca6f62c
44 changed files with 2554 additions and 735 deletions
|
|
@ -1,278 +0,0 @@
|
|||
use crate::core::component::Context;
|
||||
use crate::core::theme::{Breakpoint, Responsive};
|
||||
use crate::html::flex::{Align, AlignContent, Behavior, ContentJustify, Direction, Gap};
|
||||
use crate::{AutoDefault, Getters, builder_impl, util};
|
||||
|
||||
// **< DisplayFlex >********************************************************************************
|
||||
|
||||
// Modo de activación del posicionamiento Flexbox de un contenedor `Flex`. Detalle interno de
|
||||
// implementación: la API pública sólo expone los constructores `Flex::new()`, `Flex::at()`,
|
||||
// `Flex::inline()` e `Flex::inline_at()`, nunca esta variante directamente.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
enum DisplayFlex {
|
||||
#[default]
|
||||
Always,
|
||||
AlwaysInline,
|
||||
At(Breakpoint),
|
||||
InlineAt(Breakpoint),
|
||||
}
|
||||
|
||||
// **< Flex >***************************************************************************************
|
||||
|
||||
/// Configuración para el posicionamiento Flexbox en un contenedor.
|
||||
///
|
||||
/// Se resuelve como clases CSS generadas dinámicamente (`display`, `flex-direction`, `flex-wrap`,
|
||||
/// `justify-content`, `align-items`, `align-content`, `gap`), registradas vía
|
||||
/// [`AssetsOp::add_responsive_style()`] en [`ResponsiveStyles`] y renderizadas como reglas en el
|
||||
/// `<head>` del documento. Son propiedades nativas que no requieren interpretación por parte de los
|
||||
/// temas, siempre funcionan igual, sin una sola línea de CSS ni de código específico.
|
||||
///
|
||||
/// El nombre de cada clase se deriva de la propiedad y el valor que representa (por ejemplo
|
||||
/// `_flex-direction_row_`), así que dos contenedores con la misma configuración comparten la misma
|
||||
/// regla generada en vez de duplicarla, y el nombre generado no coincide por accidente con clases
|
||||
/// de terceros.
|
||||
///
|
||||
/// [`AssetsOp::add_responsive_style()`]: crate::core::component::AssetsOp::add_responsive_style
|
||||
/// [`ResponsiveStyles`]: crate::html::ResponsiveStyles
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use pagetop::prelude::*;
|
||||
///
|
||||
/// let actions = Container::new()
|
||||
/// .with_flex(
|
||||
/// Flex::new()
|
||||
/// .with_justify(flex::ContentJustify::End)
|
||||
/// .with_align(flex::Align::Center)
|
||||
/// .with_gap(flex::Gap::Both(UnitValue::RelRem(0.5))),
|
||||
/// )
|
||||
/// .with_child(Button::submit(Lc::n("Save")))
|
||||
/// .with_child(Button::plain(Lc::n("Cancel")));
|
||||
/// ```
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq, Getters)]
|
||||
pub struct Flex {
|
||||
// Determina si esta configuración debe aplicarse (y con qué variante de `display`) o si
|
||||
// `Flex` no está en absoluto configurado. `None` es el estado real de ausencia: lo que tiene
|
||||
// un contenedor que nunca ha llamado a `with_flex()`. Sin getter público; `new()`, `at()`,
|
||||
// `inline()` e `inline_at()` son la única forma de activarlo.
|
||||
#[getters(skip)]
|
||||
display: Option<DisplayFlex>,
|
||||
/// Devuelve la dirección del eje principal por punto de corte.
|
||||
#[getters(copy)]
|
||||
direction: Responsive<Direction>,
|
||||
/// Devuelve el comportamiento cuando los elementos no caben en una sola línea, por punto de
|
||||
/// corte.
|
||||
#[getters(copy)]
|
||||
wrap: Responsive<Behavior>,
|
||||
/// Devuelve la alineación de los elementos en el eje principal, por punto de corte.
|
||||
#[getters(copy)]
|
||||
justify: Responsive<ContentJustify>,
|
||||
/// Devuelve la alineación de los elementos en el eje transversal, por punto de corte.
|
||||
#[getters(copy)]
|
||||
align: Responsive<Align>,
|
||||
/// Devuelve la alineación de las líneas cuando hay más de una, por punto de corte.
|
||||
#[getters(copy)]
|
||||
align_content: Responsive<AlignContent>,
|
||||
/// Devuelve el espaciado entre elementos, por punto de corte.
|
||||
#[getters(copy)]
|
||||
gap: Responsive<Gap>,
|
||||
}
|
||||
|
||||
#[builder_impl]
|
||||
impl Flex {
|
||||
/// Define una configuración Flex con `display: flex`, sin punto de corte: se aplica siempre.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
display: Some(DisplayFlex::Always),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Define una configuración Flex con `display: flex` que se aplica a partir del punto de corte
|
||||
/// indicado.
|
||||
pub fn at(bp: Breakpoint) -> Self {
|
||||
Self {
|
||||
display: Some(DisplayFlex::At(bp)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Define una configuración Flex con `display: inline-flex`, sin punto de corte: se aplica
|
||||
/// siempre.
|
||||
pub fn inline() -> Self {
|
||||
Self {
|
||||
display: Some(DisplayFlex::AlwaysInline),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Define una configuración Flex con `display: inline-flex` que se aplica a partir del punto de
|
||||
/// corte indicado.
|
||||
pub fn inline_at(bp: Breakpoint) -> Self {
|
||||
Self {
|
||||
display: Some(DisplayFlex::InlineAt(bp)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// **< Flex BUILDER >***************************************************************************
|
||||
|
||||
/// Establece la dirección del eje principal.
|
||||
pub fn with_direction(mut self, dir: Direction) -> Self {
|
||||
self.direction = self.direction.set(dir);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la dirección del eje principal a partir del punto de corte indicado.
|
||||
pub fn with_direction_at(mut self, bp: Breakpoint, dir: Direction) -> Self {
|
||||
self.direction = self.direction.set_at(bp, dir);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece el comportamiento cuando los elementos no caben en una sola línea.
|
||||
pub fn with_wrap(mut self, wrap: Behavior) -> Self {
|
||||
self.wrap = self.wrap.set(wrap);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece el comportamiento cuando los elementos no caben en una sola línea, a partir del
|
||||
/// punto de corte indicado.
|
||||
pub fn with_wrap_at(mut self, bp: Breakpoint, wrap: Behavior) -> Self {
|
||||
self.wrap = self.wrap.set_at(bp, wrap);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de los elementos en el eje principal.
|
||||
pub fn with_justify(mut self, justify: ContentJustify) -> Self {
|
||||
self.justify = self.justify.set(justify);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de los elementos en el eje principal, a partir del punto de corte
|
||||
/// indicado.
|
||||
pub fn with_justify_at(mut self, bp: Breakpoint, justify: ContentJustify) -> Self {
|
||||
self.justify = self.justify.set_at(bp, justify);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de los elementos en el eje transversal.
|
||||
pub fn with_align(mut self, align: Align) -> Self {
|
||||
self.align = self.align.set(align);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de los elementos en el eje transversal, a partir del punto de corte
|
||||
/// indicado.
|
||||
pub fn with_align_at(mut self, bp: Breakpoint, align: Align) -> Self {
|
||||
self.align = self.align.set_at(bp, align);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de las líneas cuando hay más de una (ver [`AlignContent`]).
|
||||
pub fn with_align_content(mut self, align_content: AlignContent) -> Self {
|
||||
self.align_content = self.align_content.set(align_content);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación de las líneas cuando hay más de una (ver [`AlignContent`]), a partir
|
||||
/// del punto de corte indicado.
|
||||
pub fn with_align_content_at(mut self, bp: Breakpoint, align_content: AlignContent) -> Self {
|
||||
self.align_content = self.align_content.set_at(bp, align_content);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece el espaciado entre elementos.
|
||||
pub fn with_gap(mut self, gap: Gap) -> Self {
|
||||
self.gap = self.gap.set(gap);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece el espaciado entre elementos, a partir del punto de corte indicado.
|
||||
pub fn with_gap_at(mut self, bp: Breakpoint, gap: Gap) -> Self {
|
||||
self.gap = self.gap.set_at(bp, gap);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Flex {
|
||||
/// Combina esta configuración con otra `Flex`, campo a campo, o la resetea a los valores por
|
||||
/// defecto si se pasa `None`.
|
||||
///
|
||||
/// Cada campo de `flex` que tenga un valor sustituye al correspondiente de `self`; los que
|
||||
/// estén a `None` dejan intacto el valor ya presente en `self`. Así, sucesivas llamadas pueden
|
||||
/// ir completando o sobrescribiendo campos concretos sin necesidad de repetir los ya
|
||||
/// establecidos. Es el método recomendado para que un contenedor propio adopte `Flex` de forma
|
||||
/// incremental (ver [`Container::with_flex()`](crate::base::component::Container::with_flex)
|
||||
/// como referencia de uso).
|
||||
pub fn merge(mut self, flex: impl Into<Option<Flex>>) -> Self {
|
||||
let Some(flex) = flex.into() else {
|
||||
return Flex::default();
|
||||
};
|
||||
self.display = flex.display.or(self.display);
|
||||
self.direction = self.direction.merge(flex.direction);
|
||||
self.wrap = self.wrap.merge(flex.wrap);
|
||||
self.justify = self.justify.merge(flex.justify);
|
||||
self.align = self.align.merge(flex.align);
|
||||
self.align_content = self.align_content.merge(flex.align_content);
|
||||
self.gap = self.gap.merge(flex.gap);
|
||||
self
|
||||
}
|
||||
|
||||
/// Aplica esta configuración a un [`Props`] como declaraciones de estilo en línea.
|
||||
///
|
||||
/// Es el método recomendado para que un componente adopte `Flex`: concentra en un único sitio
|
||||
/// la traducción de la configuración a estilos, para no repetirla en cada componente que la
|
||||
/// use. Precedente: [`Container`](crate::base::component::Container) lo aplica sobre su
|
||||
/// propio `Props`; [`Navbar`](crate::base::component::Navbar), sobre el `Props` de su área de
|
||||
/// contenido.
|
||||
///
|
||||
/// Las clases generadas se añaden a `classes`, separadas con un espacio de las que ya hubiera,
|
||||
/// para poder compartir un único acumulador con [`FlexItem::apply()`](super::FlexItem::apply)
|
||||
/// sin cadenas intermedias.
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn apply(self, cx: &mut Context, classes: &mut String) {
|
||||
// Sin `display` no hay contenedor Flex: el resto de facetas (`flex-direction`, `gap`...)
|
||||
// no tienen ningún efecto en CSS sin `display: flex`/`inline-flex`, así que ni se generan.
|
||||
let Some(display) = self.display else {
|
||||
return;
|
||||
};
|
||||
|
||||
use crate::html::responsive::{apply, responsive_class, styles, value_to_token};
|
||||
|
||||
let (prefix, value) = match display {
|
||||
DisplayFlex::Always
|
||||
| DisplayFlex::At(_) => ("_flex_", "flex"),
|
||||
DisplayFlex::AlwaysInline
|
||||
| DisplayFlex::InlineAt(_) => ("_inline-flex_", "inline-flex"),
|
||||
};
|
||||
let entry = match display {
|
||||
DisplayFlex::At(bp) | DisplayFlex::InlineAt(bp) => bp.resolved(cx),
|
||||
_ => None,
|
||||
};
|
||||
let class = match entry {
|
||||
None => prefix.into(),
|
||||
Some(entry) => util::join!(prefix, entry.name, "_").into(),
|
||||
};
|
||||
styles(cx, classes, entry, class, "display", value.into());
|
||||
|
||||
apply!(cx, classes, self.direction, "_flex-direction_", "flex-direction");
|
||||
apply!(cx, classes, self.wrap, "_flex-wrap_", "flex-wrap");
|
||||
apply!(cx, classes, self.justify, "_flex-justify_", "justify-content");
|
||||
apply!(cx, classes, self.align, "_flex-align-items_", "align-items");
|
||||
apply!(cx, classes, self.align_content, "_flex-align-content_", "align-content");
|
||||
for (bp, gap) in self.gap.by_breakpoint() {
|
||||
let entry = bp.resolved(cx);
|
||||
for (property, value) in gap.styles().into_iter().flatten() {
|
||||
// El prefijo de `gap` no es literal (depende de la propiedad), así que se compone
|
||||
// aquí en un único `join!` en vez de pasar por `responsive_class!`.
|
||||
let token = value_to_token(&value);
|
||||
let class = match entry {
|
||||
None => util::join!("_flex-", property, "_", token, "_"),
|
||||
Some(e) => util::join!("_flex-", property, "_", token, "_", e.name, "_"),
|
||||
};
|
||||
styles(cx, classes, entry, class.into(), property, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,36 @@
|
|||
use crate::core::component::Context;
|
||||
use crate::core::theme::{Breakpoint, Responsive};
|
||||
use crate::html::PropsOp;
|
||||
use crate::html::flex::{ItemAlign, ItemGrow, ItemOffset, ItemOrder, ItemShrink, ItemSize};
|
||||
use crate::html::align;
|
||||
use crate::html::flex::{ItemGrow, ItemOffset, ItemOrder, ItemShrink, ItemSize};
|
||||
use crate::{AutoDefault, Getters, builder_impl, util};
|
||||
|
||||
/// 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`]), tamaño ([`ItemSize`]) y
|
||||
/// desplazamiento ([`ItemOffset`]).
|
||||
/// A diferencia del componente [`Flex`], que define un contenedor que aplica Flexbox para el
|
||||
/// posicionamiento de sus componentes hijo, `FlexItem` se aplica sobre un único elemento en
|
||||
/// relación con el contenedor flex padre. Usa el método `with_prop()` que suele exponer cualquier
|
||||
/// componente, y acepta `FlexItem` directamente gracias a su `From` hacia [`PropsOp`].
|
||||
///
|
||||
/// 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 sobre el `with_prop()` que suele exponer cualquier componente, que
|
||||
/// acepta `FlexItem` directamente gracias a su `From` hacia [`PropsOp`].
|
||||
/// propio para esto.
|
||||
///
|
||||
/// `FlexItem` actúa sobre propiedades del elemento para configurar su crecimiento ([`ItemGrow`]),
|
||||
/// reducción ([`ItemShrink`]), alineación individual ([`align::ItemSelf`]), orden visual
|
||||
/// ([`ItemOrder`]), tamaño ([`ItemSize`]) y desplazamiento ([`ItemOffset`]).
|
||||
///
|
||||
/// Un hijo de `Flex` sin ningún `FlexItem` aplicado participa igualmente como ítem flex, sólo que
|
||||
/// sin crecimiento, reducción, alineación individual, orden, tamaño ni desplazamiento propios
|
||||
/// (todos sus valores por defecto).
|
||||
///
|
||||
/// 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
|
||||
/// necesite.
|
||||
///
|
||||
/// [`Flex`]: crate::base::component::Flex
|
||||
/// [`align::ItemSelf`]: crate::html::align::ItemSelf
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,no_run
|
||||
|
|
@ -50,7 +60,7 @@ pub struct FlexItem {
|
|||
shrink: Responsive<ItemShrink>,
|
||||
/// Devuelve la alineación individual en el eje transversal, por punto de corte.
|
||||
#[getters(copy)]
|
||||
align_self: Responsive<ItemAlign>,
|
||||
align_self: Responsive<align::ItemSelf>,
|
||||
/// Devuelve la posición en el orden visual, por punto de corte.
|
||||
#[getters(copy)]
|
||||
order: Responsive<ItemOrder>,
|
||||
|
|
@ -124,14 +134,14 @@ impl FlexItem {
|
|||
}
|
||||
|
||||
/// Establece la alineación individual en el eje transversal.
|
||||
pub fn with_align_self(mut self, align_self: ItemAlign) -> Self {
|
||||
pub fn with_align_self(mut self, align_self: align::ItemSelf) -> Self {
|
||||
self.align_self = self.align_self.set(align_self);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la alineación individual en el eje transversal, a partir del punto de corte
|
||||
/// indicado.
|
||||
pub fn with_align_self_at(mut self, bp: Breakpoint, align_self: ItemAlign) -> Self {
|
||||
pub fn with_align_self_at(mut self, bp: Breakpoint, align_self: align::ItemSelf) -> Self {
|
||||
self.align_self = self.align_self.set_at(bp, align_self);
|
||||
self
|
||||
}
|
||||
|
|
@ -152,7 +162,7 @@ impl FlexItem {
|
|||
/// [`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)).
|
||||
/// [`align::Gap`](crate::html::align::Gap)).
|
||||
pub fn with_size(mut self, size: ItemSize) -> Self {
|
||||
self.size = self.size.set(size);
|
||||
self
|
||||
|
|
@ -189,10 +199,12 @@ impl FlexItem {
|
|||
/// que sólo establezca `with_size_at(Breakpoint::Lg, ...)` no borra el `with_size()` base que
|
||||
/// `self` ya tuviera, sólo sustituye la entrada de ese punto de corte.
|
||||
///
|
||||
/// Es el método que usa [`Props::with_prop()`](crate::html::props::Props::with_prop) para que
|
||||
/// sucesivas [`PropsOp::FlexItem`](crate::html::props::PropsOp::FlexItem) sobre el mismo
|
||||
/// componente vayan completando campos concretos sin repetir los ya establecidos, en vez de
|
||||
/// partir de cero en cada llamada.
|
||||
/// Es el método que usa [`Props::with_prop()`] para que sucesivas [`PropsOp::FlexItem`] sobre
|
||||
/// el mismo componente vayan completando campos concretos sin repetir los ya establecidos, en
|
||||
/// vez de partir de cero en cada llamada.
|
||||
///
|
||||
/// [`Props::with_prop()`]: crate::html::props::Props::with_prop
|
||||
/// [`PropsOp::FlexItem`]: crate::html::props::PropsOp::FlexItem
|
||||
pub fn merge(mut self, item: FlexItem) -> Self {
|
||||
self.grow = self.grow.merge(item.grow);
|
||||
self.shrink = self.shrink.merge(item.shrink);
|
||||
|
|
@ -204,14 +216,13 @@ impl FlexItem {
|
|||
}
|
||||
|
||||
/// Aplica esta configuración como clases de utilidad responsive en el [`Context`], igual que
|
||||
/// [`Flex::apply()`](super::Flex::apply): cada faceta con valor añade una declaración de
|
||||
/// estilo (por punto de corte, si se ha establecido alguno) y su propia clase. Un campo sin
|
||||
/// ningún valor establecido, o con un valor cuya variante es la "por defecto" del propio enum
|
||||
/// (p. ej. `ItemGrow::Default`), no añade nada.
|
||||
/// hace internamente [`Flex`](crate::base::component::Flex): cada propiedad con valor añade una
|
||||
/// declaración de estilo (por punto de corte, si se ha establecido alguno) y su propia clase.
|
||||
/// Un campo sin ningún valor establecido, o con un valor cuya variante es la "por defecto" del
|
||||
/// propio enum (p. ej. `ItemGrow::Default`), no añade nada.
|
||||
///
|
||||
/// Las clases generadas se añaden a `classes`, separadas con un espacio de las que ya hubiera,
|
||||
/// para poder compartir un único acumulador con [`Flex::apply()`](super::Flex::apply) sin
|
||||
/// cadenas intermedias.
|
||||
/// para poder compartir un único acumulador con las de `Flex` sin cadenas intermedias.
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn apply(self, cx: &mut Context, classes: &mut String) {
|
||||
use crate::html::responsive::{apply, responsive_class, styles, value_to_token};
|
||||
|
|
|
|||
|
|
@ -1,98 +1,17 @@
|
|||
//! Enums semánticos que configuran [`Flex`](super::Flex), a nivel de contenedor.
|
||||
|
||||
use crate::html::unit::UnitValue;
|
||||
use crate::{AutoDefault, CowStr};
|
||||
|
||||
// **< Align >**************************************************************************************
|
||||
|
||||
/// Alineación de los elementos en el eje transversal de un contenedor [`Flex`](super::Flex).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Align {
|
||||
/// Por defecto (`align-items: normal` no explícito), mismo efecto que [`Align::Stretch`], salvo
|
||||
/// que el elemento tenga su propio tamaño.
|
||||
#[default]
|
||||
Default,
|
||||
/// Alinea los elementos al inicio del eje transversal (`align-items: flex-start`).
|
||||
Start,
|
||||
/// Alinea los elementos al final del eje transversal (`align-items: flex-end`).
|
||||
End,
|
||||
/// Centra los elementos en el eje transversal (`align-items: center`).
|
||||
Center,
|
||||
/// Alinea los elementos por su línea base de texto (`align-items: baseline`).
|
||||
Baseline,
|
||||
/// Estira los elementos para ocupar todo el eje transversal (`align-items: stretch`).
|
||||
Stretch,
|
||||
}
|
||||
|
||||
impl Align {
|
||||
// Devuelve el valor CSS de `align-items`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::Default => "".into(),
|
||||
Self::Start => "flex-start".into(),
|
||||
Self::End => "flex-end".into(),
|
||||
Self::Center => "center".into(),
|
||||
Self::Baseline => "baseline".into(),
|
||||
Self::Stretch => "stretch".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< AlignContent >*******************************************************************************
|
||||
|
||||
/// Alineación de varias líneas en un contenedor [`Flex`](super::Flex).
|
||||
///
|
||||
/// Sólo tiene efecto si el contenedor usa [`Behavior::Wrap`] o [`Behavior::WrapReverse`] y genera
|
||||
/// más de una línea de elementos.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum AlignContent {
|
||||
/// Por defecto (`align-content: normal` no explícito), como en [`AlignContent::Stretch`], las
|
||||
/// líneas se estiran para ocupar el espacio sobrante del eje transversal, sin efecto visible si
|
||||
/// el contenedor no tiene ningún espacio sobrante que repartir (p. ej. una altura `auto`
|
||||
/// ajustada al contenido).
|
||||
#[default]
|
||||
Default,
|
||||
/// Alinea las líneas al inicio del eje transversal (`align-content: flex-start`).
|
||||
Start,
|
||||
/// Alinea las líneas al final del eje transversal (`align-content: flex-end`).
|
||||
End,
|
||||
/// Centra las líneas en el eje transversal (`align-content: center`).
|
||||
Center,
|
||||
/// Reparte el espacio sobrante entre las líneas (`align-content: space-between`).
|
||||
SpaceBetween,
|
||||
/// Reparte el espacio sobrante alrededor de cada línea (`align-content: space-around`).
|
||||
SpaceAround,
|
||||
/// Reparte el espacio sobrante en partes iguales, incluidos los extremos
|
||||
/// (`align-content: space-evenly`).
|
||||
SpaceEvenly,
|
||||
/// Estira las líneas para ocupar todo el eje transversal (`align-content: stretch`).
|
||||
Stretch,
|
||||
}
|
||||
|
||||
impl AlignContent {
|
||||
// Devuelve el valor CSS de `align-content`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::Default => "".into(),
|
||||
Self::Start => "flex-start".into(),
|
||||
Self::End => "flex-end".into(),
|
||||
Self::Center => "center".into(),
|
||||
Self::SpaceBetween => "space-between".into(),
|
||||
Self::SpaceAround => "space-around".into(),
|
||||
Self::SpaceEvenly => "space-evenly".into(),
|
||||
Self::Stretch => "stretch".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< Behavior >***********************************************************************************
|
||||
|
||||
/// Comportamiento de los elementos si no caben en una línea del contenedor [`Flex`](super::Flex).
|
||||
/// Comportamiento de los elementos si no caben en una línea del contenedor [`Flex`].
|
||||
///
|
||||
/// Si el contenedor aplica [`Gap`] y un [`ItemSize`](super::ItemSize) porcentual en los hijos,
|
||||
/// entonces usar [`Behavior::Wrap`] en vez de [`Behavior::NoWrap`] (su valor por defecto) puede
|
||||
/// provocar saltos de línea prematuros. En la sección "Cómo combinarlo con `Gap`" de `ItemSize`
|
||||
/// se explica el porqué.
|
||||
/// Si el contenedor aplica [`align::Gap`] y un [`ItemSize`] porcentual en los hijos, entonces usar
|
||||
/// [`Behavior::Wrap`] en vez de [`Behavior::NoWrap`] (su valor por defecto) puede provocar saltos
|
||||
/// de línea prematuros. En la sección "Cómo combinarlo con `Gap`" de `ItemSize` se explica el
|
||||
/// porqué.
|
||||
///
|
||||
/// [`Flex`]: crate::base::component::Flex
|
||||
/// [`align::Gap`]: crate::html::align::Gap
|
||||
/// [`ItemSize`]: crate::html::flex::ItemSize
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Behavior {
|
||||
/// Por defecto, no se dividen en varias líneas: se comprimen o desbordan (`flex-wrap: nowrap`
|
||||
|
|
@ -108,7 +27,7 @@ pub enum Behavior {
|
|||
|
||||
impl Behavior {
|
||||
// Devuelve el valor CSS de `flex-wrap`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
pub(crate) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::NoWrap => "".into(),
|
||||
Self::Wrap => "wrap".into(),
|
||||
|
|
@ -119,13 +38,14 @@ impl Behavior {
|
|||
|
||||
// **< ContentJustify >*****************************************************************************
|
||||
|
||||
/// Alineación de los elementos en el eje principal de un contenedor [`Flex`](super::Flex).
|
||||
/// Alineación de los elementos en el eje principal de un contenedor
|
||||
/// [`Flex`](crate::base::component::Flex).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ContentJustify {
|
||||
/// Por defecto, el navegador no fuerza ninguna alineación (`justify-content: normal` no
|
||||
/// explícito).
|
||||
#[default]
|
||||
Default,
|
||||
Normal,
|
||||
/// Alinea los elementos al inicio del eje principal (`justify-content: flex-start`).
|
||||
Start,
|
||||
/// Alinea los elementos al final del eje principal (`justify-content: flex-end`).
|
||||
|
|
@ -143,9 +63,9 @@ pub enum ContentJustify {
|
|||
|
||||
impl ContentJustify {
|
||||
// Devuelve el valor CSS de `justify-content`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
pub(crate) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::Default => "".into(),
|
||||
Self::Normal => "".into(),
|
||||
Self::Start => "flex-start".into(),
|
||||
Self::End => "flex-end".into(),
|
||||
Self::Center => "center".into(),
|
||||
|
|
@ -158,7 +78,7 @@ impl ContentJustify {
|
|||
|
||||
// **< Direction >**********************************************************************************
|
||||
|
||||
/// Dirección del eje principal de un contenedor [`Flex`](super::Flex).
|
||||
/// Dirección del eje principal de un contenedor [`Flex`](crate::base::component::Flex).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Direction {
|
||||
/// Por defecto, los elementos se disponen en fila, de izquierda a derecha
|
||||
|
|
@ -175,7 +95,7 @@ pub enum Direction {
|
|||
|
||||
impl Direction {
|
||||
// Devuelve el valor CSS de `flex-direction`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
pub(crate) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::Row => "".into(),
|
||||
Self::RowReverse => "row-reverse".into(),
|
||||
|
|
@ -184,44 +104,3 @@ impl Direction {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< Gap >****************************************************************************************
|
||||
|
||||
/// Espaciado entre los elementos de un contenedor [`Flex`](super::Flex).
|
||||
///
|
||||
/// Es un valor continuo, no una utilidad predefinida: se resuelve siempre como estilo
|
||||
/// `gap`/`row-gap`/`column-gap` en línea, igual que el resto de facetas de
|
||||
/// [`Flex`](super::Flex)/[`FlexItem`](super::FlexItem).
|
||||
///
|
||||
/// Si se combina con un [`ItemSize`](super::ItemSize) porcentual sobre los hijos, la sección "Cómo
|
||||
/// combinarlo con `Gap`" de `ItemSize` explica cómo evitar que el hueco desborde el contenedor.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Gap {
|
||||
/// Por defecto, no hay espaciado (`gap: normal` no explícito).
|
||||
#[default]
|
||||
None,
|
||||
/// Mismo espaciado entre filas y columnas.
|
||||
Both(UnitValue),
|
||||
/// Espaciado distinto entre filas y columnas.
|
||||
Distinct { row: UnitValue, column: UnitValue },
|
||||
}
|
||||
|
||||
impl Gap {
|
||||
// Declaraciones de estilo (propiedad, valor) para este espaciado; cada hueco a `None` si no hay
|
||||
// ninguna medible (`UnitValue::None`/`UnitValue::Auto` no producen ningún estilo).
|
||||
pub(super) fn styles(self) -> [Option<(&'static str, CowStr)>; 2] {
|
||||
match self {
|
||||
Self::None => [None, None],
|
||||
Self::Both(value) => [Self::style("gap", value), None],
|
||||
Self::Distinct { row, column } => [
|
||||
Self::style("row-gap", row),
|
||||
Self::style("column-gap", column),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
// Declaración (propiedad, valor) para un valor medible, o `None` si no lo es.
|
||||
fn style(property: &'static str, value: UnitValue) -> Option<(&'static str, CowStr)> {
|
||||
value.is_measurable().then(|| (property, value.into()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,6 @@
|
|||
//! Enums semánticos que configuran [`FlexItem`](super::FlexItem), a nivel de ítem.
|
||||
|
||||
use crate::html::unit::UnitValue;
|
||||
use crate::{AutoDefault, CowStr};
|
||||
|
||||
// **< ItemAlign >**********************************************************************************
|
||||
|
||||
/// Alineación en [`FlexItem`](super::FlexItem) para un ítem, sobrescribiendo la del contenedor.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ItemAlign {
|
||||
/// Por defecto, hereda la alineación del contenedor (`align-self: auto` no explícito).
|
||||
#[default]
|
||||
Default,
|
||||
/// Alinea el ítem al inicio del eje transversal (`align-self: flex-start`).
|
||||
Start,
|
||||
/// Alinea el ítem al final del eje transversal (`align-self: flex-end`).
|
||||
End,
|
||||
/// Centra el ítem en el eje transversal (`align-self: center`).
|
||||
Center,
|
||||
/// Alinea el ítem por su línea base de texto (`align-self: baseline`).
|
||||
Baseline,
|
||||
/// Estira el ítem para ocupar todo el eje transversal (`align-self: stretch`).
|
||||
Stretch,
|
||||
}
|
||||
|
||||
impl ItemAlign {
|
||||
// Devuelve el valor CSS de `align-self`, o "" para el valor por defecto.
|
||||
pub(super) fn value(self) -> CowStr {
|
||||
match self {
|
||||
Self::Default => "".into(),
|
||||
Self::Start => "flex-start".into(),
|
||||
Self::End => "flex-end".into(),
|
||||
Self::Center => "center".into(),
|
||||
Self::Baseline => "baseline".into(),
|
||||
Self::Stretch => "stretch".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< ItemGrow >***********************************************************************************
|
||||
|
||||
/// Factor de crecimiento en [`FlexItem`](super::FlexItem) para un ítem dentro de un contenedor.
|
||||
|
|
@ -197,10 +161,12 @@ impl ItemOrder {
|
|||
/// elemento el que ceda (uno con [`ItemGrow::Is1`] y contenido que sí admita reajuste, como texto),
|
||||
/// no éste.
|
||||
///
|
||||
/// Con [`ItemSize`] en porcentaje, `Is0` es seguro si el contenedor no tiene [`Gap`](super::Gap)
|
||||
/// (sin `gap` no hay nada que compensar). Pero **si el contenedor tiene `Gap`, no combines `Is0`
|
||||
/// con un tamaño porcentual** porque desactivas la única pieza (el reparto del espacio negativo
|
||||
/// entre elementos) que compensa el hueco por ti. La explicación completa está en [`ItemSize`].
|
||||
/// Con [`ItemSize`] en porcentaje, `Is0` es seguro si el contenedor no tiene [`align::Gap`] (sin
|
||||
/// `gap` no hay nada que compensar). Pero **si el contenedor tiene `Gap`, no combines `Is0` con un
|
||||
/// tamaño porcentual** porque desactivas la única pieza (el reparto del espacio negativo entre
|
||||
/// elementos) que compensa el hueco por ti. La explicación completa está en [`ItemSize`].
|
||||
///
|
||||
/// [`align::Gap`]: crate::html::align::Gap
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ItemShrink {
|
||||
/// Por defecto, puede encoger si hace falta (`flex-shrink: 1` no explícito).
|
||||
|
|
@ -234,10 +200,10 @@ impl ItemShrink {
|
|||
/// # Cómo combinarlo con `Gap`
|
||||
///
|
||||
/// Un porcentaje se resuelve contra el ancho del contenedor sin contar el espacio que va a ocupar
|
||||
/// el [`Gap`](super::Gap). Es una limitación del propio CSS, porque `flex-basis` en porcentaje usa
|
||||
/// la misma regla de resolución que cualquier `width: %`. Si los porcentajes de una fila suman el
|
||||
/// 100% (una rejilla completa, el caso habitual), el hueco que añade `gap` sobra respecto al ancho
|
||||
/// del contenedor.
|
||||
/// el [`align::Gap`](crate::html::align::Gap). Es una limitación del propio CSS, porque
|
||||
/// `flex-basis` en porcentaje usa la misma regla de resolución que cualquier `width: %`. Si los
|
||||
/// porcentajes de una fila suman el 100% (una rejilla completa, el caso habitual), el hueco que
|
||||
/// añade `gap` sobra respecto al ancho del contenedor.
|
||||
///
|
||||
/// Ese sobrante se compensa solo, sin ningún ajuste manual, siempre que:
|
||||
///
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue