♻️ Refactoriza Dropdown para separar propiedades

This commit is contained in:
Manuel Cillero 2025-10-25 10:55:34 +02:00
parent 6c5e1b1188
commit b6b26c23da
3 changed files with 110 additions and 107 deletions

View file

@ -10,9 +10,11 @@
//! Su propósito es ofrecer una base uniforme sobre la que construir menús consistentes, adaptados
//! al contexto de cada aplicación.
mod props;
pub use props::{AutoClose, Direction, MenuAlign, MenuPosition};
mod component;
pub use component::Dropdown;
pub use component::{AutoClose, Direction, MenuAlign, MenuPosition};
mod item;
pub use item::{Item, ItemKind};

View file

@ -3,91 +3,6 @@ use pagetop::prelude::*;
use crate::prelude::*;
use crate::LOCALES_BOOTSIER;
// **< AutoClose >**********************************************************************************
/// Estrategia para el cierre automático de un menú [`Dropdown`].
///
/// Define cuándo se cierra el menú desplegado según la interacción del usuario.
#[derive(AutoDefault)]
pub enum AutoClose {
/// Comportamiento por defecto, se cierra con clics dentro y fuera del menú, o pulsando `Esc`.
#[default]
Default,
/// Sólo se cierra con clics dentro del menú.
ClickableInside,
/// Sólo se cierra con clics fuera del menú.
ClickableOutside,
/// Cierre manual, no se cierra con clics; sólo al pulsar nuevamente el botón del menú
/// (*toggle*), o pulsando `Esc`.
ManualClose,
}
// **< Direction >**********************************************************************************
/// Dirección de despliegue de un menú [`Dropdown`].
///
/// Controla desde qué posición se muestra el menú respecto al botón.
#[derive(AutoDefault)]
pub enum Direction {
/// Comportamiento por defecto (despliega el menú hacia abajo desde la posición inicial,
/// respetando LTR/RTL).
#[default]
Default,
/// Centra horizontalmente el menú respecto al botón.
Centered,
/// Despliega el menú hacia arriba.
Dropup,
/// Despliega el menú hacia arriba y centrado.
DropupCentered,
/// Despliega el menú desde el lateral final, respetando LTR/RTL.
Dropend,
/// Despliega el menú desde el lateral inicial, respetando LTR/RTL.
Dropstart,
}
// **< MenuAlign >**********************************************************************************
/// Alineación horizontal del menú desplegable [`Dropdown`].
///
/// Permite alinear el menú al inicio o al final del botón (respetando LTR/RTL) y añadirle una
/// alineación diferente a partir de un punto de ruptura ([`BreakPoint`]).
#[derive(AutoDefault)]
pub enum MenuAlign {
/// Alineación al inicio (comportamiento por defecto).
#[default]
Start,
/// Alineación al inicio a partir del punto de ruptura indicado.
StartAt(BreakPoint),
/// Alineación al inicio por defecto, y al final a partir de un punto de ruptura válido.
StartAndEnd(BreakPoint),
/// Alineación al final.
End,
/// Alineación al final a partir del punto de ruptura indicado.
EndAt(BreakPoint),
/// Alineación al final por defecto, y al inicio a partir de un punto de ruptura válido.
EndAndStart(BreakPoint),
}
// **< MenuPosition >*******************************************************************************
/// Posición relativa del menú desplegable [`Dropdown`].
///
/// Permite indicar un desplazamiento (*offset*) manual o referenciar al elemento padre para el
/// cálculo de la posición.
#[derive(AutoDefault)]
pub enum MenuPosition {
/// Posicionamiento automático por defecto.
#[default]
Default,
/// Desplazamiento manual en píxeles `(x, y)` aplicado al menú. Se admiten valores negativos.
Offset(i8, i8),
/// Posiciona el menú tomando como referencia el botón padre. Especialmente útil cuando
/// [`button_split()`](crate::theme::Dropdown::button_split) es `true`.
Parent,
}
// **< Dropdown >**********************************************************************************
/// Componente para crear un **menú desplegable**.
///
/// Renderiza un botón (único o desdoblado, ver [`with_button_split()`](Self::with_button_split))
@ -146,13 +61,13 @@ impl Component for Dropdown {
self.alter_classes(ClassesOp::Prepend, [
if g { "btn-group" } else { "" },
match self.direction() {
Direction::Default if g => "",
Direction::Default => "dropdown",
Direction::Centered => "dropdown-center",
Direction::Dropup => "dropup",
Direction::DropupCentered => "dropup-center",
Direction::Dropend => "dropend",
Direction::Dropstart => "dropstart",
dropdown::Direction::Default if g => "",
dropdown::Direction::Default => "dropdown",
dropdown::Direction::Centered => "dropdown-center",
dropdown::Direction::Dropup => "dropup",
dropdown::Direction::DropupCentered => "dropup-center",
dropdown::Direction::Dropend => "dropend",
dropdown::Direction::Dropstart => "dropstart",
}
].join(" "));
}
@ -176,30 +91,30 @@ impl Component for Dropdown {
&self.button_color().to_string(),
].join(" "));
@let (offset, reference) = match self.menu_position() {
MenuPosition::Default => (None, None),
MenuPosition::Offset(x, y) => (Some(format!("{x},{y}")), None),
MenuPosition::Parent => (None, Some("parent")),
dropdown::MenuPosition::Default => (None, None),
dropdown::MenuPosition::Offset(x, y) => (Some(format!("{x},{y}")), None),
dropdown::MenuPosition::Parent => (None, Some("parent")),
};
@let auto_close = match self.auto_close {
AutoClose::Default => None,
AutoClose::ClickableInside => Some("inside"),
AutoClose::ClickableOutside => Some("outside"),
AutoClose::ManualClose => Some("false"),
dropdown::AutoClose::Default => None,
dropdown::AutoClose::ClickableInside => Some("inside"),
dropdown::AutoClose::ClickableOutside => Some("outside"),
dropdown::AutoClose::ManualClose => Some("false"),
};
@let menu_classes = AttrClasses::new("dropdown-menu")
.with_value(ClassesOp::Add, match self.menu_align() {
MenuAlign::Start => String::new(),
MenuAlign::StartAt(bp) => bp.try_class("dropdown-menu")
dropdown::MenuAlign::Start => String::new(),
dropdown::MenuAlign::StartAt(bp) => bp.try_class("dropdown-menu")
.map_or(String::new(), |class| join!(class, "-start")),
MenuAlign::StartAndEnd(bp) => bp.try_class("dropdown-menu")
dropdown::MenuAlign::StartAndEnd(bp) => bp.try_class("dropdown-menu")
.map_or(
"dropdown-menu-start".into(),
|class| join!("dropdown-menu-start ", class, "-end")
),
MenuAlign::End => "dropdown-menu-end".into(),
MenuAlign::EndAt(bp) => bp.try_class("dropdown-menu")
dropdown::MenuAlign::End => "dropdown-menu-end".into(),
dropdown::MenuAlign::EndAt(bp) => bp.try_class("dropdown-menu")
.map_or(String::new(), |class| join!(class, "-end")),
MenuAlign::EndAndStart(bp) => bp.try_class("dropdown-menu")
dropdown::MenuAlign::EndAndStart(bp) => bp.try_class("dropdown-menu")
.map_or(
"dropdown-menu-end".into(),
|class| join!("dropdown-menu-end ", class, "-start")
@ -237,7 +152,7 @@ impl Component for Dropdown {
};
// Orden según dirección (en `dropstart` el *toggle* se sitúa antes).
@match self.direction() {
Direction::Dropstart => {
dropdown::Direction::Dropstart => {
(btn_toggle)
ul class=[menu_classes.get()] { (items) }
(btn)

View file

@ -0,0 +1,86 @@
use pagetop::prelude::*;
use crate::prelude::*;
// **< AutoClose >**********************************************************************************
/// Estrategia para el cierre automático de un menú [`Dropdown`].
///
/// Define cuándo se cierra el menú desplegado según la interacción del usuario.
#[derive(AutoDefault)]
pub enum AutoClose {
/// Comportamiento por defecto, se cierra con clics dentro y fuera del menú, o pulsando `Esc`.
#[default]
Default,
/// Sólo se cierra con clics dentro del menú.
ClickableInside,
/// Sólo se cierra con clics fuera del menú.
ClickableOutside,
/// Cierre manual, no se cierra con clics; sólo al pulsar nuevamente el botón del menú
/// (*toggle*), o pulsando `Esc`.
ManualClose,
}
// **< Direction >**********************************************************************************
/// Dirección de despliegue de un menú [`Dropdown`].
///
/// Controla desde qué posición se muestra el menú respecto al botón.
#[derive(AutoDefault)]
pub enum Direction {
/// Comportamiento por defecto (despliega el menú hacia abajo desde la posición inicial,
/// respetando LTR/RTL).
#[default]
Default,
/// Centra horizontalmente el menú respecto al botón.
Centered,
/// Despliega el menú hacia arriba.
Dropup,
/// Despliega el menú hacia arriba y centrado.
DropupCentered,
/// Despliega el menú desde el lateral final, respetando LTR/RTL.
Dropend,
/// Despliega el menú desde el lateral inicial, respetando LTR/RTL.
Dropstart,
}
// **< MenuAlign >**********************************************************************************
/// Alineación horizontal del menú desplegable [`Dropdown`].
///
/// Permite alinear el menú al inicio o al final del botón (respetando LTR/RTL) y añadirle una
/// alineación diferente a partir de un punto de ruptura ([`BreakPoint`]).
#[derive(AutoDefault)]
pub enum MenuAlign {
/// Alineación al inicio (comportamiento por defecto).
#[default]
Start,
/// Alineación al inicio a partir del punto de ruptura indicado.
StartAt(BreakPoint),
/// Alineación al inicio por defecto, y al final a partir de un punto de ruptura válido.
StartAndEnd(BreakPoint),
/// Alineación al final.
End,
/// Alineación al final a partir del punto de ruptura indicado.
EndAt(BreakPoint),
/// Alineación al final por defecto, y al inicio a partir de un punto de ruptura válido.
EndAndStart(BreakPoint),
}
// **< MenuPosition >*******************************************************************************
/// Posición relativa del menú desplegable [`Dropdown`].
///
/// Permite indicar un desplazamiento (*offset*) manual o referenciar al elemento padre para el
/// cálculo de la posición.
#[derive(AutoDefault)]
pub enum MenuPosition {
/// Posicionamiento automático por defecto.
#[default]
Default,
/// Desplazamiento manual en píxeles `(x, y)` aplicado al menú. Se admiten valores negativos.
Offset(i8, i8),
/// Posiciona el menú tomando como referencia el botón padre. Especialmente útil cuando
/// [`button_split()`](crate::theme::Dropdown::button_split) es `true`.
Parent,
}