[bootsier] Nuevo componente Dropdown

This commit is contained in:
Manuel Cillero 2025-01-06 01:10:39 +01:00
parent 352454789f
commit fb38da4e8c
5 changed files with 221 additions and 0 deletions

View file

@ -20,6 +20,10 @@ pub use offcanvas::{
pub mod navbar;
pub use navbar::{Navbar, NavbarContent, NavbarToggler};
// Dropdown.
pub mod dropdown;
pub use dropdown::Dropdown;
/// Define los puntos de interrupción (*breakpoints*) usados por Bootstrap para diseño responsivo.
#[rustfmt::skip]
#[derive(AutoDefault)]

View file

@ -0,0 +1,5 @@
mod component;
pub use component::Dropdown;
mod item;
pub use item::Item;

View file

@ -0,0 +1,99 @@
use pagetop::prelude::*;
use crate::bs::dropdown;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Dropdown {
id : OptionId,
classes: OptionClasses,
items : Children,
}
impl ComponentTrait for Dropdown {
fn new() -> Self {
Dropdown::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.alter_classes(ClassesOp::Prepend, "dropdown");
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let items = self.items().render(cx);
if items.is_empty() {
return PrepareMarkup::None;
}
PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] {
button
type="button"
class="btn btn-secondary dropdown-toggle"
data-bs-toggle="dropdown"
aria-expanded="false"
{
("Dropdown button")
}
ul class="dropdown-menu" {
li {
a class="dropdown-item" href="#" {
("Action")
}
}
li {
a class="dropdown-item" href="#" {
("Another action")
}
}
li {
a class="dropdown-item" href="#" {
("Something else here")
}
}
}
}
})
}
}
impl Dropdown {
// Dropdown BUILDER.
#[fn_builder]
pub fn with_id(mut self, id: impl Into<String>) -> Self {
self.id.alter_value(id);
self
}
#[fn_builder]
pub fn with_classes(mut self, op: ClassesOp, classes: impl Into<String>) -> Self {
self.classes.alter_value(op, classes);
self
}
pub fn with_item(mut self, item: dropdown::Item) -> Self {
self.items.add(Child::with(item));
self
}
#[fn_builder]
pub fn with_items(mut self, op: TypedOp<dropdown::Item>) -> Self {
self.items.alter_typed(op);
self
}
// Dropdown GETTERS.
pub fn classes(&self) -> &OptionClasses {
&self.classes
}
pub fn items(&self) -> &Children {
&self.items
}
}

View file

@ -0,0 +1,109 @@
use pagetop::prelude::*;
type Label = L10n;
#[derive(AutoDefault)]
pub enum ItemType {
#[default]
Void,
Label(Label),
Link(Label, FnContextualPath),
LinkBlank(Label, FnContextualPath),
}
// Item.
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Item {
item_type: ItemType,
}
impl ComponentTrait for Item {
fn new() -> Self {
Item::default()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let description: Option<String> = None;
// Obtiene la URL actual desde `cx.request`.
let current_path = cx.request().path();
match self.item_type() {
ItemType::Void => PrepareMarkup::None,
ItemType::Label(label) => PrepareMarkup::With(html! {
li class="dropdown-item" {
span title=[description] {
//(left_icon)
(label.escaped(cx.langid()))
//(right_icon)
}
}
}),
ItemType::Link(label, path) => {
let item_path = path(cx);
let (class, aria) = if item_path == current_path {
("dropdown-item active", Some("page"))
} else {
("dropdown-item", None)
};
PrepareMarkup::With(html! {
li class=(class) aria-current=[aria] {
a class="nav-link" href=(item_path) title=[description] {
//(left_icon)
(label.escaped(cx.langid()))
//(right_icon)
}
}
})
}
ItemType::LinkBlank(label, path) => {
let item_path = path(cx);
let (class, aria) = if item_path == current_path {
("dropdown-item active", Some("page"))
} else {
("dropdown-item", None)
};
PrepareMarkup::With(html! {
li class=(class) aria-current=[aria] {
a class="nav-link" href=(item_path) title=[description] target="_blank" {
//(left_icon)
(label.escaped(cx.langid()))
//(right_icon)
}
}
})
}
}
}
}
impl Item {
pub fn label(label: L10n) -> Self {
Item {
item_type: ItemType::Label(label),
..Default::default()
}
}
pub fn link(label: L10n, path: FnContextualPath) -> Self {
Item {
item_type: ItemType::Link(label, path),
..Default::default()
}
}
pub fn link_blank(label: L10n, path: FnContextualPath) -> Self {
Item {
item_type: ItemType::LinkBlank(label, path),
..Default::default()
}
}
// Item GETTERS.
pub fn item_type(&self) -> &ItemType {
&self.item_type
}
}

View file

@ -1,5 +1,7 @@
use pagetop::prelude::*;
use crate::bs::Dropdown;
type Label = L10n;
#[derive(AutoDefault)]
@ -9,6 +11,7 @@ pub enum ItemType {
Label(Label),
Link(Label, FnContextualPath),
LinkBlank(Label, FnContextualPath),
Dropdown(Typed<Dropdown>),
}
// Item.
@ -75,6 +78,7 @@ impl ComponentTrait for Item {
}
})
}
ItemType::Dropdown(menu) => PrepareMarkup::With(html! { (menu.render(cx)) }),
}
}
}