♻️ Refactor core for minimal PageTop setup

This commit is contained in:
Manuel Cillero 2024-11-30 22:15:06 +01:00
parent 2b229573cd
commit 556c9159d3
64 changed files with 192 additions and 6561 deletions

View file

@ -1,201 +1,11 @@
use crate::core::component::{AssetsOp, Context};
use crate::html::{JavaScript, StyleSheet};
use crate::{AutoDefault, Weight};
mod html;
pub use html::Html;
use std::fmt;
// Context parameters.
pub const PARAM_BASE_WEIGHT: &str = "base.weight";
pub const PARAM_BASE_INCLUDE_ICONS: &str = "base.include.icon";
pub const PARAM_BASE_INCLUDE_FLEX_ASSETS: &str = "base.include.flex";
pub const PARAM_BASE_INCLUDE_MENU_ASSETS: &str = "base.include.menu";
pub(crate) fn add_base_assets(cx: &mut Context) {
let weight = cx.get_param::<Weight>(PARAM_BASE_WEIGHT).unwrap_or(-90);
cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/root.css")
.with_version("0.0.1")
.with_weight(weight),
))
.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/looks.css")
.with_version("0.0.2")
.with_weight(weight),
))
.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/buttons.css")
.with_version("0.0.2")
.with_weight(weight),
));
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_ICONS) {
cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/icons.min.css")
.with_version("1.11.1")
.with_weight(weight),
));
}
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_FLEX_ASSETS) {
cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/flex.css")
.with_version("0.0.1")
.with_weight(weight),
));
}
if let Ok(true) = cx.get_param::<bool>(PARAM_BASE_INCLUDE_MENU_ASSETS) {
cx.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/menu.css")
.with_version("0.0.1")
.with_weight(weight),
))
.set_assets(AssetsOp::AddJavaScript(
JavaScript::defer("/base/js/menu.js")
.with_version("0.0.1")
.with_weight(weight),
));
}
}
// *************************************************************************************************
#[rustfmt::skip]
#[derive(AutoDefault)]
pub enum BreakPoint {
#[default]
None, // Does not apply. Rest initially assume 1 pixel = 0.0625rem
SM, // @media screen and [ (max-width: 35.5rem) <= 568px < (min-width: 35.5625rem) ]
MD, // @media screen and [ (max-width: 48rem) <= 768px < (min-width: 48.0625rem) ]
LG, // @media screen and [ (max-width: 62rem) <= 992px < (min-width: 62.0625rem) ]
XL, // @media screen and [ (max-width: 80rem) <= 1280px < (min-width: 80.0625rem) ]
X2L, // @media screen and [ (max-width: 90rem) <= 1440px < (min-width: 90.0625rem) ]
X3L, // @media screen and [ (max-width: 120rem) <= 1920px < (min-width: 120.0625rem) ]
X2K, // @media screen and [ (max-width: 160rem) <= 2560px < (min-width: 160.0625rem) ]
}
#[rustfmt::skip]
impl fmt::Display for BreakPoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BreakPoint::None => write!(f, "bp__none"),
BreakPoint::SM => write!(f, "bp__sm"),
BreakPoint::MD => write!(f, "bp__md"),
BreakPoint::LG => write!(f, "bp__lg"),
BreakPoint::XL => write!(f, "bp__xl"),
BreakPoint::X2L => write!(f, "bp__x2l"),
BreakPoint::X3L => write!(f, "bp__x3l"),
BreakPoint::X2K => write!(f, "bp__x2k"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum StyleBase {
#[default]
Default,
Info,
Success,
Warning,
Danger,
Light,
Dark,
Link,
}
#[rustfmt::skip]
impl fmt::Display for StyleBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StyleBase::Default => write!(f, "style__default"),
StyleBase::Info => write!(f, "style__info"),
StyleBase::Success => write!(f, "style__success"),
StyleBase::Warning => write!(f, "style__warning"),
StyleBase::Danger => write!(f, "style__danger"),
StyleBase::Light => write!(f, "style__light"),
StyleBase::Dark => write!(f, "style__dark"),
StyleBase::Link => write!(f, "style__link"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum FontSize {
ExtraLarge,
XxLarge,
XLarge,
Large,
Medium,
#[default]
Normal,
Small,
XSmall,
XxSmall,
ExtraSmall,
}
#[rustfmt::skip]
impl fmt::Display for FontSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FontSize::ExtraLarge => write!(f, "fs__x3l"),
FontSize::XxLarge => write!(f, "fs__x2l"),
FontSize::XLarge => write!(f, "fs__xl"),
FontSize::Large => write!(f, "fs__l"),
FontSize::Medium => write!(f, "fs__m"),
FontSize::Normal => write!(f, ""),
FontSize::Small => write!(f, "fs__s"),
FontSize::XSmall => write!(f, "fs__xs"),
FontSize::XxSmall => write!(f, "fs__x2s"),
FontSize::ExtraSmall => write!(f, "fs__x3s"),
}
}
}
// *************************************************************************************************
pub mod flex;
mod basic;
pub use basic::*;
mod fluent;
pub use fluent::Fluent;
mod error403;
pub use error403::Error403;
mod error404;
pub use error404::Error404;
mod heading;
pub use heading::{Heading, HeadingSize, HeadingType};
mod paragraph;
pub use paragraph::Paragraph;
mod icon;
pub use icon::Icon;
mod button;
pub use button::{Button, ButtonTarget};
mod image;
pub use image::{Image, ImageSize};
mod block;
pub use block::Block;
mod branding;
pub use branding::Branding;
mod powered_by;
pub use powered_by::{PoweredBy, PoweredByLogo};
pub mod menu;
pub use menu::Menu;
pub mod form;
pub use form::{Form, FormMethod};

View file

@ -1,5 +0,0 @@
mod html;
pub use html::Html;
mod fluent;
pub use fluent::Fluent;

View file

@ -1,95 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Block {
id : OptionId,
classes: OptionClasses,
style : StyleBase,
title : OptionTranslated,
mixed : MixedComponents,
}
impl ComponentTrait for Block {
fn new() -> Self {
Block::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.set_classes(
ClassesOp::Prepend,
["block__container".to_string(), self.style().to_string()].join(" "),
);
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let block_body = self.components().render(cx);
if block_body.is_empty() {
return PrepareMarkup::None;
}
let id = cx.required_id::<Block>(self.id());
PrepareMarkup::With(html! {
div id=(id) class=[self.classes().get()] {
@if let Some(title) = self.title().using(cx.langid()) {
h2 class="block__title" { (title) }
}
div class="block__content" { (block_body) }
}
})
}
}
impl Block {
// Block BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_style(&mut self, style: StyleBase) -> &mut Self {
self.style = style;
self
}
#[fn_builder]
pub fn set_title(&mut self, title: L10n) -> &mut Self {
self.title.set_value(title);
self
}
#[fn_builder]
pub fn set_components(&mut self, op: AnyOp) -> &mut Self {
self.mixed.set_value(op);
self
}
#[rustfmt::skip]
pub fn add_component(mut self, component: impl ComponentTrait) -> Self {
self.mixed.set_value(AnyOp::Add(AnyComponent::with(component)));
self
}
// Block GETTERS.
pub fn style(&self) -> &StyleBase {
&self.style
}
pub fn title(&self) -> &OptionTranslated {
&self.title
}
pub fn components(&self) -> &MixedComponents {
&self.mixed
}
}

View file

@ -1,102 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Branding {
id : OptionId,
#[default(_code = "global::SETTINGS.app.name.to_owned()")]
app_name : String,
slogan : OptionTranslated,
logo : OptionComponent<Image>,
#[default(_code = "|_| \"/\"")]
frontpage: FnContextualPath,
}
impl ComponentTrait for Branding {
fn new() -> Self {
Branding::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let logo = self.logo().render(cx);
let home = self.frontpage()(cx);
let title = &L10n::l("site_home").using(cx.langid());
PrepareMarkup::With(html! {
div id=[self.id()] class="branding__container" {
div class="branding__content" {
@if !logo.is_empty() {
a class="branding__logo" href=(home) title=[title] rel="home" {
(logo)
}
}
div class="branding__text" {
a class="branding__name" href=(home) title=[title] rel="home" {
(self.app_name())
}
@if let Some(slogan) = self.slogan().using(cx.langid()) {
div class="branding__slogan" {
(slogan)
}
}
}
}
}
})
}
}
impl Branding {
// Branding BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_app_name(&mut self, app_name: impl Into<String>) -> &mut Self {
self.app_name = app_name.into();
self
}
#[fn_builder]
pub fn set_slogan(&mut self, slogan: L10n) -> &mut Self {
self.slogan.set_value(slogan);
self
}
#[fn_builder]
pub fn set_logo(&mut self, logo: Option<Image>) -> &mut Self {
self.logo.set_value(logo);
self
}
#[fn_builder]
pub fn set_frontpage(&mut self, frontpage: FnContextualPath) -> &mut Self {
self.frontpage = frontpage;
self
}
// Branding GETTERS.
pub fn app_name(&self) -> &String {
&self.app_name
}
pub fn slogan(&self) -> &OptionTranslated {
&self.slogan
}
pub fn logo(&self) -> &OptionComponent<Image> {
&self.logo
}
pub fn frontpage(&self) -> &FnContextualPath {
&self.frontpage
}
}

View file

@ -1,156 +0,0 @@
use crate::prelude::*;
#[derive(AutoDefault)]
pub enum ButtonTarget {
#[default]
Default,
Blank,
Parent,
Top,
Context(String),
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Button {
id : OptionId,
classes : OptionClasses,
style : StyleBase,
font_size : FontSize,
left_icon : OptionComponent<Icon>,
right_icon: OptionComponent<Icon>,
href : OptionString,
html : OptionTranslated,
target : ButtonTarget,
}
impl ComponentTrait for Button {
fn new() -> Self {
Button::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.set_classes(
ClassesOp::Prepend,
[
"button__tap".to_string(),
self.style().to_string(),
self.font_size().to_string(),
]
.join(" "),
);
}
#[rustfmt::skip]
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let target = match &self.target() {
ButtonTarget::Default => None,
ButtonTarget::Blank => Some("_blank"),
ButtonTarget::Parent => Some("_parent"),
ButtonTarget::Top => Some("_top"),
ButtonTarget::Context(name) => Some(name.as_str()),
};
PrepareMarkup::With(html! {
a
id=[self.id()]
class=[self.classes().get()]
href=[self.href().get()]
target=[target]
{
(self.left_icon().render(cx))
span { (self.html().escaped(cx.langid())) }
(self.right_icon().render(cx))
}
})
}
}
impl Button {
pub fn anchor(href: impl Into<String>, html: L10n) -> Self {
Button::default().with_href(href).with_html(html)
}
// Button BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_style(&mut self, style: StyleBase) -> &mut Self {
self.style = style;
self
}
#[fn_builder]
pub fn set_font_size(&mut self, font_size: FontSize) -> &mut Self {
self.font_size = font_size;
self
}
#[fn_builder]
pub fn set_left_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.left_icon.set_value(icon);
self
}
#[fn_builder]
pub fn set_right_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.right_icon.set_value(icon);
self
}
#[fn_builder]
pub fn set_href(&mut self, href: impl Into<String>) -> &mut Self {
self.href.set_value(href);
self
}
#[fn_builder]
pub fn set_html(&mut self, html: L10n) -> &mut Self {
self.html.set_value(html);
self
}
#[fn_builder]
pub fn set_target(&mut self, target: ButtonTarget) -> &mut Self {
self.target = target;
self
}
// Button GETTERS.
pub fn style(&self) -> &StyleBase {
&self.style
}
pub fn font_size(&self) -> &FontSize {
&self.font_size
}
pub fn left_icon(&self) -> &OptionComponent<Icon> {
&self.left_icon
}
pub fn right_icon(&self) -> &OptionComponent<Icon> {
&self.right_icon
}
pub fn href(&self) -> &OptionString {
&self.href
}
pub fn html(&self) -> &OptionTranslated {
&self.html
}
pub fn target(&self) -> &ButtonTarget {
&self.target
}
}

View file

@ -1,311 +0,0 @@
mod container;
pub use container::Container;
mod item;
pub use item::Item;
use crate::prelude::*;
use std::fmt;
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Direction {
#[default]
Default,
Row(BreakPoint),
RowReverse(BreakPoint),
Column(BreakPoint),
ColumnReverse(BreakPoint),
}
#[rustfmt::skip]
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Direction::Default => write!(f, "flex__row {}", BreakPoint::default()),
Direction::Row(bp) => write!(f, "flex__row {bp}"),
Direction::RowReverse(bp) => write!(f, "flex__row flex__reverse {bp}"),
Direction::Column(bp) => write!(f, "flex__col {bp}"),
Direction::ColumnReverse(bp) => write!(f, "flex__col flex__reverse {bp}"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Wrap {
#[default]
Default,
NoWrap,
Wrap(ContentAlign),
WrapReverse(ContentAlign),
}
#[rustfmt::skip]
impl fmt::Display for Wrap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Wrap::Default => write!(f, ""),
Wrap::NoWrap => write!(f, "flex__nowrap"),
Wrap::Wrap(a) => write!(f, "flex__wrap {a}"),
Wrap::WrapReverse(a) => write!(f, "flex__wrap-reverse {a}"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum ContentAlign {
#[default]
Default,
Start,
End,
Center,
Stretch,
SpaceBetween,
SpaceAround,
}
#[rustfmt::skip]
impl fmt::Display for ContentAlign {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ContentAlign::Default => write!(f, ""),
ContentAlign::Start => write!(f, "flex__align-start"),
ContentAlign::End => write!(f, "flex__align-end"),
ContentAlign::Center => write!(f, "flex__align-center"),
ContentAlign::Stretch => write!(f, "flex__align-stretch"),
ContentAlign::SpaceBetween => write!(f, "flex__align-space-between"),
ContentAlign::SpaceAround => write!(f, "flex__align-space-around"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Justify {
#[default]
Default,
Start,
End,
Center,
SpaceBetween,
SpaceAround,
SpaceEvenly,
}
#[rustfmt::skip]
impl fmt::Display for Justify {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Justify::Default => write!(f, ""),
Justify::Start => write!(f, "flex__justify-start"),
Justify::End => write!(f, "flex__justify-end"),
Justify::Center => write!(f, "flex__justify-center"),
Justify::SpaceBetween => write!(f, "flex__justify-space-between"),
Justify::SpaceAround => write!(f, "flex__justify-space-around"),
Justify::SpaceEvenly => write!(f, "flex__justify-space-evenly"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Align {
#[default]
Default,
Start,
End,
Center,
Stretch,
Baseline,
}
#[rustfmt::skip]
impl fmt::Display for Align {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Align::Default => write!(f, ""),
Align::Start => write!(f, "flex__start"),
Align::End => write!(f, "flex__end"),
Align::Center => write!(f, "flex__center"),
Align::Stretch => write!(f, "flex__stretch"),
Align::Baseline => write!(f, "flex__baseline"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Gap {
#[default]
Default,
Row(unit::Value),
Column(unit::Value),
Distinct(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::Column(c) => write!(f, "column-gap: {c};"),
Gap::Distinct(r, c) => write!(f, "gap: {r} {c};"),
Gap::Both(v) => write!(f, "gap: {v};"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Grow {
#[default]
Default,
Is1,
Is2,
Is3,
Is4,
Is5,
Is6,
Is7,
Is8,
Is9,
}
impl fmt::Display for Grow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Grow::Default => write!(f, ""),
Grow::Is1 => write!(f, "flex__grow-1"),
Grow::Is2 => write!(f, "flex__grow-2"),
Grow::Is3 => write!(f, "flex__grow-3"),
Grow::Is4 => write!(f, "flex__grow-4"),
Grow::Is5 => write!(f, "flex__grow-5"),
Grow::Is6 => write!(f, "flex__grow-6"),
Grow::Is7 => write!(f, "flex__grow-7"),
Grow::Is8 => write!(f, "flex__grow-8"),
Grow::Is9 => write!(f, "flex__grow-9"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Shrink {
#[default]
Default,
Is1,
Is2,
Is3,
Is4,
Is5,
Is6,
Is7,
Is8,
Is9,
}
impl fmt::Display for Shrink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Shrink::Default => write!(f, ""),
Shrink::Is1 => write!(f, "flex__shrink-1"),
Shrink::Is2 => write!(f, "flex__shrink-2"),
Shrink::Is3 => write!(f, "flex__shrink-3"),
Shrink::Is4 => write!(f, "flex__shrink-4"),
Shrink::Is5 => write!(f, "flex__shrink-5"),
Shrink::Is6 => write!(f, "flex__shrink-6"),
Shrink::Is7 => write!(f, "flex__shrink-7"),
Shrink::Is8 => write!(f, "flex__shrink-8"),
Shrink::Is9 => write!(f, "flex__shrink-9"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Size {
#[default]
Default,
Percent10,
Percent20,
Percent25,
Percent33,
Percent40,
Percent50,
Percent60,
Percent66,
Percent75,
Percent80,
Percent90,
}
impl fmt::Display for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Size::Default => write!(f, ""),
Size::Percent10 => write!(f, "flex__size-10"),
Size::Percent20 => write!(f, "flex__size-20"),
Size::Percent25 => write!(f, "flex__size-25"),
Size::Percent33 => write!(f, "flex__size-33"),
Size::Percent40 => write!(f, "flex__size-40"),
Size::Percent50 => write!(f, "flex__size-50"),
Size::Percent60 => write!(f, "flex__size-60"),
Size::Percent66 => write!(f, "flex__size-66"),
Size::Percent75 => write!(f, "flex__size-75"),
Size::Percent80 => write!(f, "flex__size-80"),
Size::Percent90 => write!(f, "flex__size-90"),
}
}
}
// *************************************************************************************************
#[derive(AutoDefault)]
pub enum Offset {
#[default]
Default,
Offset10,
Offset20,
Offset25,
Offset33,
Offset40,
Offset50,
Offset60,
Offset66,
Offset75,
Offset80,
Offset90,
}
impl fmt::Display for Offset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Offset::Default => write!(f, ""),
Offset::Offset10 => write!(f, "flex__offset-10"),
Offset::Offset20 => write!(f, "flex__offset-20"),
Offset::Offset25 => write!(f, "flex__offset-25"),
Offset::Offset33 => write!(f, "flex__offset-33"),
Offset::Offset40 => write!(f, "flex__offset-40"),
Offset::Offset50 => write!(f, "flex__offset-50"),
Offset::Offset60 => write!(f, "flex__offset-60"),
Offset::Offset66 => write!(f, "flex__offset-66"),
Offset::Offset75 => write!(f, "flex__offset-75"),
Offset::Offset80 => write!(f, "flex__offset-80"),
Offset::Offset90 => write!(f, "flex__offset-90"),
}
}
}

View file

@ -1,212 +0,0 @@
use crate::prelude::*;
#[derive(AutoDefault)]
pub enum ContainerType {
#[default]
Default,
Header,
Main,
Section,
Article,
Footer,
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Container {
id : OptionId,
classes : OptionClasses,
container_type: ContainerType,
direction : flex::Direction,
flex_wrap : flex::Wrap,
flex_justify : flex::Justify,
flex_align : flex::Align,
flex_gap : flex::Gap,
items : MixedComponents,
}
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.set_classes(
ClassesOp::Prepend,
[
"flex__container".to_string(),
self.direction().to_string(),
self.wrap().to_string(),
self.justify().to_string(),
self.align().to_string(),
]
.join(" "),
);
cx.set_param::<bool>(PARAM_BASE_INCLUDE_FLEX_ASSETS, &true);
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let output = self.items().render(cx);
if output.is_empty() {
return PrepareMarkup::None;
}
let gap = match self.gap() {
flex::Gap::Default => None,
_ => Some(self.gap().to_string()),
};
match self.container_type() {
ContainerType::Default => PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Header => PrepareMarkup::With(html! {
header id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Main => PrepareMarkup::With(html! {
main id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Section => PrepareMarkup::With(html! {
section id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Article => PrepareMarkup::With(html! {
article id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
ContainerType::Footer => PrepareMarkup::With(html! {
footer id=[self.id()] class=[self.classes().get()] style=[gap] {
(output)
}
}),
}
}
}
impl Container {
pub fn header() -> Self {
Container {
container_type: ContainerType::Header,
..Default::default()
}
}
pub fn main() -> Self {
Container {
container_type: ContainerType::Main,
..Default::default()
}
}
pub fn section() -> Self {
Container {
container_type: ContainerType::Section,
..Default::default()
}
}
pub fn article() -> Self {
Container {
container_type: ContainerType::Article,
..Default::default()
}
}
pub fn footer() -> Self {
Container {
container_type: ContainerType::Footer,
..Default::default()
}
}
// Container BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_direction(&mut self, direction: flex::Direction) -> &mut Self {
self.direction = direction;
self
}
#[fn_builder]
pub fn set_wrap(&mut self, wrap: flex::Wrap) -> &mut Self {
self.flex_wrap = wrap;
self
}
#[fn_builder]
pub fn set_justify(&mut self, justify: flex::Justify) -> &mut Self {
self.flex_justify = justify;
self
}
#[fn_builder]
pub fn set_align(&mut self, align: flex::Align) -> &mut Self {
self.flex_align = align;
self
}
#[fn_builder]
pub fn set_gap(&mut self, gap: flex::Gap) -> &mut Self {
self.flex_gap = gap;
self
}
#[fn_builder]
pub fn set_items(&mut self, op: TypedOp<flex::Item>) -> &mut Self {
self.items.set_typed(op);
self
}
pub fn add_item(mut self, item: flex::Item) -> Self {
self.items.set_value(AnyOp::Add(AnyComponent::with(item)));
self
}
// Container GETTERS.
pub fn container_type(&self) -> &ContainerType {
&self.container_type
}
pub fn direction(&self) -> &flex::Direction {
&self.direction
}
pub fn wrap(&self) -> &flex::Wrap {
&self.flex_wrap
}
pub fn justify(&self) -> &flex::Justify {
&self.flex_justify
}
pub fn align(&self) -> &flex::Align {
&self.flex_align
}
pub fn gap(&self) -> &flex::Gap {
&self.flex_gap
}
pub fn items(&self) -> &MixedComponents {
&self.items
}
}

View file

@ -1,200 +0,0 @@
use crate::prelude::*;
#[derive(AutoDefault)]
pub enum ItemType {
#[default]
Default,
Region,
Wrapper,
Bundle,
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Item {
id : OptionId,
classes : OptionClasses,
item_type : ItemType,
flex_grow : flex::Grow,
flex_shrink: flex::Shrink,
flex_size : flex::Size,
flex_offset: flex::Offset,
flex_align : flex::Align,
mixed : MixedComponents,
}
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.set_classes(
ClassesOp::Prepend,
[
"flex__item".to_string(),
self.grow().to_string(),
self.shrink().to_string(),
self.size().to_string(),
self.offset().to_string(),
self.align().to_string(),
]
.join(" "),
);
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let (output, region) = match self.item_type() {
ItemType::Region => (
self.components().render(cx),
if let Some(id) = self.id() {
cx.prepare_region(id)
} else {
Markup::default()
},
),
_ => (self.components().render(cx), Markup::default()),
};
if output.is_empty() && region.is_empty() {
return PrepareMarkup::None;
}
match self.item_type() {
ItemType::Default => PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] {
div class="flex__content" {
(output)
}
}
}),
ItemType::Region => PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] {
div class="flex__content flex__region" {
(region)
(output)
}
}
}),
ItemType::Wrapper => PrepareMarkup::With(html! {
div id=[self.id()] class=[self.classes().get()] {
(output)
}
}),
ItemType::Bundle => PrepareMarkup::With(html! {
(output)
}),
}
}
}
impl Item {
pub fn region() -> Self {
Item {
item_type: ItemType::Region,
..Default::default()
}
}
pub fn wrapper() -> Self {
Item {
item_type: ItemType::Wrapper,
..Default::default()
}
}
pub fn bundle() -> Self {
Item {
item_type: ItemType::Bundle,
..Default::default()
}
}
pub fn with(component: impl ComponentTrait) -> Self {
Item::default().add_component(component)
}
// Item BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_grow(&mut self, grow: flex::Grow) -> &mut Self {
self.flex_grow = grow;
self
}
#[fn_builder]
pub fn set_shrink(&mut self, shrink: flex::Shrink) -> &mut Self {
self.flex_shrink = shrink;
self
}
#[fn_builder]
// Ensures the item occupies the exact specified width, neither growing nor shrinking,
// regardless of the available space in the container or the size of other items.
pub fn set_size(&mut self, size: flex::Size) -> &mut Self {
self.flex_size = size;
self
}
#[fn_builder]
pub fn set_offset(&mut self, offset: flex::Offset) -> &mut Self {
self.flex_offset = offset;
self
}
#[fn_builder]
pub fn set_align(&mut self, align: flex::Align) -> &mut Self {
self.flex_align = align;
self
}
#[fn_builder]
pub fn set_components(&mut self, op: AnyOp) -> &mut Self {
self.mixed.set_value(op);
self
}
#[rustfmt::skip]
pub fn add_component(mut self, component: impl ComponentTrait) -> Self {
self.mixed.set_value(AnyOp::Add(AnyComponent::with(component)));
self
}
// Item GETTERS.
pub fn item_type(&self) -> &ItemType {
&self.item_type
}
pub fn grow(&self) -> &flex::Grow {
&self.flex_grow
}
pub fn shrink(&self) -> &flex::Shrink {
&self.flex_shrink
}
pub fn size(&self) -> &flex::Size {
&self.flex_size
}
pub fn offset(&self) -> &flex::Offset {
&self.flex_offset
}
pub fn align(&self) -> &flex::Align {
&self.flex_align
}
pub fn components(&self) -> &MixedComponents {
&self.mixed
}
}

View file

@ -1,14 +0,0 @@
mod form_main;
pub use form_main::{Form, FormMethod};
mod input;
pub use input::{Input, InputType};
mod hidden;
pub use hidden::Hidden;
mod date;
pub use date::Date;
mod action_button;
pub use action_button::{ActionButton, ActionButtonType};

View file

@ -1,182 +0,0 @@
use crate::prelude::*;
use std::fmt;
#[derive(AutoDefault)]
pub enum ActionButtonType {
#[default]
Submit,
Reset,
}
#[rustfmt::skip]
impl fmt::Display for ActionButtonType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ActionButtonType::Submit => write!(f, "submit"),
ActionButtonType::Reset => write!(f, "reset"),
}
}
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct ActionButton {
classes : OptionClasses,
button_type: ActionButtonType,
style : StyleBase,
font_size : FontSize,
left_icon : OptionComponent<Icon>,
right_icon : OptionComponent<Icon>,
name : OptionString,
value : OptionTranslated,
autofocus : OptionString,
disabled : OptionString,
}
impl ComponentTrait for ActionButton {
fn new() -> Self {
ActionButton::submit()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.set_classes(
ClassesOp::Prepend,
[
"button__tap".to_string(),
self.style().to_string(),
self.font_size().to_string(),
]
.join(" "),
);
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let id = self.name().get().map(|name| concat_string!("edit-", name));
PrepareMarkup::With(html! {
button
type=(self.button_type().to_string())
id=[id]
class=[self.classes().get()]
name=[self.name().get()]
value=[self.value().using(cx.langid())]
autofocus=[self.autofocus().get()]
disabled=[self.disabled().get()]
{
(self.left_icon().render(cx))
span { (self.value().escaped(cx.langid())) }
(self.right_icon().render(cx))
}
})
}
}
impl ActionButton {
pub fn submit() -> Self {
ActionButton {
button_type: ActionButtonType::Submit,
style: StyleBase::Default,
value: OptionTranslated::new(L10n::l("button_submit")),
..Default::default()
}
}
pub fn reset() -> Self {
ActionButton {
button_type: ActionButtonType::Reset,
style: StyleBase::Info,
value: OptionTranslated::new(L10n::l("button_reset")),
..Default::default()
}
}
// Button BUILDER.
#[fn_builder]
pub fn set_style(&mut self, style: StyleBase) -> &mut Self {
self.style = style;
self
}
#[fn_builder]
pub fn set_font_size(&mut self, font_size: FontSize) -> &mut Self {
self.font_size = font_size;
self
}
#[fn_builder]
pub fn set_left_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.left_icon.set_value(icon);
self
}
#[fn_builder]
pub fn set_right_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.right_icon.set_value(icon);
self
}
#[fn_builder]
pub fn set_name(&mut self, name: &str) -> &mut Self {
self.name.set_value(name);
self
}
#[fn_builder]
pub fn set_value(&mut self, value: L10n) -> &mut Self {
self.value.set_value(value);
self
}
#[fn_builder]
pub fn set_autofocus(&mut self, toggle: bool) -> &mut Self {
self.autofocus
.set_value(if toggle { "autofocus" } else { "" });
self
}
#[fn_builder]
pub fn set_disabled(&mut self, toggle: bool) -> &mut Self {
self.disabled
.set_value(if toggle { "disabled" } else { "" });
self
}
// Button GETTERS.
pub fn button_type(&self) -> &ActionButtonType {
&self.button_type
}
pub fn style(&self) -> &StyleBase {
&self.style
}
pub fn font_size(&self) -> &FontSize {
&self.font_size
}
pub fn left_icon(&self) -> &OptionComponent<Icon> {
&self.left_icon
}
pub fn right_icon(&self) -> &OptionComponent<Icon> {
&self.right_icon
}
pub fn name(&self) -> &OptionString {
&self.name
}
pub fn value(&self) -> &OptionTranslated {
&self.value
}
pub fn autofocus(&self) -> &OptionString {
&self.autofocus
}
pub fn disabled(&self) -> &OptionString {
&self.disabled
}
}

View file

@ -1,166 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Date {
classes : OptionClasses,
name : OptionString,
value : OptionString,
label : OptionString,
placeholder : OptionString,
autofocus : OptionString,
autocomplete: OptionString,
disabled : OptionString,
readonly : OptionString,
required : OptionString,
help_text : OptionString,
}
impl ComponentTrait for Date {
fn new() -> Self {
Date::default().with_classes(ClassesOp::Add, "form-item form-type-date")
}
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
let id = self.name().get().map(|name| concat_string!("edit-", name));
PrepareMarkup::With(html! {
div class=[self.classes().get()] {
@if let Some(label) = self.label().get() {
label class="form-label" for=[&id] {
(label) " "
@if self.required().get().is_some() {
span
class="form-required"
title="Este campo es obligatorio." { "*" } " "
}
}
}
input
type="date"
id=[id]
class="form-control"
name=[self.name().get()]
value=[self.value().get()]
placeholder=[self.placeholder().get()]
autofocus=[self.autofocus().get()]
autocomplete=[self.autocomplete().get()]
readonly=[self.readonly().get()]
required=[self.required().get()]
disabled=[self.disabled().get()] {}
@if let Some(help_text) = self.help_text().get() {
div class="form-text" { (help_text) }
}
}
})
}
}
impl Date {
// Date BUILDER.
#[fn_builder]
pub fn set_name(&mut self, name: &str) -> &mut Self {
self.name.set_value(name);
self
}
#[fn_builder]
pub fn set_value(&mut self, value: &str) -> &mut Self {
self.value.set_value(value);
self
}
#[fn_builder]
pub fn set_label(&mut self, label: &str) -> &mut Self {
self.label.set_value(label);
self
}
#[fn_builder]
pub fn set_placeholder(&mut self, placeholder: &str) -> &mut Self {
self.placeholder.set_value(placeholder);
self
}
#[fn_builder]
pub fn set_autofocus(&mut self, toggle: bool) -> &mut Self {
self.autofocus
.set_value(if toggle { "autofocus" } else { "" });
self
}
#[fn_builder]
pub fn set_autocomplete(&mut self, toggle: bool) -> &mut Self {
self.autocomplete.set_value(if toggle { "" } else { "off" });
self
}
#[fn_builder]
pub fn set_disabled(&mut self, toggle: bool) -> &mut Self {
self.disabled
.set_value(if toggle { "disabled" } else { "" });
self
}
#[fn_builder]
pub fn set_readonly(&mut self, toggle: bool) -> &mut Self {
self.readonly
.set_value(if toggle { "readonly" } else { "" });
self
}
#[fn_builder]
pub fn set_required(&mut self, toggle: bool) -> &mut Self {
self.required
.set_value(if toggle { "required" } else { "" });
self
}
#[fn_builder]
pub fn set_help_text(&mut self, help_text: &str) -> &mut Self {
self.help_text.set_value(help_text);
self
}
// Date GETTERS.
pub fn name(&self) -> &OptionString {
&self.name
}
pub fn value(&self) -> &OptionString {
&self.value
}
pub fn label(&self) -> &OptionString {
&self.label
}
pub fn placeholder(&self) -> &OptionString {
&self.placeholder
}
pub fn autofocus(&self) -> &OptionString {
&self.autofocus
}
pub fn autocomplete(&self) -> &OptionString {
&self.autocomplete
}
pub fn disabled(&self) -> &OptionString {
&self.disabled
}
pub fn readonly(&self) -> &OptionString {
&self.readonly
}
pub fn required(&self) -> &OptionString {
&self.required
}
pub fn help_text(&self) -> &OptionString {
&self.help_text
}
}

View file

@ -1,107 +0,0 @@
use crate::prelude::*;
#[derive(AutoDefault)]
pub enum FormMethod {
#[default]
Post,
Get,
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Form {
id : OptionId,
classes: OptionClasses,
action : OptionString,
charset: OptionString,
method : FormMethod,
mixed : MixedComponents,
}
impl ComponentTrait for Form {
fn new() -> Self {
Form::default()
.with_classes(ClassesOp::Add, "form")
.with_charset("UTF-8")
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let method = match self.method() {
FormMethod::Post => Some("post".to_owned()),
FormMethod::Get => None,
};
PrepareMarkup::With(html! {
form
id=[self.id()]
class=[self.classes().get()]
action=[self.action().get()]
method=[method]
accept-charset=[self.charset().get()]
{
div { (self.elements().render(cx)) }
}
})
}
}
impl Form {
// Form BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_action(&mut self, action: &str) -> &mut Self {
self.action.set_value(action);
self
}
#[fn_builder]
pub fn set_charset(&mut self, charset: &str) -> &mut Self {
self.charset.set_value(charset);
self
}
#[fn_builder]
pub fn set_method(&mut self, method: FormMethod) -> &mut Self {
self.method = method;
self
}
#[fn_builder]
pub fn set_elements(&mut self, op: AnyOp) -> &mut Self {
self.mixed.set_value(op);
self
}
#[rustfmt::skip]
pub fn add_element(mut self, element: impl ComponentTrait) -> Self {
self.mixed.set_value(AnyOp::Add(AnyComponent::with(element)));
self
}
// Form GETTERS.
pub fn action(&self) -> &OptionString {
&self.action
}
pub fn charset(&self) -> &OptionString {
&self.charset
}
pub fn method(&self) -> &FormMethod {
&self.method
}
pub fn elements(&self) -> &MixedComponents {
&self.mixed
}
}

View file

@ -1,51 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Hidden {
name : OptionName,
value : OptionString,
}
impl ComponentTrait for Hidden {
fn new() -> Self {
Hidden::default()
}
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
let id = self.name().get().map(|name| concat_string!("value-", name));
PrepareMarkup::With(html! {
input type="hidden" id=[id] name=[self.name().get()] value=[self.value().get()] {}
})
}
}
impl Hidden {
pub fn set(name: &str, value: &str) -> Self {
Hidden::default().with_name(name).with_value(value)
}
// Hidden BUILDER.
#[fn_builder]
pub fn set_name(&mut self, name: &str) -> &mut Self {
self.name.set_value(name);
self
}
#[fn_builder]
pub fn set_value(&mut self, value: &str) -> &mut Self {
self.value.set_value(value);
self
}
// Hidden GETTERS.
pub fn name(&self) -> &OptionName {
&self.name
}
pub fn value(&self) -> &OptionString {
&self.value
}
}

View file

@ -1,283 +0,0 @@
use crate::prelude::*;
#[derive(AutoDefault)]
pub enum InputType {
#[default]
Textfield,
Password,
Search,
Email,
Telephone,
Url,
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Input {
classes : OptionClasses,
input_type : InputType,
name : OptionName,
value : OptionString,
label : OptionTranslated,
size : Option<u16>,
minlength : Option<u16>,
maxlength : Option<u16>,
placeholder : OptionString,
autofocus : OptionString,
autocomplete: OptionString,
disabled : OptionString,
readonly : OptionString,
required : OptionString,
help_text : OptionTranslated,
}
impl ComponentTrait for Input {
fn new() -> Self {
Input::default()
.with_classes(ClassesOp::Add, "form-item form-type-textfield")
.with_size(Some(60))
.with_maxlength(Some(128))
}
#[rustfmt::skip]
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let type_input = match self.input_type() {
InputType::Textfield => "text",
InputType::Password => "password",
InputType::Search => "search",
InputType::Email => "email",
InputType::Telephone => "tel",
InputType::Url => "url",
};
let id = self.name().get().map(|name| concat_string!("edit-", name));
PrepareMarkup::With(html! {
div class=[self.classes().get()] {
@if let Some(label) = self.label().using(cx.langid()) {
label class="form-label" for=[&id] {
(label) " "
@if self.required().get().is_some() {
span
class="form-required"
title="Este campo es obligatorio." { "*" } " "
}
}
}
input
type=(type_input)
id=[id]
class="form-control"
name=[self.name().get()]
value=[self.value().get()]
size=[self.size()]
minlength=[self.minlength()]
maxlength=[self.maxlength()]
placeholder=[self.placeholder().get()]
autofocus=[self.autofocus().get()]
autocomplete=[self.autocomplete().get()]
readonly=[self.readonly().get()]
required=[self.required().get()]
disabled=[self.disabled().get()] {}
@if let Some(description) = self.help_text().using(cx.langid()) {
div class="form-text" { (description) }
}
}
})
}
}
impl Input {
pub fn textfield() -> Self {
Input::default()
}
pub fn password() -> Self {
let mut input = Input::default().with_classes(
ClassesOp::Replace("form-type-textfield".to_owned()),
"form-type-password",
);
input.input_type = InputType::Password;
input
}
pub fn search() -> Self {
let mut input = Input::default().with_classes(
ClassesOp::Replace("form-type-textfield".to_owned()),
"form-type-search",
);
input.input_type = InputType::Search;
input
}
pub fn email() -> Self {
let mut input = Input::default().with_classes(
ClassesOp::Replace("form-type-textfield".to_owned()),
"form-type-email",
);
input.input_type = InputType::Email;
input
}
pub fn telephone() -> Self {
let mut input = Input::default().with_classes(
ClassesOp::Replace("form-type-textfield".to_owned()),
"form-type-telephone",
);
input.input_type = InputType::Telephone;
input
}
pub fn url() -> Self {
let mut input = Input::default().with_classes(
ClassesOp::Replace("form-type-textfield".to_owned()),
"form-type-url",
);
input.input_type = InputType::Url;
input
}
// Input BUILDER.
#[fn_builder]
pub fn set_name(&mut self, name: &str) -> &mut Self {
if let Some(previous) = self.name.get() {
self.set_classes(ClassesOp::Remove, concat_string!("form-item-", previous));
}
self.set_classes(ClassesOp::Add, concat_string!("form-item-", name));
self.name.set_value(name);
self
}
#[fn_builder]
pub fn set_value(&mut self, value: &str) -> &mut Self {
self.value.set_value(value);
self
}
#[fn_builder]
pub fn set_label(&mut self, label: L10n) -> &mut Self {
self.label.set_value(label);
self
}
#[fn_builder]
pub fn set_size(&mut self, size: Option<u16>) -> &mut Self {
self.size = size;
self
}
#[fn_builder]
pub fn set_minlength(&mut self, minlength: Option<u16>) -> &mut Self {
self.minlength = minlength;
self
}
#[fn_builder]
pub fn set_maxlength(&mut self, maxlength: Option<u16>) -> &mut Self {
self.maxlength = maxlength;
self
}
#[fn_builder]
pub fn set_placeholder(&mut self, placeholder: &str) -> &mut Self {
self.placeholder.set_value(placeholder);
self
}
#[fn_builder]
pub fn set_autofocus(&mut self, toggle: bool) -> &mut Self {
self.autofocus
.set_value(if toggle { "autofocus" } else { "" });
self
}
#[fn_builder]
pub fn set_autocomplete(&mut self, toggle: bool) -> &mut Self {
self.autocomplete.set_value(if toggle { "" } else { "off" });
self
}
#[fn_builder]
pub fn set_disabled(&mut self, toggle: bool) -> &mut Self {
self.disabled
.set_value(if toggle { "disabled" } else { "" });
self
}
#[fn_builder]
pub fn set_readonly(&mut self, toggle: bool) -> &mut Self {
self.readonly
.set_value(if toggle { "readonly" } else { "" });
self
}
#[fn_builder]
pub fn set_required(&mut self, toggle: bool) -> &mut Self {
self.required
.set_value(if toggle { "required" } else { "" });
self
}
#[fn_builder]
pub fn set_help_text(&mut self, help_text: L10n) -> &mut Self {
self.help_text.set_value(help_text);
self
}
// Input GETTERS.
pub fn input_type(&self) -> &InputType {
&self.input_type
}
pub fn name(&self) -> &OptionName {
&self.name
}
pub fn value(&self) -> &OptionString {
&self.value
}
pub fn label(&self) -> &OptionTranslated {
&self.label
}
pub fn size(&self) -> Option<u16> {
self.size
}
pub fn minlength(&self) -> Option<u16> {
self.minlength
}
pub fn maxlength(&self) -> Option<u16> {
self.maxlength
}
pub fn placeholder(&self) -> &OptionString {
&self.placeholder
}
pub fn autofocus(&self) -> &OptionString {
&self.autofocus
}
pub fn autocomplete(&self) -> &OptionString {
&self.autocomplete
}
pub fn disabled(&self) -> &OptionString {
&self.disabled
}
pub fn readonly(&self) -> &OptionString {
&self.readonly
}
pub fn required(&self) -> &OptionString {
&self.required
}
pub fn help_text(&self) -> &OptionTranslated {
&self.help_text
}
}

View file

@ -1,157 +0,0 @@
use crate::prelude::*;
use std::fmt;
#[derive(AutoDefault)]
pub enum HeadingType {
#[default]
H1,
H2,
H3,
H4,
H5,
H6,
}
#[derive(AutoDefault)]
pub enum HeadingSize {
ExtraLarge,
XxLarge,
XLarge,
Large,
Medium,
#[default]
Normal,
Subtitle,
}
#[rustfmt::skip]
impl fmt::Display for HeadingSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HeadingSize::ExtraLarge => write!(f, "heading__title-x3l"),
HeadingSize::XxLarge => write!(f, "heading__title-x2l"),
HeadingSize::XLarge => write!(f, "heading__title-xl"),
HeadingSize::Large => write!(f, "heading__title-l"),
HeadingSize::Medium => write!(f, "heading__title-m"),
HeadingSize::Normal => write!(f, ""),
HeadingSize::Subtitle => write!(f, "heading__subtitle"),
}
}
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Heading {
id : OptionId,
classes : OptionClasses,
heading_type: HeadingType,
size : HeadingSize,
text : OptionTranslated,
}
impl ComponentTrait for Heading {
fn new() -> Self {
Heading::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.set_classes(ClassesOp::Add, self.size().to_string());
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let id = self.id();
let classes = self.classes().get();
let text = self.text().escaped(cx.langid());
PrepareMarkup::With(html! { @match &self.heading_type() {
HeadingType::H1 => h1 id=[id] class=[classes] { (text) },
HeadingType::H2 => h2 id=[id] class=[classes] { (text) },
HeadingType::H3 => h3 id=[id] class=[classes] { (text) },
HeadingType::H4 => h4 id=[id] class=[classes] { (text) },
HeadingType::H5 => h5 id=[id] class=[classes] { (text) },
HeadingType::H6 => h6 id=[id] class=[classes] { (text) },
}})
}
}
impl Heading {
pub fn h1(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H1)
.with_text(text)
}
pub fn h2(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H2)
.with_text(text)
}
pub fn h3(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H3)
.with_text(text)
}
pub fn h4(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H4)
.with_text(text)
}
pub fn h5(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H5)
.with_text(text)
}
pub fn h6(text: L10n) -> Self {
Heading::default()
.with_heading_type(HeadingType::H6)
.with_text(text)
}
// Heading BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_heading_type(&mut self, heading_type: HeadingType) -> &mut Self {
self.heading_type = heading_type;
self
}
#[fn_builder]
pub fn set_size(&mut self, size: HeadingSize) -> &mut Self {
self.size = size;
self
}
#[fn_builder]
pub fn set_text(&mut self, text: L10n) -> &mut Self {
self.text.set_value(text);
self
}
// Paragraph GETTERS.
pub fn heading_type(&self) -> &HeadingType {
&self.heading_type
}
pub fn size(&self) -> &HeadingSize {
&self.size
}
pub fn text(&self) -> &OptionTranslated {
&self.text
}
}

View file

@ -1,62 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Icon {
classes : OptionClasses,
icon_name: OptionString,
font_size: FontSize,
}
impl ComponentTrait for Icon {
fn new() -> Self {
Icon::default()
}
#[rustfmt::skip]
fn setup_before_prepare(&mut self, cx: &mut Context) {
if let Some(icon_name) = self.icon_name().get() {
self.set_classes(ClassesOp::Prepend,
concat_string!("bi-", icon_name, " ", self.font_size().to_string()),
);
cx.set_param::<bool>(PARAM_BASE_INCLUDE_ICONS, &true);
}
}
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
match self.icon_name().get() {
None => PrepareMarkup::None,
_ => PrepareMarkup::With(html! { i class=[self.classes().get()] {} }),
}
}
}
impl Icon {
pub fn with(icon_name: impl Into<String>) -> Self {
Icon::default().with_icon_name(icon_name)
}
// Icon BUILDER.
#[fn_builder]
pub fn set_icon_name(&mut self, name: impl Into<String>) -> &mut Self {
self.icon_name.set_value(name);
self
}
#[fn_builder]
pub fn set_font_size(&mut self, font_size: FontSize) -> &mut Self {
self.font_size = font_size;
self
}
// Icon GETTERS.
pub fn icon_name(&self) -> &OptionString {
&self.icon_name
}
pub fn font_size(&self) -> &FontSize {
&self.font_size
}
}

View file

@ -1,102 +0,0 @@
use crate::prelude::*;
const IMG_FLUID: &str = "img__fluid";
const IMG_FIXED: &str = "img__fixed";
#[derive(AutoDefault)]
pub enum ImageSize {
#[default]
Auto,
Size(u16, u16),
Width(u16),
Height(u16),
Both(u16),
}
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Image {
id : OptionId,
classes: OptionClasses,
source : OptionString,
size : ImageSize,
}
impl ComponentTrait for Image {
fn new() -> Self {
Image::default().with_classes(ClassesOp::Add, IMG_FLUID)
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup {
let (width, height) = match self.size() {
ImageSize::Auto => (None, None),
ImageSize::Size(width, height) => (Some(width), Some(height)),
ImageSize::Width(width) => (Some(width), None),
ImageSize::Height(height) => (None, Some(height)),
ImageSize::Both(value) => (Some(value), Some(value)),
};
PrepareMarkup::With(html! {
img
src=[self.source().get()]
id=[self.id()]
class=[self.classes().get()]
width=[width]
height=[height] {}
})
}
}
impl Image {
pub fn with(source: &str) -> Self {
Image::default()
.with_source(source)
.with_classes(ClassesOp::Add, IMG_FLUID)
}
pub fn fixed(source: &str) -> Self {
Image::default()
.with_source(source)
.with_classes(ClassesOp::Add, IMG_FIXED)
}
pub fn pagetop() -> Self {
Image::default()
.with_source("/base/pagetop-logo.svg")
.with_classes(ClassesOp::Add, IMG_FIXED)
.with_size(ImageSize::Size(64, 64))
}
// Image BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_source(&mut self, source: &str) -> &mut Self {
self.source.set_value(source);
self
}
#[fn_builder]
pub fn set_size(&mut self, size: ImageSize) -> &mut Self {
self.size = size;
self
}
// Image GETTERS.
pub fn source(&self) -> &OptionString {
&self.source
}
pub fn size(&self) -> &ImageSize {
&self.size
}
}

View file

@ -1,17 +0,0 @@
mod menu_main;
pub use menu_main::Menu;
mod item;
pub use item::{Item, ItemType};
mod submenu;
pub use submenu::Submenu;
mod megamenu;
pub use megamenu::Megamenu;
mod group;
pub use group::Group;
mod element;
pub use element::{Element, ElementType};

View file

@ -1,60 +0,0 @@
use crate::prelude::*;
use super::Submenu;
type Content = TypedComponent<Html>;
type SubmenuItems = TypedComponent<Submenu>;
#[derive(AutoDefault)]
pub enum ElementType {
#[default]
Void,
Html(Content),
Submenu(SubmenuItems),
}
// 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::Void => PrepareMarkup::None,
ElementType::Html(content) => PrepareMarkup::With(html! {
(content.render(cx))
}),
ElementType::Submenu(submenu) => PrepareMarkup::With(html! {
(submenu.render(cx))
}),
}
}
}
impl Element {
pub fn html(content: Html) -> Self {
Element {
element_type: ElementType::Html(Content::with(content)),
}
}
pub fn submenu(submenu: Submenu) -> Self {
Element {
element_type: ElementType::Submenu(SubmenuItems::with(submenu)),
}
}
// Element GETTERS.
pub fn element_type(&self) -> &ElementType {
&self.element_type
}
}

View file

@ -1,56 +0,0 @@
use crate::prelude::*;
use super::Element;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Group {
id : OptionId,
elements: MixedComponents,
}
impl ComponentTrait for Group {
fn new() -> Self {
Group::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
PrepareMarkup::With(html! {
div id=[self.id()] class="menu-group" {
(self.elements().render(cx))
}
})
}
}
impl Group {
// Group BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_elements(&mut self, op: TypedOp<Element>) -> &mut Self {
self.elements.set_typed(op);
self
}
#[rustfmt::skip]
pub fn add_element(mut self, element: Element) -> Self {
self.elements.set_value(AnyOp::Add(AnyComponent::with(element)));
self
}
// Group GETTERS.
pub fn elements(&self) -> &MixedComponents {
&self.elements
}
}

View file

@ -1,184 +0,0 @@
use crate::prelude::*;
use super::{Megamenu, Submenu};
type Label = L10n;
type Content = TypedComponent<Html>;
type SubmenuItems = TypedComponent<Submenu>;
type MegamenuGroups = TypedComponent<Megamenu>;
#[derive(AutoDefault)]
pub enum ItemType {
#[default]
Void,
Label(Label),
Link(Label, FnContextualPath),
LinkBlank(Label, FnContextualPath),
Html(Content),
Submenu(Label, SubmenuItems),
Megamenu(Label, MegamenuGroups),
}
// Item.
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Item {
item_type : ItemType,
description: OptionTranslated,
left_icon : OptionComponent<Icon>,
right_icon : OptionComponent<Icon>,
}
impl ComponentTrait for Item {
fn new() -> Self {
Item::default()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let description = self.description.using(cx.langid());
let left_icon = self.left_icon().render(cx);
let right_icon = self.right_icon().render(cx);
match self.item_type() {
ItemType::Void => PrepareMarkup::None,
ItemType::Label(label) => PrepareMarkup::With(html! {
li class="menu__label" {
span title=[description] {
(left_icon)
(label.escaped(cx.langid()))
(right_icon)
}
}
}),
ItemType::Link(label, path) => PrepareMarkup::With(html! {
li class="menu__link" {
a href=(path(cx)) title=[description] {
(left_icon)
(label.escaped(cx.langid()))
(right_icon)
}
}
}),
ItemType::LinkBlank(label, path) => PrepareMarkup::With(html! {
li class="menu__link" {
a href=(path(cx)) title=[description] target="_blank" {
(left_icon)
(label.escaped(cx.langid()))
(right_icon)
}
}
}),
ItemType::Html(content) => PrepareMarkup::With(html! {
li class="menu__html" {
(content.render(cx))
}
}),
ItemType::Submenu(label, submenu) => PrepareMarkup::With(html! {
li class="menu__children" {
a href="#" title=[description] {
(left_icon)
(label.escaped(cx.langid())) i class="menu__icon bi-chevron-down" {}
}
div class="menu__subs" {
(submenu.render(cx))
}
}
}),
ItemType::Megamenu(label, megamenu) => PrepareMarkup::With(html! {
li class="menu__children" {
a href="#" title=[description] {
(left_icon)
(label.escaped(cx.langid())) i class="menu__icon bi-chevron-down" {}
}
div class="menu__subs menu__mega" {
(megamenu.render(cx))
}
}
}),
}
}
}
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()
}
}
pub fn html(content: Html) -> Self {
Item {
item_type: ItemType::Html(Content::with(content)),
..Default::default()
}
}
pub fn submenu(label: L10n, submenu: Submenu) -> Self {
Item {
item_type: ItemType::Submenu(label, SubmenuItems::with(submenu)),
..Default::default()
}
}
pub fn megamenu(label: L10n, megamenu: Megamenu) -> Self {
Item {
item_type: ItemType::Megamenu(label, MegamenuGroups::with(megamenu)),
..Default::default()
}
}
// Item BUILDER.
#[fn_builder]
pub fn set_description(&mut self, text: L10n) -> &mut Self {
self.description.set_value(text);
self
}
#[fn_builder]
pub fn set_left_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.left_icon.set_value(icon);
self
}
#[fn_builder]
pub fn set_right_icon(&mut self, icon: Option<Icon>) -> &mut Self {
self.right_icon.set_value(icon);
self
}
// Item GETTERS.
pub fn item_type(&self) -> &ItemType {
&self.item_type
}
pub fn description(&self) -> &OptionTranslated {
&self.description
}
pub fn left_icon(&self) -> &OptionComponent<Icon> {
&self.left_icon
}
pub fn right_icon(&self) -> &OptionComponent<Icon> {
&self.right_icon
}
}

View file

@ -1,56 +0,0 @@
use crate::prelude::*;
use super::Group;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Megamenu {
id : OptionId,
groups: MixedComponents,
}
impl ComponentTrait for Megamenu {
fn new() -> Self {
Megamenu::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
PrepareMarkup::With(html! {
div id=[self.id()] class="menu__groups" {
(self.groups().render(cx))
}
})
}
}
impl Megamenu {
// Megamenu BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_groups(&mut self, op: TypedOp<Group>) -> &mut Self {
self.groups.set_typed(op);
self
}
#[rustfmt::skip]
pub fn add_group(mut self, group: Group) -> Self {
self.groups.set_value(AnyOp::Add(AnyComponent::with(group)));
self
}
// Megamenu GETTERS.
pub fn groups(&self) -> &MixedComponents {
&self.groups
}
}

View file

@ -1,84 +0,0 @@
use crate::prelude::*;
use super::Item;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Menu {
id : OptionId,
items: MixedComponents,
}
impl ComponentTrait for Menu {
fn new() -> Self {
Menu::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
cx.set_param::<bool>(PARAM_BASE_INCLUDE_MENU_ASSETS, &true);
cx.set_param::<bool>(PARAM_BASE_INCLUDE_ICONS, &true);
PrepareMarkup::With(html! {
div id=[self.id()] class="menu__container" {
div class="menu__content" {
div class="menu__main" {
div class="menu__overlay" {}
nav class="menu__nav" {
div class="menu__header" {
button type="button" class="menu__arrow" {
i class="bi-chevron-left" {}
}
div class="menu__title" {}
button type="button" class="menu__close" {
i class="bi-x" {}
}
}
ul class="menu__section" {
(self.items().render(cx))
}
}
}
button
type="button"
class="menu__trigger"
title=[L10n::l("menu_toggle").using(cx.langid())]
{
span {} span {} span {}
}
}
}
})
}
}
impl Menu {
// Menu BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_items(&mut self, op: TypedOp<Item>) -> &mut Self {
self.items.set_typed(op);
self
}
#[rustfmt::skip]
pub fn add_item(mut self, item: Item) -> Self {
self.items.set_value(AnyOp::Add(AnyComponent::with(item)));
self
}
// Menu GETTERS.
pub fn items(&self) -> &MixedComponents {
&self.items
}
}

View file

@ -1,72 +0,0 @@
use crate::prelude::*;
use super::Item;
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct Submenu {
id : OptionId,
title: OptionTranslated,
items: MixedComponents,
}
impl ComponentTrait for Submenu {
fn new() -> Self {
Submenu::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
PrepareMarkup::With(html! {
div id=[self.id()] class="menu__items" {
@if let Some(title) = self.title().using(cx.langid()) {
h4 class="menu__title" { (title) }
}
ul {
(self.items().render(cx))
}
}
})
}
}
impl Submenu {
// Submenu BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_title(&mut self, title: L10n) -> &mut Self {
self.title.set_value(title);
self
}
#[fn_builder]
pub fn set_items(&mut self, op: TypedOp<Item>) -> &mut Self {
self.items.set_typed(op);
self
}
#[rustfmt::skip]
pub fn add_item(mut self, item: Item) -> Self {
self.items.set_value(AnyOp::Add(AnyComponent::with(item)));
self
}
// Submenu GETTERS.
pub fn title(&self) -> &OptionTranslated {
&self.title
}
pub fn items(&self) -> &MixedComponents {
&self.items
}
}

View file

@ -1,81 +0,0 @@
use crate::prelude::*;
#[rustfmt::skip]
#[derive(AutoDefault, ComponentClasses)]
pub struct Paragraph {
id : OptionId,
classes : OptionClasses,
font_size: FontSize,
mixed : MixedComponents,
}
impl ComponentTrait for Paragraph {
fn new() -> Self {
Paragraph::default()
}
fn id(&self) -> Option<String> {
self.id.get()
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.set_classes(ClassesOp::Prepend, self.font_size().to_string());
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
PrepareMarkup::With(html! {
p
id=[self.id()]
class=[self.classes().get()]
{
(self.components().render(cx))
}
})
}
}
impl Paragraph {
pub fn with(component: impl ComponentTrait) -> Self {
Paragraph::default().add_component(component)
}
pub fn fluent(l10n: L10n) -> Self {
Paragraph::default().add_component(Fluent::with(l10n))
}
// Paragraph BUILDER.
#[fn_builder]
pub fn set_id(&mut self, id: impl Into<String>) -> &mut Self {
self.id.set_value(id);
self
}
#[fn_builder]
pub fn set_font_size(&mut self, font_size: FontSize) -> &mut Self {
self.font_size = font_size;
self
}
#[fn_builder]
pub fn set_components(&mut self, op: AnyOp) -> &mut Self {
self.mixed.set_value(op);
self
}
#[rustfmt::skip]
pub fn add_component(mut self, component: impl ComponentTrait) -> Self {
self.mixed.set_value(AnyOp::Add(AnyComponent::with(component)));
self
}
// Paragraph GETTERS.
pub fn font_size(&self) -> &FontSize {
&self.font_size
}
pub fn components(&self) -> &MixedComponents {
&self.mixed
}
}

View file

@ -1,108 +0,0 @@
use crate::prelude::*;
use std::convert::Into;
#[derive(Default, Eq, PartialEq)]
pub enum PoweredByLogo {
#[default]
None,
Color,
LineDark,
LineLight,
LineRGB(u8, u8, u8),
}
#[rustfmt::skip]
#[derive(AutoDefault)]
pub struct PoweredBy {
copyright: Option<String>,
logo : PoweredByLogo,
}
impl ComponentTrait for PoweredBy {
fn new() -> Self {
let year = Utc::now().format("%Y").to_string();
let c = concat_string!(year, " © ", global::SETTINGS.app.name);
PoweredBy {
copyright: Some(c),
..Default::default()
}
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
let poweredby_pagetop = L10n::l("poweredby_pagetop")
.with_arg(
"pagetop_link",
"<a href=\"https://crates.io/crates/pagetop\">PageTop</a>",
)
.escaped(cx.langid());
let pagetop_logo = match self.logo() {
PoweredByLogo::None => html! {},
PoweredByLogo::Color => self.logo_color(cx),
PoweredByLogo::LineDark => self.logo_line(10, 11, 9, cx),
PoweredByLogo::LineLight => self.logo_line(255, 255, 255, cx),
PoweredByLogo::LineRGB(r, g, b) => self.logo_line(*r, *g, *b, cx),
};
PrepareMarkup::With(html! {
div id=[self.id()] class="poweredby__container" {
@if let Some(c) = self.copyright() {
span class="poweredby__copyright" { (c) "." } " "
}
span class="poweredby__pagetop" { (poweredby_pagetop) " " (pagetop_logo) }
}
})
}
}
impl PoweredBy {
// PoweredBy BUILDER.
#[fn_builder]
pub fn set_copyright(&mut self, copyright: Option<impl Into<String>>) -> &mut Self {
self.copyright = copyright.map(Into::into);
self
}
#[fn_builder]
pub fn set_logo(&mut self, logo: PoweredByLogo) -> &mut Self {
self.logo = logo;
self
}
// PoweredBy GETTERS.
pub fn copyright(&self) -> &Option<String> {
&self.copyright
}
pub fn logo(&self) -> &PoweredByLogo {
&self.logo
}
// PoweredBy PRIVATE.
fn logo_color(&self, cx: &mut Context) -> Markup {
let logo_txt = &L10n::l("pagetop_logo").using(cx.langid());
html! {
span class="poweredby__logo" aria-label=[logo_txt] {
img src="/base/pagetop-logo.svg" alt=[logo_txt] {}
}
}
}
fn logo_line(&self, r: u8, g: u8, b: u8, cx: &mut Context) -> Markup {
let logo_txt = L10n::l("pagetop_logo").using(cx.langid());
let logo_rgb = format!("rgb({r},{g},{b})");
html! {
span class="poweredby__logo" aria-label=[logo_txt] {
svg viewBox="0 0 1614 1614" xmlns="http://www.w3.org/2000/svg" role="img" {
path fill=(logo_rgb) d="M 1573,357 L 1415,357 C 1400,357 1388,369 1388,383 L 1388,410 1335,410 1335,357 C 1335,167 1181,13 992,13 L 621,13 C 432,13 278,167 278,357 L 278,410 225,410 225,383 C 225,369 213,357 198,357 L 40,357 C 25,357 13,369 13,383 L 13,648 C 13,662 25,674 40,674 L 198,674 C 213,674 225,662 225,648 L 225,621 278,621 278,1256 C 278,1446 432,1600 621,1600 L 992,1600 C 1181,1600 1335,1446 1335,1256 L 1335,621 1388,621 1388,648 C 1388,662 1400,674 1415,674 L 1573,674 C 1588,674 1600,662 1600,648 L 1600,383 C 1600,369 1588,357 1573,357 L 1573,357 1573,357 Z M 66,410 L 172,410 172,621 66,621 66,410 66,410 Z M 1282,357 L 1282,488 C 1247,485 1213,477 1181,464 L 1196,437 C 1203,425 1199,409 1186,401 1174,394 1158,398 1150,411 L 1133,440 C 1105,423 1079,401 1056,376 L 1075,361 C 1087,352 1089,335 1079,324 1070,313 1054,311 1042,320 L 1023,335 C 1000,301 981,263 967,221 L 1011,196 C 1023,189 1028,172 1021,160 1013,147 997,143 984,150 L 953,168 C 945,136 941,102 940,66 L 992,66 C 1152,66 1282,197 1282,357 L 1282,357 1282,357 Z M 621,66 L 674,66 674,225 648,225 C 633,225 621,237 621,251 621,266 633,278 648,278 L 674,278 674,357 648,357 C 633,357 621,369 621,383 621,398 633,410 648,410 L 674,410 674,489 648,489 C 633,489 621,501 621,516 621,530 633,542 648,542 L 664,542 C 651,582 626,623 600,662 583,653 563,648 542,648 469,648 410,707 410,780 410,787 411,794 412,801 388,805 361,806 331,806 L 331,357 C 331,197 461,66 621,66 L 621,66 621,66 Z M 621,780 C 621,824 586,859 542,859 498,859 463,824 463,780 463,736 498,701 542,701 586,701 621,736 621,780 L 621,780 621,780 Z M 225,463 L 278,463 278,569 225,569 225,463 225,463 Z M 992,1547 L 621,1547 C 461,1547 331,1416 331,1256 L 331,859 C 367,859 400,858 431,851 454,888 495,912 542,912 615,912 674,853 674,780 674,747 662,718 642,695 675,645 706,594 720,542 L 780,542 C 795,542 807,530 807,516 807,501 795,489 780,489 L 727,489 727,410 780,410 C 795,410 807,398 807,383 807,369 795,357 780,357 L 727,357 727,278 780,278 C 795,278 807,266 807,251 807,237 795,225 780,225 L 727,225 727,66 887,66 C 889,111 895,155 905,196 L 869,217 C 856,224 852,240 859,253 864,261 873,266 882,266 887,266 891,265 895,263 L 921,248 C 937,291 958,331 983,367 L 938,403 C 926,412 925,429 934,440 939,447 947,450 954,450 960,450 966,448 971,444 L 1016,408 C 1043,438 1074,465 1108,485 L 1084,527 C 1076,539 1081,555 1093,563 1098,565 1102,566 1107,566 1116,566 1125,561 1129,553 L 1155,509 C 1194,527 1237,538 1282,541 L 1282,1256 C 1282,1416 1152,1547 992,1547 L 992,1547 992,1547 Z M 1335,463 L 1388,463 1388,569 1335,569 1335,463 1335,463 Z M 1441,410 L 1547,410 1547,621 1441,621 1441,410 1441,410 Z" {}
path fill=(logo_rgb) d="M 1150,1018 L 463,1018 C 448,1018 436,1030 436,1044 L 436,1177 C 436,1348 545,1468 701,1468 L 912,1468 C 1068,1468 1177,1348 1177,1177 L 1177,1044 C 1177,1030 1165,1018 1150,1018 L 1150,1018 1150,1018 Z M 912,1071 L 1018,1071 1018,1124 912,1124 912,1071 912,1071 Z M 489,1071 L 542,1071 542,1124 489,1124 489,1071 489,1071 Z M 701,1415 L 700,1415 C 701,1385 704,1352 718,1343 731,1335 759,1341 795,1359 802,1363 811,1363 818,1359 854,1341 882,1335 895,1343 909,1352 912,1385 913,1415 L 912,1415 701,1415 701,1415 701,1415 Z M 1124,1177 C 1124,1296 1061,1384 966,1408 964,1365 958,1320 922,1298 894,1281 856,1283 807,1306 757,1283 719,1281 691,1298 655,1320 649,1365 647,1408 552,1384 489,1296 489,1177 L 569,1177 C 583,1177 595,1165 595,1150 L 595,1071 859,1071 859,1150 C 859,1165 871,1177 886,1177 L 1044,1177 C 1059,1177 1071,1165 1071,1150 L 1071,1071 1124,1071 1124,1177 1124,1177 1124,1177 Z" {}
path fill=(logo_rgb) d="M 1071,648 C 998,648 939,707 939,780 939,853 998,912 1071,912 1144,912 1203,853 1203,780 1203,707 1144,648 1071,648 L 1071,648 1071,648 Z M 1071,859 C 1027,859 992,824 992,780 992,736 1027,701 1071,701 1115,701 1150,736 1150,780 1150,824 1115,859 1071,859 L 1071,859 1071,859 Z" {}
}
}
}
}
}

View file

@ -1,2 +1,133 @@
mod welcome;
pub use welcome::Welcome;
use crate::prelude::*;
pub struct Welcome;
impl PackageTrait for Welcome {
fn name(&self) -> L10n {
L10n::l("welcome_package_name")
}
fn description(&self) -> L10n {
L10n::l("welcome_package_description")
}
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
scfg.route("/", service::web::get().to(homepage));
}
}
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_title(L10n::l("welcome_page"))
.with_assets(AssetsOp::Theme("Basic"))
.with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##"
body {
background-color: #f3d060;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 20px;
}
.skip__to_content {
display: none;
}
.wrapper {
max-width: 1200px;
width: 100%;
margin: 0 auto;
padding: 0;
}
.container {
padding: 0 16px;
}
.title {
font-size: clamp(3rem, 10vw, 10rem);
letter-spacing: -0.05em;
line-height: 1.2;
margin: 0;
}
.subtitle {
font-size: clamp(1.8rem, 2vw, 3rem);
letter-spacing: -0.02em;
line-height: 1.2;
margin: 0;
}
.powered {
margin: .5em 0 1em;
}
.box-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: stretch;
gap: 1.5em;
}
.box {
flex: 1 1 280px;
border: 3px solid #25282a;
box-shadow: 5px 5px 0px #25282a;
box-sizing: border-box;
padding: 0 16px;
}
footer {
margin-top: 5em;
font-size: 14px;
font-weight: 500;
color: #a5282c;
}
"##)))
.with_component(Html::with(html! {
div class="wrapper" {
div class="container" {
h1 class="title" { (L10n::l("welcome_title").markup()) }
p class="subtitle" {
(L10n::l("welcome_intro").with_arg("app", format!(
"<span style=\"font-weight: bold;\">{}</span>",
&global::SETTINGS.app.name
)).markup())
}
p class="powered" {
(L10n::l("welcome_powered").with_arg("pagetop", format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://crates.io/crates/pagetop", "PageTop"
)).markup())
}
h2 { (L10n::l("welcome_page").markup()) }
div class="box-container" {
section class="box" style="background-color: #5eb0e5;" {
h3 {
(L10n::l("welcome_subtitle")
.with_arg("app", &global::SETTINGS.app.name)
.markup())
}
p { (L10n::l("welcome_text1").markup()) }
p { (L10n::l("welcome_text2").markup()) }
}
section class="box" style="background-color: #aee1cd;" {
h3 {
(L10n::l("welcome_pagetop_title").markup())
}
p { (L10n::l("welcome_pagetop_text1").markup()) }
p { (L10n::l("welcome_pagetop_text2").markup()) }
p { (L10n::l("welcome_pagetop_text3").markup()) }
}
section class="box" style="background-color: #ebebe3;" {
h3 {
(L10n::l("welcome_issues_title").markup())
}
p { (L10n::l("welcome_issues_text1").markup()) }
p {
(L10n::l("welcome_issues_text2")
.with_arg("app", &global::SETTINGS.app.name)
.markup())
}
}
}
footer { "[ " (L10n::l("welcome_have_fun").markup()) " ]" }
}
}
}))
.render()
}

View file

@ -1,133 +0,0 @@
use crate::prelude::*;
pub struct Welcome;
impl PackageTrait for Welcome {
fn name(&self) -> L10n {
L10n::l("welcome_package_name")
}
fn description(&self) -> L10n {
L10n::l("welcome_package_description")
}
fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {
scfg.route("/", service::web::get().to(homepage));
}
}
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_title(L10n::l("welcome_page"))
.with_assets(AssetsOp::Theme("Basic"))
.with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##"
body {
background-color: #f3d060;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 20px;
}
.skip__to_content {
display: none;
}
.wrapper {
max-width: 1200px;
width: 100%;
margin: 0 auto;
padding: 0;
}
.container {
padding: 0 16px;
}
.title {
font-size: clamp(3rem, 10vw, 10rem);
letter-spacing: -0.05em;
line-height: 1.2;
margin: 0;
}
.subtitle {
font-size: clamp(1.8rem, 2vw, 3rem);
letter-spacing: -0.02em;
line-height: 1.2;
margin: 0;
}
.powered {
margin: .5em 0 1em;
}
.box-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: stretch;
gap: 1.5em;
}
.box {
flex: 1 1 280px;
border: 3px solid #25282a;
box-shadow: 5px 5px 0px #25282a;
box-sizing: border-box;
padding: 0 16px;
}
footer {
margin-top: 5em;
font-size: 14px;
font-weight: 500;
color: #a5282c;
}
"##)))
.with_component(Html::with(html! {
div class="wrapper" {
div class="container" {
h1 class="title" { (L10n::l("welcome_title").markup()) }
p class="subtitle" {
(L10n::l("welcome_intro").with_arg("app", format!(
"<span style=\"font-weight: bold;\">{}</span>",
&global::SETTINGS.app.name
)).markup())
}
p class="powered" {
(L10n::l("welcome_powered").with_arg("pagetop", format!(
"<a href=\"{}\" target=\"_blank\">{}</a>",
"https://crates.io/crates/pagetop", "PageTop"
)).markup())
}
h2 { (L10n::l("welcome_page").markup()) }
div class="box-container" {
section class="box" style="background-color: #5eb0e5;" {
h3 {
(L10n::l("welcome_subtitle")
.with_arg("app", &global::SETTINGS.app.name)
.markup())
}
p { (L10n::l("welcome_text1").markup()) }
p { (L10n::l("welcome_text2").markup()) }
}
section class="box" style="background-color: #aee1cd;" {
h3 {
(L10n::l("welcome_pagetop_title").markup())
}
p { (L10n::l("welcome_pagetop_text1").markup()) }
p { (L10n::l("welcome_pagetop_text2").markup()) }
p { (L10n::l("welcome_pagetop_text3").markup()) }
}
section class="box" style="background-color: #ebebe3;" {
h3 {
(L10n::l("welcome_issues_title").markup())
}
p { (L10n::l("welcome_issues_text1").markup()) }
p {
(L10n::l("welcome_issues_text2")
.with_arg("app", &global::SETTINGS.app.name)
.markup())
}
}
}
footer { "[ " (L10n::l("welcome_have_fun").markup()) " ]" }
}
}
}))
.render()
}

View file

@ -1,8 +1,11 @@
mod basic;
pub use basic::Basic;
use crate::prelude::*;
mod chassis;
pub use chassis::Chassis;
pub struct Basic;
mod inception;
pub use inception::Inception;
impl PackageTrait for Basic {
fn theme(&self) -> Option<ThemeRef> {
Some(&Basic)
}
}
impl ThemeTrait for Basic {}

View file

@ -1,11 +0,0 @@
use crate::prelude::*;
pub struct Basic;
impl PackageTrait for Basic {
fn theme(&self) -> Option<ThemeRef> {
Some(&Basic)
}
}
impl ThemeTrait for Basic {}

View file

@ -1,29 +0,0 @@
use crate::prelude::*;
pub struct Chassis;
impl PackageTrait for Chassis {
fn name(&self) -> L10n {
L10n::n("Chassis")
}
fn theme(&self) -> Option<ThemeRef> {
Some(&Chassis)
}
}
impl ThemeTrait for Chassis {
fn after_prepare_body(&self, page: &mut Page) {
page.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/normalize.min.css")
.with_version("8.0.1")
.with_weight(-90),
))
.set_assets(AssetsOp::AddBaseAssets)
.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/chassis.css")
.with_version("0.0.1")
.with_weight(-90),
));
}
}

View file

@ -1,29 +0,0 @@
use crate::prelude::*;
pub struct Inception;
impl PackageTrait for Inception {
fn name(&self) -> L10n {
L10n::n("Inception")
}
fn theme(&self) -> Option<ThemeRef> {
Some(&Inception)
}
}
impl ThemeTrait for Inception {
fn after_prepare_body(&self, page: &mut Page) {
page.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/normalize.min.css")
.with_version("8.0.1")
.with_weight(-90),
))
.set_assets(AssetsOp::AddBaseAssets)
.set_assets(AssetsOp::AddStyleSheet(
StyleSheet::from("/base/css/inception.css")
.with_version("0.0.1")
.with_weight(-90),
));
}
}

View file

@ -8,7 +8,7 @@ pub use definition::{ComponentBase, ComponentTrait};
mod classes;
pub use classes::{ComponentClasses, ComponentClassesOp};
mod mixed;
pub use mixed::MixedComponents;
pub use mixed::{AnyComponent, AnyOp};
pub use mixed::{TypedComponent, TypedOp};
mod children;
pub use children::Children;
pub use children::{AnyComponent, AnyOp};
pub use children::{TypedComponent, TypedOp};

View file

@ -80,26 +80,26 @@ pub enum TypedOp<C: ComponentTrait> {
}
#[derive(Clone, Default)]
pub struct MixedComponents(Vec<AnyComponent>);
pub struct Children(Vec<AnyComponent>);
impl MixedComponents {
impl Children {
pub fn new() -> Self {
MixedComponents::default()
Children::default()
}
pub fn with(any: AnyComponent) -> Self {
MixedComponents::default().with_value(AnyOp::Add(any))
Children::default().with_value(AnyOp::Add(any))
}
pub(crate) fn merge(mixes: &[Option<&MixedComponents>]) -> Self {
let mut opt = MixedComponents::default();
pub(crate) fn merge(mixes: &[Option<&Children>]) -> Self {
let mut opt = Children::default();
for m in mixes.iter().flatten() {
opt.0.append(&mut m.0.clone());
}
opt
}
// MixedComponents BUILDER.
// Children BUILDER.
#[fn_builder]
pub fn set_value(&mut self, op: AnyOp) -> &mut Self {
@ -177,7 +177,7 @@ impl MixedComponents {
self.0.clear();
}
// MixedComponents GETTERS.
// Children GETTERS.
pub fn len(&self) -> usize {
self.0.len()
@ -201,7 +201,7 @@ impl MixedComponents {
self.0.iter().filter(move |&c| c.type_id() == type_id)
}
// MixedComponents RENDER.
// Children RENDER.
pub fn render(&self, cx: &mut Context) -> Markup {
html! {

View file

@ -1,8 +1,7 @@
use crate::base::component::add_base_assets;
use crate::concat_string;
use crate::core::component::AnyOp;
use crate::core::theme::all::{theme_by_short_name, DEFAULT_THEME};
use crate::core::theme::{ComponentsInRegions, ThemeRef};
use crate::core::theme::{ChildrenInRegions, ThemeRef};
use crate::html::{html, Markup};
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
use crate::locale::{LanguageIdentifier, DEFAULT_LANGID};
@ -28,8 +27,6 @@ pub enum AssetsOp {
// JavaScripts.
AddJavaScript(JavaScript),
RemoveJavaScript(&'static str),
// Add assets to properly use base components.
AddBaseAssets,
}
#[derive(Debug)]
@ -58,7 +55,7 @@ pub struct Context {
favicon : Option<Favicon>,
stylesheet: Assets<StyleSheet>,
javascript: Assets<JavaScript>,
regions : ComponentsInRegions,
regions : ChildrenInRegions,
params : HashMap<&'static str, String>,
id_counter: usize,
}
@ -74,7 +71,7 @@ impl Context {
favicon : None,
stylesheet: Assets::<StyleSheet>::new(),
javascript: Assets::<JavaScript>::new(),
regions : ComponentsInRegions::default(),
regions : ChildrenInRegions::default(),
params : HashMap::<&str, String>::new(),
id_counter: 0,
}
@ -114,16 +111,12 @@ impl Context {
AssetsOp::RemoveJavaScript(path) => {
self.javascript.remove(path);
}
// Add assets to properly use base components.
AssetsOp::AddBaseAssets => {
add_base_assets(self);
}
}
self
}
pub fn set_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
self.regions.set_components(region, op);
pub fn set_in_region(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
self.regions.set_in_region(region, op);
self
}
@ -150,7 +143,7 @@ impl Context {
self.layout
}
pub fn regions(&self) -> &ComponentsInRegions {
pub fn regions(&self) -> &ChildrenInRegions {
&self.regions
}
@ -163,7 +156,7 @@ impl Context {
// Context PREPARE.
pub(crate) fn prepare_assets(&mut self) -> Markup {
pub fn prepare_assets(&mut self) -> Markup {
html! {
@if let Some(favicon) = &self.favicon {
(favicon.prepare())
@ -173,7 +166,7 @@ impl Context {
}
}
pub(crate) fn prepare_region(&mut self, region: impl Into<String>) -> Markup {
pub fn prepare_region(&mut self, region: impl Into<String>) -> Markup {
self.regions
.all_components(self.theme, region.into().as_str())
.render(self)

View file

@ -2,7 +2,7 @@ mod definition;
pub use definition::{ThemeRef, ThemeTrait};
mod regions;
pub(crate) use regions::ComponentsInRegions;
pub(crate) use regions::ChildrenInRegions;
pub use regions::InRegion;
pub(crate) mod all;

View file

@ -1,70 +1,24 @@
use crate::base::component::*;
use crate::core::component::{ComponentBase, ComponentTrait};
use crate::core::package::PackageTrait;
use crate::global;
use crate::html::{html, PrepareMarkup};
use crate::locale::L10n;
use crate::response::page::Page;
use crate::{concat_string, global};
pub type ThemeRef = &'static dyn ThemeTrait;
/// Los temas deben implementar este "trait".
pub trait ThemeTrait: PackageTrait + Send + Sync {
#[rustfmt::skip]
fn regions(&self) -> Vec<(&'static str, L10n)> {
vec![
("header", L10n::l("header")),
("pagetop", L10n::l("pagetop")),
("sidebar_left", L10n::l("sidebar_left")),
("content", L10n::l("content")),
("sidebar_right", L10n::l("sidebar_right")),
("footer", L10n::l("footer")),
]
vec![]
}
#[allow(unused_variables)]
fn before_prepare_body(&self, page: &mut Page) {}
fn prepare_body(&self, page: &mut Page) -> PrepareMarkup {
let skip_to_id = page.body_skip_to().get().unwrap_or("content".to_owned());
PrepareMarkup::With(html! {
body id=[page.body_id().get()] class=[page.body_classes().get()] {
@if let Some(skip) = L10n::l("skip_to_content").using(page.context().langid()) {
div class="skip__to_content" {
a href=(concat_string!("#", skip_to_id)) { (skip) }
}
}
(flex::Container::new()
.with_id("body__wrapper")
.with_direction(flex::Direction::Column(BreakPoint::None))
.with_align(flex::Align::Center)
.add_item(flex::Item::region().with_id("header"))
.add_item(flex::Item::region().with_id("pagetop"))
.add_item(
flex::Item::with(
flex::Container::new()
.with_direction(flex::Direction::Row(BreakPoint::None))
.add_item(
flex::Item::region()
.with_id("sidebar_left")
.with_grow(flex::Grow::Is1),
)
.add_item(
flex::Item::region()
.with_id("content")
.with_grow(flex::Grow::Is3),
)
.add_item(
flex::Item::region()
.with_id("sidebar_right")
.with_grow(flex::Grow::Is1),
),
)
.with_id("flex__wrapper"),
)
.add_item(flex::Item::region().with_id("footer"))
.render(page.context()))
(page.context().prepare_region("content"))
}
})
}

View file

@ -1,40 +1,40 @@
use crate::core::component::{AnyComponent, AnyOp, MixedComponents};
use crate::core::component::{AnyComponent, AnyOp, Children};
use crate::core::theme::ThemeRef;
use crate::{fn_builder, AutoDefault, TypeId};
use std::collections::HashMap;
use std::sync::{LazyLock, RwLock};
static THEME_REGIONS: LazyLock<RwLock<HashMap<TypeId, ComponentsInRegions>>> =
static THEME_REGIONS: LazyLock<RwLock<HashMap<TypeId, ChildrenInRegions>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
static COMMON_REGIONS: LazyLock<RwLock<ComponentsInRegions>> =
LazyLock::new(|| RwLock::new(ComponentsInRegions::default()));
static COMMON_REGIONS: LazyLock<RwLock<ChildrenInRegions>> =
LazyLock::new(|| RwLock::new(ChildrenInRegions::default()));
#[derive(AutoDefault)]
pub struct ComponentsInRegions(HashMap<&'static str, MixedComponents>);
pub struct ChildrenInRegions(HashMap<&'static str, Children>);
impl ComponentsInRegions {
impl ChildrenInRegions {
pub fn new(region: &'static str, any: AnyComponent) -> Self {
ComponentsInRegions::default().with_components(region, AnyOp::Add(any))
ChildrenInRegions::default().with_in_region(region, AnyOp::Add(any))
}
#[fn_builder]
pub fn set_components(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
pub fn set_in_region(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
if let Some(region) = self.0.get_mut(region) {
region.set_value(op);
} else {
self.0.insert(region, MixedComponents::new().with_value(op));
self.0.insert(region, Children::new().with_value(op));
}
self
}
pub fn all_components(&self, theme: ThemeRef, region: &str) -> MixedComponents {
pub fn all_components(&self, theme: ThemeRef, region: &str) -> Children {
let common = COMMON_REGIONS.read().unwrap();
if let Some(r) = THEME_REGIONS.read().unwrap().get(&theme.type_id()) {
MixedComponents::merge(&[common.0.get(region), self.0.get(region), r.0.get(region)])
Children::merge(&[common.0.get(region), self.0.get(region), r.0.get(region)])
} else {
MixedComponents::merge(&[common.0.get(region), self.0.get(region)])
Children::merge(&[common.0.get(region), self.0.get(region)])
}
}
}
@ -52,20 +52,20 @@ impl InRegion {
COMMON_REGIONS
.write()
.unwrap()
.set_components("content", AnyOp::Add(any));
.set_in_region("content", AnyOp::Add(any));
}
InRegion::Named(name) => {
COMMON_REGIONS
.write()
.unwrap()
.set_components(name, AnyOp::Add(any));
.set_in_region(name, AnyOp::Add(any));
}
InRegion::OfTheme(region, theme) => {
let mut regions = THEME_REGIONS.write().unwrap();
if let Some(r) = regions.get_mut(&theme.type_id()) {
r.set_components(region, AnyOp::Add(any));
r.set_in_region(region, AnyOp::Add(any));
} else {
regions.insert(theme.type_id(), ComponentsInRegions::new(region, any));
regions.insert(theme.type_id(), ChildrenInRegions::new(region, any));
}
}
}

View file

@ -98,14 +98,14 @@ impl Page {
}
#[fn_builder]
pub fn set_regions(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
self.context.set_regions(region, op);
pub fn set_in_region(&mut self, region: &'static str, op: AnyOp) -> &mut Self {
self.context.set_in_region(region, op);
self
}
pub fn with_component(mut self, component: impl ComponentTrait) -> Self {
self.context
.set_regions("content", AnyOp::Add(AnyComponent::with(component)));
.set_in_region("content", AnyOp::Add(AnyComponent::with(component)));
self
}
@ -115,7 +115,7 @@ impl Page {
component: impl ComponentTrait,
) -> Self {
self.context
.set_regions(region, AnyOp::Add(AnyComponent::with(component)));
.set_in_region(region, AnyOp::Add(AnyComponent::with(component)));
self
}