💄 [bootsier] Añade soporte a "Container" y "Grid"

This commit is contained in:
Manuel Cillero 2025-01-02 11:53:59 +01:00
parent 2836dbd590
commit f382d48a9c
6 changed files with 547 additions and 2 deletions

View 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
}
}

View 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, ""),
}
}
}

View 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
}
}

View 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
}
}