💄 [bootsier] Añade soporte a "Container" y "Grid"
This commit is contained in:
parent
cfb96135df
commit
2f5c7f461f
6 changed files with 547 additions and 2 deletions
|
|
@ -1 +1,47 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod container;
|
||||
pub use container::{Container, ContainerType};
|
||||
|
||||
pub mod grid;
|
||||
|
||||
/// Define los puntos de interrupción (*breakpoints*) usados por Bootstrap para diseño responsivo.
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub enum BreakPoint {
|
||||
#[default]
|
||||
None, // < 576px (Dispositivos muy pequeños: teléfonos en modo vertical, menos de 576px)
|
||||
SM, // >= 576px (Dispositivos pequeños: teléfonos en modo horizontal, 576px o más)
|
||||
MD, // >= 768px (Dispositivos medianos: tabletas, 768px o más)
|
||||
LG, // >= 992px (Dispositivos grandes: puestos de escritorio, 992px o más)
|
||||
XL, // >= 1200px (Dispositivos muy grandes: puestos de escritorio grandes, 1200px o más)
|
||||
XXL, // >= 1400px (Dispositivos extragrandes: puestos de escritorio más grandes, 1400px o más)
|
||||
Fluid, // Siempre aplica el 100% del dispositivo
|
||||
}
|
||||
|
||||
impl BreakPoint {
|
||||
/// Indica si se trata de un punto de interrupción de Bootstrap.
|
||||
/// Devuelve `true` si el valor es SM, MD, LG, XL o XXL.
|
||||
/// Devuelve `false` si es None o Fluid.
|
||||
pub fn is_breakpoint(&self) -> bool {
|
||||
!matches!(self, BreakPoint::None | BreakPoint::Fluid)
|
||||
}
|
||||
}
|
||||
|
||||
/// Devuelve el texto asociado al punto de interrupción usado por Bootstrap.
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for BreakPoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BreakPoint::None => write!(f, ""),
|
||||
BreakPoint::SM => write!(f, "sm"),
|
||||
BreakPoint::MD => write!(f, "md"),
|
||||
BreakPoint::LG => write!(f, "lg"),
|
||||
BreakPoint::XL => write!(f, "xl"),
|
||||
BreakPoint::XXL => write!(f, "xxl"),
|
||||
BreakPoint::Fluid => write!(f, "fluid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
167
packages/pagetop-bootsier/src/bs/container.rs
Normal file
167
packages/pagetop-bootsier/src/bs/container.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use crate::bs::BreakPoint;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ContainerType {
|
||||
#[default]
|
||||
Default, // Contenedor genérico
|
||||
Main, // Contenido principal
|
||||
Header, // Encabezado
|
||||
Footer, // Pie
|
||||
Section, // Sección específica de contenido
|
||||
Article, // Artículo dentro de una sección
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Container {
|
||||
id : OptionId,
|
||||
classes : OptionClasses,
|
||||
container_type: ContainerType,
|
||||
breakpoint : BreakPoint,
|
||||
children : Children,
|
||||
}
|
||||
|
||||
impl ComponentTrait for Container {
|
||||
fn new() -> Self {
|
||||
Container::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
self.alter_classes(
|
||||
ClassesOp::Prepend,
|
||||
trio_string!("container", "-", self.breakpoint().to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let output = self.children().render(cx);
|
||||
if output.is_empty() {
|
||||
return PrepareMarkup::None;
|
||||
}
|
||||
match self.container_type() {
|
||||
ContainerType::Default => PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
ContainerType::Main => PrepareMarkup::With(html! {
|
||||
main id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
ContainerType::Header => PrepareMarkup::With(html! {
|
||||
header id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
ContainerType::Footer => PrepareMarkup::With(html! {
|
||||
footer id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
ContainerType::Section => PrepareMarkup::With(html! {
|
||||
section id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
ContainerType::Article => PrepareMarkup::With(html! {
|
||||
article id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Container {
|
||||
pub fn main() -> Self {
|
||||
Container {
|
||||
container_type: ContainerType::Main,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn header() -> Self {
|
||||
Container {
|
||||
container_type: ContainerType::Header,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn footer() -> Self {
|
||||
Container {
|
||||
container_type: ContainerType::Footer,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section() -> Self {
|
||||
Container {
|
||||
container_type: ContainerType::Section,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn article() -> Self {
|
||||
Container {
|
||||
container_type: ContainerType::Article,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Container 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_breakpoint(mut self, bp: BreakPoint) -> Self {
|
||||
self.breakpoint = bp;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_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
|
||||
}
|
||||
|
||||
// Container GETTERS.
|
||||
|
||||
fn classes(&self) -> &OptionClasses {
|
||||
&self.classes
|
||||
}
|
||||
|
||||
pub fn container_type(&self) -> &ContainerType {
|
||||
&self.container_type
|
||||
}
|
||||
|
||||
pub fn breakpoint(&self) -> &BreakPoint {
|
||||
&self.breakpoint
|
||||
}
|
||||
|
||||
pub fn children(&self) -> &Children {
|
||||
&self.children
|
||||
}
|
||||
}
|
||||
110
packages/pagetop-bootsier/src/bs/grid.rs
Normal file
110
packages/pagetop-bootsier/src/bs/grid.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use crate::bs::BreakPoint;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod cssgrid;
|
||||
pub use cssgrid::Grid;
|
||||
|
||||
mod item;
|
||||
pub use item::Item;
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum Layout {
|
||||
#[default]
|
||||
Default,
|
||||
Rows(u8),
|
||||
Cols(u8),
|
||||
Grid(u8, u8),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for Layout {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Layout::Rows(r) if *r > 1 => write!(f, "--bs-rows: {r};"),
|
||||
Layout::Cols(c) if *c > 0 => write!(f, "--bs-columns: {c};"),
|
||||
Layout::Grid(r, c) => write!(f, "{}", trio_string!(
|
||||
Layout::Rows(*r).to_string(), " ", Layout::Cols(*c).to_string()
|
||||
)),
|
||||
_ => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum Gap {
|
||||
#[default]
|
||||
Default,
|
||||
Row(unit::Value),
|
||||
Col(unit::Value),
|
||||
Grid(unit::Value, unit::Value),
|
||||
Both(unit::Value),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for Gap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Gap::Default => write!(f, ""),
|
||||
Gap::Row(r) => write!(f, "row-gap: {r};"),
|
||||
Gap::Col(c) => write!(f, "column-gap: {c};"),
|
||||
Gap::Grid(r, c) => write!(f, "--bs-gap: {r} {c};"),
|
||||
Gap::Both(v) => write!(f, "--bs-gap: {v};"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ItemColumns {
|
||||
#[default]
|
||||
Default,
|
||||
Cols(u8),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for ItemColumns {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ItemColumns::Cols(c) if *c > 1 => write!(f, "g-col-{c}"),
|
||||
_ => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ItemResponsive {
|
||||
#[default]
|
||||
Default,
|
||||
Cols(BreakPoint, u8),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for ItemResponsive {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ItemResponsive::Cols(bp, c) if bp.is_breakpoint() && *c > 0 => {
|
||||
write!(f, "g-col-{bp}-{c}")
|
||||
}
|
||||
_ => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ItemStart {
|
||||
#[default]
|
||||
Default,
|
||||
Col(u8),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl fmt::Display for ItemStart {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ItemStart::Col(c) if *c > 1 => write!(f, "g-start-{c}"),
|
||||
_ => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
103
packages/pagetop-bootsier/src/bs/grid/cssgrid.rs
Normal file
103
packages/pagetop-bootsier/src/bs/grid/cssgrid.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use crate::bs::grid;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Grid {
|
||||
id : OptionId,
|
||||
classes : OptionClasses,
|
||||
grid_layout: grid::Layout,
|
||||
grid_gap : grid::Gap,
|
||||
items : Children,
|
||||
}
|
||||
|
||||
impl ComponentTrait for Grid {
|
||||
fn new() -> Self {
|
||||
Grid::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
self.alter_classes(ClassesOp::Prepend, "grid");
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let output = self.items().render(cx);
|
||||
if output.is_empty() {
|
||||
return PrepareMarkup::None;
|
||||
}
|
||||
|
||||
let style = option_string!([self.layout().to_string(), self.gap().to_string()]; " ");
|
||||
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class=[self.classes().get()] style=[style] {
|
||||
(output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn with(item: grid::Item) -> Self {
|
||||
Grid::default().add_item(item)
|
||||
}
|
||||
|
||||
// Grid 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_layout(mut self, layout: grid::Layout) -> Self {
|
||||
self.grid_layout = layout;
|
||||
self
|
||||
}
|
||||
|
||||
#[fn_builder]
|
||||
pub fn with_gap(mut self, gap: grid::Gap) -> Self {
|
||||
self.grid_gap = gap;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_item(mut self, item: grid::Item) -> Self {
|
||||
self.items.add(ChildComponent::with(item));
|
||||
self
|
||||
}
|
||||
|
||||
#[fn_builder]
|
||||
pub fn with_items(mut self, op: TypedOp<grid::Item>) -> Self {
|
||||
self.items.alter_typed(op);
|
||||
self
|
||||
}
|
||||
|
||||
// Grid GETTERS.
|
||||
|
||||
fn classes(&self) -> &OptionClasses {
|
||||
&self.classes
|
||||
}
|
||||
|
||||
pub fn layout(&self) -> &grid::Layout {
|
||||
&self.grid_layout
|
||||
}
|
||||
|
||||
pub fn gap(&self) -> &grid::Gap {
|
||||
&self.grid_gap
|
||||
}
|
||||
|
||||
pub fn items(&self) -> &Children {
|
||||
&self.items
|
||||
}
|
||||
}
|
||||
119
packages/pagetop-bootsier/src/bs/grid/item.rs
Normal file
119
packages/pagetop-bootsier/src/bs/grid/item.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use crate::bs::grid;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Item {
|
||||
id : OptionId,
|
||||
classes : OptionClasses,
|
||||
columns : grid::ItemColumns,
|
||||
responsive: grid::ItemResponsive,
|
||||
start : grid::ItemStart,
|
||||
children : Children,
|
||||
}
|
||||
|
||||
impl ComponentTrait for Item {
|
||||
fn new() -> Self {
|
||||
Item::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
self.alter_classes(
|
||||
ClassesOp::Prepend,
|
||||
[
|
||||
self.columns().to_string(),
|
||||
self.responsive().to_string(),
|
||||
self.start().to_string(),
|
||||
]
|
||||
.join(" "),
|
||||
);
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let output = self.children().render(cx);
|
||||
if output.is_empty() {
|
||||
return PrepareMarkup::None;
|
||||
}
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class=[self.classes().get()] {
|
||||
(output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn with(child: impl ComponentTrait) -> Self {
|
||||
Item::default().add_child(child)
|
||||
}
|
||||
|
||||
// Item 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_columns(mut self, columns: grid::ItemColumns) -> Self {
|
||||
self.columns = columns;
|
||||
self
|
||||
}
|
||||
|
||||
#[fn_builder]
|
||||
pub fn with_responsive(mut self, responsive: grid::ItemResponsive) -> Self {
|
||||
self.responsive = responsive;
|
||||
self
|
||||
}
|
||||
|
||||
#[fn_builder]
|
||||
pub fn with_start(mut self, start: grid::ItemStart) -> Self {
|
||||
self.start = start;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_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
|
||||
}
|
||||
|
||||
// Item GETTERS.
|
||||
|
||||
fn classes(&self) -> &OptionClasses {
|
||||
&self.classes
|
||||
}
|
||||
|
||||
pub fn columns(&self) -> &grid::ItemColumns {
|
||||
&self.columns
|
||||
}
|
||||
|
||||
pub fn responsive(&self) -> &grid::ItemResponsive {
|
||||
&self.responsive
|
||||
}
|
||||
|
||||
pub fn start(&self) -> &grid::ItemStart {
|
||||
&self.start
|
||||
}
|
||||
|
||||
pub fn children(&self) -> &Children {
|
||||
&self.children
|
||||
}
|
||||
}
|
||||
|
|
@ -374,9 +374,9 @@ $enable-gradients: false !default;
|
|||
$enable-transitions: true !default;
|
||||
$enable-reduced-motion: true !default;
|
||||
$enable-smooth-scroll: true !default;
|
||||
$enable-grid-classes: true !default;
|
||||
$enable-grid-classes: false !default;
|
||||
$enable-container-classes: true !default;
|
||||
$enable-cssgrid: false !default;
|
||||
$enable-cssgrid: true !default;
|
||||
$enable-button-pointers: true !default;
|
||||
$enable-rfs: true !default;
|
||||
$enable-validation-icons: true !default;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue