✨ [bootsier] Nuevos componentes Navbar y Offcanvas
This commit is contained in:
parent
83e09c38ce
commit
db13a6ab8b
17 changed files with 743 additions and 16 deletions
|
|
@ -2,10 +2,23 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
mod container;
|
// Container.
|
||||||
|
pub mod container;
|
||||||
pub use container::{Container, ContainerType};
|
pub use container::{Container, ContainerType};
|
||||||
|
|
||||||
|
// Grid.
|
||||||
pub mod grid;
|
pub mod grid;
|
||||||
|
pub use grid::Grid;
|
||||||
|
|
||||||
|
// Offcanvas.
|
||||||
|
pub mod offcanvas;
|
||||||
|
pub use offcanvas::{
|
||||||
|
Offcanvas, OffcanvasBackdrop, OffcanvasBodyScroll, OffcanvasPlacement, OffcanvasVisibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Navbar.
|
||||||
|
pub mod navbar;
|
||||||
|
pub use navbar::{Navbar, NavbarType};
|
||||||
|
|
||||||
/// Define los puntos de interrupción (*breakpoints*) usados por Bootstrap para diseño responsivo.
|
/// Define los puntos de interrupción (*breakpoints*) usados por Bootstrap para diseño responsivo.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
|
@ -24,15 +37,46 @@ pub enum BreakPoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BreakPoint {
|
impl BreakPoint {
|
||||||
/// Indica si se trata de un punto de interrupción de Bootstrap.
|
/// Indica si el punto de interrupción es efectivo en Bootstrap.
|
||||||
/// Devuelve `true` si el valor es SM, MD, LG, XL o XXL.
|
///
|
||||||
/// Devuelve `false` si es None, Fluid o FluidMax.
|
/// Devuelve `true` si el valor es `SM`, `MD`, `LG`, `XL` o `XXL`.
|
||||||
|
/// Devuelve `false` si es `None`, `Fluid` o `FluidMax`.
|
||||||
pub fn is_breakpoint(&self) -> bool {
|
pub fn is_breakpoint(&self) -> bool {
|
||||||
!matches!(
|
!matches!(
|
||||||
self,
|
self,
|
||||||
BreakPoint::None | BreakPoint::Fluid | BreakPoint::FluidMax(_)
|
BreakPoint::None | BreakPoint::Fluid | BreakPoint::FluidMax(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Genera un nombre de clase CSS basado en el punto de interrupción.
|
||||||
|
///
|
||||||
|
/// Si es un punto de interrupción efectivo (ver [`is_breakpoint()`] se concatenan el prefijo
|
||||||
|
/// proporcionado, un guion (`-`) y el texto asociado al punto de interrupción. En otro caso, se
|
||||||
|
/// devuelve únicamente el prefijo.
|
||||||
|
///
|
||||||
|
/// # Parámetros
|
||||||
|
///
|
||||||
|
/// - `prefix`: Prefijo para concatenar con el punto de interrupción.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust#ignore
|
||||||
|
/// let breakpoint = BreakPoint::MD;
|
||||||
|
/// let class = breakpoint.breakpoint_class("col");
|
||||||
|
/// assert_eq!(class, "col-md".to_string());
|
||||||
|
///
|
||||||
|
/// let breakpoint = BreakPoint::Fluid;
|
||||||
|
/// let class = breakpoint.breakpoint_class("offcanvas");
|
||||||
|
/// assert_eq!(class, "offcanvas".to_string());
|
||||||
|
/// ```
|
||||||
|
pub fn breakpoint_class(&self, prefix: impl Into<String>) -> String {
|
||||||
|
let prefix: String = prefix.into();
|
||||||
|
if self.is_breakpoint() {
|
||||||
|
join_string!(prefix, "-", self.to_string())
|
||||||
|
} else {
|
||||||
|
prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve el texto asociado al punto de interrupción usado por Bootstrap.
|
/// Devuelve el texto asociado al punto de interrupción usado por Bootstrap.
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ impl Container {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_child(mut self, child: impl ComponentTrait) -> Self {
|
pub fn with_child(mut self, child: impl ComponentTrait) -> Self {
|
||||||
self.children.add(ChildComponent::with(child));
|
self.children.add(ChildComponent::with(child));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +154,7 @@ impl Container {
|
||||||
|
|
||||||
// Container GETTERS.
|
// Container GETTERS.
|
||||||
|
|
||||||
fn classes(&self) -> &OptionClasses {
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
&self.classes
|
&self.classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use crate::bs::BreakPoint;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
mod cssgrid;
|
mod component;
|
||||||
pub use cssgrid::Grid;
|
pub use component::Grid;
|
||||||
|
|
||||||
mod item;
|
mod item;
|
||||||
pub use item::Item;
|
pub use item::Item;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ impl ComponentTrait for Grid {
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
pub fn with(item: grid::Item) -> Self {
|
pub fn with(item: grid::Item) -> Self {
|
||||||
Grid::default().add_item(item)
|
Grid::default().with_item(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid BUILDER.
|
// Grid BUILDER.
|
||||||
|
|
@ -72,7 +72,7 @@ impl Grid {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(mut self, item: grid::Item) -> Self {
|
pub fn with_item(mut self, item: grid::Item) -> Self {
|
||||||
self.items.add(ChildComponent::with(item));
|
self.items.add(ChildComponent::with(item));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ impl Grid {
|
||||||
|
|
||||||
// Grid GETTERS.
|
// Grid GETTERS.
|
||||||
|
|
||||||
fn classes(&self) -> &OptionClasses {
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
&self.classes
|
&self.classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ impl ComponentTrait for Item {
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
pub fn with(child: impl ComponentTrait) -> Self {
|
pub fn with(child: impl ComponentTrait) -> Self {
|
||||||
Item::default().add_child(child)
|
Item::default().with_child(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item BUILDER.
|
// Item BUILDER.
|
||||||
|
|
@ -84,7 +84,7 @@ impl Item {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_child(mut self, child: impl ComponentTrait) -> Self {
|
pub fn with_child(mut self, child: impl ComponentTrait) -> Self {
|
||||||
self.children.add(ChildComponent::with(child));
|
self.children.add(ChildComponent::with(child));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ impl Item {
|
||||||
|
|
||||||
// Item GETTERS.
|
// Item GETTERS.
|
||||||
|
|
||||||
fn classes(&self) -> &OptionClasses {
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
&self.classes
|
&self.classes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
packages/pagetop-bootsier/src/bs/navbar.rs
Normal file
11
packages/pagetop-bootsier/src/bs/navbar.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
mod component;
|
||||||
|
pub use component::{Navbar, NavbarType};
|
||||||
|
|
||||||
|
mod element;
|
||||||
|
pub use element::{Element, ElementType};
|
||||||
|
|
||||||
|
mod nav;
|
||||||
|
pub use nav::Nav;
|
||||||
|
|
||||||
|
mod item;
|
||||||
|
pub use item::{Item, ItemType};
|
||||||
157
packages/pagetop-bootsier/src/bs/navbar/component.rs
Normal file
157
packages/pagetop-bootsier/src/bs/navbar/component.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::bs::navbar;
|
||||||
|
use crate::bs::{BreakPoint, Offcanvas};
|
||||||
|
use crate::LOCALES_BOOTSIER;
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum NavbarType {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Basic,
|
||||||
|
Offcanvas(TypedComponent<Offcanvas>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub struct Navbar {
|
||||||
|
id : OptionId,
|
||||||
|
classes : OptionClasses,
|
||||||
|
navbar_type: NavbarType,
|
||||||
|
expand : BreakPoint,
|
||||||
|
elements : Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for Navbar {
|
||||||
|
fn new() -> Self {
|
||||||
|
Navbar::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<String> {
|
||||||
|
self.id.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||||
|
self.alter_classes(
|
||||||
|
ClassesOp::Prepend,
|
||||||
|
[
|
||||||
|
"navbar".to_string(),
|
||||||
|
self.expand().breakpoint_class("navbar-expand"),
|
||||||
|
]
|
||||||
|
.join(" "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||||
|
let elements = self.elements().render(cx);
|
||||||
|
if elements.is_empty() {
|
||||||
|
return PrepareMarkup::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = cx.required_id::<Self>(self.id());
|
||||||
|
let (output, id_content) = if let NavbarType::Offcanvas(oc) = self.navbar_type() {
|
||||||
|
(
|
||||||
|
oc.writable()
|
||||||
|
.alter_children(ChildOp::Prepend(ChildComponent::with(Html::with(elements))))
|
||||||
|
.render(cx),
|
||||||
|
cx.required_id::<Offcanvas>(oc.id()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(elements, join_string!(id, "-content"))
|
||||||
|
};
|
||||||
|
let id_content_target = join_string!("#", id_content);
|
||||||
|
|
||||||
|
PrepareMarkup::With(html! {
|
||||||
|
nav id=(id) class=[self.classes().get()] {
|
||||||
|
div class="container-fluid" {
|
||||||
|
@match self.navbar_type() {
|
||||||
|
NavbarType::Default => {
|
||||||
|
button
|
||||||
|
type="button"
|
||||||
|
class="navbar-toggler"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target=(id_content_target)
|
||||||
|
aria-controls=(id_content)
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label=[L10n::t("toggle", &LOCALES_BOOTSIER).using(cx.langid())]
|
||||||
|
{
|
||||||
|
span class="navbar-toggler-icon" {}
|
||||||
|
}
|
||||||
|
div id=(id_content) class="collapse navbar-collapse" {
|
||||||
|
(output)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
NavbarType::Basic => {
|
||||||
|
(output)
|
||||||
|
},
|
||||||
|
NavbarType::Offcanvas(_) => {
|
||||||
|
button
|
||||||
|
type="button"
|
||||||
|
class="navbar-toggler"
|
||||||
|
data-bs-toggle="offcanvas"
|
||||||
|
data-bs-target=(id_content_target)
|
||||||
|
aria-controls=(id_content)
|
||||||
|
aria-label=[L10n::t("toggle", &LOCALES_BOOTSIER).using(cx.langid())]
|
||||||
|
{
|
||||||
|
span class="navbar-toggler-icon" {}
|
||||||
|
}
|
||||||
|
(output)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Navbar {
|
||||||
|
// Navbar 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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_type(mut self, navbar_type: NavbarType) -> Self {
|
||||||
|
self.navbar_type = navbar_type;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_expand(mut self, bp: BreakPoint) -> Self {
|
||||||
|
self.expand = bp;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_nav(mut self, op: TypedOp<navbar::Nav>) -> Self {
|
||||||
|
self.elements.alter_typed(op);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navbar GETTERS.
|
||||||
|
|
||||||
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn navbar_type(&self) -> &NavbarType {
|
||||||
|
&self.navbar_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand(&self) -> &BreakPoint {
|
||||||
|
&self.expand
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elements(&self) -> &Children {
|
||||||
|
&self.elements
|
||||||
|
}
|
||||||
|
}
|
||||||
63
packages/pagetop-bootsier/src/bs/navbar/element.rs
Normal file
63
packages/pagetop-bootsier/src/bs/navbar/element.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::bs::navbar;
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum ElementType {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Nav(navbar::Nav),
|
||||||
|
Text(L10n),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element.
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub struct Element {
|
||||||
|
element_type: ElementType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for Element {
|
||||||
|
fn new() -> Self {
|
||||||
|
Element::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||||
|
match self.element_type() {
|
||||||
|
ElementType::None => PrepareMarkup::None,
|
||||||
|
ElementType::Nav(_nav) => PrepareMarkup::With(html! {
|
||||||
|
span class="navbar-text" {
|
||||||
|
("Prueba")
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ElementType::Text(label) => PrepareMarkup::With(html! {
|
||||||
|
span class="navbar-text" {
|
||||||
|
(label.escaped(cx.langid()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element {
|
||||||
|
pub fn nav(nav: navbar::Nav) -> Self {
|
||||||
|
Element {
|
||||||
|
element_type: ElementType::Nav(nav),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(label: L10n) -> Self {
|
||||||
|
Element {
|
||||||
|
element_type: ElementType::Text(label),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element GETTERS.
|
||||||
|
|
||||||
|
pub fn element_type(&self) -> &ElementType {
|
||||||
|
&self.element_type
|
||||||
|
}
|
||||||
|
}
|
||||||
90
packages/pagetop-bootsier/src/bs/navbar/item.rs
Normal file
90
packages/pagetop-bootsier/src/bs/navbar/item.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
match self.item_type() {
|
||||||
|
ItemType::Void => PrepareMarkup::None,
|
||||||
|
ItemType::Label(label) => PrepareMarkup::With(html! {
|
||||||
|
li class="nav-item" {
|
||||||
|
span title=[description] {
|
||||||
|
//(left_icon)
|
||||||
|
(label.escaped(cx.langid()))
|
||||||
|
//(right_icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ItemType::Link(label, path) => PrepareMarkup::With(html! {
|
||||||
|
li class="nav-item" {
|
||||||
|
a class="nav-link" href=(path(cx)) title=[description] {
|
||||||
|
//(left_icon)
|
||||||
|
(label.escaped(cx.langid()))
|
||||||
|
//(right_icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ItemType::LinkBlank(label, path) => PrepareMarkup::With(html! {
|
||||||
|
li class="nav-item" {
|
||||||
|
a class="nav-link" href=(path(cx)) 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
|
||||||
|
}
|
||||||
|
}
|
||||||
75
packages/pagetop-bootsier/src/bs/navbar/nav.rs
Normal file
75
packages/pagetop-bootsier/src/bs/navbar/nav.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::bs::navbar;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub struct Nav {
|
||||||
|
id : OptionId,
|
||||||
|
classes: OptionClasses,
|
||||||
|
items : Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for Nav {
|
||||||
|
fn new() -> Self {
|
||||||
|
Nav::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<String> {
|
||||||
|
self.id.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||||
|
self.alter_classes(ClassesOp::Prepend, "navbar-nav");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||||
|
let items = self.items().render(cx);
|
||||||
|
if items.is_empty() {
|
||||||
|
return PrepareMarkup::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrepareMarkup::With(html! {
|
||||||
|
ul id=[self.id()] class=[self.classes().get()] {
|
||||||
|
(items)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nav {
|
||||||
|
// Nav 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: navbar::Item) -> Self {
|
||||||
|
self.items.add(ChildComponent::with(item));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_items(mut self, op: TypedOp<navbar::Item>) -> Self {
|
||||||
|
self.items.alter_typed(op);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nav GETTERS.
|
||||||
|
|
||||||
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn items(&self) -> &Children {
|
||||||
|
&self.items
|
||||||
|
}
|
||||||
|
}
|
||||||
59
packages/pagetop-bootsier/src/bs/offcanvas.rs
Normal file
59
packages/pagetop-bootsier/src/bs/offcanvas.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
mod component;
|
||||||
|
pub use component::Offcanvas;
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum OffcanvasPlacement {
|
||||||
|
#[default]
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl fmt::Display for OffcanvasPlacement {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
OffcanvasPlacement::Start => write!(f, "offcanvas-start"),
|
||||||
|
OffcanvasPlacement::End => write!(f, "offcanvas-end"),
|
||||||
|
OffcanvasPlacement::Top => write!(f, "offcanvas-top"),
|
||||||
|
OffcanvasPlacement::Bottom => write!(f, "offcanvas-bottom"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum OffcanvasVisibility {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Show,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
impl fmt::Display for OffcanvasVisibility {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
OffcanvasVisibility::Default => write!(f, "show"),
|
||||||
|
OffcanvasVisibility::Show => write!(f, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum OffcanvasBodyScroll {
|
||||||
|
#[default]
|
||||||
|
Disabled,
|
||||||
|
Enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub enum OffcanvasBackdrop {
|
||||||
|
Disabled,
|
||||||
|
#[default]
|
||||||
|
Enabled,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
188
packages/pagetop-bootsier/src/bs/offcanvas/component.rs
Normal file
188
packages/pagetop-bootsier/src/bs/offcanvas/component.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::bs::{
|
||||||
|
BreakPoint, OffcanvasBackdrop, OffcanvasBodyScroll, OffcanvasPlacement, OffcanvasVisibility,
|
||||||
|
};
|
||||||
|
use crate::LOCALES_BOOTSIER;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(AutoDefault)]
|
||||||
|
pub struct Offcanvas {
|
||||||
|
id : OptionId,
|
||||||
|
classes : OptionClasses,
|
||||||
|
title : OptionTranslated,
|
||||||
|
breakpoint: BreakPoint,
|
||||||
|
placement : OffcanvasPlacement,
|
||||||
|
visibility: OffcanvasVisibility,
|
||||||
|
scrolling : OffcanvasBodyScroll,
|
||||||
|
backdrop : OffcanvasBackdrop,
|
||||||
|
children : Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for Offcanvas {
|
||||||
|
fn new() -> Self {
|
||||||
|
Offcanvas::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> Option<String> {
|
||||||
|
self.id.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||||
|
self.alter_classes(
|
||||||
|
ClassesOp::Prepend,
|
||||||
|
[
|
||||||
|
self.breakpoint().breakpoint_class("offcanvas"),
|
||||||
|
self.placement().to_string(),
|
||||||
|
self.visibility().to_string(),
|
||||||
|
]
|
||||||
|
.join(" "),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||||
|
let body = self.children().render(cx);
|
||||||
|
if body.is_empty() {
|
||||||
|
return PrepareMarkup::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = cx.required_id::<Self>(self.id());
|
||||||
|
let id_label = join_string!(id, "-label");
|
||||||
|
let id_target = join_string!("#", id);
|
||||||
|
|
||||||
|
let body_scroll = match self.body_scroll() {
|
||||||
|
OffcanvasBodyScroll::Disabled => None,
|
||||||
|
OffcanvasBodyScroll::Enabled => Some("true".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let backdrop = match self.backdrop() {
|
||||||
|
OffcanvasBackdrop::Disabled => Some("true".to_string()),
|
||||||
|
OffcanvasBackdrop::Enabled => None,
|
||||||
|
OffcanvasBackdrop::Static => Some("static".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
PrepareMarkup::With(html! {
|
||||||
|
div
|
||||||
|
id=(id)
|
||||||
|
class=[self.classes().get()]
|
||||||
|
tabindex="-1"
|
||||||
|
data-bs-scroll=[body_scroll]
|
||||||
|
data-bs-backdrop=[backdrop]
|
||||||
|
aria-labelledby=(id_label)
|
||||||
|
{
|
||||||
|
div class="offcanvas-header" {
|
||||||
|
h5 class="offcanvas-title" id=(id_label) {
|
||||||
|
(self.title().escaped(cx.langid()))
|
||||||
|
}
|
||||||
|
button
|
||||||
|
type="button"
|
||||||
|
class="btn-close"
|
||||||
|
data-bs-dismiss="offcanvas"
|
||||||
|
data-bs-target=(id_target)
|
||||||
|
aria-label=[L10n::t("close", &LOCALES_BOOTSIER).using(cx.langid())]
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
div class="offcanvas-body" {
|
||||||
|
(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Offcanvas {
|
||||||
|
// Offcanvas 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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_title(mut self, title: L10n) -> Self {
|
||||||
|
self.title.alter_value(title);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_breakpoint(mut self, bp: BreakPoint) -> Self {
|
||||||
|
self.breakpoint = bp;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_placement(mut self, placement: OffcanvasPlacement) -> Self {
|
||||||
|
self.placement = placement;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_visibility(mut self, visibility: OffcanvasVisibility) -> Self {
|
||||||
|
self.visibility = visibility;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_body_scroll(mut self, scrolling: OffcanvasBodyScroll) -> Self {
|
||||||
|
self.scrolling = scrolling;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_backdrop(mut self, backdrop: OffcanvasBackdrop) -> Self {
|
||||||
|
self.backdrop = backdrop;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_child(mut self, child: impl ComponentTrait) -> Self {
|
||||||
|
self.children.add(ChildComponent::with(child));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fn_builder]
|
||||||
|
pub fn with_children(mut self, op: ChildOp) -> Self {
|
||||||
|
self.children.alter_child(op);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offcanvas GETTERS.
|
||||||
|
|
||||||
|
pub fn classes(&self) -> &OptionClasses {
|
||||||
|
&self.classes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> &OptionTranslated {
|
||||||
|
&self.title
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn breakpoint(&self) -> &BreakPoint {
|
||||||
|
&self.breakpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn placement(&self) -> &OffcanvasPlacement {
|
||||||
|
&self.placement
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visibility(&self) -> &OffcanvasVisibility {
|
||||||
|
&self.visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_scroll(&self) -> &OffcanvasBodyScroll {
|
||||||
|
&self.scrolling
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn backdrop(&self) -> &OffcanvasBackdrop {
|
||||||
|
&self.backdrop
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self) -> &Children {
|
||||||
|
&self.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,7 +65,7 @@ impl ThemeTrait for Bootsier {
|
||||||
(bs::Container::new()
|
(bs::Container::new()
|
||||||
.with_id("container-wrapper")
|
.with_id("container-wrapper")
|
||||||
.with_breakpoint(bs::BreakPoint::FluidMax(config::SETTINGS.bootsier.max_width))
|
.with_breakpoint(bs::BreakPoint::FluidMax(config::SETTINGS.bootsier.max_width))
|
||||||
.add_child(Region::of("region-content"))
|
.with_child(Region::of("region-content"))
|
||||||
.render(page.context()))
|
.render(page.context()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Offcanvas
|
||||||
|
close = Close
|
||||||
|
|
||||||
|
# Navbar
|
||||||
|
toggle = Toggle navigation
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Offcanvas
|
||||||
|
close = Cerrar
|
||||||
|
|
||||||
|
# Navbar
|
||||||
|
toggle = Mostrar/ocultar navegación
|
||||||
|
|
@ -4,7 +4,7 @@ description = "An opinionated web framework to build modular Server-Side Renderi
|
||||||
theme = "Bootsier"
|
theme = "Bootsier"
|
||||||
|
|
||||||
[bootsier]
|
[bootsier]
|
||||||
max_width = "1600px"
|
max_width = "1520px"
|
||||||
|
|
||||||
[log]
|
[log]
|
||||||
rolling = "Daily"
|
rolling = "Daily"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use pagetop_bootsier::bs::*;
|
||||||
|
|
||||||
include_files!(BUNDLE_DOC => doc);
|
include_files!(BUNDLE_DOC => doc);
|
||||||
|
|
||||||
include_locales!(LOCALES_WEBSITE);
|
include_locales!(LOCALES_WEBSITE);
|
||||||
|
|
@ -25,6 +27,34 @@ impl PackageTrait for PageTopWebSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self) {
|
fn init(&self) {
|
||||||
|
let nav = Navbar::new().with_nav(TypedOp::Add(TypedComponent::with(
|
||||||
|
navbar::Nav::new()
|
||||||
|
.with_item(navbar::Item::link(
|
||||||
|
L10n::t("menu_home", &LOCALES_WEBSITE),
|
||||||
|
|cx| match cx.langid().language.as_str() {
|
||||||
|
"es" => "/es",
|
||||||
|
_ => "/",
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_item(navbar::Item::link(
|
||||||
|
L10n::t("menu_documentation", &LOCALES_WEBSITE),
|
||||||
|
|cx| match cx.langid().language.as_str() {
|
||||||
|
"es" => "/doc/latest/es",
|
||||||
|
_ => "/doc/latest/en",
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_item(navbar::Item::link_blank(
|
||||||
|
L10n::t("menu_api", &LOCALES_WEBSITE),
|
||||||
|
|_| "https://docs.rs/pagetop",
|
||||||
|
))
|
||||||
|
.with_item(navbar::Item::link_blank(
|
||||||
|
L10n::t("menu_code", &LOCALES_WEBSITE),
|
||||||
|
|_| "https://github.com/manuelcillero/pagetop",
|
||||||
|
)),
|
||||||
|
)));
|
||||||
|
|
||||||
|
InRegion::Content.add(ChildComponent::with(nav));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
let branding = Branding::new()
|
let branding = Branding::new()
|
||||||
.with_logo(Some(Image::pagetop()))
|
.with_logo(Some(Image::pagetop()))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue