♻️ (bootsier): Reorganiza en capas token/class/bs
Sustituye `attrs/` y `classes/` por tres capas diferenciadas: - `token/`: primitivas CSS (BreakPoint, Color, ScaleSize, etc.). - `class/`: tipos compuestos (Border, Background, Margin, etc.). - `bs/*/props.rs`: props tipadas para cada componente de Bootstrap. El patrón de construcción es `suffix()` -> `push_to()` -> `to_class()`. `push_to()` es `pub` en `class/` y `bs/*/props.rs` para que los temas derivados puedan acceder a los mismos mecanismos de construcción.
This commit is contained in:
parent
64db114eb0
commit
f37db56ec5
47 changed files with 1307 additions and 1201 deletions
|
|
@ -1,17 +0,0 @@
|
||||||
//! Colección de elementos auxiliares de Bootstrap para Bootsier.
|
|
||||||
|
|
||||||
mod breakpoint;
|
|
||||||
pub use breakpoint::BreakPoint;
|
|
||||||
|
|
||||||
mod color;
|
|
||||||
pub use color::{Color, Opacity};
|
|
||||||
pub use color::{ColorBg, ColorText};
|
|
||||||
|
|
||||||
mod layout;
|
|
||||||
pub use layout::{ScaleSize, Side};
|
|
||||||
|
|
||||||
mod border;
|
|
||||||
pub use border::BorderColor;
|
|
||||||
|
|
||||||
mod rounded;
|
|
||||||
pub use rounded::RoundedRadius;
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
use crate::theme::attrs::Color;
|
|
||||||
|
|
||||||
/// Esquema de color para los bordes ([`classes::Border`](crate::theme::classes::Border)).
|
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum BorderColor {
|
|
||||||
/// No define ninguna clase.
|
|
||||||
#[default]
|
|
||||||
Default,
|
|
||||||
/// Genera la clase `border-{color}`.
|
|
||||||
Theme(Color),
|
|
||||||
/// Genera la clase `border-{color}-subtle` (un tono suavizado del color).
|
|
||||||
Subtle(Color),
|
|
||||||
/// Color negro.
|
|
||||||
Black,
|
|
||||||
/// Color blanco.
|
|
||||||
White,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BorderColor {
|
|
||||||
const BORDER: &str = "border";
|
|
||||||
const BORDER_PREFIX: &str = "border-";
|
|
||||||
|
|
||||||
/// Devuelve el sufijo de la clase `border-*`, o `None` si no define ninguna clase.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[inline]
|
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
Self::Default => None,
|
|
||||||
Self::Theme(_) => Some(""),
|
|
||||||
Self::Subtle(_) => Some("-subtle"),
|
|
||||||
Self::Black => Some("-black"),
|
|
||||||
Self::White => Some("-white"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade la clase `border-*` a la cadena de clases.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
if !classes.is_empty() {
|
|
||||||
classes.push(' ');
|
|
||||||
}
|
|
||||||
match self {
|
|
||||||
Self::Theme(c) | Self::Subtle(c) => {
|
|
||||||
classes.push_str(Self::BORDER_PREFIX);
|
|
||||||
classes.push_str(c.as_str());
|
|
||||||
}
|
|
||||||
_ => classes.push_str(Self::BORDER),
|
|
||||||
}
|
|
||||||
classes.push_str(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clase `border-*` correspondiente al color de borde.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(BorderColor::Theme(Color::Primary).to_class(), "border-primary");
|
|
||||||
/// assert_eq!(BorderColor::Subtle(Color::Warning).to_class(), "border-warning-subtle");
|
|
||||||
/// assert_eq!(BorderColor::Black.to_class(), "border-black");
|
|
||||||
/// assert_eq!(BorderColor::Default.to_class(), "");
|
|
||||||
/// ```
|
|
||||||
pub fn to_class(self) -> String {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
let base_len = match self {
|
|
||||||
Self::Theme(c) | Self::Subtle(c) => Self::BORDER_PREFIX.len() + c.as_str().len(),
|
|
||||||
_ => Self::BORDER.len(),
|
|
||||||
};
|
|
||||||
let mut class = String::with_capacity(base_len + suffix.len());
|
|
||||||
match self {
|
|
||||||
Self::Theme(c) | Self::Subtle(c) => {
|
|
||||||
class.push_str(Self::BORDER_PREFIX);
|
|
||||||
class.push_str(c.as_str());
|
|
||||||
}
|
|
||||||
_ => class.push_str(Self::BORDER),
|
|
||||||
}
|
|
||||||
class.push_str(suffix);
|
|
||||||
return class;
|
|
||||||
}
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
/// Define los puntos de ruptura (*breakpoints*) para aplicar diseño *responsive*.
|
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum BreakPoint {
|
|
||||||
/// **Menos de 576px**. Dispositivos muy pequeños: teléfonos en modo vertical.
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
/// **576px o más** - Dispositivos pequeños: teléfonos en modo horizontal.
|
|
||||||
SM,
|
|
||||||
/// **768px o más** - Dispositivos medianos: tabletas.
|
|
||||||
MD,
|
|
||||||
/// **992px o más** - Dispositivos grandes: puestos de escritorio.
|
|
||||||
LG,
|
|
||||||
/// **1200px o más** - Dispositivos muy grandes: puestos de escritorio grandes.
|
|
||||||
XL,
|
|
||||||
/// **1400px o más** - Dispositivos extragrandes: puestos de escritorio más grandes.
|
|
||||||
XXL,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BreakPoint {
|
|
||||||
/// Devuelve la identificación del punto de ruptura.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[inline]
|
|
||||||
pub(crate) const fn as_str(self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::None => "",
|
|
||||||
Self::SM => "sm",
|
|
||||||
Self::MD => "md",
|
|
||||||
Self::LG => "lg",
|
|
||||||
Self::XL => "xl",
|
|
||||||
Self::XXL => "xxl",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade el punto de ruptura con un prefijo y un sufijo (opcional) separados por un guion `-` a
|
|
||||||
/// la cadena de clases.
|
|
||||||
///
|
|
||||||
/// - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío).
|
|
||||||
/// - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(self, classes: &mut String, prefix: &str, suffix: &str) {
|
|
||||||
if prefix.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !classes.is_empty() {
|
|
||||||
classes.push(' ');
|
|
||||||
}
|
|
||||||
match self {
|
|
||||||
Self::None => classes.push_str(prefix),
|
|
||||||
_ => {
|
|
||||||
classes.push_str(prefix);
|
|
||||||
classes.push('-');
|
|
||||||
classes.push_str(self.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !suffix.is_empty() {
|
|
||||||
classes.push('-');
|
|
||||||
classes.push_str(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clase para el punto de ruptura, con un prefijo y un sufijo opcional, separados
|
|
||||||
/// por un guion `-`.
|
|
||||||
///
|
|
||||||
/// - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío).
|
|
||||||
/// - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`.
|
|
||||||
/// - Si `prefix` está vacío devuelve `""`.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// let bp = BreakPoint::MD;
|
|
||||||
/// assert_eq!(bp.class_with("col", ""), "col-md");
|
|
||||||
/// assert_eq!(bp.class_with("col", "6"), "col-md-6");
|
|
||||||
///
|
|
||||||
/// let bp = BreakPoint::None;
|
|
||||||
/// assert_eq!(bp.class_with("offcanvas", ""), "offcanvas");
|
|
||||||
/// assert_eq!(bp.class_with("col", "12"), "col-12");
|
|
||||||
///
|
|
||||||
/// let bp = BreakPoint::LG;
|
|
||||||
/// assert_eq!(bp.class_with("", "3"), "");
|
|
||||||
/// ```
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn class_with(self, prefix: &str, suffix: &str) -> String {
|
|
||||||
if prefix.is_empty() {
|
|
||||||
return String::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
let bp = self.as_str();
|
|
||||||
let has_bp = !bp.is_empty();
|
|
||||||
let has_suffix = !suffix.is_empty();
|
|
||||||
|
|
||||||
let mut len = prefix.len();
|
|
||||||
if has_bp {
|
|
||||||
len += 1 + bp.len();
|
|
||||||
}
|
|
||||||
if has_suffix {
|
|
||||||
len += 1 + suffix.len();
|
|
||||||
}
|
|
||||||
let mut class = String::with_capacity(len);
|
|
||||||
class.push_str(prefix);
|
|
||||||
if has_bp {
|
|
||||||
class.push('-');
|
|
||||||
class.push_str(bp);
|
|
||||||
}
|
|
||||||
if has_suffix {
|
|
||||||
class.push('-');
|
|
||||||
class.push_str(suffix);
|
|
||||||
}
|
|
||||||
class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
/// Radio para el redondeo de esquinas ([`classes::Rounded`](crate::theme::classes::Rounded)).
|
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum RoundedRadius {
|
|
||||||
/// No define ninguna clase.
|
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
/// Genera `rounded` (radio por defecto del tema).
|
|
||||||
Default,
|
|
||||||
/// Genera `rounded-0` (sin redondeo).
|
|
||||||
Zero,
|
|
||||||
/// Genera `rounded-1`.
|
|
||||||
Scale1,
|
|
||||||
/// Genera `rounded-2`.
|
|
||||||
Scale2,
|
|
||||||
/// Genera `rounded-3`.
|
|
||||||
Scale3,
|
|
||||||
/// Genera `rounded-4`.
|
|
||||||
Scale4,
|
|
||||||
/// Genera `rounded-5`.
|
|
||||||
Scale5,
|
|
||||||
/// Genera `rounded-circle`.
|
|
||||||
Circle,
|
|
||||||
/// Genera `rounded-pill`.
|
|
||||||
Pill,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RoundedRadius {
|
|
||||||
const ROUNDED: &str = "rounded";
|
|
||||||
|
|
||||||
/// Devuelve el sufijo para `*rounded-*`, o `None` si no define ninguna clase, o `""` para el
|
|
||||||
/// redondeo por defecto.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[inline]
|
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
|
||||||
match self {
|
|
||||||
Self::None => None,
|
|
||||||
Self::Default => Some(""),
|
|
||||||
Self::Zero => Some("-0"),
|
|
||||||
Self::Scale1 => Some("-1"),
|
|
||||||
Self::Scale2 => Some("-2"),
|
|
||||||
Self::Scale3 => Some("-3"),
|
|
||||||
Self::Scale4 => Some("-4"),
|
|
||||||
Self::Scale5 => Some("-5"),
|
|
||||||
Self::Circle => Some("-circle"),
|
|
||||||
Self::Pill => Some("-pill"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade el redondeo de esquinas a la cadena de clases usando el prefijo dado (`rounded-top`,
|
|
||||||
/// `rounded-bottom-start`, o vacío para `rounded-*`).
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(self, classes: &mut String, prefix: &str) {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
if !classes.is_empty() {
|
|
||||||
classes.push(' ');
|
|
||||||
}
|
|
||||||
if prefix.is_empty() {
|
|
||||||
classes.push_str(Self::ROUNDED);
|
|
||||||
} else {
|
|
||||||
classes.push_str(prefix);
|
|
||||||
}
|
|
||||||
classes.push_str(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clase para el redondeo de esquinas con el prefijo dado (`rounded-top`,
|
|
||||||
/// `rounded-bottom-start`, o vacío para `rounded-*`).
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(RoundedRadius::Scale2.class_with(""), "rounded-2");
|
|
||||||
/// assert_eq!(RoundedRadius::Zero.class_with("rounded-top"), "rounded-top-0");
|
|
||||||
/// assert_eq!(RoundedRadius::Scale3.class_with("rounded-top-end"), "rounded-top-end-3");
|
|
||||||
/// assert_eq!(RoundedRadius::Circle.class_with(""), "rounded-circle");
|
|
||||||
/// assert_eq!(RoundedRadius::None.class_with("rounded-bottom-start"), "");
|
|
||||||
/// ```
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn class_with(self, prefix: &str) -> String {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
let base_len = if prefix.is_empty() {
|
|
||||||
Self::ROUNDED.len()
|
|
||||||
} else {
|
|
||||||
prefix.len()
|
|
||||||
};
|
|
||||||
let mut class = String::with_capacity(base_len + suffix.len());
|
|
||||||
if prefix.is_empty() {
|
|
||||||
class.push_str(Self::ROUNDED);
|
|
||||||
} else {
|
|
||||||
class.push_str(prefix);
|
|
||||||
}
|
|
||||||
class.push_str(suffix);
|
|
||||||
return class;
|
|
||||||
}
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clase `rounded-*` para el redondeo de esquinas.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(RoundedRadius::Default.to_class(), "rounded");
|
|
||||||
/// assert_eq!(RoundedRadius::Zero.to_class(), "rounded-0");
|
|
||||||
/// assert_eq!(RoundedRadius::Scale3.to_class(), "rounded-3");
|
|
||||||
/// assert_eq!(RoundedRadius::Circle.to_class(), "rounded-circle");
|
|
||||||
/// assert_eq!(RoundedRadius::None.to_class(), "");
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
pub fn to_class(self) -> String {
|
|
||||||
self.class_with("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
53
extensions/pagetop-bootsier/src/theme/bs.rs
Normal file
53
extensions/pagetop-bootsier/src/theme/bs.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
//! Tipos y componentes disponibles.
|
||||||
|
//!
|
||||||
|
//! A continuación, el apartado **Modules** incluye las definiciones necesarias para los componentes
|
||||||
|
//! que se muestran en el apartado **Structs**, mientras que en **Enums** se listan los elementos
|
||||||
|
//! auxiliares del tema utilizados en clases y componentes.
|
||||||
|
|
||||||
|
mod attrs;
|
||||||
|
pub use attrs::*;
|
||||||
|
|
||||||
|
mod classes;
|
||||||
|
pub use classes::*;
|
||||||
|
|
||||||
|
// Button.
|
||||||
|
mod button;
|
||||||
|
pub use button::{Button, ButtonAction};
|
||||||
|
|
||||||
|
// Container.
|
||||||
|
pub mod container;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use container::Container;
|
||||||
|
|
||||||
|
// Dropdown.
|
||||||
|
pub mod dropdown;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use dropdown::Dropdown;
|
||||||
|
|
||||||
|
// Form.
|
||||||
|
pub mod form;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use form::Form;
|
||||||
|
|
||||||
|
// Image.
|
||||||
|
pub mod image;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use image::Image;
|
||||||
|
|
||||||
|
// Nav.
|
||||||
|
pub mod nav;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use nav::Nav;
|
||||||
|
|
||||||
|
// Navbar.
|
||||||
|
pub mod navbar;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use navbar::Navbar;
|
||||||
|
|
||||||
|
// Offcanvas.
|
||||||
|
pub mod offcanvas;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use offcanvas::Offcanvas;
|
||||||
|
|
||||||
|
// Sidebar (componentes de navegación de AdminLTE).
|
||||||
|
pub mod sidebar;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//! Definiciones para crear contenedores de componentes ([`Container`]).
|
//! Definiciones para crear contenedores de componentes ([`Container`]).
|
||||||
//!
|
//!
|
||||||
//! Cada contenedor envuelve contenido usando la etiqueta semántica indicada por
|
//! Cada contenedor envuelve contenido usando la etiqueta semántica indicada por
|
||||||
//! [`container::Kind`](crate::theme::container::Kind).
|
//! [`container::Kind`](crate::theme::bs::container::Kind).
|
||||||
//!
|
//!
|
||||||
//! Con [`container::Width`](crate::theme::container::Width) se puede definir el ancho y el
|
//! Con [`container::Width`](crate::theme::bs::container::Width) se puede definir el ancho y el
|
||||||
//! comportamiento *responsive* del contenedor. También permite aplicar utilidades de estilo para el
|
//! comportamiento *responsive* del contenedor. También permite aplicar utilidades de estilo para el
|
||||||
//! fondo, texto, borde o esquinas redondeadas.
|
//! fondo, texto, borde o esquinas redondeadas.
|
||||||
|
|
||||||
|
|
@ -2,10 +2,11 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Componente para crear un **contenedor de componentes** ([`container`]).
|
/// Componente para crear un **contenedor de componentes**
|
||||||
|
/// ([`container`](crate::theme::bs::container)).
|
||||||
///
|
///
|
||||||
/// Envuelve un conjunto de componentes en un contenedor establecido que se crea aplicando uno de
|
/// Envuelve un conjunto de componentes en un contenedor establecido que se crea aplicando uno de
|
||||||
/// los tipos definidos en [`container::Kind`].
|
/// los tipos definidos en [`container::Kind`](crate::theme::bs::container::Kind).
|
||||||
///
|
///
|
||||||
/// Si no contiene elementos, el componente **no se renderiza**.
|
/// Si no contiene elementos, el componente **no se renderiza**.
|
||||||
///
|
///
|
||||||
|
|
@ -14,18 +15,18 @@ use crate::theme::*;
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let main = Container::main()
|
/// let main = bs::Container::main()
|
||||||
/// .with_id("main-page")
|
/// .with_id("main-page")
|
||||||
/// .with_width(container::Width::From(BreakPoint::LG));
|
/// .with_width(bs::container::Width::From(token::BreakPoint::LG));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
||||||
props: Props,
|
props: Props,
|
||||||
/// Devuelve el tipo semántico del contenedor.
|
/// Devuelve el tipo semántico del contenedor.
|
||||||
container_kind: container::Kind,
|
container_kind: bs::container::Kind,
|
||||||
/// Devuelve el comportamiento para el ancho del contenedor.
|
/// Devuelve el comportamiento para el ancho del contenedor.
|
||||||
container_width: container::Width,
|
container_width: bs::container::Width,
|
||||||
/// Devuelve la lista de componentes (`children`) del contenedor.
|
/// Devuelve la lista de componentes (`children`) del contenedor.
|
||||||
children: Children,
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
@ -49,38 +50,38 @@ impl Component for Container {
|
||||||
return Ok(html! {});
|
return Ok(html! {});
|
||||||
}
|
}
|
||||||
let style = match self.container_width() {
|
let style = match self.container_width() {
|
||||||
container::Width::FluidMax(w) if w.is_measurable() => {
|
bs::container::Width::FluidMax(w) if w.is_measurable() => {
|
||||||
Some(util::join!("max-width: ", w.to_string(), ";"))
|
Some(util::join!("max-width: ", w.to_string(), ";"))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Ok(match self.container_kind() {
|
Ok(match self.container_kind() {
|
||||||
container::Kind::Default => html! {
|
bs::container::Kind::Default => html! {
|
||||||
div (self.props()) style=[style] {
|
div (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
container::Kind::Main => html! {
|
bs::container::Kind::Main => html! {
|
||||||
main (self.props()) style=[style] {
|
main (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
container::Kind::Header => html! {
|
bs::container::Kind::Header => html! {
|
||||||
header (self.props()) style=[style] {
|
header (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
container::Kind::Footer => html! {
|
bs::container::Kind::Footer => html! {
|
||||||
footer (self.props()) style=[style] {
|
footer (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
container::Kind::Section => html! {
|
bs::container::Kind::Section => html! {
|
||||||
section (self.props()) style=[style] {
|
section (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
container::Kind::Article => html! {
|
bs::container::Kind::Article => html! {
|
||||||
article (self.props()) style=[style] {
|
article (self.props()) style=[style] {
|
||||||
(output)
|
(output)
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +94,7 @@ impl Container {
|
||||||
/// Crea un contenedor de tipo `Main` (`<main>`).
|
/// Crea un contenedor de tipo `Main` (`<main>`).
|
||||||
pub fn main() -> Self {
|
pub fn main() -> Self {
|
||||||
Self {
|
Self {
|
||||||
container_kind: container::Kind::Main,
|
container_kind: bs::container::Kind::Main,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +102,7 @@ impl Container {
|
||||||
/// Crea un contenedor de tipo `Header` (`<header>`).
|
/// Crea un contenedor de tipo `Header` (`<header>`).
|
||||||
pub fn header() -> Self {
|
pub fn header() -> Self {
|
||||||
Self {
|
Self {
|
||||||
container_kind: container::Kind::Header,
|
container_kind: bs::container::Kind::Header,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +110,7 @@ impl Container {
|
||||||
/// Crea un contenedor de tipo `Footer` (`<footer>`).
|
/// Crea un contenedor de tipo `Footer` (`<footer>`).
|
||||||
pub fn footer() -> Self {
|
pub fn footer() -> Self {
|
||||||
Self {
|
Self {
|
||||||
container_kind: container::Kind::Footer,
|
container_kind: bs::container::Kind::Footer,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +118,7 @@ impl Container {
|
||||||
/// Crea un contenedor de tipo `Section` (`<section>`).
|
/// Crea un contenedor de tipo `Section` (`<section>`).
|
||||||
pub fn section() -> Self {
|
pub fn section() -> Self {
|
||||||
Self {
|
Self {
|
||||||
container_kind: container::Kind::Section,
|
container_kind: bs::container::Kind::Section,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +126,7 @@ impl Container {
|
||||||
/// Crea un contenedor de tipo `Article` (`<article>`).
|
/// Crea un contenedor de tipo `Article` (`<article>`).
|
||||||
pub fn article() -> Self {
|
pub fn article() -> Self {
|
||||||
Self {
|
Self {
|
||||||
container_kind: container::Kind::Article,
|
container_kind: bs::container::Kind::Article,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,10 +144,10 @@ impl Container {
|
||||||
///
|
///
|
||||||
/// También acepta clases predefinidas para:
|
/// También acepta clases predefinidas para:
|
||||||
///
|
///
|
||||||
/// - Modificar el color de fondo ([`classes::Background`]).
|
/// - Modificar el color de fondo ([`Background`]).
|
||||||
/// - Definir la apariencia del texto ([`classes::Text`]).
|
/// - Definir la apariencia del texto ([`Text`]).
|
||||||
/// - Establecer bordes ([`classes::Border`]).
|
/// - Establecer bordes ([`Border`]).
|
||||||
/// - Redondear las esquinas ([`classes::Rounded`]).
|
/// - Redondear las esquinas ([`Rounded`]).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
||||||
self.props.alter_prop(op);
|
self.props.alter_prop(op);
|
||||||
|
|
@ -155,7 +156,7 @@ impl Container {
|
||||||
|
|
||||||
/// Establece el comportamiento del ancho para el contenedor.
|
/// Establece el comportamiento del ancho para el contenedor.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_width(mut self, width: container::Width) -> Self {
|
pub fn with_width(mut self, width: bs::container::Width) -> Self {
|
||||||
self.container_width = width;
|
self.container_width = width;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::attrs::BreakPoint;
|
use crate::theme::*;
|
||||||
|
|
||||||
// **< Kind >***************************************************************************************
|
// **< Kind >***************************************************************************************
|
||||||
|
|
||||||
/// Tipo de contenedor ([`Container`](crate::theme::Container)).
|
/// Tipo de contenedor ([`Container`](crate::theme::bs::Container)).
|
||||||
///
|
///
|
||||||
/// Permite aplicar la etiqueta HTML apropiada (`<main>`, `<header>`, etc.) manteniendo una API
|
/// Permite aplicar la etiqueta HTML apropiada (`<main>`, `<header>`, etc.) manteniendo una API
|
||||||
/// común a todos los contenedores.
|
/// común a todos los contenedores.
|
||||||
|
|
@ -27,7 +27,7 @@ pub enum Kind {
|
||||||
|
|
||||||
// **< Width >**************************************************************************************
|
// **< Width >**************************************************************************************
|
||||||
|
|
||||||
/// Define cómo se comporta el ancho de un contenedor ([`Container`](crate::theme::Container)).
|
/// Define cómo se comporta el ancho de un contenedor ([`Container`](crate::theme::bs::Container)).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Width {
|
pub enum Width {
|
||||||
/// Comportamiento por defecto, aplica los anchos máximos predefinidos para cada punto de
|
/// Comportamiento por defecto, aplica los anchos máximos predefinidos para cada punto de
|
||||||
|
|
@ -36,7 +36,7 @@ pub enum Width {
|
||||||
Default,
|
Default,
|
||||||
/// Aplica los anchos máximos predefinidos a partir del punto de ruptura indicado. Por debajo de
|
/// Aplica los anchos máximos predefinidos a partir del punto de ruptura indicado. Por debajo de
|
||||||
/// ese punto de ruptura ocupa el 100% del ancho disponible.
|
/// ese punto de ruptura ocupa el 100% del ancho disponible.
|
||||||
From(BreakPoint),
|
From(token::BreakPoint),
|
||||||
/// Ocupa el 100% del ancho disponible siempre.
|
/// Ocupa el 100% del ancho disponible siempre.
|
||||||
Fluid,
|
Fluid,
|
||||||
/// Ocupa el 100% del ancho disponible hasta un ancho máximo explícito.
|
/// Ocupa el 100% del ancho disponible hasta un ancho máximo explícito.
|
||||||
|
|
@ -46,26 +46,22 @@ pub enum Width {
|
||||||
impl Width {
|
impl Width {
|
||||||
const CONTAINER: &str = "container";
|
const CONTAINER: &str = "container";
|
||||||
|
|
||||||
/* Añade el comportamiento del contenedor a la cadena de clases según ancho (reservado).
|
/// Añade la clase asociada al ancho del contenedor a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => BreakPoint::None.push_class(classes, Self::CONTAINER, ""),
|
Self::Default => token::BreakPoint::None.push_to(classes, Self::CONTAINER, ""),
|
||||||
Self::From(bp) => bp.push_class(classes, Self::CONTAINER, ""),
|
Self::From(bp) => bp.push_to(classes, Self::CONTAINER, ""),
|
||||||
Self::Fluid | Self::FluidMax(_) => {
|
Self::Fluid | Self::FluidMax(_) => {
|
||||||
BreakPoint::None.push_class(classes, Self::CONTAINER, "fluid")
|
token::BreakPoint::None.push_to(classes, Self::CONTAINER, "fluid")
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
/// Devuelve la clase asociada al comportamiento del contenedor según el ajuste de su ancho.
|
|
||||||
pub fn to_class(self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Default => BreakPoint::None.class_with(Self::CONTAINER, ""),
|
|
||||||
Self::From(bp) => bp.class_with(Self::CONTAINER, ""),
|
|
||||||
Self::Fluid | Self::FluidMax(_) => {
|
|
||||||
BreakPoint::None.class_with(Self::CONTAINER, "fluid")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Devuelve la clase asociada al ancho del contenedor.
|
||||||
|
pub fn to_class(self) -> String {
|
||||||
|
let mut class = String::new();
|
||||||
|
self.push_to(&mut class);
|
||||||
|
class
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Definiciones para crear menús desplegables ([`Dropdown`]).
|
//! Definiciones para crear menús desplegables ([`Dropdown`]).
|
||||||
//!
|
//!
|
||||||
//! Cada [`dropdown::Item`](crate::theme::dropdown::Item) representa un elemento individual del
|
//! Cada [`dropdown::Item`](crate::theme::bs::dropdown::Item) representa un elemento individual del
|
||||||
//! desplegable [`Dropdown`], con distintos comportamientos según su finalidad, como enlaces de
|
//! desplegable [`Dropdown`], con distintos comportamientos según su finalidad, como enlaces de
|
||||||
//! navegación, botones de acción, encabezados o divisores visuales.
|
//! navegación, botones de acción, encabezados o divisores visuales.
|
||||||
//!
|
//!
|
||||||
|
|
@ -3,19 +3,20 @@ use pagetop::prelude::*;
|
||||||
use crate::LOCALES_BOOTSIER;
|
use crate::LOCALES_BOOTSIER;
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Componente para crear un **menú desplegable** ([`dropdown`]).
|
/// Componente para crear un **menú desplegable** ([`dropdown`](crate::theme::bs::dropdown)).
|
||||||
///
|
///
|
||||||
/// Renderiza un botón (único o desdoblado, ver [`with_button_split()`](Self::with_button_split))
|
/// Renderiza un botón (único o desdoblado, ver [`with_button_split()`](Self::with_button_split))
|
||||||
/// para mostrar un menú desplegable de elementos [`dropdown::Item`], que se muestra u oculta según
|
/// con un menú desplegable de elementos [`dropdown::Item`](crate::theme::bs::dropdown::Item), que
|
||||||
/// la interacción del usuario. Admite variaciones para el tamaño y el color del botón, también para
|
/// se muestra u oculta según la interacción del usuario. Admite variaciones para el tamaño y el
|
||||||
/// la dirección de apertura, alineación o política de cierre.
|
/// color del botón, también para la dirección de apertura, alineación o política de cierre.
|
||||||
///
|
///
|
||||||
/// Si no tiene título (ver [`with_title()`](Self::with_title)) se muestra únicamente la lista de
|
/// Si no tiene título (ver [`with_title()`](Self::with_title)) se muestra únicamente la lista de
|
||||||
/// elementos sin ningún botón para interactuar.
|
/// elementos sin ningún botón para interactuar.
|
||||||
///
|
///
|
||||||
/// Si este componente se usa en un menú [`Nav`] (ver [`nav::Item::dropdown()`]) sólo se tendrán en
|
/// Si este componente se usa en un menú [`Nav`](crate::theme::bs::Nav) (ver
|
||||||
/// cuenta **el título** (si no existe le asigna uno por defecto) y **la lista de elementos**; el
|
/// [`nav::Item::dropdown()`](crate::theme::bs::nav::Item::dropdown)) sólo se tendrán en cuenta **el
|
||||||
/// resto de propiedades no afectarán a su representación en [`Nav`].
|
/// título** (si no existe le asigna uno por defecto) y **la lista de elementos**; el resto de
|
||||||
|
/// propiedades no afectarán a su representación en [`Nav`](crate::theme::bs::Nav).
|
||||||
///
|
///
|
||||||
/// Si no contiene elementos, el componente **no se renderiza**.
|
/// Si no contiene elementos, el componente **no se renderiza**.
|
||||||
///
|
///
|
||||||
|
|
@ -25,16 +26,16 @@ use crate::theme::*;
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let dd = Dropdown::new()
|
/// let dd = bs::Dropdown::new()
|
||||||
/// .with_title(L10n::n("Menu"))
|
/// .with_title(L10n::n("Menu"))
|
||||||
/// .with_button_color(classes::ButtonColor::solid(Color::Secondary))
|
/// .with_button_color(class::ButtonColor::solid(token::Color::Secondary))
|
||||||
/// .with_auto_close(dropdown::AutoClose::ClickableInside)
|
/// .with_auto_close(bs::dropdown::AutoClose::ClickableInside)
|
||||||
/// .with_direction(dropdown::Direction::Dropend)
|
/// .with_direction(bs::dropdown::Direction::Dropend)
|
||||||
/// .with_item(dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
|
/// .with_item(bs::dropdown::Item::link_blank(L10n::n("Doc"), |_| "https://docs.rs".into()))
|
||||||
/// .with_item(dropdown::Item::divider())
|
/// .with_item(bs::dropdown::Item::divider())
|
||||||
/// .with_item(dropdown::Item::header(L10n::n("User session")))
|
/// .with_item(bs::dropdown::Item::header(L10n::n("User session")))
|
||||||
/// .with_item(dropdown::Item::button(L10n::n("Sign out")));
|
/// .with_item(bs::dropdown::Item::button(L10n::n("Sign out")));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Dropdown {
|
pub struct Dropdown {
|
||||||
|
|
@ -43,21 +44,21 @@ pub struct Dropdown {
|
||||||
/// Devuelve el título del menú desplegable.
|
/// Devuelve el título del menú desplegable.
|
||||||
title: L10n,
|
title: L10n,
|
||||||
/// Devuelve el tamaño configurado del botón.
|
/// Devuelve el tamaño configurado del botón.
|
||||||
button_size: classes::ButtonSize,
|
button_size: class::ButtonSize,
|
||||||
/// Devuelve el color/estilo configurado del botón.
|
/// Devuelve el color/estilo configurado del botón.
|
||||||
button_color: classes::ButtonColor,
|
button_color: class::ButtonColor,
|
||||||
/// Devuelve si se debe desdoblar (*split*) el botón (botón de acción + *toggle*).
|
/// Devuelve si se debe desdoblar (*split*) el botón (botón de acción + *toggle*).
|
||||||
button_split: bool,
|
button_split: bool,
|
||||||
/// Devuelve si el botón del menú está integrado en un grupo de botones.
|
/// Devuelve si el botón del menú está integrado en un grupo de botones.
|
||||||
button_grouped: bool,
|
button_grouped: bool,
|
||||||
/// Devuelve la política de cierre automático del menú desplegado.
|
/// Devuelve la política de cierre automático del menú desplegado.
|
||||||
auto_close: dropdown::AutoClose,
|
auto_close: bs::dropdown::AutoClose,
|
||||||
/// Devuelve la dirección de despliegue configurada.
|
/// Devuelve la dirección de despliegue configurada.
|
||||||
direction: dropdown::Direction,
|
direction: bs::dropdown::Direction,
|
||||||
/// Devuelve la configuración de alineación horizontal del menú desplegable.
|
/// Devuelve la configuración de alineación horizontal del menú desplegable.
|
||||||
menu_align: dropdown::MenuAlign,
|
menu_align: bs::dropdown::MenuAlign,
|
||||||
/// Devuelve la posición configurada para el menú desplegable.
|
/// Devuelve la posición configurada para el menú desplegable.
|
||||||
menu_position: dropdown::MenuPosition,
|
menu_position: bs::dropdown::MenuPosition,
|
||||||
/// Devuelve la lista de elementos del menú.
|
/// Devuelve la lista de elementos del menú.
|
||||||
items: Children,
|
items: Children,
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +74,7 @@ impl Component for Dropdown {
|
||||||
|
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
self.alter_prop(PropsOp::prepend_classes(
|
self.alter_prop(PropsOp::prepend_classes(
|
||||||
self.direction().class_with(*self.button_grouped()),
|
self.direction().to_class(*self.button_grouped()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,20 +92,18 @@ impl Component for Dropdown {
|
||||||
div (self.props()) {
|
div (self.props()) {
|
||||||
@if !title.is_empty() {
|
@if !title.is_empty() {
|
||||||
@let btn_base = {
|
@let btn_base = {
|
||||||
let size = self.button_size().to_class();
|
|
||||||
let color = self.button_color().to_class();
|
|
||||||
let mut classes = String::from("btn");
|
let mut classes = String::from("btn");
|
||||||
if !size.is_empty() { classes.push(' '); classes.push_str(&size); }
|
self.button_size().push_to(&mut classes);
|
||||||
if !color.is_empty() { classes.push(' '); classes.push_str(&color); }
|
self.button_color().push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
};
|
};
|
||||||
@let pos = self.menu_position();
|
@let pos = self.menu_position();
|
||||||
@let offset = pos.data_offset();
|
@let offset = pos.data_offset();
|
||||||
@let reference = pos.data_reference();
|
@let reference = pos.data_reference();
|
||||||
@let auto_close = self.auto_close.as_str();
|
@let auto_close = self.auto_close().opt_str();
|
||||||
@let menu_classes = {
|
@let menu_classes = {
|
||||||
let mut classes = "dropdown-menu".to_string();
|
let mut classes = "dropdown-menu".to_string();
|
||||||
self.menu_align().push_class(&mut classes);
|
self.menu_align().push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -139,7 +138,7 @@ impl Component for Dropdown {
|
||||||
};
|
};
|
||||||
// Orden según dirección (en `dropstart` el *toggle* se sitúa antes).
|
// Orden según dirección (en `dropstart` el *toggle* se sitúa antes).
|
||||||
@match self.direction() {
|
@match self.direction() {
|
||||||
dropdown::Direction::Dropstart => {
|
bs::dropdown::Direction::Dropstart => {
|
||||||
(btn_toggle)
|
(btn_toggle)
|
||||||
ul class=(&menu_classes) { (items) }
|
ul class=(&menu_classes) { (items) }
|
||||||
(btn)
|
(btn)
|
||||||
|
|
@ -201,14 +200,14 @@ impl Dropdown {
|
||||||
|
|
||||||
/// Ajusta el tamaño del botón.
|
/// Ajusta el tamaño del botón.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_button_size(mut self, size: classes::ButtonSize) -> Self {
|
pub fn with_button_size(mut self, size: class::ButtonSize) -> Self {
|
||||||
self.button_size = size;
|
self.button_size = size;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define el color/estilo del botón.
|
/// Define el color/estilo del botón.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_button_color(mut self, color: classes::ButtonColor) -> Self {
|
pub fn with_button_color(mut self, color: class::ButtonColor) -> Self {
|
||||||
self.button_color = color;
|
self.button_color = color;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -229,28 +228,28 @@ impl Dropdown {
|
||||||
|
|
||||||
/// Establece la política de cierre automático del menú desplegable.
|
/// Establece la política de cierre automático del menú desplegable.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_auto_close(mut self, auto_close: dropdown::AutoClose) -> Self {
|
pub fn with_auto_close(mut self, auto_close: bs::dropdown::AutoClose) -> Self {
|
||||||
self.auto_close = auto_close;
|
self.auto_close = auto_close;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la dirección de despliegue del menú.
|
/// Establece la dirección de despliegue del menú.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_direction(mut self, direction: dropdown::Direction) -> Self {
|
pub fn with_direction(mut self, direction: bs::dropdown::Direction) -> Self {
|
||||||
self.direction = direction;
|
self.direction = direction;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configura la alineación horizontal (con posible comportamiento *responsive* adicional).
|
/// Configura la alineación horizontal (con posible comportamiento *responsive* adicional).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_menu_align(mut self, align: dropdown::MenuAlign) -> Self {
|
pub fn with_menu_align(mut self, align: bs::dropdown::MenuAlign) -> Self {
|
||||||
self.menu_align = align;
|
self.menu_align = align;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configura la posición del menú.
|
/// Configura la posición del menú.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_menu_position(mut self, position: dropdown::MenuPosition) -> Self {
|
pub fn with_menu_position(mut self, position: bs::dropdown::MenuPosition) -> Self {
|
||||||
self.menu_position = position;
|
self.menu_position = position;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,8 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
// **< ItemKind >***********************************************************************************
|
// **< ItemKind >***********************************************************************************
|
||||||
|
|
||||||
/// Tipos de [`dropdown::Item`](crate::theme::dropdown::Item) disponibles en un menú desplegable
|
/// Tipos de [`dropdown::Item`](crate::theme::bs::dropdown::Item) disponibles en un menú desplegable
|
||||||
/// [`Dropdown`](crate::theme::Dropdown).
|
/// [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar
|
/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar
|
||||||
/// con él.
|
/// con él.
|
||||||
|
|
@ -35,11 +35,11 @@ pub enum ItemKind {
|
||||||
// **< Item >***************************************************************************************
|
// **< Item >***************************************************************************************
|
||||||
|
|
||||||
/// Representa un **elemento individual** de un menú desplegable
|
/// Representa un **elemento individual** de un menú desplegable
|
||||||
/// [`Dropdown`](crate::theme::Dropdown).
|
/// [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Cada instancia de [`dropdown::Item`](crate::theme::dropdown::Item) se traduce en un componente
|
/// Cada instancia de [`dropdown::Item`](crate::theme::bs::dropdown::Item) se traduce en un
|
||||||
/// visible que puede comportarse como texto, enlace, botón, encabezado o separador, según su
|
/// componente visible que puede comportarse como texto, enlace, botón, encabezado o separador,
|
||||||
/// [`ItemKind`].
|
/// según su [`ItemKind`].
|
||||||
///
|
///
|
||||||
/// Permite definir el identificador, las clases de estilo adicionales y el tipo de interacción
|
/// Permite definir el identificador, las clases de estilo adicionales y el tipo de interacción
|
||||||
/// asociada, manteniendo una interfaz común para renderizar todos los elementos del menú.
|
/// asociada, manteniendo una interfaz común para renderizar todos los elementos del menú.
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::theme::*;
|
||||||
|
|
||||||
// **< AutoClose >**********************************************************************************
|
// **< AutoClose >**********************************************************************************
|
||||||
|
|
||||||
/// Estrategia para el cierre automático de un menú [`Dropdown`].
|
/// Estrategia para el cierre automático de un menú [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Define cuándo se cierra el menú desplegado según la interacción del usuario.
|
/// Define cuándo se cierra el menú desplegado según la interacción del usuario.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -25,7 +25,7 @@ impl AutoClose {
|
||||||
/// Devuelve el valor para `data-bs-auto-close`, o `None` si es el comportamiento por defecto.
|
/// Devuelve el valor para `data-bs-auto-close`, o `None` si es el comportamiento por defecto.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) const fn as_str(self) -> Option<&'static str> {
|
pub const fn opt_str(self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => None,
|
Self::Default => None,
|
||||||
Self::ClickableInside => Some("inside"),
|
Self::ClickableInside => Some("inside"),
|
||||||
|
|
@ -37,7 +37,7 @@ impl AutoClose {
|
||||||
|
|
||||||
// **< Direction >**********************************************************************************
|
// **< Direction >**********************************************************************************
|
||||||
|
|
||||||
/// Dirección de despliegue de un menú [`Dropdown`].
|
/// Dirección de despliegue de un menú [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Controla desde qué posición se muestra el menú respecto al botón.
|
/// Controla desde qué posición se muestra el menú respecto al botón.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -59,10 +59,13 @@ pub enum Direction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Direction {
|
impl Direction {
|
||||||
/// Mapea la dirección teniendo en cuenta si se agrupa con otros menús [`Dropdown`].
|
/// Devuelve la clase base asociada a la dirección del menú.
|
||||||
#[rustfmt::skip ]
|
///
|
||||||
|
/// Cuando `grouped` es `true` y la variante es `Default`, devuelve `""` porque el grupo de
|
||||||
|
/// botones ya implica el contenedor; en caso contrario devuelve `"dropdown"`.
|
||||||
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self, grouped: bool) -> &'static str {
|
pub const fn as_str(self, grouped: bool) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Default if grouped => "",
|
Self::Default if grouped => "",
|
||||||
Self::Default => "dropdown",
|
Self::Default => "dropdown",
|
||||||
|
|
@ -74,10 +77,11 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade la dirección de despliegue a la cadena de clases teniendo en cuenta si se agrupa con
|
/// Añade la clase asociada a la dirección del menú a la cadena de clases.
|
||||||
/// otros menús [`Dropdown`].
|
///
|
||||||
|
/// Ver [`as_str()`](Self::as_str) para el efecto del parámetro `grouped`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String, grouped: bool) {
|
pub fn push_to(self, classes: &mut String, grouped: bool) {
|
||||||
if grouped {
|
if grouped {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
|
|
@ -93,19 +97,19 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la clase asociada a la dirección teniendo en cuenta si se agrupa con otros menús
|
/// Devuelve la clase asociada a la dirección del menú.
|
||||||
/// [`Dropdown`], o `""` si no corresponde ninguna.
|
///
|
||||||
#[doc(hidden)]
|
/// Ver [`as_str()`](Self::as_str) para el efecto del parámetro `grouped`.
|
||||||
pub fn class_with(self, grouped: bool) -> String {
|
pub fn to_class(self, grouped: bool) -> String {
|
||||||
let mut classes = String::new();
|
let mut classes = String::new();
|
||||||
self.push_class(&mut classes, grouped);
|
self.push_to(&mut classes, grouped);
|
||||||
classes
|
classes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< MenuAlign >**********************************************************************************
|
// **< MenuAlign >**********************************************************************************
|
||||||
|
|
||||||
/// Alineación horizontal del menú desplegable [`Dropdown`].
|
/// Alineación horizontal del menú desplegable [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Permite alinear el menú al inicio o al final del botón (respetando LTR/RTL) y añadirle una
|
/// Permite alinear el menú al inicio o al final del botón (respetando LTR/RTL) y añadirle una
|
||||||
/// alineación diferente a partir de un punto de ruptura ([`BreakPoint`]).
|
/// alineación diferente a partir de un punto de ruptura ([`BreakPoint`]).
|
||||||
|
|
@ -115,80 +119,65 @@ pub enum MenuAlign {
|
||||||
#[default]
|
#[default]
|
||||||
Start,
|
Start,
|
||||||
/// Alineación al inicio a partir del punto de ruptura indicado.
|
/// Alineación al inicio a partir del punto de ruptura indicado.
|
||||||
StartAt(BreakPoint),
|
StartAt(token::BreakPoint),
|
||||||
/// Alineación al inicio por defecto, y al final a partir de un punto de ruptura válido.
|
/// Alineación al inicio por defecto, y al final a partir de un punto de ruptura válido.
|
||||||
StartAndEnd(BreakPoint),
|
StartAndEnd(token::BreakPoint),
|
||||||
/// Alineación al final.
|
/// Alineación al final.
|
||||||
End,
|
End,
|
||||||
/// Alineación al final a partir del punto de ruptura indicado.
|
/// Alineación al final a partir del punto de ruptura indicado.
|
||||||
EndAt(BreakPoint),
|
EndAt(token::BreakPoint),
|
||||||
/// Alineación al final por defecto, y al inicio a partir de un punto de ruptura válido.
|
/// Alineación al final por defecto, y al inicio a partir de un punto de ruptura válido.
|
||||||
EndAndStart(BreakPoint),
|
EndAndStart(token::BreakPoint),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuAlign {
|
impl MenuAlign {
|
||||||
|
/// Añade las clases de alineación a la cadena de clases (sin incluir la base `dropdown-menu`).
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_one(classes: &mut String, class: &str) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
if class.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !classes.is_empty() {
|
|
||||||
classes.push(' ');
|
|
||||||
}
|
|
||||||
classes.push_str(class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade las clases de alineación a `classes` (sin incluir la base `dropdown-menu`).
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
|
||||||
match self {
|
match self {
|
||||||
// Alineación por defecto (start), no añade clases extra.
|
// Alineación por defecto (start), no añade clases extra.
|
||||||
Self::Start => {}
|
Self::Start => {}
|
||||||
|
|
||||||
// `dropdown-menu-{bp}-start`
|
// `dropdown-menu-{bp}-start`
|
||||||
Self::StartAt(bp) => {
|
Self::StartAt(bp) => {
|
||||||
let class = bp.class_with("dropdown-menu", "start");
|
bp.push_to(classes, "dropdown-menu", "start");
|
||||||
Self::push_one(classes, &class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `dropdown-menu-start` + `dropdown-menu-{bp}-end`
|
// `dropdown-menu-start` + `dropdown-menu-{bp}-end`
|
||||||
Self::StartAndEnd(bp) => {
|
Self::StartAndEnd(bp) => {
|
||||||
Self::push_one(classes, "dropdown-menu-start");
|
token::BreakPoint::None.push_to(classes, "dropdown-menu", "start");
|
||||||
let bp_class = bp.class_with("dropdown-menu", "end");
|
bp.push_to(classes, "dropdown-menu", "end");
|
||||||
Self::push_one(classes, &bp_class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `dropdown-menu-end`
|
// `dropdown-menu-end`
|
||||||
Self::End => {
|
Self::End => {
|
||||||
Self::push_one(classes, "dropdown-menu-end");
|
token::BreakPoint::None.push_to(classes, "dropdown-menu", "end");
|
||||||
}
|
}
|
||||||
|
|
||||||
// `dropdown-menu-{bp}-end`
|
// `dropdown-menu-{bp}-end`
|
||||||
Self::EndAt(bp) => {
|
Self::EndAt(bp) => {
|
||||||
let class = bp.class_with("dropdown-menu", "end");
|
bp.push_to(classes, "dropdown-menu", "end");
|
||||||
Self::push_one(classes, &class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `dropdown-menu-end` + `dropdown-menu-{bp}-start`
|
// `dropdown-menu-end` + `dropdown-menu-{bp}-start`
|
||||||
Self::EndAndStart(bp) => {
|
Self::EndAndStart(bp) => {
|
||||||
Self::push_one(classes, "dropdown-menu-end");
|
token::BreakPoint::None.push_to(classes, "dropdown-menu", "end");
|
||||||
let bp_class = bp.class_with("dropdown-menu", "start");
|
bp.push_to(classes, "dropdown-menu", "start");
|
||||||
Self::push_one(classes, &bp_class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve las clases de alineación sin incluir `dropdown-menu` (reservado).
|
/// Devuelve las clases de alineación del menú (sin incluir la base `dropdown-menu`).
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut classes = String::new();
|
let mut classes = String::new();
|
||||||
self.push_class(&mut classes);
|
self.push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
} */
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< MenuPosition >*******************************************************************************
|
// **< MenuPosition >*******************************************************************************
|
||||||
|
|
||||||
/// Posición relativa del menú desplegable [`Dropdown`].
|
/// Posición relativa del menú desplegable [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Permite indicar un desplazamiento (*offset*) manual o referenciar al elemento padre para el
|
/// Permite indicar un desplazamiento (*offset*) manual o referenciar al elemento padre para el
|
||||||
/// cálculo de la posición.
|
/// cálculo de la posición.
|
||||||
|
|
@ -200,12 +189,12 @@ pub enum MenuPosition {
|
||||||
/// Desplazamiento manual en píxeles `(x, y)` aplicado al menú. Se admiten valores negativos.
|
/// Desplazamiento manual en píxeles `(x, y)` aplicado al menú. Se admiten valores negativos.
|
||||||
Offset(i8, i8),
|
Offset(i8, i8),
|
||||||
/// Posiciona el menú tomando como referencia el botón padre. Especialmente útil cuando
|
/// Posiciona el menú tomando como referencia el botón padre. Especialmente útil cuando
|
||||||
/// [`button_split()`](crate::theme::Dropdown::button_split) es `true`.
|
/// [`button_split()`](crate::theme::bs::Dropdown::button_split) es `true`.
|
||||||
Parent,
|
Parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuPosition {
|
impl MenuPosition {
|
||||||
/// Devuelve el valor para `data-bs-offset` o `None` si no aplica.
|
// Devuelve el valor para `data-bs-offset` o `None` si no aplica.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn data_offset(self) -> Option<String> {
|
pub(crate) fn data_offset(self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -214,7 +203,7 @@ impl MenuPosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve el valor para `data-bs-reference` o `None` si no aplica.
|
// Devuelve el valor para `data-bs-reference` o `None` si no aplica.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn data_reference(self) -> Option<&'static str> {
|
pub(crate) fn data_reference(self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -11,10 +11,10 @@ pub use pagetop::base::component::form::input::{Field, Kind, Mode};
|
||||||
/// enfocado.
|
/// enfocado.
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::locale::L10n;
|
/// # use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let nombre = form::input::Field::text()
|
/// let nombre = bs::form::input::Field::text()
|
||||||
/// .with_name("name")
|
/// .with_name("name")
|
||||||
/// .with_label(L10n::n("Name"))
|
/// .with_label(L10n::n("Name"))
|
||||||
/// .with_placeholder(L10n::n("Enter your name"))
|
/// .with_placeholder(L10n::n("Enter your name"))
|
||||||
|
|
@ -11,17 +11,16 @@ pub use pagetop::base::component::form::select::{Entry, Field, Group, Item};
|
||||||
/// una selección activa.
|
/// una selección activa.
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::locale::L10n;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop::core::component::Component;
|
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let language = form::select::Field::new()
|
/// let language = bs::form::select::Field::new()
|
||||||
/// .with_name("language")
|
/// .with_name("language")
|
||||||
/// .with_label(L10n::n("Language"))
|
/// .with_label(L10n::n("Language"))
|
||||||
/// .with_floating_label(true)
|
/// .with_floating_label(true)
|
||||||
/// .with_item(form::select::Item::new("", L10n::n("— Choose —")).with_selected(true))
|
/// .with_item(bs::form::select::Item::new("", L10n::n("— Choose —")).with_selected(true))
|
||||||
/// .with_item(form::select::Item::new("es", L10n::n("Spanish")))
|
/// .with_item(bs::form::select::Item::new("es", L10n::n("Spanish")))
|
||||||
/// .with_item(form::select::Item::new("en", L10n::n("English")));
|
/// .with_item(bs::form::select::Item::new("en", L10n::n("English")));
|
||||||
/// ```
|
/// ```
|
||||||
pub trait SelectBootsier {
|
pub trait SelectBootsier {
|
||||||
/// Establece si la etiqueta se muestra flotante sobre el campo.
|
/// Establece si la etiqueta se muestra flotante sobre el campo.
|
||||||
|
|
@ -11,11 +11,10 @@ pub use pagetop::base::component::form::Textarea;
|
||||||
/// una selección activa.
|
/// una selección activa.
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::locale::L10n;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop::core::component::Component;
|
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let comentario = form::Textarea::new()
|
/// let comentario = bs::form::Textarea::new()
|
||||||
/// .with_name("comment")
|
/// .with_name("comment")
|
||||||
/// .with_label(L10n::n("Comment"))
|
/// .with_label(L10n::n("Comment"))
|
||||||
/// .with_placeholder(L10n::n("Write here..."))
|
/// .with_placeholder(L10n::n("Write here..."))
|
||||||
|
|
@ -2,23 +2,24 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Componente para renderizar una **imagen** ([`image`]).
|
/// Componente para renderizar una **imagen** ([`image`](crate::theme::bs::image)).
|
||||||
///
|
///
|
||||||
/// A una imagen se le puede:
|
/// A una imagen se le puede:
|
||||||
///
|
///
|
||||||
/// - Establecer su contenido a partir del origen definido en [`image::Source`].
|
/// - Establecer su contenido a partir del origen definido en
|
||||||
|
/// [`image::Source`](crate::theme::bs::image::Source).
|
||||||
/// - Configurar sus **dimensiones** ([`with_size()`](Self::with_size)), **borde**
|
/// - Configurar sus **dimensiones** ([`with_size()`](Self::with_size)), **borde**
|
||||||
/// ([`classes::Border`](crate::theme::classes::Border)) y **redondeo de esquinas**
|
/// ([`Border`](crate::theme::class::Border)) y **redondeo de esquinas**
|
||||||
/// ([`classes::Rounded`](crate::theme::classes::Rounded)).
|
/// ([`Rounded`](crate::theme::class::Rounded)).
|
||||||
/// - Aplicar el texto alternativo `alt` con **localización** mediante [`L10n`].
|
/// - Aplicar el texto alternativo `alt` con **localización** mediante [`L10n`].
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
||||||
props: Props,
|
props: Props,
|
||||||
/// Devuelve las dimensiones de la imagen.
|
/// Devuelve las dimensiones de la imagen.
|
||||||
size: image::Size,
|
size: bs::image::Size,
|
||||||
/// Devuelve el origen de la imagen.
|
/// Devuelve el origen de la imagen.
|
||||||
source: image::Source,
|
source: bs::image::Source,
|
||||||
/// Devuelve el texto alternativo localizado.
|
/// Devuelve el texto alternativo localizado.
|
||||||
alternative: Attr<L10n>,
|
alternative: Attr<L10n>,
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +43,7 @@ impl Component for Image {
|
||||||
let alt_text = self.alternative().lookup(cx).unwrap_or_default();
|
let alt_text = self.alternative().lookup(cx).unwrap_or_default();
|
||||||
let is_decorative = alt_text.is_empty();
|
let is_decorative = alt_text.is_empty();
|
||||||
let source = match self.source() {
|
let source = match self.source() {
|
||||||
image::Source::Logo(logo) => {
|
bs::image::Source::Logo(logo) => {
|
||||||
return Ok(html! {
|
return Ok(html! {
|
||||||
span
|
span
|
||||||
(self.props())
|
(self.props())
|
||||||
|
|
@ -55,9 +56,9 @@ impl Component for Image {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
image::Source::Responsive(source) => Some(source),
|
bs::image::Source::Responsive(source) => Some(source),
|
||||||
image::Source::Thumbnail(source) => Some(source),
|
bs::image::Source::Thumbnail(source) => Some(source),
|
||||||
image::Source::Plain(source) => Some(source),
|
bs::image::Source::Plain(source) => Some(source),
|
||||||
};
|
};
|
||||||
Ok(html! {
|
Ok(html! {
|
||||||
img
|
img
|
||||||
|
|
@ -71,7 +72,7 @@ impl Component for Image {
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
/// Crea rápidamente una imagen especificando su origen.
|
/// Crea rápidamente una imagen especificando su origen.
|
||||||
pub fn with(source: image::Source) -> Self {
|
pub fn with(source: bs::image::Source) -> Self {
|
||||||
Self::default().with_source(source)
|
Self::default().with_source(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,8 +89,8 @@ impl Image {
|
||||||
///
|
///
|
||||||
/// También acepta clases predefinidas para:
|
/// También acepta clases predefinidas para:
|
||||||
///
|
///
|
||||||
/// - Establecer bordes ([`classes::Border`]).
|
/// - Establecer bordes ([`Border`]).
|
||||||
/// - Redondear las esquinas ([`classes::Rounded`]).
|
/// - Redondear las esquinas ([`Rounded`]).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
||||||
self.props.alter_prop(op);
|
self.props.alter_prop(op);
|
||||||
|
|
@ -98,14 +99,14 @@ impl Image {
|
||||||
|
|
||||||
/// Define las dimensiones de la imagen (auto, ancho/alto, ambos).
|
/// Define las dimensiones de la imagen (auto, ancho/alto, ambos).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_size(mut self, size: image::Size) -> Self {
|
pub fn with_size(mut self, size: bs::image::Size) -> Self {
|
||||||
self.size = size;
|
self.size = size;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el origen de la imagen, influyendo en su disposición en el contenido.
|
/// Establece el origen de la imagen, influyendo en su disposición en el contenido.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_source(mut self, source: image::Source) -> Self {
|
pub fn with_source(mut self, source: bs::image::Source) -> Self {
|
||||||
self.source = source;
|
self.source = source;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
// **< Size >***************************************************************************************
|
// **< Size >***************************************************************************************
|
||||||
|
|
||||||
/// Define las **dimensiones** de una imagen ([`Image`](crate::theme::Image)).
|
/// Define las **dimensiones** de una imagen ([`Image`](crate::theme::bs::Image)).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Size {
|
pub enum Size {
|
||||||
/// Ajuste automático por defecto.
|
/// Ajuste automático por defecto.
|
||||||
|
|
@ -33,7 +33,7 @@ pub enum Size {
|
||||||
impl Size {
|
impl Size {
|
||||||
/// Devuelve el valor del atributo `style` en función del tamaño, o `None` si no aplica.
|
/// Devuelve el valor del atributo `style` en función del tamaño, o `None` si no aplica.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn to_style(self) -> Option<String> {
|
pub fn to_style(self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Self::Auto => None,
|
Self::Auto => None,
|
||||||
Self::Dimensions(w, h) => Some(format!("width: {w}; height: {h};")),
|
Self::Dimensions(w, h) => Some(format!("width: {w}; height: {h};")),
|
||||||
|
|
@ -46,7 +46,7 @@ impl Size {
|
||||||
|
|
||||||
// **< Source >*************************************************************************************
|
// **< Source >*************************************************************************************
|
||||||
|
|
||||||
/// Especifica la **fuente** para publicar una imagen ([`Image`](crate::theme::Image)).
|
/// Especifica la **fuente** para publicar una imagen ([`Image`](crate::theme::bs::Image)).
|
||||||
#[derive(AutoDefault, Clone, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Debug, PartialEq)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
/// Imagen con el logotipo de PageTop.
|
/// Imagen con el logotipo de PageTop.
|
||||||
|
|
@ -96,7 +96,7 @@ impl Source {
|
||||||
|
|
||||||
/// Devuelve la clase base asociada a la imagen según la fuente.
|
/// Devuelve la clase base asociada a la imagen según la fuente.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_str(&self) -> &'static str {
|
pub const fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Source::Logo(_) | Source::Responsive(_) => Self::IMG_FLUID,
|
Source::Logo(_) | Source::Responsive(_) => Self::IMG_FLUID,
|
||||||
Source::Thumbnail(_) => Self::IMG_THUMBNAIL,
|
Source::Thumbnail(_) => Self::IMG_THUMBNAIL,
|
||||||
|
|
@ -104,9 +104,9 @@ impl Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Añade la clase base asociada a la imagen según la fuente a la cadena de clases (reservado).
|
/// Añade la clase asociada al tipo de imagen a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(&self, classes: &mut String) {
|
pub fn push_to(&self, classes: &mut String) {
|
||||||
let s = self.as_str();
|
let s = self.as_str();
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -115,17 +115,12 @@ impl Source {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
}
|
}
|
||||||
classes.push_str(s);
|
classes.push_str(s);
|
||||||
} */
|
}
|
||||||
|
|
||||||
/// Devuelve la clase asociada a la imagen según la fuente.
|
/// Devuelve la clase asociada al tipo de imagen.
|
||||||
pub fn to_class(&self) -> String {
|
pub fn to_class(&self) -> String {
|
||||||
let s = self.as_str();
|
let mut class = String::new();
|
||||||
if s.is_empty() {
|
self.push_to(&mut class);
|
||||||
String::new()
|
class
|
||||||
} else {
|
|
||||||
let mut class = String::with_capacity(s.len());
|
|
||||||
class.push_str(s);
|
|
||||||
class
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
//! Definiciones para crear menús ([`Nav`]).
|
//! Definiciones para crear menús ([`Nav`]).
|
||||||
//!
|
//!
|
||||||
//! Cada [`nav::Item`](crate::theme::nav::Item) representa un elemento individual del menú [`Nav`],
|
//! Cada [`nav::Item`](crate::theme::bs::nav::Item) representa un elemento individual del menú
|
||||||
//! con distintos comportamientos según su finalidad, como enlaces de navegación o menús
|
//! [`Nav`], con distintos comportamientos según su finalidad, como enlaces de navegación o menús
|
||||||
//! desplegables [`Dropdown`](crate::theme::Dropdown).
|
//! desplegables [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
//!
|
//!
|
||||||
//! Los ítems pueden estar activos, deshabilitados o abrirse en nueva ventana según su contexto y
|
//! Los ítems pueden estar activos, deshabilitados o abrirse en nueva ventana según su contexto y
|
||||||
//! configuración, y permiten incluir etiquetas localizables usando [`L10n`](pagetop::locale::L10n).
|
//! configuración, y permiten incluir etiquetas localizables usando [`L10n`](pagetop::locale::L10n).
|
||||||
|
|
@ -2,11 +2,13 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Componente para crear un **menú** ([`nav`]).
|
/// Componente para crear un **menú** ([`nav`](crate::theme::bs::nav)).
|
||||||
///
|
///
|
||||||
/// Presenta un menú con una lista de elementos usando una vista básica, o alguna de sus variantes
|
/// Presenta un menú con una lista de elementos usando una vista básica, o alguna de sus variantes
|
||||||
/// ([`nav::Kind`]) como *pestañas* (`Tabs`), *botones* (`Pills`) o *subrayado* (`Underline`).
|
/// ([`nav::Kind`](crate::theme::bs::nav::Kind)) como *pestañas* (`Tabs`), *botones* (`Pills`) o
|
||||||
/// También permite controlar su distribución y orientación ([`nav::Layout`](crate::theme::nav::Layout)).
|
/// *subrayado* (`Underline`).
|
||||||
|
/// También permite controlar su distribución y orientación
|
||||||
|
/// ([`nav::Layout`](crate::theme::bs::nav::Layout)).
|
||||||
///
|
///
|
||||||
/// Si no contiene elementos, el componente **no se renderiza**.
|
/// Si no contiene elementos, el componente **no se renderiza**.
|
||||||
///
|
///
|
||||||
|
|
@ -16,28 +18,28 @@ use crate::theme::*;
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let nav = Nav::tabs()
|
/// let nav = bs::Nav::tabs()
|
||||||
/// .with_layout(nav::Layout::End)
|
/// .with_layout(bs::nav::Layout::End)
|
||||||
/// .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
|
/// .with_item(bs::nav::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
|
||||||
/// .with_item(nav::Item::dropdown(
|
/// .with_item(bs::nav::Item::dropdown(
|
||||||
/// Dropdown::new()
|
/// bs::Dropdown::new()
|
||||||
/// .with_title(L10n::n("Options"))
|
/// .with_title(L10n::n("Options"))
|
||||||
/// .with_item(ChildOp::AddMany(vec![
|
/// .with_item(ChildOp::AddMany(vec![
|
||||||
/// dropdown::Item::link(L10n::n("Action"), |_| "/action".into()).into(),
|
/// bs::dropdown::Item::link(L10n::n("Action"), |_| "/action".into()).into(),
|
||||||
/// dropdown::Item::link(L10n::n("Another"), |_| "/another".into()).into(),
|
/// bs::dropdown::Item::link(L10n::n("Another"), |_| "/another".into()).into(),
|
||||||
/// ])),
|
/// ])),
|
||||||
/// ))
|
/// ))
|
||||||
/// .with_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()));
|
/// .with_item(bs::nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Nav {
|
pub struct Nav {
|
||||||
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
||||||
props: Props,
|
props: Props,
|
||||||
/// Devuelve el estilo visual seleccionado.
|
/// Devuelve el estilo visual seleccionado.
|
||||||
nav_kind: nav::Kind,
|
nav_kind: bs::nav::Kind,
|
||||||
/// Devuelve la distribución y orientación seleccionada.
|
/// Devuelve la distribución y orientación seleccionada.
|
||||||
nav_layout: nav::Layout,
|
nav_layout: bs::nav::Layout,
|
||||||
/// Devuelve la lista de elementos del menú.
|
/// Devuelve la lista de elementos del menú.
|
||||||
items: Children,
|
items: Children,
|
||||||
}
|
}
|
||||||
|
|
@ -55,8 +57,8 @@ impl Component for Nav {
|
||||||
// Clases CSS por defecto para el menú, según el estilo y la distribución seleccionados.
|
// Clases CSS por defecto para el menú, según el estilo y la distribución seleccionados.
|
||||||
self.alter_prop(PropsOp::prepend_classes({
|
self.alter_prop(PropsOp::prepend_classes({
|
||||||
let mut classes = "nav".to_string();
|
let mut classes = "nav".to_string();
|
||||||
self.nav_kind().push_class(&mut classes);
|
self.nav_kind().push_to(&mut classes);
|
||||||
self.nav_layout().push_class(&mut classes);
|
self.nav_layout().push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -78,17 +80,17 @@ impl Component for Nav {
|
||||||
impl Nav {
|
impl Nav {
|
||||||
/// Crea un `Nav` usando pestañas para los elementos (*Tabs*).
|
/// Crea un `Nav` usando pestañas para los elementos (*Tabs*).
|
||||||
pub fn tabs() -> Self {
|
pub fn tabs() -> Self {
|
||||||
Self::default().with_kind(nav::Kind::Tabs)
|
Self::default().with_kind(bs::nav::Kind::Tabs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un `Nav` usando botones para los elementos (*Pills*).
|
/// Crea un `Nav` usando botones para los elementos (*Pills*).
|
||||||
pub fn pills() -> Self {
|
pub fn pills() -> Self {
|
||||||
Self::default().with_kind(nav::Kind::Pills)
|
Self::default().with_kind(bs::nav::Kind::Pills)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un `Nav` usando elementos subrayados (*Underline*).
|
/// Crea un `Nav` usando elementos subrayados (*Underline*).
|
||||||
pub fn underline() -> Self {
|
pub fn underline() -> Self {
|
||||||
Self::default().with_kind(nav::Kind::Underline)
|
Self::default().with_kind(bs::nav::Kind::Underline)
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Nav BUILDER >****************************************************************************
|
// **< Nav BUILDER >****************************************************************************
|
||||||
|
|
@ -109,14 +111,14 @@ impl Nav {
|
||||||
|
|
||||||
/// Cambia el estilo del menú (*Tabs*, *Pills*, *Underline* o *Default*).
|
/// Cambia el estilo del menú (*Tabs*, *Pills*, *Underline* o *Default*).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_kind(mut self, kind: nav::Kind) -> Self {
|
pub fn with_kind(mut self, kind: bs::nav::Kind) -> Self {
|
||||||
self.nav_kind = kind;
|
self.nav_kind = kind;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selecciona la distribución y orientación del menú.
|
/// Selecciona la distribución y orientación del menú.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_layout(mut self, layout: nav::Layout) -> Self {
|
pub fn with_layout(mut self, layout: bs::nav::Layout) -> Self {
|
||||||
self.nav_layout = layout;
|
self.nav_layout = layout;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -5,8 +5,8 @@ use crate::theme::*;
|
||||||
|
|
||||||
// **< ItemKind >***********************************************************************************
|
// **< ItemKind >***********************************************************************************
|
||||||
|
|
||||||
/// Tipos de [`nav::Item`](crate::theme::nav::Item) disponibles en un menú
|
/// Tipos de [`nav::Item`](crate::theme::bs::nav::Item) disponibles en un menú
|
||||||
/// [`Nav`](crate::theme::Nav).
|
/// [`Nav`](crate::theme::bs::Nav).
|
||||||
///
|
///
|
||||||
/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar
|
/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar
|
||||||
/// con él.
|
/// con él.
|
||||||
|
|
@ -29,49 +29,30 @@ pub enum ItemKind {
|
||||||
/// Contenido HTML arbitrario. El componente [`Html`] se renderiza tal cual como elemento del
|
/// Contenido HTML arbitrario. El componente [`Html`] se renderiza tal cual como elemento del
|
||||||
/// menú, sin añadir ningún comportamiento de navegación adicional.
|
/// menú, sin añadir ningún comportamiento de navegación adicional.
|
||||||
Html(Embed<Html>),
|
Html(Embed<Html>),
|
||||||
/// Elemento que despliega un menú [`Dropdown`].
|
/// Elemento que despliega un menú [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
Dropdown(Embed<Dropdown>),
|
Dropdown(Embed<bs::Dropdown>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemKind {
|
impl ItemKind {
|
||||||
const ITEM: &str = "nav-item";
|
const ITEM: &str = "nav-item";
|
||||||
const DROPDOWN: &str = "nav-item dropdown";
|
const DROPDOWN: &str = "nav-item dropdown";
|
||||||
|
|
||||||
// Devuelve las clases base asociadas al tipo de elemento.
|
/// Devuelve las clases base asociadas al tipo de elemento.
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(&self) -> &'static str {
|
pub const fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Void => "",
|
Self::Void => "",
|
||||||
Self::Dropdown(_) => Self::DROPDOWN,
|
Self::Dropdown(_) => Self::DROPDOWN,
|
||||||
_ => Self::ITEM,
|
_ => Self::ITEM,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Añade las clases asociadas al tipo de elemento a la cadena de clases (reservado).
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(&self, classes: &mut String) {
|
|
||||||
let class = self.as_str();
|
|
||||||
if class.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if !classes.is_empty() {
|
|
||||||
classes.push(' ');
|
|
||||||
}
|
|
||||||
classes.push_str(class);
|
|
||||||
} */
|
|
||||||
|
|
||||||
/// Devuelve las clases asociadas al tipo de elemento.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn to_class(&self) -> String {
|
|
||||||
self.as_str().to_owned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Item >***************************************************************************************
|
// **< Item >***************************************************************************************
|
||||||
|
|
||||||
/// Representa un **elemento individual** de un menú [`Nav`](crate::theme::Nav).
|
/// Representa un **elemento individual** de un menú [`Nav`](crate::theme::bs::Nav).
|
||||||
///
|
///
|
||||||
/// Cada instancia de [`nav::Item`](crate::theme::nav::Item) se traduce en un componente visible que
|
/// Cada instancia de [`nav::Item`](crate::theme::bs::nav::Item) se traduce en un componente visible que
|
||||||
/// puede comportarse como texto, enlace, contenido HTML o menú desplegable, según su [`ItemKind`].
|
/// puede comportarse como texto, enlace, contenido HTML o menú desplegable, según su [`ItemKind`].
|
||||||
///
|
///
|
||||||
/// Permite definir el identificador, las clases de estilo adicionales y el tipo de interacción
|
/// Permite definir el identificador, las clases de estilo adicionales y el tipo de interacción
|
||||||
|
|
@ -94,7 +75,7 @@ impl Component for Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
self.alter_prop(PropsOp::prepend_classes(self.item_kind().to_class()));
|
self.alter_prop(PropsOp::prepend_classes(self.item_kind().as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
|
|
@ -267,12 +248,13 @@ impl Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un elemento de navegación que contiene un menú desplegable [`Dropdown`].
|
/// Crea un elemento de navegación que contiene un menú desplegable
|
||||||
|
/// [`Dropdown`](crate::theme::bs::Dropdown).
|
||||||
///
|
///
|
||||||
/// Sólo se tienen en cuenta **el título** (si no existe, se asigna uno por defecto) y **la
|
/// Sólo se tienen en cuenta **el título** (si no existe, se asigna uno por defecto) y **la
|
||||||
/// lista de elementos** del [`Dropdown`]; el resto de propiedades del componente no afectarán
|
/// lista de elementos** del [`Dropdown`](crate::theme::bs::Dropdown); el resto de propiedades
|
||||||
/// a su representación en [`Nav`].
|
/// del componente no afectarán a su representación en [`Nav`](crate::theme::bs::Nav).
|
||||||
pub fn dropdown(menu: Dropdown) -> Self {
|
pub fn dropdown(menu: bs::Dropdown) -> Self {
|
||||||
Self {
|
Self {
|
||||||
item_kind: ItemKind::Dropdown(Embed::with(menu)),
|
item_kind: ItemKind::Dropdown(Embed::with(menu)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -2,7 +2,7 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
// **< Kind >***************************************************************************************
|
// **< Kind >***************************************************************************************
|
||||||
|
|
||||||
/// Define la variante de presentación de un menú [`Nav`](crate::theme::Nav).
|
/// Define la variante de presentación de un menú [`Nav`](crate::theme::bs::Nav).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
/// Estilo por defecto, lista de enlaces flexible y minimalista.
|
/// Estilo por defecto, lista de enlaces flexible y minimalista.
|
||||||
|
|
@ -24,7 +24,7 @@ impl Kind {
|
||||||
/// Devuelve la clase base asociada al tipo de menú, o una cadena vacía si no aplica.
|
/// Devuelve la clase base asociada al tipo de menú, o una cadena vacía si no aplica.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => "",
|
Self::Default => "",
|
||||||
Self::Tabs => Self::TABS,
|
Self::Tabs => Self::TABS,
|
||||||
|
|
@ -35,7 +35,7 @@ impl Kind {
|
||||||
|
|
||||||
/// Añade la clase asociada al tipo de menú a la cadena de clases.
|
/// Añade la clase asociada al tipo de menú a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
let class = self.as_str();
|
let class = self.as_str();
|
||||||
if class.is_empty() {
|
if class.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -46,16 +46,17 @@ impl Kind {
|
||||||
classes.push_str(class);
|
classes.push_str(class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase asociada al tipo de menú, o una cadena vacía si no aplica (reservado).
|
/// Devuelve la clase asociada al tipo de menú.
|
||||||
#[inline]
|
pub fn to_class(self) -> String {
|
||||||
pub(crate) fn to_class(self) -> String {
|
let mut class = String::new();
|
||||||
self.as_str().to_owned()
|
self.push_to(&mut class);
|
||||||
} */
|
class
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Layout >*************************************************************************************
|
// **< Layout >*************************************************************************************
|
||||||
|
|
||||||
/// Distribución y orientación de un menú [`Nav`](crate::theme::Nav).
|
/// Distribución y orientación de un menú [`Nav`](crate::theme::bs::Nav).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Layout {
|
pub enum Layout {
|
||||||
/// Comportamiento por defecto, ancho definido por el contenido y sin alineación forzada.
|
/// Comportamiento por defecto, ancho definido por el contenido y sin alineación forzada.
|
||||||
|
|
@ -86,7 +87,7 @@ impl Layout {
|
||||||
/// Devuelve la clase base asociada a la distribución y orientación del menú.
|
/// Devuelve la clase base asociada a la distribución y orientación del menú.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => "",
|
Self::Default => "",
|
||||||
Self::Start => Self::START,
|
Self::Start => Self::START,
|
||||||
|
|
@ -100,7 +101,7 @@ impl Layout {
|
||||||
|
|
||||||
/// Añade la clase asociada a la distribución y orientación del menú a la cadena de clases.
|
/// Añade la clase asociada a la distribución y orientación del menú a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
let class = self.as_str();
|
let class = self.as_str();
|
||||||
if class.is_empty() {
|
if class.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -111,10 +112,10 @@ impl Layout {
|
||||||
classes.push_str(class);
|
classes.push_str(class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase asociada a la distribución y orientación del menú, o una cadena vacía si no
|
/// Devuelve la clase asociada a la distribución y orientación del menú.
|
||||||
/// aplica (reservado).
|
pub fn to_class(self) -> String {
|
||||||
#[inline]
|
let mut class = String::new();
|
||||||
pub(crate) fn to_class(self) -> String {
|
self.push_to(&mut class);
|
||||||
self.as_str().to_owned()
|
class
|
||||||
} */
|
}
|
||||||
}
|
}
|
||||||
20
extensions/pagetop-bootsier/src/theme/bs/navbar.rs
Normal file
20
extensions/pagetop-bootsier/src/theme/bs/navbar.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
//! Definiciones para crear barras de navegación ([`Navbar`]).
|
||||||
|
//!
|
||||||
|
//! Cada [`navbar::Item`](crate::theme::bs::navbar::Item) representa un elemento individual de la
|
||||||
|
//! barra de navegación [`Navbar`], con distintos comportamientos según su finalidad, como menús
|
||||||
|
//! [`Nav`](crate::theme::bs::Nav) o *textos localizados* usando [`L10n`](pagetop::locale::L10n).
|
||||||
|
//!
|
||||||
|
//! También puede añadir una marca de identidad ([`navbar::Brand`](crate::theme::bs::navbar::Brand))
|
||||||
|
//! que identifique la compañía, producto o nombre del proyecto asociado a la solución web.
|
||||||
|
|
||||||
|
mod props;
|
||||||
|
pub use props::{Layout, Position};
|
||||||
|
|
||||||
|
mod brand;
|
||||||
|
pub use brand::Brand;
|
||||||
|
|
||||||
|
mod component;
|
||||||
|
pub use component::Navbar;
|
||||||
|
|
||||||
|
mod item;
|
||||||
|
pub use item::Item;
|
||||||
|
|
@ -2,7 +2,7 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Marca de identidad para mostrar en una barra de navegación [`Navbar`].
|
/// Marca de identidad para mostrar en una barra de navegación [`Navbar`](crate::theme::bs::Navbar).
|
||||||
///
|
///
|
||||||
/// Representa la identidad del sitio con una imagen, título y eslogan:
|
/// Representa la identidad del sitio con una imagen, título y eslogan:
|
||||||
///
|
///
|
||||||
|
|
@ -14,7 +14,7 @@ use crate::theme::*;
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Brand {
|
pub struct Brand {
|
||||||
/// Devuelve la imagen de marca (si la hay).
|
/// Devuelve la imagen de marca (si la hay).
|
||||||
image: Embed<Image>,
|
image: Embed<bs::Image>,
|
||||||
/// Devuelve el título de la identidad de marca.
|
/// Devuelve el título de la identidad de marca.
|
||||||
#[default(_code = "L10n::n(&global::SETTINGS.app.name)")]
|
#[default(_code = "L10n::n(&global::SETTINGS.app.name)")]
|
||||||
title: L10n,
|
title: L10n,
|
||||||
|
|
@ -52,7 +52,7 @@ impl Brand {
|
||||||
|
|
||||||
/// Asigna o quita la imagen de marca. Si se pasa `None`, no se mostrará.
|
/// Asigna o quita la imagen de marca. Si se pasa `None`, no se mostrará.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_image(mut self, image: Option<Image>) -> Self {
|
pub fn with_image(mut self, image: Option<bs::Image>) -> Self {
|
||||||
self.image.alter_component(image);
|
self.image.alter_component(image);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -6,11 +6,12 @@ use crate::theme::*;
|
||||||
const TOGGLE_COLLAPSE: &str = "collapse";
|
const TOGGLE_COLLAPSE: &str = "collapse";
|
||||||
const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
|
|
||||||
/// Componente para crear una **barra de navegación** ([`navbar`]).
|
/// Componente para crear una **barra de navegación** ([`navbar`](crate::theme::bs::navbar)).
|
||||||
///
|
///
|
||||||
/// Permite mostrar enlaces, menús y una marca de identidad en distintas disposiciones (simples, con
|
/// Permite mostrar enlaces, menús y una marca de identidad en distintas disposiciones (simples, con
|
||||||
/// botón de despliegue o dentro de un [`offcanvas`]), controladas por [`navbar::Layout`]. También
|
/// botón de despliegue o dentro de un [`Offcanvas`](crate::theme::bs::Offcanvas)), controladas por
|
||||||
/// puede fijarse en la parte superior o inferior del documento mediante [`navbar::Position`].
|
/// [`navbar::Layout`](crate::theme::bs::navbar::Layout). También puede fijarse en la parte superior
|
||||||
|
/// o inferior del documento mediante [`navbar::Position`](crate::theme::bs::navbar::Position).
|
||||||
///
|
///
|
||||||
/// Si no contiene elementos, el componente **no se renderiza**.
|
/// Si no contiene elementos, el componente **no se renderiza**.
|
||||||
///
|
///
|
||||||
|
|
@ -22,12 +23,12 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let navbar = Navbar::simple()
|
/// let navbar = bs::Navbar::simple()
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::new()
|
/// bs::Nav::new()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("About"), |_| "/about".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("About"), |_| "/about".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Contact"), |_| "/contact".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Contact"), |_| "/contact".into()))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
@ -36,13 +37,13 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let navbar = Navbar::simple_toggle()
|
/// let navbar = bs::Navbar::simple_toggle()
|
||||||
/// .with_expand(BreakPoint::MD)
|
/// .with_expand(token::BreakPoint::MD)
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::new()
|
/// bs::Nav::new()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
|
/// .with_item(bs::nav::Item::link_blank(L10n::n("Doc"), |_| "https://docs.rs".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Support"), |_| "/support".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Support"), |_| "/support".into()))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
@ -51,25 +52,25 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let brand = navbar::Brand::new()
|
/// let brand = bs::navbar::Brand::new()
|
||||||
/// .with_title(L10n::n("PageTop"))
|
/// .with_title(L10n::n("PageTop"))
|
||||||
/// .with_route(Some(|cx| cx.route("/")));
|
/// .with_route(Some(|cx| cx.route("/")));
|
||||||
///
|
///
|
||||||
/// let navbar = Navbar::brand_left(brand)
|
/// let navbar = bs::Navbar::brand_left(brand)
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::new()
|
/// bs::Nav::new()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::dropdown(
|
/// .with_item(bs::nav::Item::dropdown(
|
||||||
/// Dropdown::new()
|
/// bs::Dropdown::new()
|
||||||
/// .with_title(L10n::n("Tools"))
|
/// .with_title(L10n::n("Tools"))
|
||||||
/// .with_item(dropdown::Item::link(
|
/// .with_item(bs::dropdown::Item::link(
|
||||||
/// L10n::n("Generator"), |_| "/tools/gen".into())
|
/// L10n::n("Generator"), |_| "/tools/gen".into())
|
||||||
/// )
|
/// )
|
||||||
/// .with_item(dropdown::Item::link(
|
/// .with_item(bs::dropdown::Item::link(
|
||||||
/// L10n::n("Reports"), |_| "/tools/reports".into())
|
/// L10n::n("Reports"), |_| "/tools/reports".into())
|
||||||
/// )
|
/// )
|
||||||
/// ))
|
/// ))
|
||||||
/// .with_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()))
|
/// .with_item(bs::nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
@ -78,16 +79,16 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let brand = navbar::Brand::new()
|
/// let brand = bs::navbar::Brand::new()
|
||||||
/// .with_title(L10n::n("Intranet"))
|
/// .with_title(L10n::n("Intranet"))
|
||||||
/// .with_route(Some(|cx| cx.route("/")));
|
/// .with_route(Some(|cx| cx.route("/")));
|
||||||
///
|
///
|
||||||
/// let navbar = Navbar::brand_right(brand)
|
/// let navbar = bs::Navbar::brand_right(brand)
|
||||||
/// .with_expand(BreakPoint::LG)
|
/// .with_expand(token::BreakPoint::LG)
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::pills()
|
/// bs::Nav::pills()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Users"), |_| "/users".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Users"), |_| "/users".into()))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
@ -96,22 +97,22 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let oc = Offcanvas::new()
|
/// let oc = bs::Offcanvas::new()
|
||||||
/// .with_id("main_offcanvas")
|
/// .with_id("main_offcanvas")
|
||||||
/// .with_title(L10n::n("Main menu"))
|
/// .with_title(L10n::n("Main menu"))
|
||||||
/// .with_placement(offcanvas::Placement::Start)
|
/// .with_placement(bs::offcanvas::Placement::Start)
|
||||||
/// .with_backdrop(offcanvas::Backdrop::Enabled);
|
/// .with_backdrop(bs::offcanvas::Backdrop::Enabled);
|
||||||
///
|
///
|
||||||
/// let navbar = Navbar::offcanvas(oc)
|
/// let navbar = bs::Navbar::offcanvas(oc)
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::new()
|
/// bs::Nav::new()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
|
||||||
/// .with_item(nav::Item::dropdown(
|
/// .with_item(bs::nav::Item::dropdown(
|
||||||
/// Dropdown::new()
|
/// bs::Dropdown::new()
|
||||||
/// .with_title(L10n::n("More"))
|
/// .with_title(L10n::n("More"))
|
||||||
/// .with_item(dropdown::Item::link(L10n::n("Settings"), |_| "/settings".into()))
|
/// .with_item(bs::dropdown::Item::link(L10n::n("Settings"), |_| "/settings".into()))
|
||||||
/// .with_item(dropdown::Item::link(L10n::n("Help"), |_| "/help".into()))
|
/// .with_item(bs::dropdown::Item::link(L10n::n("Help"), |_| "/help".into()))
|
||||||
/// ))
|
/// ))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -121,17 +122,17 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let brand = navbar::Brand::new()
|
/// let brand = bs::navbar::Brand::new()
|
||||||
/// .with_title(L10n::n("Main App"))
|
/// .with_title(L10n::n("Main App"))
|
||||||
/// .with_route(Some(|cx| cx.route("/")));
|
/// .with_route(Some(|cx| cx.route("/")));
|
||||||
///
|
///
|
||||||
/// let navbar = Navbar::brand_left(brand)
|
/// let navbar = bs::Navbar::brand_left(brand)
|
||||||
/// .with_position(navbar::Position::FixedTop)
|
/// .with_position(bs::navbar::Position::FixedTop)
|
||||||
/// .with_item(navbar::Item::nav(
|
/// .with_item(bs::navbar::Item::nav(
|
||||||
/// Nav::new()
|
/// bs::Nav::new()
|
||||||
/// .with_item(nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Donors"), |_| "/donors".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Donors"), |_| "/donors".into()))
|
||||||
/// .with_item(nav::Item::link(L10n::n("Stock"), |_| "/stock".into()))
|
/// .with_item(bs::nav::Item::link(L10n::n("Stock"), |_| "/stock".into()))
|
||||||
/// ));
|
/// ));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
|
|
@ -139,11 +140,11 @@ pub struct Navbar {
|
||||||
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
/// Devuelve identificador, clases CSS y atributos HTML del componente.
|
||||||
props: Props,
|
props: Props,
|
||||||
/// Devuelve el punto de ruptura configurado.
|
/// Devuelve el punto de ruptura configurado.
|
||||||
expand: BreakPoint,
|
expand: token::BreakPoint,
|
||||||
/// Devuelve la disposición configurada para la barra de navegación.
|
/// Devuelve la disposición configurada para la barra de navegación.
|
||||||
layout: navbar::Layout,
|
layout: bs::navbar::Layout,
|
||||||
/// Devuelve la posición configurada para la barra de navegación.
|
/// Devuelve la posición configurada para la barra de navegación.
|
||||||
position: navbar::Position,
|
position: bs::navbar::Position,
|
||||||
/// Devuelve la lista de contenidos.
|
/// Devuelve la lista de contenidos.
|
||||||
items: Children,
|
items: Children,
|
||||||
}
|
}
|
||||||
|
|
@ -164,8 +165,8 @@ impl Component for Navbar {
|
||||||
// Clases CSS por defecto para la barra de navegación.
|
// Clases CSS por defecto para la barra de navegación.
|
||||||
self.alter_prop(PropsOp::prepend_classes({
|
self.alter_prop(PropsOp::prepend_classes({
|
||||||
let mut classes = "navbar".to_string();
|
let mut classes = "navbar".to_string();
|
||||||
self.expand().push_class(&mut classes, "navbar-expand", "");
|
self.expand().push_to(&mut classes, "navbar-expand", "");
|
||||||
self.position().push_class(&mut classes);
|
self.position().push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -208,12 +209,12 @@ impl Component for Navbar {
|
||||||
div class="container-fluid" {
|
div class="container-fluid" {
|
||||||
@match self.layout() {
|
@match self.layout() {
|
||||||
// Barra más sencilla: sólo contenido.
|
// Barra más sencilla: sólo contenido.
|
||||||
navbar::Layout::Simple => {
|
bs::navbar::Layout::Simple => {
|
||||||
(items)
|
(items)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra sencilla que se puede contraer/expandir.
|
// Barra sencilla que se puede contraer/expandir.
|
||||||
navbar::Layout::SimpleToggle => {
|
bs::navbar::Layout::SimpleToggle => {
|
||||||
@let id_content = util::join!(id, "-content");
|
@let id_content = util::join!(id, "-content");
|
||||||
|
|
||||||
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
||||||
|
|
@ -223,13 +224,13 @@ impl Component for Navbar {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra con marca a la izquierda, siempre visible.
|
// Barra con marca a la izquierda, siempre visible.
|
||||||
navbar::Layout::SimpleBrandLeft(brand) => {
|
bs::navbar::Layout::SimpleBrandLeft(brand) => {
|
||||||
(brand.render(cx))
|
(brand.render(cx))
|
||||||
(items)
|
(items)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra con marca a la izquierda y botón a la derecha.
|
// Barra con marca a la izquierda y botón a la derecha.
|
||||||
navbar::Layout::BrandLeft(brand) => {
|
bs::navbar::Layout::BrandLeft(brand) => {
|
||||||
@let id_content = util::join!(id, "-content");
|
@let id_content = util::join!(id, "-content");
|
||||||
|
|
||||||
(brand.render(cx))
|
(brand.render(cx))
|
||||||
|
|
@ -240,7 +241,7 @@ impl Component for Navbar {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra con botón a la izquierda y marca a la derecha.
|
// Barra con botón a la izquierda y marca a la derecha.
|
||||||
navbar::Layout::BrandRight(brand) => {
|
bs::navbar::Layout::BrandRight(brand) => {
|
||||||
@let id_content = util::join!(id, "-content");
|
@let id_content = util::join!(id, "-content");
|
||||||
|
|
||||||
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
(button(cx, TOGGLE_COLLAPSE, &id_content))
|
||||||
|
|
@ -251,7 +252,7 @@ impl Component for Navbar {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra cuyo contenido se muestra en un offcanvas, sin marca.
|
// Barra cuyo contenido se muestra en un offcanvas, sin marca.
|
||||||
navbar::Layout::Offcanvas(offcanvas) => {
|
bs::navbar::Layout::Offcanvas(offcanvas) => {
|
||||||
@let id_content = offcanvas.id().unwrap_or_default();
|
@let id_content = offcanvas.id().unwrap_or_default();
|
||||||
|
|
||||||
(button(cx, TOGGLE_OFFCANVAS, &id_content))
|
(button(cx, TOGGLE_OFFCANVAS, &id_content))
|
||||||
|
|
@ -261,7 +262,7 @@ impl Component for Navbar {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra con marca a la izquierda y contenido en offcanvas.
|
// Barra con marca a la izquierda y contenido en offcanvas.
|
||||||
navbar::Layout::OffcanvasBrandLeft(brand, offcanvas) => {
|
bs::navbar::Layout::OffcanvasBrandLeft(brand, offcanvas) => {
|
||||||
@let id_content = offcanvas.id().unwrap_or_default();
|
@let id_content = offcanvas.id().unwrap_or_default();
|
||||||
|
|
||||||
(brand.render(cx))
|
(brand.render(cx))
|
||||||
|
|
@ -272,7 +273,7 @@ impl Component for Navbar {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Barra con contenido en offcanvas y marca a la derecha.
|
// Barra con contenido en offcanvas y marca a la derecha.
|
||||||
navbar::Layout::OffcanvasBrandRight(brand, offcanvas) => {
|
bs::navbar::Layout::OffcanvasBrandRight(brand, offcanvas) => {
|
||||||
@let id_content = offcanvas.id().unwrap_or_default();
|
@let id_content = offcanvas.id().unwrap_or_default();
|
||||||
|
|
||||||
(button(cx, TOGGLE_OFFCANVAS, &id_content))
|
(button(cx, TOGGLE_OFFCANVAS, &id_content))
|
||||||
|
|
@ -291,45 +292,45 @@ impl Component for Navbar {
|
||||||
impl Navbar {
|
impl Navbar {
|
||||||
/// Crea una barra de navegación **simple**, sin marca y sin botón.
|
/// Crea una barra de navegación **simple**, sin marca y sin botón.
|
||||||
pub fn simple() -> Self {
|
pub fn simple() -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::Simple)
|
Self::default().with_layout(bs::navbar::Layout::Simple)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación **simple pero colapsable**, con botón a la izquierda.
|
/// Crea una barra de navegación **simple pero colapsable**, con botón a la izquierda.
|
||||||
pub fn simple_toggle() -> Self {
|
pub fn simple_toggle() -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::SimpleToggle)
|
Self::default().with_layout(bs::navbar::Layout::SimpleToggle)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación **con marca a la izquierda**, siempre visible.
|
/// Crea una barra de navegación **con marca a la izquierda**, siempre visible.
|
||||||
pub fn simple_brand_left(brand: navbar::Brand) -> Self {
|
pub fn simple_brand_left(brand: bs::navbar::Brand) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::SimpleBrandLeft(Embed::with(brand)))
|
Self::default().with_layout(bs::navbar::Layout::SimpleBrandLeft(Embed::with(brand)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación con **marca a la izquierda** y **botón a la derecha**.
|
/// Crea una barra de navegación con **marca a la izquierda** y **botón a la derecha**.
|
||||||
pub fn brand_left(brand: navbar::Brand) -> Self {
|
pub fn brand_left(brand: bs::navbar::Brand) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::BrandLeft(Embed::with(brand)))
|
Self::default().with_layout(bs::navbar::Layout::BrandLeft(Embed::with(brand)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación con **botón a la izquierda** y **marca a la derecha**.
|
/// Crea una barra de navegación con **botón a la izquierda** y **marca a la derecha**.
|
||||||
pub fn brand_right(brand: navbar::Brand) -> Self {
|
pub fn brand_right(brand: bs::navbar::Brand) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::BrandRight(Embed::with(brand)))
|
Self::default().with_layout(bs::navbar::Layout::BrandRight(Embed::with(brand)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación cuyo contenido se muestra en un **offcanvas**.
|
/// Crea una barra de navegación cuyo contenido se muestra en un **offcanvas**.
|
||||||
pub fn offcanvas(oc: Offcanvas) -> Self {
|
pub fn offcanvas(oc: bs::Offcanvas) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::Offcanvas(Embed::with(oc)))
|
Self::default().with_layout(bs::navbar::Layout::Offcanvas(Embed::with(oc)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación con **marca a la izquierda** y contenido en **offcanvas**.
|
/// Crea una barra de navegación con **marca a la izquierda** y contenido en **offcanvas**.
|
||||||
pub fn offcanvas_brand_left(brand: navbar::Brand, oc: Offcanvas) -> Self {
|
pub fn offcanvas_brand_left(brand: bs::navbar::Brand, oc: bs::Offcanvas) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::OffcanvasBrandLeft(
|
Self::default().with_layout(bs::navbar::Layout::OffcanvasBrandLeft(
|
||||||
Embed::with(brand),
|
Embed::with(brand),
|
||||||
Embed::with(oc),
|
Embed::with(oc),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una barra de navegación con **marca a la derecha** y contenido en **offcanvas**.
|
/// Crea una barra de navegación con **marca a la derecha** y contenido en **offcanvas**.
|
||||||
pub fn offcanvas_brand_right(brand: navbar::Brand, oc: Offcanvas) -> Self {
|
pub fn offcanvas_brand_right(brand: bs::navbar::Brand, oc: bs::Offcanvas) -> Self {
|
||||||
Self::default().with_layout(navbar::Layout::OffcanvasBrandRight(
|
Self::default().with_layout(bs::navbar::Layout::OffcanvasBrandRight(
|
||||||
Embed::with(brand),
|
Embed::with(brand),
|
||||||
Embed::with(oc),
|
Embed::with(oc),
|
||||||
))
|
))
|
||||||
|
|
@ -348,8 +349,8 @@ impl Navbar {
|
||||||
///
|
///
|
||||||
/// También acepta clases predefinidas para:
|
/// También acepta clases predefinidas para:
|
||||||
///
|
///
|
||||||
/// - Modificar el color de fondo ([`classes::Background`]).
|
/// - Modificar el color de fondo ([`Background`]).
|
||||||
/// - Definir la apariencia del texto ([`classes::Text`]).
|
/// - Definir la apariencia del texto ([`Text`]).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
||||||
self.props.alter_prop(op);
|
self.props.alter_prop(op);
|
||||||
|
|
@ -358,21 +359,21 @@ impl Navbar {
|
||||||
|
|
||||||
/// Define a partir de qué punto de ruptura la barra de navegación deja de colapsar.
|
/// Define a partir de qué punto de ruptura la barra de navegación deja de colapsar.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_expand(mut self, bp: BreakPoint) -> Self {
|
pub fn with_expand(mut self, bp: token::BreakPoint) -> Self {
|
||||||
self.expand = bp;
|
self.expand = bp;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define el tipo de disposición que tendrá la barra de navegación.
|
/// Define el tipo de disposición que tendrá la barra de navegación.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_layout(mut self, layout: navbar::Layout) -> Self {
|
pub fn with_layout(mut self, layout: bs::navbar::Layout) -> Self {
|
||||||
self.layout = layout;
|
self.layout = layout;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define dónde se mostrará la barra de navegación dentro del documento.
|
/// Define dónde se mostrará la barra de navegación dentro del documento.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_position(mut self, position: navbar::Position) -> Self {
|
pub fn with_position(mut self, position: bs::navbar::Position) -> Self {
|
||||||
self.position = position;
|
self.position = position;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,12 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Elementos que puede contener una barra de navegación [`Navbar`](crate::theme::Navbar).
|
/// Elementos que puede contener una barra de navegación [`Navbar`](crate::theme::bs::Navbar).
|
||||||
///
|
///
|
||||||
/// Cada variante determina qué se renderiza y cómo. Estos elementos se colocan **dentro del
|
/// Cada variante determina qué se renderiza y cómo. Estos elementos se colocan **dentro del
|
||||||
/// contenido** de la barra (la parte colapsable, el *offcanvas* o el bloque simple), por lo que son
|
/// contenido** de la barra (la parte colapsable, el *offcanvas* o el bloque simple), por lo que son
|
||||||
/// independientes de la marca o del botón que ya pueda definir el propio [`navbar::Layout`].
|
/// independientes de la marca o del botón que ya pueda definir el propio
|
||||||
|
/// [`navbar::Layout`](crate::theme::bs::navbar::Layout).
|
||||||
#[derive(AutoDefault, Clone, Debug)]
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
pub enum Item {
|
pub enum Item {
|
||||||
/// Sin contenido, no produce salida.
|
/// Sin contenido, no produce salida.
|
||||||
|
|
@ -14,12 +15,13 @@ pub enum Item {
|
||||||
Void,
|
Void,
|
||||||
/// Marca de identidad mostrada dentro del contenido de la barra de navegación.
|
/// Marca de identidad mostrada dentro del contenido de la barra de navegación.
|
||||||
///
|
///
|
||||||
/// Útil cuando el [`navbar::Layout`] no incluye marca, y se quiere incluir dentro del área
|
/// Útil cuando el [`navbar::Layout`](crate::theme::bs::navbar::Layout) no incluye marca, y se
|
||||||
|
/// quiere incluir dentro del área
|
||||||
/// colapsable/*offcanvas*. Si el *layout* ya muestra una marca, esta variante no la sustituye,
|
/// colapsable/*offcanvas*. Si el *layout* ya muestra una marca, esta variante no la sustituye,
|
||||||
/// sólo añade otra dentro del bloque de contenidos.
|
/// sólo añade otra dentro del bloque de contenidos.
|
||||||
Brand(Embed<navbar::Brand>),
|
Brand(Embed<bs::navbar::Brand>),
|
||||||
/// Representa un menú de navegación [`Nav`](crate::theme::Nav).
|
/// Representa un menú de navegación [`Nav`](crate::theme::bs::Nav).
|
||||||
Nav(Embed<Nav>),
|
Nav(Embed<bs::Nav>),
|
||||||
/// Representa un *texto localizado* libre.
|
/// Representa un *texto localizado* libre.
|
||||||
Text(L10n),
|
Text(L10n),
|
||||||
}
|
}
|
||||||
|
|
@ -75,16 +77,16 @@ impl Component for Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
/// Crea un elemento de tipo [`navbar::Brand`] para añadir en el contenido de [`Navbar`].
|
/// Crea un elemento de tipo [`navbar::Brand`](crate::theme::bs::navbar::Brand) para añadir en el contenido de [`Navbar`](crate::theme::bs::Navbar).
|
||||||
///
|
///
|
||||||
/// Pensado para barras colapsables u offcanvas donde se quiere que la marca aparezca en la zona
|
/// Pensado para barras colapsables u offcanvas donde se quiere que la marca aparezca en la zona
|
||||||
/// desplegable.
|
/// desplegable.
|
||||||
pub fn brand(brand: navbar::Brand) -> Self {
|
pub fn brand(brand: bs::navbar::Brand) -> Self {
|
||||||
Self::Brand(Embed::with(brand))
|
Self::Brand(Embed::with(brand))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un elemento de tipo [`Nav`] para añadir al contenido de [`Navbar`].
|
/// Crea un elemento de tipo [`Nav`](crate::theme::bs::Nav) para añadir al contenido de [`Navbar`](crate::theme::bs::Navbar).
|
||||||
pub fn nav(item: Nav) -> Self {
|
pub fn nav(item: bs::Nav) -> Self {
|
||||||
Self::Nav(Embed::with(item))
|
Self::Nav(Embed::with(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4,7 +4,8 @@ use crate::theme::*;
|
||||||
|
|
||||||
// **< Layout >*************************************************************************************
|
// **< Layout >*************************************************************************************
|
||||||
|
|
||||||
/// Representa los diferentes tipos de presentación de una barra de navegación [`Navbar`].
|
/// Representa los diferentes tipos de presentación de una barra de navegación
|
||||||
|
/// [`Navbar`](crate::theme::bs::Navbar).
|
||||||
#[derive(AutoDefault, Clone, Debug)]
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
pub enum Layout {
|
pub enum Layout {
|
||||||
/// Barra simple, sin marca de identidad y sin botón de despliegue.
|
/// Barra simple, sin marca de identidad y sin botón de despliegue.
|
||||||
|
|
@ -19,29 +20,30 @@ pub enum Layout {
|
||||||
/// Barra simple, con marca de identidad a la izquierda y sin botón de despliegue.
|
/// Barra simple, con marca de identidad a la izquierda y sin botón de despliegue.
|
||||||
///
|
///
|
||||||
/// La barra de navegación no se colapsa.
|
/// La barra de navegación no se colapsa.
|
||||||
SimpleBrandLeft(Embed<navbar::Brand>),
|
SimpleBrandLeft(Embed<bs::navbar::Brand>),
|
||||||
|
|
||||||
/// Barra con marca de identidad a la izquierda y botón de despliegue a la derecha.
|
/// Barra con marca de identidad a la izquierda y botón de despliegue a la derecha.
|
||||||
BrandLeft(Embed<navbar::Brand>),
|
BrandLeft(Embed<bs::navbar::Brand>),
|
||||||
|
|
||||||
/// Barra con botón de despliegue a la izquierda y marca de identidad a la derecha.
|
/// Barra con botón de despliegue a la izquierda y marca de identidad a la derecha.
|
||||||
BrandRight(Embed<navbar::Brand>),
|
BrandRight(Embed<bs::navbar::Brand>),
|
||||||
|
|
||||||
/// Contenido en [`Offcanvas`], con botón de despliegue a la izquierda y sin marca de identidad.
|
/// Contenido en [`Offcanvas`](crate::theme::bs::Offcanvas), con botón de despliegue a la
|
||||||
Offcanvas(Embed<Offcanvas>),
|
/// izquierda y sin marca de identidad.
|
||||||
|
Offcanvas(Embed<bs::Offcanvas>),
|
||||||
|
|
||||||
/// Contenido en [`Offcanvas`], con marca de identidad a la izquierda y botón de despliegue a la
|
/// Contenido en [`Offcanvas`](crate::theme::bs::Offcanvas), con marca de identidad a la
|
||||||
/// derecha.
|
/// izquierda y botón de despliegue a la derecha.
|
||||||
OffcanvasBrandLeft(Embed<navbar::Brand>, Embed<Offcanvas>),
|
OffcanvasBrandLeft(Embed<bs::navbar::Brand>, Embed<bs::Offcanvas>),
|
||||||
|
|
||||||
/// Contenido en [`Offcanvas`], con botón de despliegue a la izquierda y marca de identidad a la
|
/// Contenido en [`Offcanvas`](crate::theme::bs::Offcanvas), con botón de despliegue a la
|
||||||
/// derecha.
|
/// izquierda y marca de identidad a la derecha.
|
||||||
OffcanvasBrandRight(Embed<navbar::Brand>, Embed<Offcanvas>),
|
OffcanvasBrandRight(Embed<bs::navbar::Brand>, Embed<bs::Offcanvas>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Position >***********************************************************************************
|
// **< Position >***********************************************************************************
|
||||||
|
|
||||||
/// Posición global de una barra de navegación [`Navbar`] en el documento.
|
/// Posición global de una barra de navegación [`Navbar`](crate::theme::bs::Navbar) en el documento.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Position {
|
pub enum Position {
|
||||||
/// Barra normal, fluye con el documento.
|
/// Barra normal, fluye con el documento.
|
||||||
|
|
@ -65,20 +67,21 @@ pub enum Position {
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
/// Devuelve la clase base asociada a la posición de la barra de navegación.
|
/// Devuelve la clase base asociada a la posición de la barra de navegación.
|
||||||
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Static => "",
|
Self::Static => "",
|
||||||
Self::FixedTop => "fixed-top",
|
Self::FixedTop => "fixed-top",
|
||||||
Self::FixedBottom => "fixed-bottom",
|
Self::FixedBottom => "fixed-bottom",
|
||||||
Self::StickyTop => "sticky-top",
|
Self::StickyTop => "sticky-top",
|
||||||
Self::StickyBottom => "sticky-bottom",
|
Self::StickyBottom => "sticky-bottom",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade la clase asociada a la posición de la barra de navegación a la cadena de clases.
|
/// Añade la clase asociada a la posición de la barra de navegación a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
let class = self.as_str();
|
let class = self.as_str();
|
||||||
if class.is_empty() {
|
if class.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -89,10 +92,10 @@ impl Position {
|
||||||
classes.push_str(class);
|
classes.push_str(class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase asociada a la posición de la barra de navegación, o cadena vacía si no
|
/// Devuelve la clase asociada a la posición de la barra de navegación.
|
||||||
/// aplica (reservado).
|
pub fn to_class(self) -> String {
|
||||||
#[inline]
|
let mut class = String::new();
|
||||||
pub(crate) fn to_class(self) -> String {
|
self.push_to(&mut class);
|
||||||
self.as_str().to_string()
|
class
|
||||||
} */
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ use pagetop::prelude::*;
|
||||||
use crate::LOCALES_BOOTSIER;
|
use crate::LOCALES_BOOTSIER;
|
||||||
use crate::theme::*;
|
use crate::theme::*;
|
||||||
|
|
||||||
/// Componente para crear un **panel lateral deslizante** ([`offcanvas`]).
|
/// Componente para crear un **panel lateral deslizante** ([`offcanvas`](crate::theme::bs::offcanvas)).
|
||||||
///
|
///
|
||||||
/// Útil para navegación, filtros, formularios o menús contextuales. Incluye las siguientes
|
/// Útil para navegación, filtros, formularios o menús contextuales. Incluye las siguientes
|
||||||
/// características principales:
|
/// características principales:
|
||||||
|
|
@ -27,18 +27,18 @@ use crate::theme::*;
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let panel = Offcanvas::new()
|
/// let panel = bs::Offcanvas::new()
|
||||||
/// .with_id("offcanvas_example")
|
/// .with_id("offcanvas_example")
|
||||||
/// .with_title(L10n::n("Offcanvas title"))
|
/// .with_title(L10n::n("Offcanvas title"))
|
||||||
/// .with_placement(offcanvas::Placement::End)
|
/// .with_placement(bs::offcanvas::Placement::End)
|
||||||
/// .with_backdrop(offcanvas::Backdrop::Enabled)
|
/// .with_backdrop(bs::offcanvas::Backdrop::Enabled)
|
||||||
/// .with_body_scroll(offcanvas::BodyScroll::Enabled)
|
/// .with_body_scroll(bs::offcanvas::BodyScroll::Enabled)
|
||||||
/// .with_visibility(offcanvas::Visibility::Default)
|
/// .with_visibility(bs::offcanvas::Visibility::Default)
|
||||||
/// .with_child(Dropdown::new()
|
/// .with_child(bs::Dropdown::new()
|
||||||
/// .with_title(L10n::n("Menu"))
|
/// .with_title(L10n::n("Menu"))
|
||||||
/// .with_item(dropdown::Item::label(L10n::n("Label")))
|
/// .with_item(bs::dropdown::Item::label(L10n::n("Label")))
|
||||||
/// .with_item(dropdown::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
|
/// .with_item(bs::dropdown::Item::link_blank(L10n::n("Doc"), |_| "https://docs.rs".into()))
|
||||||
/// .with_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
|
/// .with_item(bs::dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
|
|
@ -48,15 +48,15 @@ pub struct Offcanvas {
|
||||||
/// Devuelve el título del panel.
|
/// Devuelve el título del panel.
|
||||||
title: L10n,
|
title: L10n,
|
||||||
/// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel.
|
/// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel.
|
||||||
breakpoint: BreakPoint,
|
breakpoint: token::BreakPoint,
|
||||||
/// Devuelve el comportamiento configurado para la capa de fondo.
|
/// Devuelve el comportamiento configurado para la capa de fondo.
|
||||||
backdrop: offcanvas::Backdrop,
|
backdrop: bs::offcanvas::Backdrop,
|
||||||
/// Indica si la página principal puede desplazarse mientras el panel está abierto.
|
/// Indica si la página principal puede desplazarse mientras el panel está abierto.
|
||||||
body_scroll: offcanvas::BodyScroll,
|
body_scroll: bs::offcanvas::BodyScroll,
|
||||||
/// Devuelve la posición de inicio del panel.
|
/// Devuelve la posición de inicio del panel.
|
||||||
placement: offcanvas::Placement,
|
placement: bs::offcanvas::Placement,
|
||||||
/// Devuelve el estado inicial del panel.
|
/// Devuelve el estado inicial del panel.
|
||||||
visibility: offcanvas::Visibility,
|
visibility: bs::offcanvas::Visibility,
|
||||||
/// Devuelve la lista de componentes (`children`) del panel.
|
/// Devuelve la lista de componentes (`children`) del panel.
|
||||||
children: Children,
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
@ -77,9 +77,9 @@ impl Component for Offcanvas {
|
||||||
// Clases CSS por defecto para el panel.
|
// Clases CSS por defecto para el panel.
|
||||||
self.alter_prop(PropsOp::prepend_classes({
|
self.alter_prop(PropsOp::prepend_classes({
|
||||||
let mut classes = "offcanvas".to_string();
|
let mut classes = "offcanvas".to_string();
|
||||||
self.breakpoint().push_class(&mut classes, "offcanvas", "");
|
self.breakpoint().push_to(&mut classes, "offcanvas", "");
|
||||||
self.placement().push_class(&mut classes);
|
self.placement().push_to(&mut classes);
|
||||||
self.visibility().push_class(&mut classes);
|
self.visibility().push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -119,11 +119,11 @@ impl Offcanvas {
|
||||||
/// ([`Offcanvas`]).
|
/// ([`Offcanvas`]).
|
||||||
/// - **Por encima**, el contenido del panel se muestra tal cual, integrado en la página.
|
/// - **Por encima**, el contenido del panel se muestra tal cual, integrado en la página.
|
||||||
///
|
///
|
||||||
/// Por ejemplo, con `BreakPoint::LG`, será *offcanvas* en móviles y tabletas, y visible
|
/// Por ejemplo, con `BreakPoint::Lg`, será *offcanvas* en móviles y tabletas, y visible
|
||||||
/// directamente en pantallas grandes. Por defecto usa `BreakPoint::None` para que sea
|
/// directamente en pantallas grandes. Por defecto usa `BreakPoint::None` para que sea
|
||||||
/// *offcanvas* siempre.
|
/// *offcanvas* siempre.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_breakpoint(mut self, bp: BreakPoint) -> Self {
|
pub fn with_breakpoint(mut self, bp: token::BreakPoint) -> Self {
|
||||||
self.breakpoint = bp;
|
self.breakpoint = bp;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -131,28 +131,28 @@ impl Offcanvas {
|
||||||
/// Ajusta la capa de fondo del panel para definir su comportamiento al hacer clic fuera del
|
/// Ajusta la capa de fondo del panel para definir su comportamiento al hacer clic fuera del
|
||||||
/// panel.
|
/// panel.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_backdrop(mut self, backdrop: offcanvas::Backdrop) -> Self {
|
pub fn with_backdrop(mut self, backdrop: bs::offcanvas::Backdrop) -> Self {
|
||||||
self.backdrop = backdrop;
|
self.backdrop = backdrop;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permite o bloquea el desplazamiento de la página principal mientras el panel está abierto.
|
/// Permite o bloquea el desplazamiento de la página principal mientras el panel está abierto.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_body_scroll(mut self, scrolling: offcanvas::BodyScroll) -> Self {
|
pub fn with_body_scroll(mut self, scrolling: bs::offcanvas::BodyScroll) -> Self {
|
||||||
self.body_scroll = scrolling;
|
self.body_scroll = scrolling;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indica desde qué borde de la ventana entra y se ancla el panel.
|
/// Indica desde qué borde de la ventana entra y se ancla el panel.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_placement(mut self, placement: offcanvas::Placement) -> Self {
|
pub fn with_placement(mut self, placement: bs::offcanvas::Placement) -> Self {
|
||||||
self.placement = placement;
|
self.placement = placement;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fija el estado inicial del panel (oculto o visible al cargar).
|
/// Fija el estado inicial del panel (oculto o visible al cargar).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_visibility(mut self, visibility: offcanvas::Visibility) -> Self {
|
pub fn with_visibility(mut self, visibility: bs::offcanvas::Visibility) -> Self {
|
||||||
self.visibility = visibility;
|
self.visibility = visibility;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -179,16 +179,8 @@ impl Offcanvas {
|
||||||
let id_label = util::join!(id, "-label");
|
let id_label = util::join!(id, "-label");
|
||||||
let id_target = util::join!("#", id);
|
let id_target = util::join!("#", id);
|
||||||
|
|
||||||
let body_scroll = match self.body_scroll() {
|
let body_scroll = self.body_scroll().opt_str();
|
||||||
offcanvas::BodyScroll::Disabled => None,
|
let backdrop = self.backdrop().opt_str();
|
||||||
offcanvas::BodyScroll::Enabled => Some("true"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let backdrop = match self.backdrop() {
|
|
||||||
offcanvas::Backdrop::Disabled => Some("false"),
|
|
||||||
offcanvas::Backdrop::Enabled => None,
|
|
||||||
offcanvas::Backdrop::Static => Some("static"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let title = self.title().using(cx);
|
let title = self.title().using(cx);
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ use pagetop::prelude::*;
|
||||||
// **< Backdrop >***********************************************************************************
|
// **< Backdrop >***********************************************************************************
|
||||||
|
|
||||||
/// Comportamiento de la capa de fondo (*backdrop*) de un panel
|
/// Comportamiento de la capa de fondo (*backdrop*) de un panel
|
||||||
/// [`Offcanvas`](crate::theme::Offcanvas) al deslizarse.
|
/// [`Offcanvas`](crate::theme::bs::Offcanvas) al deslizarse.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Backdrop {
|
pub enum Backdrop {
|
||||||
/// Sin capa de fondo, la página principal permanece visible e interactiva.
|
/// Sin capa de fondo, la página principal permanece visible e interactiva.
|
||||||
|
|
@ -16,10 +16,23 @@ pub enum Backdrop {
|
||||||
Static,
|
Static,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Backdrop {
|
||||||
|
/// Devuelve el valor para `data-bs-backdrop`, o `None` si es el comportamiento por defecto.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
pub const fn opt_str(self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
Self::Disabled => Some("false"),
|
||||||
|
Self::Enabled => None,
|
||||||
|
Self::Static => Some("static"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// **< BodyScroll >*********************************************************************************
|
// **< BodyScroll >*********************************************************************************
|
||||||
|
|
||||||
/// Controla si la página principal puede desplazarse al abrir un panel
|
/// Controla si la página principal puede desplazarse al abrir un panel
|
||||||
/// [`Offcanvas`](crate::theme::Offcanvas).
|
/// [`Offcanvas`](crate::theme::bs::Offcanvas).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum BodyScroll {
|
pub enum BodyScroll {
|
||||||
/// Opción por defecto, la página principal se bloquea centrando la interacción en el panel.
|
/// Opción por defecto, la página principal se bloquea centrando la interacción en el panel.
|
||||||
|
|
@ -29,9 +42,20 @@ pub enum BodyScroll {
|
||||||
Enabled,
|
Enabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BodyScroll {
|
||||||
|
/// Devuelve el valor para `data-bs-scroll`, o `None` si es el comportamiento por defecto.
|
||||||
|
#[inline]
|
||||||
|
pub const fn opt_str(self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
Self::Disabled => None,
|
||||||
|
Self::Enabled => Some("true"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// **< Placement >**********************************************************************************
|
// **< Placement >**********************************************************************************
|
||||||
|
|
||||||
/// Posición de aparición de un panel [`Offcanvas`](crate::theme::Offcanvas) al deslizarse.
|
/// Posición de aparición de un panel [`Offcanvas`](crate::theme::bs::Offcanvas) al deslizarse.
|
||||||
///
|
///
|
||||||
/// Define desde qué borde de la ventana entra y se ancla el panel.
|
/// Define desde qué borde de la ventana entra y se ancla el panel.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -51,7 +75,7 @@ impl Placement {
|
||||||
/// Devuelve la clase base asociada a la posición de aparición del panel.
|
/// Devuelve la clase base asociada a la posición de aparición del panel.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Placement::Start => "offcanvas-start",
|
Placement::Start => "offcanvas-start",
|
||||||
Placement::End => "offcanvas-end",
|
Placement::End => "offcanvas-end",
|
||||||
|
|
@ -62,23 +86,24 @@ impl Placement {
|
||||||
|
|
||||||
/// Añade la clase asociada a la posición de aparición del panel a la cadena de clases.
|
/// Añade la clase asociada a la posición de aparición del panel a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
}
|
}
|
||||||
classes.push_str(self.as_str());
|
classes.push_str(self.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase asociada a la posición de aparición del panel (reservado).
|
/// Devuelve la clase asociada a la posición de aparición del panel.
|
||||||
#[inline]
|
pub fn to_class(self) -> String {
|
||||||
pub(crate) fn to_class(self) -> String {
|
let mut class = String::new();
|
||||||
self.as_str().to_owned()
|
self.push_to(&mut class);
|
||||||
} */
|
class
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Visibility >*********************************************************************************
|
// **< Visibility >*********************************************************************************
|
||||||
|
|
||||||
/// Estado inicial de un panel [`Offcanvas`](crate::theme::Offcanvas).
|
/// Estado inicial de un panel [`Offcanvas`](crate::theme::bs::Offcanvas).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Visibility {
|
pub enum Visibility {
|
||||||
/// El panel permanece oculto desde el principio.
|
/// El panel permanece oculto desde el principio.
|
||||||
|
|
@ -91,7 +116,7 @@ pub enum Visibility {
|
||||||
impl Visibility {
|
impl Visibility {
|
||||||
/// Devuelve la clase base asociada al estado inicial del panel.
|
/// Devuelve la clase base asociada al estado inicial del panel.
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Visibility::Default => "",
|
Visibility::Default => "",
|
||||||
Visibility::Show => "show",
|
Visibility::Show => "show",
|
||||||
|
|
@ -100,7 +125,7 @@ impl Visibility {
|
||||||
|
|
||||||
/// Añade la clase asociada al estado inicial del panel a la cadena de clases.
|
/// Añade la clase asociada al estado inicial del panel a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
let class = self.as_str();
|
let class = self.as_str();
|
||||||
if class.is_empty() {
|
if class.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -111,9 +136,10 @@ impl Visibility {
|
||||||
classes.push_str(class);
|
classes.push_str(class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase asociada al estado inicial, o una cadena vacía si no aplica (reservado).
|
/// Devuelve la clase asociada al estado inicial del panel.
|
||||||
#[inline]
|
pub fn to_class(self) -> String {
|
||||||
pub(crate) fn to_class(self) -> String {
|
let mut class = String::new();
|
||||||
self.as_str().to_owned()
|
self.push_to(&mut class);
|
||||||
} */
|
class
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! Conjunto de clases para aplicar en componentes del tema.
|
//! Define clases para aplicar en componentes del tema.
|
||||||
|
|
||||||
mod color;
|
mod color;
|
||||||
pub use color::{Background, Text};
|
pub use color::{Background, Text};
|
||||||
170
extensions/pagetop-bootsier/src/theme/class/border.rs
Normal file
170
extensions/pagetop-bootsier/src/theme/class/border.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::theme::token::{ColorBorder, Opacity, ScaleSize, Side};
|
||||||
|
|
||||||
|
/// Clases para definir **bordes**.
|
||||||
|
///
|
||||||
|
/// Se puede:
|
||||||
|
///
|
||||||
|
/// - Iniciar un borde sin tamaño inicial (`Border::default()`).
|
||||||
|
/// - Crear un borde con tamaño por defecto (`Border::new()`).
|
||||||
|
/// - Ajustar el tamaño de cada **lado lógico** (`side`, respetando LTR/RTL).
|
||||||
|
/// - Asignar **un tamaño global** para todo el borde (`size`).
|
||||||
|
/// - Aplicar un **color** al borde (`ColorBorder`).
|
||||||
|
/// - Aplicar un nivel de **opacidad** (`Opacity`).
|
||||||
|
///
|
||||||
|
/// # Comportamiento aditivo / sustractivo
|
||||||
|
///
|
||||||
|
/// - **Aditivo**: basta con crear un borde sin tamaño con `class::Border::default()` para ir
|
||||||
|
/// añadiendo cada lado lógico con el tamaño deseado usando `token::ScaleSize::{One..Five}`.
|
||||||
|
///
|
||||||
|
/// - **Sustractivo**: se crea un borde con tamaño predefinido, por ejemplo usando
|
||||||
|
/// `class::Border::new()` o `class::Border::with(token::ScaleSize::Two)` y eliminar los lados
|
||||||
|
/// deseados con `token::ScaleSize::Zero`.
|
||||||
|
///
|
||||||
|
/// - **Anchos diferentes por lado**: usando `token::ScaleSize::{Zero..Five}` en cada lado deseado.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use pagetop_bootsier::theme::*;
|
||||||
|
///
|
||||||
|
/// // Borde global.
|
||||||
|
/// let b = class::Border::with(token::ScaleSize::Two);
|
||||||
|
/// assert_eq!(b.to_class(), "border-2");
|
||||||
|
///
|
||||||
|
/// // Aditivo (sólo borde superior):
|
||||||
|
/// let b = class::Border::default().with_side(token::Side::Top, token::ScaleSize::One);
|
||||||
|
/// assert_eq!(b.to_class(), "border-top-1");
|
||||||
|
///
|
||||||
|
/// // Sustractivo (borde global menos el superior):
|
||||||
|
/// let b = class::Border::new().with_side(token::Side::Top, token::ScaleSize::Zero);
|
||||||
|
/// assert_eq!(b.to_class(), "border border-top-0");
|
||||||
|
///
|
||||||
|
/// // Ancho por lado (lado lógico inicial a 2 y final a 4):
|
||||||
|
/// let b = class::Border::default()
|
||||||
|
/// .with_side(token::Side::Start, token::ScaleSize::Two)
|
||||||
|
/// .with_side(token::Side::End, token::ScaleSize::Four);
|
||||||
|
/// assert_eq!(b.to_class(), "border-end-4 border-start-2");
|
||||||
|
///
|
||||||
|
/// // Combinado (ejemplo completo):
|
||||||
|
/// let b = class::Border::new() // Borde por defecto.
|
||||||
|
/// .with_side(token::Side::Top, token::ScaleSize::Zero) // Quita borde superior.
|
||||||
|
/// .with_side(token::Side::End, token::ScaleSize::Three) // Ancho 3 para lado lógico final.
|
||||||
|
/// .with_color(token::Color::Primary)
|
||||||
|
/// .with_opacity(token::Opacity::Half);
|
||||||
|
/// assert_eq!(b.to_class(), "border border-top-0 border-end-3 border-primary border-opacity-50");
|
||||||
|
/// ```
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Border {
|
||||||
|
all : ScaleSize,
|
||||||
|
top : ScaleSize,
|
||||||
|
end : ScaleSize,
|
||||||
|
bottom : ScaleSize,
|
||||||
|
start : ScaleSize,
|
||||||
|
color : ColorBorder,
|
||||||
|
opacity: Opacity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Border {
|
||||||
|
/// Prepara un borde del tamaño predefinido. Equivale a `border` (ancho por defecto del tema).
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::with(ScaleSize::Auto)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define un borde con un tamaño global (`size`) para todos los lados.
|
||||||
|
pub fn with(size: ScaleSize) -> Self {
|
||||||
|
Self::default().with_side(Side::All, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Border BUILDER >*************************************************************************
|
||||||
|
|
||||||
|
/// Ajusta el tamaño del borde en el lado indicado (ver [`Side`](crate::theme::token::Side)).
|
||||||
|
pub fn with_side(mut self, side: Side, size: ScaleSize) -> Self {
|
||||||
|
match side {
|
||||||
|
Side::All => self.all = size,
|
||||||
|
Side::Top => self.top = size,
|
||||||
|
Side::Bottom => self.bottom = size,
|
||||||
|
Side::Start => self.start = size,
|
||||||
|
Side::End => self.end = size,
|
||||||
|
Side::LeftAndRight => {
|
||||||
|
self.start = size;
|
||||||
|
self.end = size;
|
||||||
|
}
|
||||||
|
Side::TopAndBottom => {
|
||||||
|
self.top = size;
|
||||||
|
self.bottom = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establece el color del borde.
|
||||||
|
///
|
||||||
|
/// Acepta un tipo convertible en [`ColorBorder`]. Un [`Color`](crate::theme::token::Color) se
|
||||||
|
/// convierte automáticamente en [`ColorBorder::Theme`].
|
||||||
|
pub fn with_color(mut self, color: impl Into<ColorBorder>) -> Self {
|
||||||
|
self.color = color.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establece la opacidad del borde.
|
||||||
|
pub fn with_opacity(mut self, opacity: Opacity) -> Self {
|
||||||
|
self.opacity = opacity;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Border HELPERS >*************************************************************************
|
||||||
|
|
||||||
|
/// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`, *color*
|
||||||
|
/// y *opacidad*; respetando LTR/RTL y omitiendo las definiciones vacías.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
pub fn push_to(self, classes: &mut String) {
|
||||||
|
self.all .push_to(classes, "border");
|
||||||
|
self.top .push_to(classes, "border-top");
|
||||||
|
self.end .push_to(classes, "border-end");
|
||||||
|
self.bottom .push_to(classes, "border-bottom");
|
||||||
|
self.start .push_to(classes, "border-start");
|
||||||
|
self.color .push_to(classes);
|
||||||
|
self.opacity.push_to(classes, "border");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve las clases de borde como cadena (una combinación sencilla como `"border-2"`, o una
|
||||||
|
/// más compleja como `"border border-top-0 border-end-3 border-primary border-opacity-50"`).
|
||||||
|
///
|
||||||
|
/// Si no se define ningún tamaño, color ni opacidad, devuelve `""`.
|
||||||
|
pub fn to_class(self) -> String {
|
||||||
|
let mut classes = String::new();
|
||||||
|
self.push_to(&mut classes);
|
||||||
|
classes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ScaleSize> for Border {
|
||||||
|
/// Crea un [`Border`] con un tamaño global para todos los lados.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// // Convertir explícitamente con `From::from`:
|
||||||
|
/// let b = class::Border::from(token::ScaleSize::Two);
|
||||||
|
/// assert_eq!(b.to_class(), "border-2");
|
||||||
|
///
|
||||||
|
/// // Convertir implícitamente con `into()`:
|
||||||
|
/// let b: class::Border = token::ScaleSize::Auto.into();
|
||||||
|
/// assert_eq!(b.to_class(), "border");
|
||||||
|
/// ```
|
||||||
|
fn from(size: ScaleSize) -> Self {
|
||||||
|
Self::with(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Border {
|
||||||
|
/// Permite pasar [`Border`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::attrs::Color;
|
use crate::theme::token::Color;
|
||||||
|
|
||||||
// **< ButtonColor >********************************************************************************
|
// **< ButtonColor >********************************************************************************
|
||||||
|
|
||||||
|
|
@ -22,16 +22,16 @@ enum ButtonColorStyle {
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// // Botón sólido.
|
/// // Botón sólido.
|
||||||
/// let save = Button::submit(L10n::n("Save"))
|
/// let save = bs::Button::submit(L10n::n("Save"))
|
||||||
/// .with_prop(PropsOp::add_classes(classes::ButtonColor::solid(Color::Primary)));
|
/// .with_prop(PropsOp::add_classes(class::ButtonColor::solid(token::Color::Primary)));
|
||||||
///
|
///
|
||||||
/// // Botón con contorno.
|
/// // Botón con contorno.
|
||||||
/// let cancel = Button::plain(L10n::n("Cancel"))
|
/// let cancel = bs::Button::plain(L10n::n("Cancel"))
|
||||||
/// .with_prop(PropsOp::add_classes(classes::ButtonColor::outline(Color::Secondary)));
|
/// .with_prop(PropsOp::add_classes(class::ButtonColor::outline(token::Color::Secondary)));
|
||||||
///
|
///
|
||||||
/// // Botón tipo enlace.
|
/// // Botón tipo enlace.
|
||||||
/// let back = Button::plain(L10n::n("Back"))
|
/// let back = bs::Button::plain(L10n::n("Back"))
|
||||||
/// .with_prop(PropsOp::add_classes(classes::ButtonColor::link()));
|
/// .with_prop(PropsOp::add_classes(class::ButtonColor::link()));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct ButtonColor {
|
pub struct ButtonColor {
|
||||||
|
|
@ -81,20 +81,35 @@ impl ButtonColor {
|
||||||
|
|
||||||
// **< ButtonColor HELPERS >********************************************************************
|
// **< ButtonColor HELPERS >********************************************************************
|
||||||
|
|
||||||
|
/// Añade la clase `btn-*` a la cadena de clases.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
pub fn push_to(self, classes: &mut String) {
|
||||||
|
let (prefix, suffix) = match self.style {
|
||||||
|
ButtonColorStyle::None => return,
|
||||||
|
ButtonColorStyle::Solid => ("btn-", self.color.as_str()),
|
||||||
|
ButtonColorStyle::Outline => ("btn-outline-", self.color.as_str()),
|
||||||
|
ButtonColorStyle::Link => ("btn-link", ""),
|
||||||
|
};
|
||||||
|
if !classes.is_empty() {
|
||||||
|
classes.push(' ');
|
||||||
|
}
|
||||||
|
classes.push_str(prefix);
|
||||||
|
classes.push_str(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
/// Devuelve la clase `btn-*` correspondiente al color del botón.
|
/// Devuelve la clase `btn-*` correspondiente al color del botón.
|
||||||
///
|
///
|
||||||
/// Si no se ha definido ningún estilo, devuelve `""`.
|
/// Si no se ha definido ningún estilo, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
match self.style {
|
let mut class = String::new();
|
||||||
ButtonColorStyle::None => String::new(),
|
self.push_to(&mut class);
|
||||||
ButtonColorStyle::Solid => format!("btn-{}", self.color.as_str()),
|
class
|
||||||
ButtonColorStyle::Outline => format!("btn-outline-{}", self.color.as_str()),
|
|
||||||
ButtonColorStyle::Link => "btn-link".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<CowStr> for ButtonColor {
|
impl Into<CowStr> for ButtonColor {
|
||||||
|
/// Permite pasar [`ButtonColor`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
fn into(self) -> CowStr {
|
fn into(self) -> CowStr {
|
||||||
self.to_class().into()
|
self.to_class().into()
|
||||||
}
|
}
|
||||||
|
|
@ -118,11 +133,11 @@ enum ButtonSizeVariant {
|
||||||
/// use pagetop::prelude::*;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let small = Button::submit(L10n::n("Save"))
|
/// let small = bs::Button::submit(L10n::n("Save"))
|
||||||
/// .with_prop(PropsOp::add_classes(classes::ButtonSize::small()));
|
/// .with_prop(PropsOp::add_classes(class::ButtonSize::small()));
|
||||||
///
|
///
|
||||||
/// let large = Button::submit(L10n::n("Save"))
|
/// let large = bs::Button::submit(L10n::n("Save"))
|
||||||
/// .with_prop(PropsOp::add_classes(classes::ButtonSize::large()));
|
/// .with_prop(PropsOp::add_classes(class::ButtonSize::large()));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct ButtonSize {
|
pub struct ButtonSize {
|
||||||
|
|
@ -151,19 +166,32 @@ impl ButtonSize {
|
||||||
|
|
||||||
// **< ButtonSize HELPERS >*********************************************************************
|
// **< ButtonSize HELPERS >*********************************************************************
|
||||||
|
|
||||||
|
/// Añade la clase `btn-sm` o `btn-lg` a la cadena de clases.
|
||||||
|
#[inline]
|
||||||
|
pub fn push_to(self, classes: &mut String) {
|
||||||
|
let class = match self.size {
|
||||||
|
ButtonSizeVariant::None => return,
|
||||||
|
ButtonSizeVariant::Small => "btn-sm",
|
||||||
|
ButtonSizeVariant::Large => "btn-lg",
|
||||||
|
};
|
||||||
|
if !classes.is_empty() {
|
||||||
|
classes.push(' ');
|
||||||
|
}
|
||||||
|
classes.push_str(class);
|
||||||
|
}
|
||||||
|
|
||||||
/// Devuelve la clase `btn-sm` o `btn-lg` correspondiente al tamaño del botón.
|
/// Devuelve la clase `btn-sm` o `btn-lg` correspondiente al tamaño del botón.
|
||||||
///
|
///
|
||||||
/// Si no se ha definido ningún tamaño, devuelve `""`.
|
/// Si no se ha definido ningún tamaño, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
match self.size {
|
let mut class = String::new();
|
||||||
ButtonSizeVariant::None => String::new(),
|
self.push_to(&mut class);
|
||||||
ButtonSizeVariant::Small => "btn-sm".to_owned(),
|
class
|
||||||
ButtonSizeVariant::Large => "btn-lg".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<CowStr> for ButtonSize {
|
impl Into<CowStr> for ButtonSize {
|
||||||
|
/// Permite pasar [`ButtonSize`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
fn into(self) -> CowStr {
|
fn into(self) -> CowStr {
|
||||||
self.to_class().into()
|
self.to_class().into()
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::attrs::{ColorBg, ColorText, Opacity};
|
use crate::theme::token::{ColorBg, ColorText, Opacity};
|
||||||
|
|
||||||
// **< Background >*********************************************************************************
|
// **< Background >*********************************************************************************
|
||||||
|
|
||||||
|
|
@ -8,27 +8,28 @@ use crate::theme::attrs::{ColorBg, ColorText, Opacity};
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// // Sin clases.
|
/// // Sin clases.
|
||||||
/// let s = classes::Background::new();
|
/// let s = class::Background::new();
|
||||||
/// assert_eq!(s.to_class(), "");
|
/// assert_eq!(s.to_class(), "");
|
||||||
///
|
///
|
||||||
/// // Sólo color de fondo.
|
/// // Sólo color de fondo (forma corta con Color).
|
||||||
/// let s = classes::Background::with(ColorBg::Theme(Color::Primary));
|
/// let s = class::Background::with(token::Color::Primary);
|
||||||
/// assert_eq!(s.to_class(), "bg-primary");
|
/// assert_eq!(s.to_class(), "bg-primary");
|
||||||
///
|
///
|
||||||
/// // Color más opacidad.
|
/// // Color más opacidad.
|
||||||
/// let s = classes::Background::with(ColorBg::BodySecondary).with_opacity(Opacity::Half);
|
/// let s = class::Background::with(token::ColorBg::BodySecondary)
|
||||||
|
/// .with_opacity(token::Opacity::Half);
|
||||||
/// assert_eq!(s.to_class(), "bg-body-secondary bg-opacity-50");
|
/// assert_eq!(s.to_class(), "bg-body-secondary bg-opacity-50");
|
||||||
///
|
///
|
||||||
/// // Usando `From<ColorBg>`.
|
/// // Usando `From<ColorBg>`.
|
||||||
/// let s: classes::Background = ColorBg::Black.into();
|
/// let s: class::Background = token::ColorBg::Black.into();
|
||||||
/// assert_eq!(s.to_class(), "bg-black");
|
/// assert_eq!(s.to_class(), "bg-black");
|
||||||
///
|
///
|
||||||
/// // Usando `From<(ColorBg, Opacity)>`.
|
/// // Usando `From<(ColorBg, Opacity)>`.
|
||||||
/// let s: classes::Background = (ColorBg::White, Opacity::SemiTransparent).into();
|
/// let s: class::Background = (token::ColorBg::White, token::Opacity::SemiTransparent).into();
|
||||||
/// assert_eq!(s.to_class(), "bg-white bg-opacity-25");
|
/// assert_eq!(s.to_class(), "bg-white bg-opacity-25");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -44,15 +45,32 @@ impl Background {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un estilo fijando el color de fondo (`bg-*`).
|
/// Crea un estilo fijando el color de fondo (`bg-*`).
|
||||||
pub fn with(color: ColorBg) -> Self {
|
///
|
||||||
|
/// Acepta cualquier tipo convertible en [`ColorBg`]. Un [`Color`](crate::theme::token::Color)
|
||||||
|
/// se convierte automáticamente en [`ColorBg::Theme`]:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// // Forma corta con Color:
|
||||||
|
/// let s = class::Background::with(token::Color::Primary);
|
||||||
|
/// assert_eq!(s.to_class(), "bg-primary");
|
||||||
|
///
|
||||||
|
/// // Forma explícita para variantes no temáticas:
|
||||||
|
/// let s = class::Background::with(token::ColorBg::Body);
|
||||||
|
/// assert_eq!(s.to_class(), "bg-body");
|
||||||
|
/// ```
|
||||||
|
pub fn with(color: impl Into<ColorBg>) -> Self {
|
||||||
Self::default().with_color(color)
|
Self::default().with_color(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Background BUILDER >*********************************************************************
|
// **< Background BUILDER >*********************************************************************
|
||||||
|
|
||||||
/// Establece el color de fondo (`bg-*`).
|
/// Establece el color de fondo (`bg-*`).
|
||||||
pub fn with_color(mut self, color: ColorBg) -> Self {
|
///
|
||||||
self.color = color;
|
/// Acepta cualquier tipo convertible en [`ColorBg`]. Un [`Color`](crate::theme::token::Color)
|
||||||
|
/// se convierte automáticamente en [`ColorBg::Theme`].
|
||||||
|
pub fn with_color(mut self, color: impl Into<ColorBg>) -> Self {
|
||||||
|
self.color = color.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,35 +82,34 @@ impl Background {
|
||||||
|
|
||||||
// **< Background HELPERS >*********************************************************************
|
// **< Background HELPERS >*********************************************************************
|
||||||
|
|
||||||
/// Añade las clases de fondo a la cadena de clases.
|
/// Concatena, en este orden, color del fondo (`bg-*`) y opacidad (`bg-opacity-*`), omitiendo
|
||||||
///
|
/// los fragmentos vacíos.
|
||||||
/// Concatena, en este orden, color del fondo (`bg-*`) y opacidad (`bg-opacity-*`),
|
|
||||||
/// omitiendo los fragmentos vacíos.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
self.color.push_class(classes);
|
self.color.push_to(classes);
|
||||||
self.opacity.push_class(classes, "bg");
|
self.opacity.push_to(classes, "bg");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve las clases de fondo como cadena (`"bg-primary"`, `"bg-body-secondary bg-opacity-50"`, etc.).
|
/// Devuelve las clases de fondo como cadena (`"bg-primary"`,
|
||||||
|
/// `"bg-body-secondary bg-opacity-50"`, etc.).
|
||||||
///
|
///
|
||||||
/// Si no se define ni color ni opacidad, devuelve `""`.
|
/// Si no se define ni color ni opacidad, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut classes = String::new();
|
let mut classes = String::new();
|
||||||
self.push_class(&mut classes);
|
self.push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(ColorBg, Opacity)> for Background {
|
impl From<(ColorBg, Opacity)> for Background {
|
||||||
/// Atajo para crear un [`classes::Background`](crate::theme::classes::Background) a partir del color de fondo y
|
/// Crea la clase para un [`Background`](crate::theme::class::Background) a partir del color de
|
||||||
/// la opacidad.
|
/// fondo y la opacidad.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let s: classes::Background = (ColorBg::White, Opacity::SemiTransparent).into();
|
/// let s: class::Background = (token::ColorBg::White, token::Opacity::SemiTransparent).into();
|
||||||
/// assert_eq!(s.to_class(), "bg-white bg-opacity-25");
|
/// assert_eq!(s.to_class(), "bg-white bg-opacity-25");
|
||||||
/// ```
|
/// ```
|
||||||
fn from((color, opacity): (ColorBg, Opacity)) -> Self {
|
fn from((color, opacity): (ColorBg, Opacity)) -> Self {
|
||||||
|
|
@ -101,13 +118,14 @@ impl From<(ColorBg, Opacity)> for Background {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ColorBg> for Background {
|
impl From<ColorBg> for Background {
|
||||||
/// Atajo para crear un [`classes::Background`](crate::theme::classes::Background) a partir del color de fondo.
|
/// Crea la clase para un [`Background`](crate::theme::class::Background) a partir del color de
|
||||||
|
/// fondo.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let s: classes::Background = ColorBg::Black.into();
|
/// let s: class::Background = token::ColorBg::Black.into();
|
||||||
/// assert_eq!(s.to_class(), "bg-black");
|
/// assert_eq!(s.to_class(), "bg-black");
|
||||||
/// ```
|
/// ```
|
||||||
fn from(color: ColorBg) -> Self {
|
fn from(color: ColorBg) -> Self {
|
||||||
|
|
@ -115,33 +133,44 @@ impl From<ColorBg> for Background {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Background {
|
||||||
|
/// Permite pasar [`Background`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// **< Text >***************************************************************************************
|
// **< Text >***************************************************************************************
|
||||||
|
|
||||||
/// Clases para establecer **color/opacidad del texto**.
|
/// Clases para establecer **color/opacidad del texto**.
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// // Sin clases.
|
/// // Sin clases.
|
||||||
/// let s = classes::Text::new();
|
/// let s = class::Text::new();
|
||||||
/// assert_eq!(s.to_class(), "");
|
/// assert_eq!(s.to_class(), "");
|
||||||
///
|
///
|
||||||
/// // Sólo color del texto.
|
/// // Sólo color del texto (forma corta con Color).
|
||||||
/// let s = classes::Text::with(ColorText::Theme(Color::Primary));
|
/// let s = class::Text::with(token::Color::Primary);
|
||||||
/// assert_eq!(s.to_class(), "text-primary");
|
/// assert_eq!(s.to_class(), "text-primary");
|
||||||
///
|
///
|
||||||
/// // Color del texto y opacidad.
|
/// // Color del texto y opacidad.
|
||||||
/// let s = classes::Text::new().with_color(ColorText::White).with_opacity(Opacity::SemiTransparent);
|
/// let s = class::Text::new().with_color(token::ColorText::White)
|
||||||
|
/// .with_opacity(token::Opacity::SemiTransparent);
|
||||||
/// assert_eq!(s.to_class(), "text-white text-opacity-25");
|
/// assert_eq!(s.to_class(), "text-white text-opacity-25");
|
||||||
///
|
///
|
||||||
/// // Usando `From<ColorText>`.
|
/// // Usando `From<ColorText>`.
|
||||||
/// let s: classes::Text = ColorText::Black.into();
|
/// let s: class::Text = token::ColorText::Black.into();
|
||||||
/// assert_eq!(s.to_class(), "text-black");
|
/// assert_eq!(s.to_class(), "text-black");
|
||||||
///
|
///
|
||||||
/// // Usando `From<(ColorText, Opacity)>`.
|
/// // Usando `From<(ColorText, Opacity)>`.
|
||||||
/// let s: classes::Text = (ColorText::Theme(Color::Danger), Opacity::Opaque).into();
|
/// let s: class::Text = (
|
||||||
|
/// token::ColorText::Theme(token::Color::Danger),
|
||||||
|
/// token::Opacity::Opaque,
|
||||||
|
/// ).into();
|
||||||
/// assert_eq!(s.to_class(), "text-danger text-opacity-100");
|
/// assert_eq!(s.to_class(), "text-danger text-opacity-100");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -157,15 +186,32 @@ impl Text {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea un estilo fijando el color del texto (`text-*`).
|
/// Crea un estilo fijando el color del texto (`text-*`).
|
||||||
pub fn with(color: ColorText) -> Self {
|
///
|
||||||
|
/// Acepta cualquier tipo convertible en [`ColorText`]. Un [`Color`](crate::theme::token::Color)
|
||||||
|
/// se convierte automáticamente en [`ColorText::Theme`]:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// // Forma corta con Color:
|
||||||
|
/// let s = class::Text::with(token::Color::Danger);
|
||||||
|
/// assert_eq!(s.to_class(), "text-danger");
|
||||||
|
///
|
||||||
|
/// // Forma explícita para variantes no temáticas:
|
||||||
|
/// let s = class::Text::with(token::ColorText::Body);
|
||||||
|
/// assert_eq!(s.to_class(), "text-body");
|
||||||
|
/// ```
|
||||||
|
pub fn with(color: impl Into<ColorText>) -> Self {
|
||||||
Self::default().with_color(color)
|
Self::default().with_color(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Text BUILDER >***************************************************************************
|
// **< Text BUILDER >***************************************************************************
|
||||||
|
|
||||||
/// Establece el color del texto (`text-*`).
|
/// Establece el color del texto (`text-*`).
|
||||||
pub fn with_color(mut self, color: ColorText) -> Self {
|
///
|
||||||
self.color = color;
|
/// Acepta cualquier tipo convertible en [`ColorText`]. Un [`Color`](crate::theme::token::Color)
|
||||||
|
/// se convierte automáticamente en [`ColorText::Theme`].
|
||||||
|
pub fn with_color(mut self, color: impl Into<ColorText>) -> Self {
|
||||||
|
self.color = color.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,13 +223,11 @@ impl Text {
|
||||||
|
|
||||||
// **< Text HELPERS >***************************************************************************
|
// **< Text HELPERS >***************************************************************************
|
||||||
|
|
||||||
/// Añade las clases de texto a la cadena de clases.
|
|
||||||
///
|
|
||||||
/// Concatena, en este orden, `text-*` y `text-opacity-*`, omitiendo los fragmentos vacíos.
|
/// Concatena, en este orden, `text-*` y `text-opacity-*`, omitiendo los fragmentos vacíos.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
self.color.push_class(classes);
|
self.color.push_to(classes);
|
||||||
self.opacity.push_class(classes, "text");
|
self.opacity.push_to(classes, "text");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve las clases de texto como cadena (`"text-primary"`, `"text-white text-opacity-25"`,
|
/// Devuelve las clases de texto como cadena (`"text-primary"`, `"text-white text-opacity-25"`,
|
||||||
|
|
@ -192,20 +236,23 @@ impl Text {
|
||||||
/// Si no se define ni color ni opacidad, devuelve `""`.
|
/// Si no se define ni color ni opacidad, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut classes = String::new();
|
let mut classes = String::new();
|
||||||
self.push_class(&mut classes);
|
self.push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(ColorText, Opacity)> for Text {
|
impl From<(ColorText, Opacity)> for Text {
|
||||||
/// Atajo para crear un [`classes::Text`](crate::theme::classes::Text) a partir del color del
|
/// Crea la clase para [`Text`](crate::theme::class::Text) a partir del color del texto y su
|
||||||
/// texto y su opacidad.
|
/// opacidad.
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let s: classes::Text = (ColorText::Theme(Color::Danger), Opacity::Opaque).into();
|
/// let s: class::Text = (
|
||||||
|
/// token::ColorText::Theme(token::Color::Danger),
|
||||||
|
/// token::Opacity::Opaque,
|
||||||
|
/// ).into();
|
||||||
/// assert_eq!(s.to_class(), "text-danger text-opacity-100");
|
/// assert_eq!(s.to_class(), "text-danger text-opacity-100");
|
||||||
/// ```
|
/// ```
|
||||||
fn from((color, opacity): (ColorText, Opacity)) -> Self {
|
fn from((color, opacity): (ColorText, Opacity)) -> Self {
|
||||||
|
|
@ -214,17 +261,23 @@ impl From<(ColorText, Opacity)> for Text {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ColorText> for Text {
|
impl From<ColorText> for Text {
|
||||||
/// Atajo para crear un [`classes::Text`](crate::theme::classes::Text) a partir del color del
|
/// Crea la clase para [`Text`](crate::theme::class::Text) a partir del color del texto.
|
||||||
/// texto.
|
|
||||||
///
|
///
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// let s: classes::Text = ColorText::Black.into();
|
/// let s: class::Text = token::ColorText::Black.into();
|
||||||
/// assert_eq!(s.to_class(), "text-black");
|
/// assert_eq!(s.to_class(), "text-black");
|
||||||
/// ```
|
/// ```
|
||||||
fn from(color: ColorText) -> Self {
|
fn from(color: ColorText) -> Self {
|
||||||
Text::with(color)
|
Text::with(color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Text {
|
||||||
|
/// Permite pasar [`Text`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::BreakPoint;
|
use crate::theme::token::{BreakPoint, ScaleSize, Side};
|
||||||
use crate::theme::attrs::{ScaleSize, Side};
|
|
||||||
|
|
||||||
// **< Margin >*************************************************************************************
|
// **< Margin >*************************************************************************************
|
||||||
|
|
||||||
|
|
@ -9,16 +8,17 @@ use crate::theme::attrs::{ScaleSize, Side};
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let m = classes::Margin::with(Side::Top, ScaleSize::Three);
|
/// let m = class::Margin::with(token::Side::Top, token::ScaleSize::Three);
|
||||||
/// assert_eq!(m.to_class(), "mt-3");
|
/// assert_eq!(m.to_class(), "mt-3");
|
||||||
///
|
///
|
||||||
/// let m = classes::Margin::with(Side::Start, ScaleSize::Auto).with_breakpoint(BreakPoint::LG);
|
/// let m = class::Margin::with(token::Side::Start, token::ScaleSize::Auto)
|
||||||
|
/// .with_breakpoint(token::BreakPoint::LG);
|
||||||
/// assert_eq!(m.to_class(), "ms-lg-auto");
|
/// assert_eq!(m.to_class(), "ms-lg-auto");
|
||||||
///
|
///
|
||||||
/// let m = classes::Margin::with(Side::All, ScaleSize::None);
|
/// let m = class::Margin::with(token::Side::All, token::ScaleSize::None);
|
||||||
/// assert_eq!(m.to_class(), "");
|
/// assert_eq!(m.to_class(), "");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -49,7 +49,7 @@ impl Margin {
|
||||||
|
|
||||||
// **< Margin HELPERS >*************************************************************************
|
// **< Margin HELPERS >*************************************************************************
|
||||||
|
|
||||||
/// Devuelve el prefijo `m*` según el lado.
|
// Devuelve el prefijo `m*` según el lado.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn side_prefix(&self) -> &'static str {
|
const fn side_prefix(&self) -> &'static str {
|
||||||
|
|
@ -64,7 +64,7 @@ impl Margin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve el sufijo del tamaño (`auto`, `0`..`5`), o `None` si no define clase.
|
// Devuelve el sufijo del tamaño (`auto`, `0`..`5`), o `None` si no define clase.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn size_suffix(&self) -> Option<&'static str> {
|
const fn size_suffix(&self) -> Option<&'static str> {
|
||||||
|
|
@ -80,14 +80,28 @@ impl Margin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la clase de **margin** como cadena (`"mt-3"`, `"ms-lg-auto"`, etc.).
|
/// Añade la clase de **margin** a la cadena de clases.
|
||||||
|
pub fn push_to(self, classes: &mut String) {
|
||||||
|
if let Some(size) = self.size_suffix() {
|
||||||
|
let side = self.side_prefix();
|
||||||
|
self.breakpoint.push_to(classes, side, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la clase de *margin* como cadena (`"mt-3"`, `"ms-lg-auto"`, etc.).
|
||||||
///
|
///
|
||||||
/// Si `size` es `ScaleSize::None`, devuelve `""`.
|
/// Si `size` es `ScaleSize::None`, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let Some(size) = self.size_suffix() else {
|
let mut class = String::new();
|
||||||
return String::new();
|
self.push_to(&mut class);
|
||||||
};
|
class
|
||||||
self.breakpoint.class_with(self.side_prefix(), size)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Margin {
|
||||||
|
/// Permite pasar [`Margin`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,16 +111,17 @@ impl Margin {
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// let p = classes::Padding::with(Side::LeftAndRight, ScaleSize::Two);
|
/// let p = class::Padding::with(token::Side::LeftAndRight, token::ScaleSize::Two);
|
||||||
/// assert_eq!(p.to_class(), "px-2");
|
/// assert_eq!(p.to_class(), "px-2");
|
||||||
///
|
///
|
||||||
/// let p = classes::Padding::with(Side::End, ScaleSize::Four).with_breakpoint(BreakPoint::SM);
|
/// let p = class::Padding::with(token::Side::End, token::ScaleSize::Four)
|
||||||
|
/// .with_breakpoint(token::BreakPoint::SM);
|
||||||
/// assert_eq!(p.to_class(), "pe-sm-4");
|
/// assert_eq!(p.to_class(), "pe-sm-4");
|
||||||
///
|
///
|
||||||
/// let p = classes::Padding::with(Side::All, ScaleSize::Auto);
|
/// let p = class::Padding::with(token::Side::All, token::ScaleSize::Auto);
|
||||||
/// assert_eq!(p.to_class(), ""); // `Auto` no aplica a padding.
|
/// assert_eq!(p.to_class(), ""); // `Auto` no aplica a padding.
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
@ -137,10 +152,10 @@ impl Padding {
|
||||||
|
|
||||||
// **< Padding HELPERS >************************************************************************
|
// **< Padding HELPERS >************************************************************************
|
||||||
|
|
||||||
/// Devuelve el prefijo `p*` según el lado.
|
// Devuelve el prefijo `p*` según el lado.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn prefix(&self) -> &'static str {
|
const fn side_prefix(&self) -> &'static str {
|
||||||
match self.side {
|
match self.side {
|
||||||
Side::All => "p",
|
Side::All => "p",
|
||||||
Side::Top => "pt",
|
Side::Top => "pt",
|
||||||
|
|
@ -152,12 +167,12 @@ impl Padding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve el sufijo del tamaño (`0`..`5`), o `None` si no define clase.
|
// Devuelve el sufijo del tamaño (`0`..`5`), o None si no define clase.
|
||||||
///
|
//
|
||||||
/// Nota: `ScaleSize::Auto` **no aplica** a padding ⇒ devuelve `None`.
|
// Nota: `ScaleSize::Auto` **no aplica** a *padding* => devuelve `None`.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn suffix(&self) -> Option<&'static str> {
|
const fn size_suffix(&self) -> Option<&'static str> {
|
||||||
match self.size {
|
match self.size {
|
||||||
ScaleSize::None => None,
|
ScaleSize::None => None,
|
||||||
ScaleSize::Auto => None,
|
ScaleSize::Auto => None,
|
||||||
|
|
@ -170,13 +185,27 @@ impl Padding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la clase de **padding** como cadena (`"px-2"`, `"pe-sm-4"`, etc.).
|
/// Añade la clase de **padding** a la cadena de clases.
|
||||||
|
pub fn push_to(self, classes: &mut String) {
|
||||||
|
if let Some(size) = self.size_suffix() {
|
||||||
|
let side = self.side_prefix();
|
||||||
|
self.breakpoint.push_to(classes, side, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la clase de *padding* como cadena (`"px-2"`, `"pe-sm-4"`, etc.).
|
||||||
///
|
///
|
||||||
/// Si `size` es `ScaleSize::None` o `ScaleSize::Auto`, devuelve `""`.
|
/// Si `size` es `ScaleSize::None` o `ScaleSize::Auto`, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let Some(size) = self.suffix() else {
|
let mut class = String::new();
|
||||||
return String::new();
|
self.push_to(&mut class);
|
||||||
};
|
class
|
||||||
self.breakpoint.class_with(self.prefix(), size)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Padding {
|
||||||
|
/// Permite pasar [`Padding`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use pagetop::prelude::*;
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
use crate::theme::attrs::RoundedRadius;
|
use crate::theme::token::RoundedRadius;
|
||||||
|
|
||||||
/// Clases para definir **esquinas redondeadas**.
|
/// Clases para definir **esquinas redondeadas**.
|
||||||
///
|
///
|
||||||
|
|
@ -14,30 +14,30 @@ use crate::theme::attrs::RoundedRadius;
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// use pagetop_bootsier::theme::*;
|
/// use pagetop_bootsier::theme::*;
|
||||||
///
|
///
|
||||||
/// // Radio global:
|
/// // Radio global:
|
||||||
/// let r = classes::Rounded::with(RoundedRadius::Default);
|
/// let r = class::Rounded::with(token::RoundedRadius::Default);
|
||||||
/// assert_eq!(r.to_class(), "rounded");
|
/// assert_eq!(r.to_class(), "rounded");
|
||||||
///
|
///
|
||||||
/// // Sin redondeo:
|
/// // Sin redondeo:
|
||||||
/// let r = classes::Rounded::new();
|
/// let r = class::Rounded::new();
|
||||||
/// assert_eq!(r.to_class(), "");
|
/// assert_eq!(r.to_class(), "");
|
||||||
///
|
///
|
||||||
/// // Radio en las esquinas de un lado lógico:
|
/// // Radio en las esquinas de un lado lógico:
|
||||||
/// let r = classes::Rounded::new().with_end(RoundedRadius::Scale2);
|
/// let r = class::Rounded::new().with_end(token::RoundedRadius::Scale2);
|
||||||
/// assert_eq!(r.to_class(), "rounded-end-2");
|
/// assert_eq!(r.to_class(), "rounded-end-2");
|
||||||
///
|
///
|
||||||
/// // Radio en una esquina concreta:
|
/// // Radio en una esquina concreta:
|
||||||
/// let r = classes::Rounded::new().with_top_start(RoundedRadius::Scale3);
|
/// let r = class::Rounded::new().with_top_start(token::RoundedRadius::Scale3);
|
||||||
/// assert_eq!(r.to_class(), "rounded-top-start-3");
|
/// assert_eq!(r.to_class(), "rounded-top-start-3");
|
||||||
///
|
///
|
||||||
/// // Combinado (ejemplo completo):
|
/// // Combinado (ejemplo completo):
|
||||||
/// let r = classes::Rounded::new()
|
/// let r = class::Rounded::new()
|
||||||
/// .with_top(RoundedRadius::Default) // Añade redondeo arriba.
|
/// .with_top(token::RoundedRadius::Default) // Añade redondeo arriba.
|
||||||
/// .with_bottom_start(RoundedRadius::Scale4) // Añade una esquina redondeada concreta.
|
/// .with_bottom_start(token::RoundedRadius::Scale4) // Añade una esquina redondeada concreta.
|
||||||
/// .with_bottom_end(RoundedRadius::Circle); // Añade redondeo extremo en otra esquina.
|
/// .with_bottom_end(token::RoundedRadius::Circle); // Añade redondeo extremo en otra esquina.
|
||||||
/// assert_eq!(r.to_class(), "rounded-top rounded-bottom-start-4 rounded-bottom-end-circle");
|
/// assert_eq!(r.to_class(), "rounded-top rounded-bottom-start-4 rounded-bottom-end-circle");
|
||||||
/// ```
|
/// ```
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
|
@ -125,23 +125,21 @@ impl Rounded {
|
||||||
|
|
||||||
// **< Rounded HELPERS >************************************************************************
|
// **< Rounded HELPERS >************************************************************************
|
||||||
|
|
||||||
/// Añade las clases de redondeo a la cadena de clases.
|
|
||||||
///
|
|
||||||
/// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`,
|
/// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`,
|
||||||
/// `top-start`, `top-end`, `bottom-start` y `bottom-end`; respetando LTR/RTL y omitiendo las
|
/// `top-start`, `top-end`, `bottom-start` y `bottom-end`; respetando LTR/RTL y omitiendo las
|
||||||
/// definiciones vacías.
|
/// definiciones vacías.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub fn push_to(self, classes: &mut String) {
|
||||||
self.radius .push_class(classes, "");
|
self.radius .push_to(classes, "");
|
||||||
self.top .push_class(classes, "rounded-top");
|
self.top .push_to(classes, "rounded-top");
|
||||||
self.end .push_class(classes, "rounded-end");
|
self.end .push_to(classes, "rounded-end");
|
||||||
self.bottom .push_class(classes, "rounded-bottom");
|
self.bottom .push_to(classes, "rounded-bottom");
|
||||||
self.start .push_class(classes, "rounded-start");
|
self.start .push_to(classes, "rounded-start");
|
||||||
self.top_start .push_class(classes, "rounded-top-start");
|
self.top_start .push_to(classes, "rounded-top-start");
|
||||||
self.top_end .push_class(classes, "rounded-top-end");
|
self.top_end .push_to(classes, "rounded-top-end");
|
||||||
self.bottom_start.push_class(classes, "rounded-bottom-start");
|
self.bottom_start.push_to(classes, "rounded-bottom-start");
|
||||||
self.bottom_end .push_class(classes, "rounded-bottom-end");
|
self.bottom_end .push_to(classes, "rounded-bottom-end");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve las clases de redondeo como cadena (`"rounded"`,
|
/// Devuelve las clases de redondeo como cadena (`"rounded"`,
|
||||||
|
|
@ -150,7 +148,14 @@ impl Rounded {
|
||||||
/// Si no se define ningún radio, devuelve `""`.
|
/// Si no se define ningún radio, devuelve `""`.
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut classes = String::new();
|
let mut classes = String::new();
|
||||||
self.push_class(&mut classes);
|
self.push_to(&mut classes);
|
||||||
classes
|
classes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<CowStr> for Rounded {
|
||||||
|
/// Permite pasar [`Rounded`] directamente a [`PropsOp`](pagetop::prelude::PropsOp).
|
||||||
|
fn into(self) -> CowStr {
|
||||||
|
self.to_class().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
use crate::theme::attrs::{BorderColor, Opacity, ScaleSize, Side};
|
|
||||||
|
|
||||||
/// Clases para crear **bordes**.
|
|
||||||
///
|
|
||||||
/// Permite:
|
|
||||||
///
|
|
||||||
/// - Iniciar un borde sin tamaño inicial (`Border::default()`).
|
|
||||||
/// - Crear un borde con tamaño por defecto (`Border::new()`).
|
|
||||||
/// - Ajustar el tamaño de cada **lado lógico** (`side`, respetando LTR/RTL).
|
|
||||||
/// - Definir un tamaño **global** para todo el borde (`size`).
|
|
||||||
/// - Aplicar un **color** al borde (`BorderColor`).
|
|
||||||
/// - Aplicar un nivel de **opacidad** (`Opacity`).
|
|
||||||
///
|
|
||||||
/// # Comportamiento aditivo / sustractivo
|
|
||||||
///
|
|
||||||
/// - **Aditivo**: basta con crear un borde sin tamaño con `classes::Border::default()` para ir
|
|
||||||
/// añadiendo cada lado lógico con el tamaño deseado usando `ScaleSize::{One..Five}`.
|
|
||||||
///
|
|
||||||
/// - **Sustractivo**: se crea un borde con tamaño predefinido, p. ej. usando
|
|
||||||
/// `classes::Border::new()` o `classes::Border::with(ScaleSize::Two)` y eliminar los lados
|
|
||||||
/// deseados con `ScaleSize::Zero`.
|
|
||||||
///
|
|
||||||
/// - **Anchos diferentes por lado**: usando `ScaleSize::{Zero..Five}` en cada lado deseado.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use pagetop_bootsier::theme::*;
|
|
||||||
///
|
|
||||||
/// // Borde global.
|
|
||||||
/// let b = classes::Border::with(ScaleSize::Two);
|
|
||||||
/// assert_eq!(b.to_class(), "border-2");
|
|
||||||
///
|
|
||||||
/// // Aditivo (sólo borde superior):
|
|
||||||
/// let b = classes::Border::default().with_side(Side::Top, ScaleSize::One);
|
|
||||||
/// assert_eq!(b.to_class(), "border-top-1");
|
|
||||||
///
|
|
||||||
/// // Sustractivo (borde global menos el superior):
|
|
||||||
/// let b = classes::Border::new().with_side(Side::Top, ScaleSize::Zero);
|
|
||||||
/// assert_eq!(b.to_class(), "border border-top-0");
|
|
||||||
///
|
|
||||||
/// // Ancho por lado (lado lógico inicial a 2 y final a 4):
|
|
||||||
/// let b = classes::Border::default()
|
|
||||||
/// .with_side(Side::Start, ScaleSize::Two)
|
|
||||||
/// .with_side(Side::End, ScaleSize::Four);
|
|
||||||
/// assert_eq!(b.to_class(), "border-end-4 border-start-2");
|
|
||||||
///
|
|
||||||
/// // Combinado (ejemplo completo):
|
|
||||||
/// let b = classes::Border::new() // Borde por defecto.
|
|
||||||
/// .with_side(Side::Top, ScaleSize::Zero) // Quita borde superior.
|
|
||||||
/// .with_side(Side::End, ScaleSize::Three) // Ancho 3 para el lado lógico final.
|
|
||||||
/// .with_color(BorderColor::Theme(Color::Primary))
|
|
||||||
/// .with_opacity(Opacity::Half);
|
|
||||||
/// assert_eq!(b.to_class(), "border border-top-0 border-end-3 border-primary border-opacity-50");
|
|
||||||
/// ```
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub struct Border {
|
|
||||||
all : ScaleSize,
|
|
||||||
top : ScaleSize,
|
|
||||||
end : ScaleSize,
|
|
||||||
bottom : ScaleSize,
|
|
||||||
start : ScaleSize,
|
|
||||||
color : BorderColor,
|
|
||||||
opacity: Opacity,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Border {
|
|
||||||
/// Prepara un borde del tamaño predefinido. Equivale a `border` (ancho por defecto del tema).
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::with(ScaleSize::Auto)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Crea un borde **con un tamaño global** (`size`).
|
|
||||||
pub fn with(size: ScaleSize) -> Self {
|
|
||||||
Self::default().with_side(Side::All, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Border BUILDER >*************************************************************************
|
|
||||||
|
|
||||||
pub fn with_side(mut self, side: Side, size: ScaleSize) -> Self {
|
|
||||||
match side {
|
|
||||||
Side::All => self.all = size,
|
|
||||||
Side::Top => self.top = size,
|
|
||||||
Side::Bottom => self.bottom = size,
|
|
||||||
Side::Start => self.start = size,
|
|
||||||
Side::End => self.end = size,
|
|
||||||
Side::LeftAndRight => {
|
|
||||||
self.start = size;
|
|
||||||
self.end = size;
|
|
||||||
}
|
|
||||||
Side::TopAndBottom => {
|
|
||||||
self.top = size;
|
|
||||||
self.bottom = size;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Establece el color del borde.
|
|
||||||
pub fn with_color(mut self, color: BorderColor) -> Self {
|
|
||||||
self.color = color;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Establece la opacidad del borde.
|
|
||||||
pub fn with_opacity(mut self, opacity: Opacity) -> Self {
|
|
||||||
self.opacity = opacity;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Border HELPERS >*************************************************************************
|
|
||||||
|
|
||||||
/// Añade las clases de borde a la cadena de clases.
|
|
||||||
///
|
|
||||||
/// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`,
|
|
||||||
/// *color* y *opacidad*; respetando LTR/RTL y omitiendo las definiciones vacías.
|
|
||||||
#[rustfmt::skip]
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
|
||||||
self.all .push_class(classes, "border");
|
|
||||||
self.top .push_class(classes, "border-top");
|
|
||||||
self.end .push_class(classes, "border-end");
|
|
||||||
self.bottom .push_class(classes, "border-bottom");
|
|
||||||
self.start .push_class(classes, "border-start");
|
|
||||||
self.color .push_class(classes);
|
|
||||||
self.opacity.push_class(classes, "border");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve las clases de borde como cadena (`"border-2"`,
|
|
||||||
/// `"border border-top-0 border-end-3 border-primary border-opacity-50"`, etc.).
|
|
||||||
///
|
|
||||||
/// Si no se define ningún tamaño, color ni opacidad, devuelve `""`.
|
|
||||||
pub fn to_class(self) -> String {
|
|
||||||
let mut classes = String::new();
|
|
||||||
self.push_class(&mut classes);
|
|
||||||
classes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Atajo para crear un [`classes::Border`](crate::theme::classes::Border) a partir de un tamaño
|
|
||||||
/// [`ScaleSize`] aplicado a todo el borde.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// // Convertir explícitamente con `From::from`:
|
|
||||||
/// let b = classes::Border::from(ScaleSize::Two);
|
|
||||||
/// assert_eq!(b.to_class(), "border-2");
|
|
||||||
///
|
|
||||||
/// // Convertir implícitamente con `into()`:
|
|
||||||
/// let b: classes::Border = ScaleSize::Auto.into();
|
|
||||||
/// assert_eq!(b.to_class(), "border");
|
|
||||||
/// ```
|
|
||||||
impl From<ScaleSize> for Border {
|
|
||||||
fn from(size: ScaleSize) -> Self {
|
|
||||||
Self::with(size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
//! Definiciones para crear barras de navegación ([`Navbar`]).
|
|
||||||
//!
|
|
||||||
//! Cada [`navbar::Item`](crate::theme::navbar::Item) representa un elemento individual de la barra
|
|
||||||
//! de navegación [`Navbar`], con distintos comportamientos según su finalidad, como menús
|
|
||||||
//! [`Nav`](crate::theme::Nav) o *textos localizados* usando [`L10n`](pagetop::locale::L10n).
|
|
||||||
//!
|
|
||||||
//! También puede mostrar una marca de identidad ([`navbar::Brand`](crate::theme::navbar::Brand))
|
|
||||||
//! que identifique la compañía, producto o nombre del proyecto asociado a la solución web.
|
|
||||||
|
|
||||||
mod props;
|
|
||||||
pub use props::{Layout, Position};
|
|
||||||
|
|
||||||
mod brand;
|
|
||||||
pub use brand::Brand;
|
|
||||||
|
|
||||||
mod component;
|
|
||||||
pub use component::Navbar;
|
|
||||||
|
|
||||||
mod item;
|
|
||||||
pub use item::Item;
|
|
||||||
28
extensions/pagetop-bootsier/src/theme/token.rs
Normal file
28
extensions/pagetop-bootsier/src/theme/token.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
//! Tipos enumerados para construir clases del tema.
|
||||||
|
//!
|
||||||
|
//! Incluyen puntos de ruptura, colores, escalas de tamaño, lados, etc. Se pueden importar
|
||||||
|
//! globalmente junto al resto del tema:
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! use pagetop_bootsier::theme::*;
|
||||||
|
//!
|
||||||
|
//! let bg = class::Background::with(token::Color::Primary);
|
||||||
|
//! let border = class::Border::new()
|
||||||
|
//! .with_side(token::Side::Top, token::ScaleSize::Zero)
|
||||||
|
//! .with_color(token::Color::Danger);
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
mod breakpoint;
|
||||||
|
pub use breakpoint::BreakPoint;
|
||||||
|
|
||||||
|
mod color;
|
||||||
|
pub use color::{Color, ColorBg, ColorText, Opacity};
|
||||||
|
|
||||||
|
mod border;
|
||||||
|
pub use border::ColorBorder;
|
||||||
|
|
||||||
|
mod rounded;
|
||||||
|
pub use rounded::RoundedRadius;
|
||||||
|
|
||||||
|
mod layout;
|
||||||
|
pub use layout::{ScaleSize, Side};
|
||||||
103
extensions/pagetop-bootsier/src/theme/token/border.rs
Normal file
103
extensions/pagetop-bootsier/src/theme/token/border.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::theme::token::Color;
|
||||||
|
|
||||||
|
/// Esquema de color para los bordes ([`Border`](crate::theme::class::Border)).
|
||||||
|
///
|
||||||
|
/// - `Theme(Color)` y `Subtle(Color)` usan la paleta de colores temáticos ([`Color`]).
|
||||||
|
/// - `Black` y `White` son colores fijos independientes del tema.
|
||||||
|
/// - `Default` no genera ninguna clase.
|
||||||
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ColorBorder {
|
||||||
|
/// No define ninguna clase.
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
/// Genera la clase `border-{color}`.
|
||||||
|
Theme(Color),
|
||||||
|
/// Genera la clase `border-{color}-subtle` (un tono suavizado del color).
|
||||||
|
Subtle(Color),
|
||||||
|
/// Color negro.
|
||||||
|
Black,
|
||||||
|
/// Color blanco.
|
||||||
|
White,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorBorder {
|
||||||
|
// Devuelve el sufijo de la clase `border-*`, o `None` si no define ninguna clase.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
Self::Default => None,
|
||||||
|
Self::Theme(_) => Some(""),
|
||||||
|
Self::Subtle(_) => Some("-subtle"),
|
||||||
|
Self::Black => Some("-black"),
|
||||||
|
Self::White => Some("-white"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añade la clase `border-*` a la cadena de clases.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn push_to(self, classes: &mut String) {
|
||||||
|
if let Some(suffix) = self.suffix() {
|
||||||
|
if !classes.is_empty() {
|
||||||
|
classes.push(' ');
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Theme(c) | Self::Subtle(c) => {
|
||||||
|
classes.push_str("border-");
|
||||||
|
classes.push_str(c.as_str());
|
||||||
|
}
|
||||||
|
_ => classes.push_str("border"),
|
||||||
|
}
|
||||||
|
classes.push_str(suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la clase `border-*` correspondiente al color de borde.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// let theme = token::ColorBorder::Theme(token::Color::Primary).to_class();
|
||||||
|
/// assert_eq!(theme, "border-primary");
|
||||||
|
///
|
||||||
|
/// let subtle = token::ColorBorder::Subtle(token::Color::Warning).to_class();
|
||||||
|
/// assert_eq!(subtle, "border-warning-subtle");
|
||||||
|
///
|
||||||
|
/// let black = token::ColorBorder::Black.to_class();
|
||||||
|
/// assert_eq!(black, "border-black");
|
||||||
|
///
|
||||||
|
/// let none = token::ColorBorder::Default.to_class();
|
||||||
|
/// assert_eq!(none, "");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_class(self) -> String {
|
||||||
|
let mut class = String::new();
|
||||||
|
self.push_to(&mut class);
|
||||||
|
class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for ColorBorder {
|
||||||
|
/// Convierte un [`Color`] en [`ColorBorder::Theme`].
|
||||||
|
///
|
||||||
|
/// Permite pasar un [`Color`] directamente donde se espera un [`ColorBorder`], sin necesidad
|
||||||
|
/// de envolver el valor en [`ColorBorder::Theme`]. Es el atajo habitual para los colores
|
||||||
|
/// temáticos.
|
||||||
|
///
|
||||||
|
/// Para los demás esquemas (`Subtle`, `Black`, `White`) se sigue usando [`ColorBorder`]
|
||||||
|
/// directamente.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// let border: token::ColorBorder = token::Color::Success.into();
|
||||||
|
/// assert_eq!(border.to_class(), "border-success");
|
||||||
|
/// ```
|
||||||
|
fn from(color: Color) -> Self {
|
||||||
|
Self::Theme(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
61
extensions/pagetop-bootsier/src/theme/token/breakpoint.rs
Normal file
61
extensions/pagetop-bootsier/src/theme/token/breakpoint.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
/// Define los puntos de ruptura (*breakpoints*) para aplicar diseño *responsive*.
|
||||||
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum BreakPoint {
|
||||||
|
/// **Menos de 576px**. Dispositivos muy pequeños: teléfonos en modo vertical.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// **576px o más**. Dispositivos pequeños: teléfonos en modo horizontal.
|
||||||
|
SM,
|
||||||
|
/// **768px o más**. Dispositivos medianos: tabletas.
|
||||||
|
MD,
|
||||||
|
/// **992px o más**. Dispositivos grandes: puestos de escritorio.
|
||||||
|
LG,
|
||||||
|
/// **1200px o más**. Dispositivos muy grandes: puestos de escritorio grandes.
|
||||||
|
XL,
|
||||||
|
/// **1400px o más**. Dispositivos extragrandes: puestos de escritorio más grandes.
|
||||||
|
XXL,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BreakPoint {
|
||||||
|
/// Devuelve la identificación del punto de ruptura (`"sm"`, `"md"`, etc.), o `""` para `None`.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
pub const fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::None => "",
|
||||||
|
Self::SM => "sm",
|
||||||
|
Self::MD => "md",
|
||||||
|
Self::LG => "lg",
|
||||||
|
Self::XL => "xl",
|
||||||
|
Self::XXL => "xxl",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añade el punto de ruptura con un prefijo y un sufijo (opcional) a la cadena de clases.
|
||||||
|
//
|
||||||
|
// - Para `None`: `prefix` o `prefix-suffix` (si `suffix` no está vacío).
|
||||||
|
// - Para `SM..XXL`: `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn push_to(self, classes: &mut String, prefix: &str, suffix: &str) {
|
||||||
|
if prefix.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !classes.is_empty() {
|
||||||
|
classes.push(' ');
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::None => classes.push_str(prefix),
|
||||||
|
_ => {
|
||||||
|
classes.push_str(prefix);
|
||||||
|
classes.push('-');
|
||||||
|
classes.push_str(self.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !suffix.is_empty() {
|
||||||
|
classes.push('-');
|
||||||
|
classes.push_str(suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,9 +6,8 @@ use pagetop::prelude::*;
|
||||||
///
|
///
|
||||||
/// Equivalen a los nombres estándar definidos por Bootstrap (`primary`, `secondary`, `success`,
|
/// Equivalen a los nombres estándar definidos por Bootstrap (`primary`, `secondary`, `success`,
|
||||||
/// etc.). Este tipo enumerado sirve de referencia para componer las clases de color para el fondo
|
/// etc.). Este tipo enumerado sirve de referencia para componer las clases de color para el fondo
|
||||||
/// ([`classes::Background`](crate::theme::classes::Background)), los bordes
|
/// ([`Background`](crate::theme::class::Background)), bordes
|
||||||
/// ([`classes::Border`](crate::theme::classes::Border)) o para el texto
|
/// ([`Border`](crate::theme::class::Border)) o texto ([`Text`](crate::theme::class::Text)).
|
||||||
/// ([`classes::Text`](crate::theme::classes::Text)).
|
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -23,10 +22,10 @@ pub enum Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
/// Devuelve el nombre del color.
|
/// Devuelve el nombre del color Bootstrap (`"primary"`, `"danger"`, etc.).
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) const fn as_str(self) -> &'static str {
|
pub const fn as_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Primary => "primary",
|
Self::Primary => "primary",
|
||||||
Self::Secondary => "secondary",
|
Self::Secondary => "secondary",
|
||||||
|
|
@ -38,20 +37,6 @@ impl Color {
|
||||||
Self::Dark => "dark",
|
Self::Dark => "dark",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la clase correspondiente al color.
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(Color::Primary.to_class(), "primary");
|
|
||||||
/// assert_eq!(Color::Danger.to_class(), "danger");
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
pub fn to_class(self) -> String {
|
|
||||||
self.as_str().to_owned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Opacity >************************************************************************************
|
// **< Opacity >************************************************************************************
|
||||||
|
|
@ -59,9 +44,9 @@ impl Color {
|
||||||
/// Niveles de opacidad (`opacity-*`).
|
/// Niveles de opacidad (`opacity-*`).
|
||||||
///
|
///
|
||||||
/// Se usa normalmente para graduar la transparencia del color de fondo `bg-opacity-*`
|
/// Se usa normalmente para graduar la transparencia del color de fondo `bg-opacity-*`
|
||||||
/// ([`classes::Background`](crate::theme::classes::Background)), de los bordes `border-opacity-*`
|
/// ([`Background`](crate::theme::class::Background)), de los bordes `border-opacity-*`
|
||||||
/// ([`classes::Border`](crate::theme::classes::Border)) o del texto `text-opacity-*`
|
/// ([`Border`](crate::theme::class::Border)) o del texto `text-opacity-*`
|
||||||
/// ([`classes::Text`](crate::theme::classes::Text)).
|
/// ([`Text`](crate::theme::class::Text)).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Opacity {
|
pub enum Opacity {
|
||||||
/// No define ninguna clase.
|
/// No define ninguna clase.
|
||||||
|
|
@ -82,10 +67,7 @@ pub enum Opacity {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opacity {
|
impl Opacity {
|
||||||
const OPACITY: &str = "opacity";
|
// Devuelve el sufijo para `*opacity-*`, o `None` si no define ninguna clase.
|
||||||
const OPACITY_PREFIX: &str = "-opacity";
|
|
||||||
|
|
||||||
/// Devuelve el sufijo para `*opacity-*`, o `None` si no define ninguna clase.
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
|
@ -100,76 +82,50 @@ impl Opacity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade la opacidad a la cadena de clases usando el prefijo dado (`bg`, `border`, `text`, o
|
// Añade la opacidad a la cadena de clases usando el prefijo dado (`bg`, `border`, `text`, o
|
||||||
/// vacío para `opacity-*`).
|
// vacío para `opacity-*`).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String, prefix: &str) {
|
pub(crate) fn push_to(self, classes: &mut String, prefix: &str) {
|
||||||
if let Some(suffix) = self.suffix() {
|
if let Some(suffix) = self.suffix() {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
}
|
}
|
||||||
if prefix.is_empty() {
|
if prefix.is_empty() {
|
||||||
classes.push_str(Self::OPACITY);
|
classes.push_str("opacity");
|
||||||
} else {
|
} else {
|
||||||
classes.push_str(prefix);
|
classes.push_str(prefix);
|
||||||
classes.push_str(Self::OPACITY_PREFIX);
|
classes.push_str("-opacity");
|
||||||
}
|
}
|
||||||
classes.push_str(suffix);
|
classes.push_str(suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la clase de opacidad con el prefijo dado (`bg`, `border`, `text`, o vacío para
|
|
||||||
/// `opacity-*`).
|
|
||||||
///
|
|
||||||
/// # Ejemplos
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(Opacity::Opaque.class_with(""), "opacity-100");
|
|
||||||
/// assert_eq!(Opacity::Half.class_with("bg"), "bg-opacity-50");
|
|
||||||
/// assert_eq!(Opacity::SemiTransparent.class_with("text"), "text-opacity-25");
|
|
||||||
/// assert_eq!(Opacity::Default.class_with("bg"), "");
|
|
||||||
/// ```
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn class_with(self, prefix: &str) -> String {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
let base_len = if prefix.is_empty() {
|
|
||||||
Self::OPACITY.len()
|
|
||||||
} else {
|
|
||||||
prefix.len() + Self::OPACITY_PREFIX.len()
|
|
||||||
};
|
|
||||||
let mut class = String::with_capacity(base_len + suffix.len());
|
|
||||||
if prefix.is_empty() {
|
|
||||||
class.push_str(Self::OPACITY);
|
|
||||||
} else {
|
|
||||||
class.push_str(prefix);
|
|
||||||
class.push_str(Self::OPACITY_PREFIX);
|
|
||||||
}
|
|
||||||
class.push_str(suffix);
|
|
||||||
return class;
|
|
||||||
}
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve la clase de opacidad `opacity-*`.
|
/// Devuelve la clase de opacidad `opacity-*`.
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// assert_eq!(Opacity::Opaque.to_class(), "opacity-100");
|
/// assert_eq!(token::Opacity::Opaque.to_class(), "opacity-100");
|
||||||
/// assert_eq!(Opacity::Half.to_class(), "opacity-50");
|
/// assert_eq!(token::Opacity::Half.to_class(), "opacity-50");
|
||||||
/// assert_eq!(Opacity::Default.to_class(), "");
|
/// assert_eq!(token::Opacity::Default.to_class(), "");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
self.class_with("")
|
let mut class = String::new();
|
||||||
|
self.push_to(&mut class, "");
|
||||||
|
class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< ColorBg >************************************************************************************
|
// **< ColorBg >************************************************************************************
|
||||||
|
|
||||||
/// Esquema de color para el fondo.
|
/// Esquema de color para el fondo ([`Background`](crate::theme::class::Background)).
|
||||||
|
///
|
||||||
|
/// - `Body`, `BodySecondary` y `BodyTertiary` siguen el esquema del tema (claro/oscuro).
|
||||||
|
/// - `Theme(Color)` y `Subtle(Color)` usan la paleta de colores temáticos ([`Color`]).
|
||||||
|
/// - `Black`, `White`, `Transparent` son colores fijos independientes del tema.
|
||||||
|
/// - `Default` no genera ninguna clase.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ColorBg {
|
pub enum ColorBg {
|
||||||
/// No define ninguna clase.
|
/// No define ninguna clase.
|
||||||
|
|
@ -189,15 +145,12 @@ pub enum ColorBg {
|
||||||
Black,
|
Black,
|
||||||
/// Color blanco.
|
/// Color blanco.
|
||||||
White,
|
White,
|
||||||
/// No aplica ningún color de fondo (`bg-transparent`).
|
/// Fondo transparente (`bg-transparent`).
|
||||||
Transparent,
|
Transparent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorBg {
|
impl ColorBg {
|
||||||
const BG: &str = "bg";
|
// Devuelve el sufijo de la clase `bg-*`, o `None` si no define ninguna clase.
|
||||||
const BG_PREFIX: &str = "bg-";
|
|
||||||
|
|
||||||
/// Devuelve el sufijo de la clase `bg-*`, o `None` si no define ninguna clase.
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
|
@ -214,19 +167,19 @@ impl ColorBg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade la clase de fondo `bg-*` a la cadena de clases.
|
// Añade la clase de fondo `bg-*` a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub(crate) fn push_to(self, classes: &mut String) {
|
||||||
if let Some(suffix) = self.suffix() {
|
if let Some(suffix) = self.suffix() {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Self::Theme(c) | Self::Subtle(c) => {
|
Self::Theme(c) | Self::Subtle(c) => {
|
||||||
classes.push_str(Self::BG_PREFIX);
|
classes.push_str("bg-");
|
||||||
classes.push_str(c.as_str());
|
classes.push_str(c.as_str());
|
||||||
}
|
}
|
||||||
_ => classes.push_str(Self::BG),
|
_ => classes.push_str("bg"),
|
||||||
}
|
}
|
||||||
classes.push_str(suffix);
|
classes.push_str(suffix);
|
||||||
}
|
}
|
||||||
|
|
@ -236,24 +189,60 @@ impl ColorBg {
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// assert_eq!(ColorBg::Body.to_class(), "bg-body");
|
/// let body = token::ColorBg::Body.to_class();
|
||||||
/// assert_eq!(ColorBg::Theme(Color::Primary).to_class(), "bg-primary");
|
/// assert_eq!(body, "bg-body");
|
||||||
/// assert_eq!(ColorBg::Subtle(Color::Warning).to_class(), "bg-warning-subtle");
|
///
|
||||||
/// assert_eq!(ColorBg::Transparent.to_class(), "bg-transparent");
|
/// let theme = token::ColorBg::Theme(token::Color::Primary).to_class();
|
||||||
/// assert_eq!(ColorBg::Default.to_class(), "");
|
/// assert_eq!(theme, "bg-primary");
|
||||||
|
///
|
||||||
|
/// let subtle = token::ColorBg::Subtle(token::Color::Warning).to_class();
|
||||||
|
/// assert_eq!(subtle, "bg-warning-subtle");
|
||||||
|
///
|
||||||
|
/// let transparent = token::ColorBg::Transparent.to_class();
|
||||||
|
/// assert_eq!(transparent, "bg-transparent");
|
||||||
|
///
|
||||||
|
/// let none = token::ColorBg::Default.to_class();
|
||||||
|
/// assert_eq!(none, "");
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut class = String::new();
|
let mut class = String::new();
|
||||||
self.push_class(&mut class);
|
self.push_to(&mut class);
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Color> for ColorBg {
|
||||||
|
/// Convierte un [`Color`] en [`ColorBg::Theme`].
|
||||||
|
///
|
||||||
|
/// Permite pasar un [`Color`] directamente donde se espera un [`ColorBg`], sin necesidad de
|
||||||
|
/// envolver el valor en [`ColorBg::Theme`]. Es el atajo habitual para los colores temáticos.
|
||||||
|
///
|
||||||
|
/// Para los demás esquemas (`Body`, `Subtle`, `Black`, etc.) se sigue usando [`ColorBg`]
|
||||||
|
/// directamente.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// let bg: token::ColorBg = token::Color::Primary.into();
|
||||||
|
/// assert_eq!(bg.to_class(), "bg-primary");
|
||||||
|
/// ```
|
||||||
|
fn from(color: Color) -> Self {
|
||||||
|
Self::Theme(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// **< ColorText >**********************************************************************************
|
// **< ColorText >**********************************************************************************
|
||||||
|
|
||||||
/// Esquema de color para el texto.
|
/// Esquema de color para el texto ([`Text`](crate::theme::class::Text)).
|
||||||
|
///
|
||||||
|
/// - `Body`, `BodyEmphasis`, `BodySecondary` y `BodyTertiary` siguen el tema (claro/oscuro).
|
||||||
|
/// - `Theme(Color)` y `Emphasis(Color)` usan la paleta de colores temáticos ([`Color`]).
|
||||||
|
/// - `Black` y `White` son colores fijos independientes del tema.
|
||||||
|
/// - `Default` no genera ninguna clase.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ColorText {
|
pub enum ColorText {
|
||||||
/// No define ninguna clase.
|
/// No define ninguna clase.
|
||||||
|
|
@ -261,7 +250,7 @@ pub enum ColorText {
|
||||||
Default,
|
Default,
|
||||||
/// Color predefinido del tema (`text-body`).
|
/// Color predefinido del tema (`text-body`).
|
||||||
Body,
|
Body,
|
||||||
/// Color predefinido del tema (`text-body-emphasis`).
|
/// Color de mayor contraste según el tema (`text-body-emphasis`).
|
||||||
BodyEmphasis,
|
BodyEmphasis,
|
||||||
/// Color predefinido del tema (`text-body-secondary`).
|
/// Color predefinido del tema (`text-body-secondary`).
|
||||||
BodySecondary,
|
BodySecondary,
|
||||||
|
|
@ -278,10 +267,7 @@ pub enum ColorText {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorText {
|
impl ColorText {
|
||||||
const TEXT: &str = "text";
|
// Devuelve el sufijo de la clase `text-*`, o `None` si no define ninguna clase.
|
||||||
const TEXT_PREFIX: &str = "text-";
|
|
||||||
|
|
||||||
/// Devuelve el sufijo de la clase `text-*`, o `None` si no define ninguna clase.
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
|
@ -298,19 +284,19 @@ impl ColorText {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade la clase de texto `text-*` a la cadena de clases.
|
// Añade la clase de texto `text-*` a la cadena de clases.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String) {
|
pub(crate) fn push_to(self, classes: &mut String) {
|
||||||
if let Some(suffix) = self.suffix() {
|
if let Some(suffix) = self.suffix() {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
classes.push(' ');
|
classes.push(' ');
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Self::Theme(c) | Self::Emphasis(c) => {
|
Self::Theme(c) | Self::Emphasis(c) => {
|
||||||
classes.push_str(Self::TEXT_PREFIX);
|
classes.push_str("text-");
|
||||||
classes.push_str(c.as_str());
|
classes.push_str(c.as_str());
|
||||||
}
|
}
|
||||||
_ => classes.push_str(Self::TEXT),
|
_ => classes.push_str("text"),
|
||||||
}
|
}
|
||||||
classes.push_str(suffix);
|
classes.push_str(suffix);
|
||||||
}
|
}
|
||||||
|
|
@ -320,17 +306,48 @@ impl ColorText {
|
||||||
///
|
///
|
||||||
/// # Ejemplos
|
/// # Ejemplos
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust
|
||||||
/// # use pagetop_bootsier::theme::*;
|
/// # use pagetop_bootsier::theme::*;
|
||||||
/// assert_eq!(ColorText::Body.to_class(), "text-body");
|
/// let body = token::ColorText::Body.to_class();
|
||||||
/// assert_eq!(ColorText::Theme(Color::Primary).to_class(), "text-primary");
|
/// assert_eq!(body, "text-body");
|
||||||
/// assert_eq!(ColorText::Emphasis(Color::Danger).to_class(), "text-danger-emphasis");
|
///
|
||||||
/// assert_eq!(ColorText::Black.to_class(), "text-black");
|
/// let theme = token::ColorText::Theme(token::Color::Primary).to_class();
|
||||||
/// assert_eq!(ColorText::Default.to_class(), "");
|
/// assert_eq!(theme, "text-primary");
|
||||||
|
///
|
||||||
|
/// let emphasis = token::ColorText::Emphasis(token::Color::Danger).to_class();
|
||||||
|
/// assert_eq!(emphasis, "text-danger-emphasis");
|
||||||
|
///
|
||||||
|
/// let black = token::ColorText::Black.to_class();
|
||||||
|
/// assert_eq!(black, "text-black");
|
||||||
|
///
|
||||||
|
/// let none = token::ColorText::Default.to_class();
|
||||||
|
/// assert_eq!(none, "");
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
pub fn to_class(self) -> String {
|
pub fn to_class(self) -> String {
|
||||||
let mut class = String::new();
|
let mut class = String::new();
|
||||||
self.push_class(&mut class);
|
self.push_to(&mut class);
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Color> for ColorText {
|
||||||
|
/// Convierte un [`Color`] en [`ColorText::Theme`].
|
||||||
|
///
|
||||||
|
/// Permite pasar un [`Color`] directamente donde se espera un [`ColorText`], sin necesidad de
|
||||||
|
/// envolver el valor en [`ColorText::Theme`]. Es el atajo habitual para los colores temáticos.
|
||||||
|
///
|
||||||
|
/// Para los demás esquemas (`Body`, `Emphasis`, `Black`, etc.) se sigue usando [`ColorText`]
|
||||||
|
/// directamente.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// let text: token::ColorText = token::Color::Danger.into();
|
||||||
|
/// assert_eq!(text.to_class(), "text-danger");
|
||||||
|
/// ```
|
||||||
|
fn from(color: Color) -> Self {
|
||||||
|
Self::Theme(color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,11 @@ use pagetop::prelude::*;
|
||||||
|
|
||||||
// **< ScaleSize >**********************************************************************************
|
// **< ScaleSize >**********************************************************************************
|
||||||
|
|
||||||
/// Escala discreta de tamaños para definir clases utilitarias.
|
/// Escala discreta de tamaños para clases utilitarias de Bootstrap.
|
||||||
|
///
|
||||||
|
/// Se usa como parámetro de tamaño en [`Border`](crate::theme::class::Border),
|
||||||
|
/// [`Margin`](crate::theme::class::Margin) y [`Padding`](crate::theme::class::Padding). La
|
||||||
|
/// variante `Auto` no aplica en `Padding`.
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum ScaleSize {
|
pub enum ScaleSize {
|
||||||
/// Sin tamaño (no define ninguna clase).
|
/// Sin tamaño (no define ninguna clase).
|
||||||
|
|
@ -25,8 +29,8 @@ pub enum ScaleSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScaleSize {
|
impl ScaleSize {
|
||||||
/// Devuelve el sufijo para el tamaño (`"-0"`, `"-1"`, etc.), o `None` si no define ninguna
|
// Devuelve el sufijo de tamaño (`"-0"`, `"-1"`, etc.), o `None` si no define ninguna clase.
|
||||||
/// clase, o `""` para el tamaño automático.
|
// `Auto` devuelve `Some("")` para que `push_to` emita sólo el prefijo, sin sufijo de tamaño.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn suffix(self) -> Option<&'static str> {
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
|
@ -42,9 +46,9 @@ impl ScaleSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade el tamaño a la cadena de clases usando el prefijo dado.
|
// Añade el tamaño a la cadena de clases usando el prefijo dado.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_class(self, classes: &mut String, prefix: &str) {
|
pub(crate) fn push_to(self, classes: &mut String, prefix: &str) {
|
||||||
if !prefix.is_empty() {
|
if !prefix.is_empty() {
|
||||||
if let Some(suffix) = self.suffix() {
|
if let Some(suffix) = self.suffix() {
|
||||||
if !classes.is_empty() {
|
if !classes.is_empty() {
|
||||||
|
|
@ -55,35 +59,14 @@ impl ScaleSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Devuelve la clase del tamaño para el prefijo, o una cadena vacía si no aplica (reservado).
|
|
||||||
///
|
|
||||||
/// # Ejemplo
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// # use pagetop_bootsier::theme::*;
|
|
||||||
/// assert_eq!(ScaleSize::Auto.class_with("border"), "border");
|
|
||||||
/// assert_eq!(ScaleSize::Zero.class_with("m"), "m-0");
|
|
||||||
/// assert_eq!(ScaleSize::Three.class_with("p"), "p-3");
|
|
||||||
/// assert_eq!(ScaleSize::None.class_with("border"), "");
|
|
||||||
/// ```
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn class_with(self, prefix: &str) -> String {
|
|
||||||
if !prefix.is_empty() {
|
|
||||||
if let Some(suffix) = self.suffix() {
|
|
||||||
let mut class = String::with_capacity(prefix.len() + suffix.len());
|
|
||||||
class.push_str(prefix);
|
|
||||||
class.push_str(suffix);
|
|
||||||
return class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String::new()
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Side >***************************************************************************************
|
// **< Side >***************************************************************************************
|
||||||
|
|
||||||
/// Lados sobre los que aplicar una clase utilitaria (respetando LTR/RTL).
|
/// Lados sobre los que aplicar una clase utilitaria (respetando LTR/RTL).
|
||||||
|
///
|
||||||
|
/// Se usa como selector de lado en [`Border`](crate::theme::class::Border),
|
||||||
|
/// [`Margin`](crate::theme::class::Margin) y [`Padding`](crate::theme::class::Padding).
|
||||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Side {
|
pub enum Side {
|
||||||
/// Todos los lados.
|
/// Todos los lados.
|
||||||
84
extensions/pagetop-bootsier/src/theme/token/rounded.rs
Normal file
84
extensions/pagetop-bootsier/src/theme/token/rounded.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
/// Radio para el redondeo de esquinas ([`Rounded`](crate::theme::class::Rounded)).
|
||||||
|
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum RoundedRadius {
|
||||||
|
/// No define ninguna clase.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
/// Genera `rounded` (radio por defecto del tema).
|
||||||
|
Default,
|
||||||
|
/// Genera `rounded-0` (sin redondeo).
|
||||||
|
Zero,
|
||||||
|
/// Genera `rounded-1`.
|
||||||
|
Scale1,
|
||||||
|
/// Genera `rounded-2`.
|
||||||
|
Scale2,
|
||||||
|
/// Genera `rounded-3`.
|
||||||
|
Scale3,
|
||||||
|
/// Genera `rounded-4`.
|
||||||
|
Scale4,
|
||||||
|
/// Genera `rounded-5`.
|
||||||
|
Scale5,
|
||||||
|
/// Genera `rounded-circle`.
|
||||||
|
Circle,
|
||||||
|
/// Genera `rounded-pill`.
|
||||||
|
Pill,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoundedRadius {
|
||||||
|
// Devuelve el sufijo para `*rounded-*`, o `None` si no define ninguna clase, o `""` para el
|
||||||
|
// redondeo por defecto.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
#[inline]
|
||||||
|
const fn suffix(self) -> Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
Self::None => None,
|
||||||
|
Self::Default => Some(""),
|
||||||
|
Self::Zero => Some("-0"),
|
||||||
|
Self::Scale1 => Some("-1"),
|
||||||
|
Self::Scale2 => Some("-2"),
|
||||||
|
Self::Scale3 => Some("-3"),
|
||||||
|
Self::Scale4 => Some("-4"),
|
||||||
|
Self::Scale5 => Some("-5"),
|
||||||
|
Self::Circle => Some("-circle"),
|
||||||
|
Self::Pill => Some("-pill"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añade el redondeo de esquinas a la cadena de clases usando el prefijo dado (`rounded-top`,
|
||||||
|
// `rounded-bottom-start`, o vacío para `rounded-*`).
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn push_to(self, classes: &mut String, prefix: &str) {
|
||||||
|
if let Some(suffix) = self.suffix() {
|
||||||
|
if !classes.is_empty() {
|
||||||
|
classes.push(' ');
|
||||||
|
}
|
||||||
|
if prefix.is_empty() {
|
||||||
|
classes.push_str("rounded");
|
||||||
|
} else {
|
||||||
|
classes.push_str(prefix);
|
||||||
|
}
|
||||||
|
classes.push_str(suffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la clase `rounded-*` para el redondeo de esquinas.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop_bootsier::theme::*;
|
||||||
|
/// assert_eq!(token::RoundedRadius::Default.to_class(), "rounded");
|
||||||
|
/// assert_eq!(token::RoundedRadius::Zero.to_class(), "rounded-0");
|
||||||
|
/// assert_eq!(token::RoundedRadius::Scale3.to_class(), "rounded-3");
|
||||||
|
/// assert_eq!(token::RoundedRadius::Circle.to_class(), "rounded-circle");
|
||||||
|
/// assert_eq!(token::RoundedRadius::None.to_class(), "");
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn to_class(self) -> String {
|
||||||
|
let mut class = String::new();
|
||||||
|
self.push_to(&mut class, "");
|
||||||
|
class
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue