Compare commits
No commits in common. "fe2b2c176f652f49cb0ca64fef1ad2a60c216595" and "c3ff8a6ff83d2a60f4135f3713ba0f9cdc9a7ef1" have entirely different histories.
fe2b2c176f
...
c3ff8a6ff8
33 changed files with 471 additions and 579 deletions
|
|
@ -143,8 +143,6 @@ impl Theme for Bootsier {
|
||||||
cx: &mut Context,
|
cx: &mut Context,
|
||||||
) -> Option<Result<Markup, ComponentError>> {
|
) -> Option<Result<Markup, ComponentError>> {
|
||||||
setup_component!(component, {
|
setup_component!(component, {
|
||||||
Badge => |c| theme::bs::badge::setup(c),
|
|
||||||
Brand => |c| theme::bs::brand::setup(c),
|
|
||||||
Button => |c| theme::bs::button::setup(c),
|
Button => |c| theme::bs::button::setup(c),
|
||||||
Container => |c| theme::bs::container::setup(c),
|
Container => |c| theme::bs::container::setup(c),
|
||||||
Image => |c| theme::bs::image::setup(c),
|
Image => |c| theme::bs::image::setup(c),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
pub use pagetop::base::component::Brand;
|
|
||||||
|
|
||||||
// **< Brand SETUP >********************************************************************************
|
|
||||||
|
|
||||||
pub(crate) fn setup(brand: &mut Brand) {
|
|
||||||
brand.alter_prop(PropsOp::replace_classes("brand", "navbar-brand"));
|
|
||||||
}
|
|
||||||
|
|
@ -63,9 +63,9 @@ pub(crate) fn render(field: &Field, cx: &mut Context) -> Result<Markup, Componen
|
||||||
let strict = field.kind().is_strict();
|
let strict = field.kind().is_strict();
|
||||||
let masked = *field.kind() == Kind::StrictPassword;
|
let masked = *field.kind() == Kind::StrictPassword;
|
||||||
let autocomplete = if strict {
|
let autocomplete = if strict {
|
||||||
Some(&form::Autocomplete::Off)
|
Some(form::Autocomplete::Off)
|
||||||
} else {
|
} else {
|
||||||
field.autocomplete()
|
field.autocomplete().get()
|
||||||
};
|
};
|
||||||
|
|
||||||
// La etiqueta flotante requiere `placeholder` para animar la etiqueta.
|
// La etiqueta flotante requiere `placeholder` para animar la etiqueta.
|
||||||
|
|
@ -100,12 +100,12 @@ pub(crate) fn render(field: &Field, cx: &mut Context) -> Result<Markup, Componen
|
||||||
type=(field.kind())
|
type=(field.kind())
|
||||||
id=[input_id.as_deref()]
|
id=[input_id.as_deref()]
|
||||||
class=(input_class)
|
class=(input_class)
|
||||||
name=[field.name().as_deref()]
|
name=[field.name().get()]
|
||||||
value=[field.value().as_deref()]
|
value=[field.value().get()]
|
||||||
minlength=[field.minlength()]
|
minlength=[field.minlength().get()]
|
||||||
maxlength=[field.maxlength()]
|
maxlength=[field.maxlength().get()]
|
||||||
placeholder=[placeholder]
|
placeholder=[placeholder]
|
||||||
inputmode=[field.inputmode()]
|
inputmode=[field.inputmode().get()]
|
||||||
autocomplete=[autocomplete]
|
autocomplete=[autocomplete]
|
||||||
spellcheck=[strict.then_some("false")]
|
spellcheck=[strict.then_some("false")]
|
||||||
autocorrect=[strict.then_some("off")]
|
autocorrect=[strict.then_some("off")]
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,10 @@ pub(crate) fn render(field: &Field, cx: &mut Context) -> Result<Markup, Componen
|
||||||
select
|
select
|
||||||
id=[select_id.as_deref()]
|
id=[select_id.as_deref()]
|
||||||
class="form-select"
|
class="form-select"
|
||||||
name=[field.name().as_deref()]
|
name=[field.name().get()]
|
||||||
multiple[*field.multiple()]
|
multiple[*field.multiple()]
|
||||||
size=[field.rows()]
|
size=[field.rows().get()]
|
||||||
autocomplete=[field.autocomplete()]
|
autocomplete=[field.autocomplete().get()]
|
||||||
autofocus[*field.autofocus()]
|
autofocus[*field.autofocus()]
|
||||||
required[*field.required()]
|
required[*field.required()]
|
||||||
disabled[*field.disabled()]
|
disabled[*field.disabled()]
|
||||||
|
|
@ -97,7 +97,7 @@ pub(crate) fn render(field: &Field, cx: &mut Context) -> Result<Markup, Componen
|
||||||
@match entry {
|
@match entry {
|
||||||
form::select::Entry::Item(opt) => {
|
form::select::Entry::Item(opt) => {
|
||||||
option
|
option
|
||||||
value=(opt.value().as_deref().unwrap_or(""))
|
value=(opt.value().as_str().unwrap_or(""))
|
||||||
selected[*opt.selected()]
|
selected[*opt.selected()]
|
||||||
disabled[*opt.disabled()]
|
disabled[*opt.disabled()]
|
||||||
{
|
{
|
||||||
|
|
@ -111,7 +111,7 @@ pub(crate) fn render(field: &Field, cx: &mut Context) -> Result<Markup, Componen
|
||||||
{
|
{
|
||||||
@for opt in group.items() {
|
@for opt in group.items() {
|
||||||
option
|
option
|
||||||
value=(opt.value().as_deref().unwrap_or(""))
|
value=(opt.value().as_str().unwrap_or(""))
|
||||||
selected[*opt.selected()]
|
selected[*opt.selected()]
|
||||||
disabled[*opt.disabled()]
|
disabled[*opt.disabled()]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,18 +91,18 @@ pub(crate) fn render(field: &Textarea, cx: &mut Context) -> Result<Markup, Compo
|
||||||
textarea
|
textarea
|
||||||
id=[textarea_id.as_deref()]
|
id=[textarea_id.as_deref()]
|
||||||
class="form-control"
|
class="form-control"
|
||||||
name=[field.name().as_deref()]
|
name=[field.name().get()]
|
||||||
rows=[field.rows()]
|
rows=[field.rows().get()]
|
||||||
minlength=[field.minlength()]
|
minlength=[field.minlength().get()]
|
||||||
maxlength=[field.maxlength()]
|
maxlength=[field.maxlength().get()]
|
||||||
placeholder=[placeholder]
|
placeholder=[placeholder]
|
||||||
autocomplete=[field.autocomplete()]
|
autocomplete=[field.autocomplete().get()]
|
||||||
autofocus[*field.autofocus()]
|
autofocus[*field.autofocus()]
|
||||||
readonly[*field.readonly()]
|
readonly[*field.readonly()]
|
||||||
required[*field.required()]
|
required[*field.required()]
|
||||||
disabled[*field.disabled()]
|
disabled[*field.disabled()]
|
||||||
{
|
{
|
||||||
@if let Some(value) = field.value().as_deref() { (value) }
|
@if let Some(value) = field.value().get() { (value) }
|
||||||
}
|
}
|
||||||
@if floating { (label) }
|
@if floating { (label) }
|
||||||
@if let Some(description) = field.help_text().lookup(cx) {
|
@if let Some(description) = field.help_text().lookup(cx) {
|
||||||
|
|
|
||||||
81
extensions/pagetop-bootsier/src/theme/bs/navbar/brand.rs
Normal file
81
extensions/pagetop-bootsier/src/theme/bs/navbar/brand.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
use crate::theme::*;
|
||||||
|
|
||||||
|
/// 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:
|
||||||
|
///
|
||||||
|
/// - Si hay URL ([`with_route()`](Self::with_route)), el bloque completo actúa como enlace. Por
|
||||||
|
/// defecto enlaza a la raíz del sitio (`/`).
|
||||||
|
/// - Si no hay imagen ([`with_image()`](Self::with_image)) ni título
|
||||||
|
/// ([`with_title()`](Self::with_title)), la marca de identidad no se renderiza.
|
||||||
|
/// - El eslogan ([`with_slogan()`](Self::with_slogan)) es opcional; por defecto no tiene contenido.
|
||||||
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
|
pub struct Brand {
|
||||||
|
/// Devuelve la imagen de marca (si la hay).
|
||||||
|
image: Embed<bs::Image>,
|
||||||
|
/// Devuelve el título de la identidad de marca.
|
||||||
|
#[default(_code = "Lc::n(&global::SETTINGS.app.name)")]
|
||||||
|
title: Lc,
|
||||||
|
/// Devuelve el eslogan de la marca.
|
||||||
|
slogan: Lc,
|
||||||
|
/// Devuelve la ruta asociada a la marca (si existe).
|
||||||
|
#[default(_code = "Some(\"/\".into())")]
|
||||||
|
route: Option<Route>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Component for Brand {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
|
let image = self.image().render(cx).await;
|
||||||
|
let title = self.title().using(cx);
|
||||||
|
if title.is_empty() && image.is_empty() {
|
||||||
|
return Ok(html! {});
|
||||||
|
}
|
||||||
|
let slogan = self.slogan().using(cx);
|
||||||
|
Ok(html! {
|
||||||
|
@if let Some(route) = self.route() {
|
||||||
|
a class="navbar-brand" href=(route.resolve(cx)) { (image) (title) (slogan) }
|
||||||
|
} @else {
|
||||||
|
span class="navbar-brand" { (image) (title) (slogan) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Brand {
|
||||||
|
// **< Brand BUILDER >**************************************************************************
|
||||||
|
|
||||||
|
/// Asigna o quita la imagen de marca. Si se pasa `None`, no se mostrará.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_image(mut self, image: Option<bs::Image>) -> Self {
|
||||||
|
self.image.alter_component(image);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establece el título de la identidad de marca.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_title(mut self, title: Lc) -> Self {
|
||||||
|
self.title = title;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define el eslogan de la marca.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_slogan(mut self, slogan: Lc) -> Self {
|
||||||
|
self.slogan = slogan;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define la ruta de destino. Si es `None`, la marca no será un enlace.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_route(mut self, route: Option<Route>) -> Self {
|
||||||
|
self.route = route;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,6 @@ pub mod layout;
|
||||||
mod badge;
|
mod badge;
|
||||||
pub use badge::Badge;
|
pub use badge::Badge;
|
||||||
|
|
||||||
mod brand;
|
|
||||||
pub use brand::Brand;
|
|
||||||
|
|
||||||
pub mod breadcrumb;
|
pub mod breadcrumb;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use breadcrumb::Breadcrumb;
|
pub use breadcrumb::Breadcrumb;
|
||||||
|
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
/// Componente para mostrar la **identidad de marca** de un sitio o aplicación.
|
|
||||||
///
|
|
||||||
/// Combina una imagen, un título y un eslogan opcional, típicamente en la cabecera de la página o
|
|
||||||
/// dentro de una barra de navegación proporcionada por un tema.
|
|
||||||
///
|
|
||||||
/// - Si hay ruta ([`with_route()`]), el bloque completo actúa como enlace. Por defecto enlaza a la
|
|
||||||
/// raíz del sitio (`/`).
|
|
||||||
/// - Si no hay imagen ([`with_image()`]) ni título ([`with_title()`]), la marca de identidad no se
|
|
||||||
/// renderiza.
|
|
||||||
/// - El eslogan ([`with_slogan()`]) es opcional; por defecto no tiene contenido.
|
|
||||||
///
|
|
||||||
/// # Ejemplo
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use pagetop::prelude::*;
|
|
||||||
///
|
|
||||||
/// let brand = Brand::new()
|
|
||||||
/// .with_image(Some(Image::with(image::Source::logo(PageTopSvg::Color))))
|
|
||||||
/// .with_title(Lc::n("PageTop"))
|
|
||||||
/// .with_route(Route::from("/"));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// [`with_route()`]: Self::with_route
|
|
||||||
/// [`with_image()`]: Self::with_image
|
|
||||||
/// [`with_title()`]: Self::with_title
|
|
||||||
/// [`with_slogan()`]: Self::with_slogan
|
|
||||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
|
||||||
pub struct Brand {
|
|
||||||
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
|
|
||||||
props: Props,
|
|
||||||
/// Devuelve la imagen de marca (si la hay).
|
|
||||||
image: Embed<Image>,
|
|
||||||
/// Devuelve el título de la identidad de marca.
|
|
||||||
#[default(_code = "Lc::n(&global::SETTINGS.app.name)")]
|
|
||||||
title: Lc,
|
|
||||||
/// Devuelve el eslogan de la marca.
|
|
||||||
slogan: Lc,
|
|
||||||
/// Devuelve la ruta asociada a la marca (si existe).
|
|
||||||
#[default(_code = "Some(\"/\".into())")]
|
|
||||||
route: Option<Route>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Component for Brand {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn id(&self) -> Option<String> {
|
|
||||||
self.props.get_id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup(&mut self, _cx: &Context) {
|
|
||||||
self.alter_prop(PropsOp::prepend_classes("brand"));
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
|
||||||
let image = self.image().render(cx).await;
|
|
||||||
let title = self.title().using(cx);
|
|
||||||
if image.is_empty() && title.is_empty() {
|
|
||||||
return Ok(html! {});
|
|
||||||
}
|
|
||||||
let slogan = self.slogan().using(cx);
|
|
||||||
Ok(html! {
|
|
||||||
@if let Some(route) = self.route() {
|
|
||||||
a (self.props()) href=(route.resolve(cx)) { (image) (title) (slogan) }
|
|
||||||
} @else {
|
|
||||||
span (self.props()) { (image) (title) (slogan) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Brand {
|
|
||||||
// **< Brand BUILDER >**************************************************************************
|
|
||||||
|
|
||||||
/// Establece el identificador único del componente; igual a `with_prop(PropsOp::set_id(id))`.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_id(mut self, id: impl Into<CowStr>) -> Self {
|
|
||||||
self.props.alter_id(id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
|
||||||
self.props.alter_prop(op);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asigna o quita la imagen de marca. Si se pasa `None`, no se mostrará.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_image(mut self, image: impl Into<Option<Image>>) -> Self {
|
|
||||||
self.image.alter_component(image);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Establece el título de la identidad de marca.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_title(mut self, title: Lc) -> Self {
|
|
||||||
self.title = title;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define el eslogan de la marca.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_slogan(mut self, slogan: Lc) -> Self {
|
|
||||||
self.slogan = slogan;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define la ruta de destino. Si es `None`, la marca no será un enlace.
|
|
||||||
#[builder_fn]
|
|
||||||
pub fn with_route(mut self, route: impl Into<Option<Route>>) -> Self {
|
|
||||||
self.route = route.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -74,9 +74,9 @@ pub struct Button {
|
||||||
/// Devuelve el valor del botón.
|
/// Devuelve el valor del botón.
|
||||||
value: AttrValue,
|
value: AttrValue,
|
||||||
/// Devuelve la etiqueta del botón.
|
/// Devuelve la etiqueta del botón.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto emergente del botón (atributo `title`).
|
/// Devuelve el texto emergente del botón (atributo `title`).
|
||||||
title: Lc,
|
title: Attr<Lc>,
|
||||||
/// Devuelve si el botón recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el botón recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si el botón está deshabilitado.
|
/// Devuelve si el botón está deshabilitado.
|
||||||
|
|
@ -102,8 +102,8 @@ impl Component for Button {
|
||||||
button
|
button
|
||||||
type=(self.kind())
|
type=(self.kind())
|
||||||
(self.props())
|
(self.props())
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
value=[self.value().as_deref()]
|
value=[self.value().get()]
|
||||||
title=[self.title().lookup(cx)]
|
title=[self.title().lookup(cx)]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
disabled[*self.disabled()]
|
disabled[*self.disabled()]
|
||||||
|
|
@ -124,7 +124,7 @@ impl Button {
|
||||||
pub fn submit(label: Lc) -> Self {
|
pub fn submit(label: Lc) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ButtonAction::Submit,
|
kind: ButtonAction::Submit,
|
||||||
label,
|
label: Attr::some(label),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +135,7 @@ impl Button {
|
||||||
pub fn reset(label: Lc) -> Self {
|
pub fn reset(label: Lc) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ButtonAction::Reset,
|
kind: ButtonAction::Reset,
|
||||||
label,
|
label: Attr::some(label),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +147,7 @@ impl Button {
|
||||||
pub fn plain(label: Lc) -> Self {
|
pub fn plain(label: Lc) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ButtonAction::Plain,
|
kind: ButtonAction::Plain,
|
||||||
label,
|
label: Attr::some(label),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,17 +188,17 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del botón (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del botón (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto emergente del botón (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto emergente del botón (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_title(mut self, title: Lc) -> Self {
|
pub fn with_title(mut self, title: impl Into<Option<Lc>>) -> Self {
|
||||||
self.title = title;
|
self.title.alter_opt(title.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,9 @@ pub struct Field {
|
||||||
/// Devuelve el nombre compartido por todas las casillas del grupo.
|
/// Devuelve el nombre compartido por todas las casillas del grupo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve la etiqueta del grupo.
|
/// Devuelve la etiqueta del grupo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del grupo.
|
/// Devuelve el texto de ayuda del grupo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve las casillas del grupo.
|
/// Devuelve las casillas del grupo.
|
||||||
items: Vec<Item>,
|
items: Vec<Item>,
|
||||||
/// Devuelve si todo el grupo está deshabilitado.
|
/// Devuelve si todo el grupo está deshabilitado.
|
||||||
|
|
@ -141,7 +141,7 @@ impl Component for Field {
|
||||||
|
|
||||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
||||||
let name = self.name().as_deref().unwrap();
|
let name = self.name().get().unwrap();
|
||||||
let container_id = self.id().unwrap();
|
let container_id = self.id().unwrap();
|
||||||
|
|
||||||
Ok(html! {
|
Ok(html! {
|
||||||
|
|
@ -162,8 +162,8 @@ impl Component for Field {
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id=(&item_id)
|
id=(&item_id)
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
name=(name)
|
name=(&name)
|
||||||
value=[item.value().as_deref()]
|
value=[item.value().get()]
|
||||||
checked[*item.checked()]
|
checked[*item.checked()]
|
||||||
disabled[*item.disabled() || *self.disabled()];
|
disabled[*item.disabled() || *self.disabled()];
|
||||||
label class="form-check-label" for=(&item_id) {
|
label class="form-check-label" for=(&item_id) {
|
||||||
|
|
@ -207,17 +207,17 @@ impl Field {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del grupo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del grupo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del grupo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del grupo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ pub struct Checkbox {
|
||||||
/// Devuelve el nombre del campo.
|
/// Devuelve el nombre del campo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve la etiqueta del control.
|
/// Devuelve la etiqueta del control.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve si el control debe estar marcado/activo por defecto.
|
/// Devuelve si el control debe estar marcado/activo por defecto.
|
||||||
checked: bool,
|
checked: bool,
|
||||||
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
|
||||||
|
|
@ -98,7 +98,7 @@ impl Component for Checkbox {
|
||||||
|
|
||||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
||||||
let name = self.name().as_deref().unwrap();
|
let name = self.name().get().unwrap();
|
||||||
let container_id = self.id().unwrap();
|
let container_id = self.id().unwrap();
|
||||||
|
|
||||||
let checkbox_id = util::join!(&container_id, "-checkbox");
|
let checkbox_id = util::join!(&container_id, "-checkbox");
|
||||||
|
|
@ -111,7 +111,7 @@ impl Component for Checkbox {
|
||||||
role=[is_switch.then_some("switch")]
|
role=[is_switch.then_some("switch")]
|
||||||
id=(&checkbox_id)
|
id=(&checkbox_id)
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
name=(name)
|
name=(&name)
|
||||||
value="true"
|
value="true"
|
||||||
checked[*self.checked()]
|
checked[*self.checked()]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
|
|
@ -182,10 +182,10 @@ impl Checkbox {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del control (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del control (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ impl Component for Form {
|
||||||
(self.props())
|
(self.props())
|
||||||
action=[self.action().try_resolve(cx)]
|
action=[self.action().try_resolve(cx)]
|
||||||
method=[method]
|
method=[method]
|
||||||
accept-charset=[self.charset().as_deref()]
|
accept-charset=[self.charset().get()]
|
||||||
{
|
{
|
||||||
(self.children().render(cx).await)
|
(self.children().render(cx).await)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ pub struct Fieldset {
|
||||||
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
|
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
|
||||||
props: Props,
|
props: Props,
|
||||||
/// Devuelve la leyenda del `fieldset`.
|
/// Devuelve la leyenda del `fieldset`.
|
||||||
legend: Lc,
|
legend: Attr<Lc>,
|
||||||
/// Devuelve la descripción del `fieldset`.
|
/// Devuelve la descripción del `fieldset`.
|
||||||
description: Lc,
|
description: Attr<Lc>,
|
||||||
/// Devuelve si el `fieldset` está deshabilitado.
|
/// Devuelve si el `fieldset` está deshabilitado.
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
/// Devuelve la lista de componentes del `fieldset`.
|
/// Devuelve la lista de componentes del `fieldset`.
|
||||||
|
|
@ -84,17 +84,17 @@ impl Fieldset {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la leyenda del `fieldset` (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la leyenda del `fieldset` (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_legend(mut self, legend: Lc) -> Self {
|
pub fn with_legend(mut self, legend: impl Into<Option<Lc>>) -> Self {
|
||||||
self.legend = legend;
|
self.legend.alter_opt(legend.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la descripción del `fieldset` (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la descripción del `fieldset` (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_description(mut self, description: Lc) -> Self {
|
pub fn with_description(mut self, description: impl Into<Option<Lc>>) -> Self {
|
||||||
self.description = description;
|
self.description.alter_opt(description.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ impl Component for Hidden {
|
||||||
Ok(html! {
|
Ok(html! {
|
||||||
input
|
input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
value=[self.value().as_deref()];
|
value=[self.value().get()];
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,19 +161,17 @@ pub struct Field {
|
||||||
/// Devuelve el valor inicial del campo.
|
/// Devuelve el valor inicial del campo.
|
||||||
value: AttrValue,
|
value: AttrValue,
|
||||||
/// Devuelve la etiqueta del campo.
|
/// Devuelve la etiqueta del campo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del campo.
|
/// Devuelve el texto de ayuda del campo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve la longitud mínima permitida en caracteres.
|
/// Devuelve la longitud mínima permitida en caracteres.
|
||||||
#[getters(copy)]
|
minlength: Attr<u16>,
|
||||||
minlength: Option<u16>,
|
|
||||||
/// Devuelve la longitud máxima permitida en caracteres.
|
/// Devuelve la longitud máxima permitida en caracteres.
|
||||||
#[getters(copy)]
|
maxlength: Attr<u16>,
|
||||||
maxlength: Option<u16>,
|
|
||||||
/// Devuelve el texto indicativo del campo.
|
/// Devuelve el texto indicativo del campo.
|
||||||
placeholder: Lc,
|
placeholder: Attr<Lc>,
|
||||||
/// Devuelve la configuración de autocompletado del campo.
|
/// Devuelve la configuración de autocompletado del campo.
|
||||||
autocomplete: Option<form::Autocomplete>,
|
autocomplete: Attr<form::Autocomplete>,
|
||||||
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si el campo es de sólo lectura.
|
/// Devuelve si el campo es de sólo lectura.
|
||||||
|
|
@ -185,8 +183,7 @@ pub struct Field {
|
||||||
/// Devuelve si el campo se muestra como texto plano sin bordes ni fondo.
|
/// Devuelve si el campo se muestra como texto plano sin bordes ni fondo.
|
||||||
plaintext: bool,
|
plaintext: bool,
|
||||||
/// Devuelve la sugerencia de teclado virtual para el campo.
|
/// Devuelve la sugerencia de teclado virtual para el campo.
|
||||||
#[getters(copy)]
|
inputmode: Attr<Mode>,
|
||||||
inputmode: Option<Mode>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
@ -202,7 +199,7 @@ impl Component for Field {
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
if let Some(container_id) = self
|
if let Some(container_id) = self
|
||||||
.id()
|
.id()
|
||||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
.or_else(|| self.name().get().map(|n| util::join!("edit-", n)))
|
||||||
{
|
{
|
||||||
self.alter_prop(PropsOp::ensure_id(container_id));
|
self.alter_prop(PropsOp::ensure_id(container_id));
|
||||||
}
|
}
|
||||||
|
|
@ -228,9 +225,9 @@ impl Component for Field {
|
||||||
let strict = self.kind().is_strict();
|
let strict = self.kind().is_strict();
|
||||||
let masked = *self.kind() == Kind::StrictPassword;
|
let masked = *self.kind() == Kind::StrictPassword;
|
||||||
let autocomplete = if strict {
|
let autocomplete = if strict {
|
||||||
Some(&form::Autocomplete::Off)
|
Some(form::Autocomplete::Off)
|
||||||
} else {
|
} else {
|
||||||
self.autocomplete()
|
self.autocomplete().get()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(html! {
|
Ok(html! {
|
||||||
|
|
@ -252,12 +249,12 @@ impl Component for Field {
|
||||||
type=(self.kind())
|
type=(self.kind())
|
||||||
id=[input_id.as_deref()]
|
id=[input_id.as_deref()]
|
||||||
class=(input_class)
|
class=(input_class)
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
value=[self.value().as_deref()]
|
value=[self.value().get()]
|
||||||
minlength=[self.minlength()]
|
minlength=[self.minlength().get()]
|
||||||
maxlength=[self.maxlength()]
|
maxlength=[self.maxlength().get()]
|
||||||
placeholder=[self.placeholder().lookup(cx)]
|
placeholder=[self.placeholder().lookup(cx)]
|
||||||
inputmode=[self.inputmode()]
|
inputmode=[self.inputmode().get()]
|
||||||
autocomplete=[autocomplete]
|
autocomplete=[autocomplete]
|
||||||
spellcheck=[strict.then_some("false")]
|
spellcheck=[strict.then_some("false")]
|
||||||
autocorrect=[strict.then_some("off")]
|
autocorrect=[strict.then_some("off")]
|
||||||
|
|
@ -421,41 +418,41 @@ impl Field {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del campo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la longitud mínima permitida en caracteres (`None` para no imponer mínimo).
|
/// Establece la longitud mínima permitida en caracteres (`None` para no imponer mínimo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_minlength(mut self, minlength: impl Into<Option<u16>>) -> Self {
|
pub fn with_minlength(mut self, minlength: Option<u16>) -> Self {
|
||||||
self.minlength = minlength.into();
|
self.minlength.alter_opt(minlength);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la longitud máxima permitida en caracteres (`None` para no imponer límite).
|
/// Establece la longitud máxima permitida en caracteres (`None` para no imponer límite).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_maxlength(mut self, maxlength: impl Into<Option<u16>>) -> Self {
|
pub fn with_maxlength(mut self, maxlength: Option<u16>) -> Self {
|
||||||
self.maxlength = maxlength.into();
|
self.maxlength.alter_opt(maxlength);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto indicativo del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto indicativo del campo (`None` para quitarlo).
|
||||||
///
|
///
|
||||||
/// Este texto aparece en el mismo campo y desaparece en cuanto el usuario empieza a escribir.
|
/// Este texto aparece en el mismo campo y desaparece en cuanto el usuario empieza a escribir.
|
||||||
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
|
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_placeholder(mut self, placeholder: Lc) -> Self {
|
pub fn with_placeholder(mut self, placeholder: impl Into<Option<Lc>>) -> Self {
|
||||||
self.placeholder = placeholder;
|
self.placeholder.alter_opt(placeholder.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -465,11 +462,8 @@ impl Field {
|
||||||
/// [`Autocomplete::email()`](form::Autocomplete::email) o
|
/// [`Autocomplete::email()`](form::Autocomplete::email) o
|
||||||
/// [`Autocomplete::current_password()`](form::Autocomplete::current_password)).
|
/// [`Autocomplete::current_password()`](form::Autocomplete::current_password)).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_autocomplete(
|
pub fn with_autocomplete(mut self, autocomplete: Option<form::Autocomplete>) -> Self {
|
||||||
mut self,
|
self.autocomplete.alter_opt(autocomplete);
|
||||||
autocomplete: impl Into<Option<form::Autocomplete>>,
|
|
||||||
) -> Self {
|
|
||||||
self.autocomplete = autocomplete.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,8 +510,8 @@ impl Field {
|
||||||
/// A diferencia del atributo `type` ([`form::input::Kind`]), no restringe los valores aceptados
|
/// A diferencia del atributo `type` ([`form::input::Kind`]), no restringe los valores aceptados
|
||||||
/// ni activa la validación del navegador; es sólo una sugerencia de presentación.
|
/// ni activa la validación del navegador; es sólo una sugerencia de presentación.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_inputmode(mut self, inputmode: impl Into<Option<Mode>>) -> Self {
|
pub fn with_inputmode(mut self, inputmode: Option<Mode>) -> Self {
|
||||||
self.inputmode = inputmode.into();
|
self.inputmode.alter_opt(inputmode);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,21 +35,17 @@ pub struct Number {
|
||||||
/// Devuelve el nombre del campo.
|
/// Devuelve el nombre del campo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve el valor inicial del campo.
|
/// Devuelve el valor inicial del campo.
|
||||||
#[getters(copy)]
|
value: Attr<u64>,
|
||||||
value: Option<u64>,
|
|
||||||
/// Devuelve la etiqueta del campo.
|
/// Devuelve la etiqueta del campo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del campo.
|
/// Devuelve el texto de ayuda del campo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve el valor mínimo permitido.
|
/// Devuelve el valor mínimo permitido.
|
||||||
#[getters(copy)]
|
min: Attr<u64>,
|
||||||
min: Option<u64>,
|
|
||||||
/// Devuelve el valor máximo permitido.
|
/// Devuelve el valor máximo permitido.
|
||||||
#[getters(copy)]
|
max: Attr<u64>,
|
||||||
max: Option<u64>,
|
|
||||||
/// Devuelve el incremento entre valores del campo.
|
/// Devuelve el incremento entre valores del campo.
|
||||||
#[getters(copy)]
|
step: Attr<u64>,
|
||||||
step: Option<u64>,
|
|
||||||
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si el campo es de sólo lectura.
|
/// Devuelve si el campo es de sólo lectura.
|
||||||
|
|
@ -73,7 +69,7 @@ impl Component for Number {
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
if let Some(container_id) = self
|
if let Some(container_id) = self
|
||||||
.id()
|
.id()
|
||||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
.or_else(|| self.name().get().map(|n| util::join!("edit-", n)))
|
||||||
{
|
{
|
||||||
self.alter_prop(PropsOp::ensure_id(container_id));
|
self.alter_prop(PropsOp::ensure_id(container_id));
|
||||||
}
|
}
|
||||||
|
|
@ -104,11 +100,11 @@ impl Component for Number {
|
||||||
type="number"
|
type="number"
|
||||||
id=[input_id.as_deref()]
|
id=[input_id.as_deref()]
|
||||||
class="form-control"
|
class="form-control"
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
min=[self.min()]
|
min=[self.min().get()]
|
||||||
max=[self.max()]
|
max=[self.max().get()]
|
||||||
step=[self.step()]
|
step=[self.step().get()]
|
||||||
value=[self.value()]
|
value=[self.value().get()]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
readonly[*self.readonly()]
|
readonly[*self.readonly()]
|
||||||
required[*self.required()]
|
required[*self.required()]
|
||||||
|
|
@ -150,36 +146,36 @@ impl Number {
|
||||||
|
|
||||||
/// Establece el valor inicial del campo.
|
/// Establece el valor inicial del campo.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_value(mut self, value: impl Into<Option<u64>>) -> Self {
|
pub fn with_value(mut self, value: Option<u64>) -> Self {
|
||||||
self.value = value.into();
|
self.value.alter_opt(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del campo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el valor mínimo permitido (`None` para no imponer mínimo).
|
/// Establece el valor mínimo permitido (`None` para no imponer mínimo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_min(mut self, min: impl Into<Option<u64>>) -> Self {
|
pub fn with_min(mut self, min: Option<u64>) -> Self {
|
||||||
self.min = min.into();
|
self.min.alter_opt(min);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el valor máximo permitido (`None` para no imponer máximo).
|
/// Establece el valor máximo permitido (`None` para no imponer máximo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_max(mut self, max: impl Into<Option<u64>>) -> Self {
|
pub fn with_max(mut self, max: Option<u64>) -> Self {
|
||||||
self.max = max.into();
|
self.max.alter_opt(max);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,8 +184,8 @@ impl Number {
|
||||||
/// Pasar `None` omite el atributo `step` y deja que el navegador aplique su valor por defecto
|
/// Pasar `None` omite el atributo `step` y deja que el navegador aplique su valor por defecto
|
||||||
/// (normalmente `1`).
|
/// (normalmente `1`).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_step(mut self, step: impl Into<Option<u64>>) -> Self {
|
pub fn with_step(mut self, step: Option<u64>) -> Self {
|
||||||
self.step = step.into();
|
self.step.alter_opt(step);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,9 @@ pub struct Field {
|
||||||
/// Devuelve el nombre compartido por todos los botones de opción del grupo.
|
/// Devuelve el nombre compartido por todos los botones de opción del grupo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve la etiqueta del grupo.
|
/// Devuelve la etiqueta del grupo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del grupo.
|
/// Devuelve el texto de ayuda del grupo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve las opciones del grupo.
|
/// Devuelve las opciones del grupo.
|
||||||
items: Vec<Item>,
|
items: Vec<Item>,
|
||||||
/// Devuelve si la selección de alguna opción del grupo es obligatoria.
|
/// Devuelve si la selección de alguna opción del grupo es obligatoria.
|
||||||
|
|
@ -141,7 +141,7 @@ impl Component for Field {
|
||||||
|
|
||||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||||
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
// En `setup()` se garantiza que `name` e `id` están definidos antes del renderizado.
|
||||||
let name = self.name().as_deref().unwrap();
|
let name = self.name().get().unwrap();
|
||||||
let container_id = self.id().unwrap();
|
let container_id = self.id().unwrap();
|
||||||
|
|
||||||
Ok(html! {
|
Ok(html! {
|
||||||
|
|
@ -178,8 +178,8 @@ impl Component for Field {
|
||||||
type="radio"
|
type="radio"
|
||||||
id=(&item_id)
|
id=(&item_id)
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
name=(name)
|
name=(&name)
|
||||||
value=[item.value().as_deref()]
|
value=[item.value().get()]
|
||||||
checked[checked]
|
checked[checked]
|
||||||
required[*self.required()]
|
required[*self.required()]
|
||||||
disabled[*item.disabled() || *self.disabled()];
|
disabled[*item.disabled() || *self.disabled()];
|
||||||
|
|
@ -227,17 +227,17 @@ impl Field {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del grupo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del grupo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del grupo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del grupo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,21 +36,17 @@ pub struct Range {
|
||||||
/// Devuelve el nombre del campo.
|
/// Devuelve el nombre del campo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve el valor inicial del campo.
|
/// Devuelve el valor inicial del campo.
|
||||||
#[getters(copy)]
|
value: Attr<f64>,
|
||||||
value: Option<f64>,
|
|
||||||
/// Devuelve la etiqueta del campo.
|
/// Devuelve la etiqueta del campo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del campo.
|
/// Devuelve el texto de ayuda del campo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve el valor mínimo permitido.
|
/// Devuelve el valor mínimo permitido.
|
||||||
#[getters(copy)]
|
min: Attr<f64>,
|
||||||
min: Option<f64>,
|
|
||||||
/// Devuelve el valor máximo permitido.
|
/// Devuelve el valor máximo permitido.
|
||||||
#[getters(copy)]
|
max: Attr<f64>,
|
||||||
max: Option<f64>,
|
|
||||||
/// Devuelve el incremento entre valores del campo.
|
/// Devuelve el incremento entre valores del campo.
|
||||||
#[getters(copy)]
|
step: Attr<f64>,
|
||||||
step: Option<f64>,
|
|
||||||
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el control recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si el control está deshabilitado.
|
/// Devuelve si el control está deshabilitado.
|
||||||
|
|
@ -70,7 +66,7 @@ impl Component for Range {
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
if let Some(container_id) = self
|
if let Some(container_id) = self
|
||||||
.id()
|
.id()
|
||||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
.or_else(|| self.name().get().map(|n| util::join!("edit-", n)))
|
||||||
{
|
{
|
||||||
self.alter_prop(PropsOp::ensure_id(container_id));
|
self.alter_prop(PropsOp::ensure_id(container_id));
|
||||||
};
|
};
|
||||||
|
|
@ -91,11 +87,11 @@ impl Component for Range {
|
||||||
type="range"
|
type="range"
|
||||||
id=[range_id.as_deref()]
|
id=[range_id.as_deref()]
|
||||||
class="form-range"
|
class="form-range"
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
min=[self.min()]
|
min=[self.min().get()]
|
||||||
max=[self.max()]
|
max=[self.max().get()]
|
||||||
step=[self.step()]
|
step=[self.step().get()]
|
||||||
value=[self.value()]
|
value=[self.value().get()]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
disabled[*self.disabled()];
|
disabled[*self.disabled()];
|
||||||
@if let Some(description) = self.help_text().lookup(cx) {
|
@if let Some(description) = self.help_text().lookup(cx) {
|
||||||
|
|
@ -138,22 +134,22 @@ impl Range {
|
||||||
/// Pasar `None` omite el atributo `value` y deja que el navegador aplique su valor por defecto
|
/// Pasar `None` omite el atributo `value` y deja que el navegador aplique su valor por defecto
|
||||||
/// (normalmente el punto medio del rango).
|
/// (normalmente el punto medio del rango).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_value(mut self, value: impl Into<Option<f64>>) -> Self {
|
pub fn with_value(mut self, value: Option<f64>) -> Self {
|
||||||
self.value = value.into();
|
self.value.alter_opt(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del campo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,8 +157,8 @@ impl Range {
|
||||||
///
|
///
|
||||||
/// Pasar `None` omite el atributo `min` y deja que el navegador aplique su valor por defecto.
|
/// Pasar `None` omite el atributo `min` y deja que el navegador aplique su valor por defecto.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_min(mut self, min: impl Into<Option<f64>>) -> Self {
|
pub fn with_min(mut self, min: Option<f64>) -> Self {
|
||||||
self.min = min.into();
|
self.min.alter_opt(min);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,8 +166,8 @@ impl Range {
|
||||||
///
|
///
|
||||||
/// Pasar `None` omite el atributo `max` y deja que el navegador aplique su valor por defecto.
|
/// Pasar `None` omite el atributo `max` y deja que el navegador aplique su valor por defecto.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_max(mut self, max: impl Into<Option<f64>>) -> Self {
|
pub fn with_max(mut self, max: Option<f64>) -> Self {
|
||||||
self.max = max.into();
|
self.max.alter_opt(max);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,8 +176,8 @@ impl Range {
|
||||||
/// Pasar `None` omite el atributo `step` y deja que el navegador aplique su valor por defecto
|
/// Pasar `None` omite el atributo `step` y deja que el navegador aplique su valor por defecto
|
||||||
/// (normalmente `1`).
|
/// (normalmente `1`).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_step(mut self, step: impl Into<Option<f64>>) -> Self {
|
pub fn with_step(mut self, step: Option<f64>) -> Self {
|
||||||
self.step = step.into();
|
self.step.alter_opt(step);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,18 +197,17 @@ pub struct Field {
|
||||||
/// Devuelve el nombre del campo.
|
/// Devuelve el nombre del campo.
|
||||||
name: AttrName,
|
name: AttrName,
|
||||||
/// Devuelve la etiqueta del campo.
|
/// Devuelve la etiqueta del campo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del campo.
|
/// Devuelve el texto de ayuda del campo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve las entradas de la lista (elementos individuales y grupos de elementos).
|
/// Devuelve las entradas de la lista (elementos individuales y grupos de elementos).
|
||||||
entries: Vec<Entry>,
|
entries: Vec<Entry>,
|
||||||
/// Devuelve si la lista permite selección múltiple.
|
/// Devuelve si la lista permite selección múltiple.
|
||||||
multiple: bool,
|
multiple: bool,
|
||||||
/// Devuelve el número de filas visibles de la lista de selección.
|
/// Devuelve el número de filas visibles de la lista de selección.
|
||||||
#[getters(copy)]
|
rows: Attr<u16>,
|
||||||
rows: Option<u16>,
|
|
||||||
/// Devuelve la configuración de autocompletado del campo.
|
/// Devuelve la configuración de autocompletado del campo.
|
||||||
autocomplete: Option<form::Autocomplete>,
|
autocomplete: Attr<form::Autocomplete>,
|
||||||
/// Devuelve si la lista recibe el foco automáticamente al cargar la página.
|
/// Devuelve si la lista recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si la selección de un elemento es obligatoria.
|
/// Devuelve si la selección de un elemento es obligatoria.
|
||||||
|
|
@ -230,7 +229,7 @@ impl Component for Field {
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
if let Some(container_id) = self
|
if let Some(container_id) = self
|
||||||
.id()
|
.id()
|
||||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
.or_else(|| self.name().get().map(|n| util::join!("edit-", n)))
|
||||||
{
|
{
|
||||||
self.alter_prop(PropsOp::ensure_id(container_id));
|
self.alter_prop(PropsOp::ensure_id(container_id));
|
||||||
}
|
}
|
||||||
|
|
@ -261,10 +260,10 @@ impl Component for Field {
|
||||||
select
|
select
|
||||||
id=[select_id.as_deref()]
|
id=[select_id.as_deref()]
|
||||||
class="form-select"
|
class="form-select"
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
multiple[*self.multiple()]
|
multiple[*self.multiple()]
|
||||||
size=[self.rows()]
|
size=[self.rows().get()]
|
||||||
autocomplete=[self.autocomplete()]
|
autocomplete=[self.autocomplete().get()]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
required[*self.required()]
|
required[*self.required()]
|
||||||
disabled[*self.disabled()]
|
disabled[*self.disabled()]
|
||||||
|
|
@ -273,7 +272,7 @@ impl Component for Field {
|
||||||
@match entry {
|
@match entry {
|
||||||
Entry::Item(opt) => {
|
Entry::Item(opt) => {
|
||||||
option
|
option
|
||||||
value=(opt.value().as_deref().unwrap_or(""))
|
value=(opt.value().as_str().unwrap_or(""))
|
||||||
selected[*opt.selected()]
|
selected[*opt.selected()]
|
||||||
disabled[*opt.disabled()]
|
disabled[*opt.disabled()]
|
||||||
{
|
{
|
||||||
|
|
@ -287,7 +286,7 @@ impl Component for Field {
|
||||||
{
|
{
|
||||||
@for opt in group.items() {
|
@for opt in group.items() {
|
||||||
option
|
option
|
||||||
value=(opt.value().as_deref().unwrap_or(""))
|
value=(opt.value().as_str().unwrap_or(""))
|
||||||
selected[*opt.selected()]
|
selected[*opt.selected()]
|
||||||
disabled[*opt.disabled()]
|
disabled[*opt.disabled()]
|
||||||
{
|
{
|
||||||
|
|
@ -334,17 +333,17 @@ impl Field {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del campo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -390,8 +389,8 @@ impl Field {
|
||||||
/// Es especialmente útil con selección múltiple para controlar el número de filas visibles sin
|
/// Es especialmente útil con selección múltiple para controlar el número de filas visibles sin
|
||||||
/// necesidad de recurrir al desplazamiento.
|
/// necesidad de recurrir al desplazamiento.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_rows(mut self, rows: impl Into<Option<u16>>) -> Self {
|
pub fn with_rows(mut self, rows: Option<u16>) -> Self {
|
||||||
self.rows = rows.into();
|
self.rows.alter_opt(rows);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,11 +404,8 @@ impl Field {
|
||||||
/// Usa los métodos de [`form::Autocomplete`] para los valores más habituales. Pasa `None` para
|
/// Usa los métodos de [`form::Autocomplete`] para los valores más habituales. Pasa `None` para
|
||||||
/// omitir el atributo.
|
/// omitir el atributo.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_autocomplete(
|
pub fn with_autocomplete(mut self, autocomplete: Option<form::Autocomplete>) -> Self {
|
||||||
mut self,
|
self.autocomplete.alter_opt(autocomplete);
|
||||||
autocomplete: impl Into<Option<form::Autocomplete>>,
|
|
||||||
) -> Self {
|
|
||||||
self.autocomplete = autocomplete.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,22 +40,19 @@ pub struct Textarea {
|
||||||
/// Devuelve el valor inicial del área de texto.
|
/// Devuelve el valor inicial del área de texto.
|
||||||
value: AttrValue,
|
value: AttrValue,
|
||||||
/// Devuelve la etiqueta del campo.
|
/// Devuelve la etiqueta del campo.
|
||||||
label: Lc,
|
label: Attr<Lc>,
|
||||||
/// Devuelve el texto de ayuda del campo.
|
/// Devuelve el texto de ayuda del campo.
|
||||||
help_text: Lc,
|
help_text: Attr<Lc>,
|
||||||
/// Devuelve el número de filas visibles del área de texto.
|
/// Devuelve el número de filas visibles del área de texto.
|
||||||
#[getters(copy)]
|
rows: Attr<u16>,
|
||||||
rows: Option<u16>,
|
|
||||||
/// Devuelve la longitud mínima permitida en caracteres.
|
/// Devuelve la longitud mínima permitida en caracteres.
|
||||||
#[getters(copy)]
|
minlength: Attr<u16>,
|
||||||
minlength: Option<u16>,
|
|
||||||
/// Devuelve la longitud máxima permitida en caracteres.
|
/// Devuelve la longitud máxima permitida en caracteres.
|
||||||
#[getters(copy)]
|
maxlength: Attr<u16>,
|
||||||
maxlength: Option<u16>,
|
|
||||||
/// Devuelve el texto indicativo del área de texto.
|
/// Devuelve el texto indicativo del área de texto.
|
||||||
placeholder: Lc,
|
placeholder: Attr<Lc>,
|
||||||
/// Devuelve la configuración de autocompletado del campo.
|
/// Devuelve la configuración de autocompletado del campo.
|
||||||
autocomplete: Option<form::Autocomplete>,
|
autocomplete: Attr<form::Autocomplete>,
|
||||||
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
/// Devuelve si el campo recibe el foco automáticamente al cargar la página.
|
||||||
autofocus: bool,
|
autofocus: bool,
|
||||||
/// Devuelve si el campo es de sólo lectura.
|
/// Devuelve si el campo es de sólo lectura.
|
||||||
|
|
@ -79,7 +76,7 @@ impl Component for Textarea {
|
||||||
fn setup(&mut self, _cx: &Context) {
|
fn setup(&mut self, _cx: &Context) {
|
||||||
if let Some(container_id) = self
|
if let Some(container_id) = self
|
||||||
.id()
|
.id()
|
||||||
.or_else(|| self.name().as_deref().map(|n| util::join!("edit-", n)))
|
.or_else(|| self.name().get().map(|n| util::join!("edit-", n)))
|
||||||
{
|
{
|
||||||
self.alter_prop(PropsOp::ensure_id(container_id));
|
self.alter_prop(PropsOp::ensure_id(container_id));
|
||||||
}
|
}
|
||||||
|
|
@ -112,18 +109,18 @@ impl Component for Textarea {
|
||||||
textarea
|
textarea
|
||||||
id=[textarea_id.as_deref()]
|
id=[textarea_id.as_deref()]
|
||||||
class="form-control"
|
class="form-control"
|
||||||
name=[self.name().as_deref()]
|
name=[self.name().get()]
|
||||||
rows=[self.rows()]
|
rows=[self.rows().get()]
|
||||||
minlength=[self.minlength()]
|
minlength=[self.minlength().get()]
|
||||||
maxlength=[self.maxlength()]
|
maxlength=[self.maxlength().get()]
|
||||||
placeholder=[self.placeholder().lookup(cx)]
|
placeholder=[self.placeholder().lookup(cx)]
|
||||||
autocomplete=[self.autocomplete()]
|
autocomplete=[self.autocomplete().get()]
|
||||||
autofocus[*self.autofocus()]
|
autofocus[*self.autofocus()]
|
||||||
readonly[*self.readonly()]
|
readonly[*self.readonly()]
|
||||||
required[*self.required()]
|
required[*self.required()]
|
||||||
disabled[*self.disabled()]
|
disabled[*self.disabled()]
|
||||||
{
|
{
|
||||||
@if let Some(value) = self.value().as_deref() {
|
@if let Some(value) = self.value().get() {
|
||||||
(value)
|
(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,17 +166,17 @@ impl Textarea {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la etiqueta visible del campo (usa [`Lc::none()`] para quitarla).
|
/// Establece o elimina la etiqueta visible del campo (basta pasar `None` para quitarla).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_label(mut self, label: Lc) -> Self {
|
pub fn with_label(mut self, label: impl Into<Option<Lc>>) -> Self {
|
||||||
self.label = label;
|
self.label.alter_opt(label.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto de ayuda del campo (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto de ayuda del campo (basta pasar `None` para quitarlo).
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_help_text(mut self, help_text: Lc) -> Self {
|
pub fn with_help_text(mut self, help_text: impl Into<Option<Lc>>) -> Self {
|
||||||
self.help_text = help_text;
|
self.help_text.alter_opt(help_text.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,32 +185,32 @@ impl Textarea {
|
||||||
/// Sin valor o pasando `None`, el área muestra su altura predeterminada, dos filas según el
|
/// Sin valor o pasando `None`, el área muestra su altura predeterminada, dos filas según el
|
||||||
/// estándar.
|
/// estándar.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_rows(mut self, rows: impl Into<Option<u16>>) -> Self {
|
pub fn with_rows(mut self, rows: Option<u16>) -> Self {
|
||||||
self.rows = rows.into();
|
self.rows.alter_opt(rows);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la longitud mínima permitida en caracteres.
|
/// Establece la longitud mínima permitida en caracteres.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_minlength(mut self, minlength: impl Into<Option<u16>>) -> Self {
|
pub fn with_minlength(mut self, minlength: Option<u16>) -> Self {
|
||||||
self.minlength = minlength.into();
|
self.minlength.alter_opt(minlength);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la longitud máxima permitida en caracteres.
|
/// Establece la longitud máxima permitida en caracteres.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_maxlength(mut self, maxlength: impl Into<Option<u16>>) -> Self {
|
pub fn with_maxlength(mut self, maxlength: Option<u16>) -> Self {
|
||||||
self.maxlength = maxlength.into();
|
self.maxlength.alter_opt(maxlength);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece el texto indicativo del área de texto (usa [`Lc::none()`] para quitarlo).
|
/// Establece o elimina el texto indicativo del área de texto (`None` para quitarlo).
|
||||||
///
|
///
|
||||||
/// Este texto aparece en el área de texto y desaparece en cuanto el usuario empieza a escribir.
|
/// Este texto aparece en el área de texto y desaparece en cuanto el usuario empieza a escribir.
|
||||||
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
|
/// Al ser texto visible para el usuario se acepta [`Lc`] para poder localizarlo.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_placeholder(mut self, placeholder: Lc) -> Self {
|
pub fn with_placeholder(mut self, placeholder: impl Into<Option<Lc>>) -> Self {
|
||||||
self.placeholder = placeholder;
|
self.placeholder.alter_opt(placeholder.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,11 +222,8 @@ impl Textarea {
|
||||||
/// Usa los métodos de [`form::Autocomplete`] para los valores más habituales. Pasa `None` para
|
/// Usa los métodos de [`form::Autocomplete`] para los valores más habituales. Pasa `None` para
|
||||||
/// omitir el atributo.
|
/// omitir el atributo.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_autocomplete(
|
pub fn with_autocomplete(mut self, autocomplete: Option<form::Autocomplete>) -> Self {
|
||||||
mut self,
|
self.autocomplete.alter_opt(autocomplete);
|
||||||
autocomplete: impl Into<Option<form::Autocomplete>>,
|
|
||||||
) -> Self {
|
|
||||||
self.autocomplete = autocomplete.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ pub struct Image {
|
||||||
/// Devuelve el origen de la imagen.
|
/// Devuelve el origen de la imagen.
|
||||||
source: image::Source,
|
source: image::Source,
|
||||||
/// Devuelve el texto alternativo localizado.
|
/// Devuelve el texto alternativo localizado.
|
||||||
alternative: Lc,
|
alternative: Attr<Lc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
@ -140,7 +140,7 @@ impl Image {
|
||||||
/// decorativa.
|
/// decorativa.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_alternative(mut self, alt: Lc) -> Self {
|
pub fn with_alternative(mut self, alt: Lc) -> Self {
|
||||||
self.alternative = alt;
|
self.alternative.alter_value(alt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,8 +263,8 @@ impl Intro {
|
||||||
/// let intro_no_button = Intro::default().with_button(None);
|
/// let intro_no_button = Intro::default().with_button(None);
|
||||||
/// ```
|
/// ```
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_button(mut self, button: impl Into<Option<(Lc, Route)>>) -> Self {
|
pub fn with_button(mut self, button: Option<(Lc, Route)>) -> Self {
|
||||||
self.button = button.into();
|
self.button = button;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,11 +203,11 @@ impl Component for Pager {
|
||||||
return Ok(html! {});
|
return Ok(html! {});
|
||||||
}
|
}
|
||||||
let page = self.current_page().clamp(1, total_pages);
|
let page = self.current_page().clamp(1, total_pages);
|
||||||
let base_path = self.base_path().as_deref().unwrap_or("");
|
let base_path = self.base_path().as_str().unwrap_or_default();
|
||||||
|
|
||||||
// Ruta común a los enlaces del paginador, con los parámetros de `extra_query` añadidos a
|
// Ruta común a los enlaces del paginador, con los parámetros de `extra_query` añadidos a
|
||||||
// `base_path`. Pasa por `cx.route()` para preservar el parámetro `lang` si corresponde.
|
// `base_path`. Pasa por `cx.route()` para preservar el parámetro `lang` si corresponde.
|
||||||
let mut route = cx.route(base_path);
|
let mut route = cx.route(base_path.to_owned());
|
||||||
for (key, value) in self.extra_query() {
|
for (key, value) in self.extra_query() {
|
||||||
route.alter_param(key, value);
|
route.alter_param(key, value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ impl Column {
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
th (self.props()) scope="col" aria-sort=(aria_sort) {
|
th (self.props()) scope="col" aria-sort=(aria_sort) {
|
||||||
a href=[sort.href().as_deref()] (link_props) { (label) }
|
a href=[sort.href().as_str()] (link_props) { (label) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::prelude::*;
|
||||||
/// .with_prop(PropsOp::set("data-sort", "email"));
|
/// .with_prop(PropsOp::set("data-sort", "email"));
|
||||||
///
|
///
|
||||||
/// assert_eq!(link.props().get_id(), Some("sort-email".to_string()));
|
/// assert_eq!(link.props().get_id(), Some("sort-email".to_string()));
|
||||||
/// assert_eq!(link.href().as_deref(), Some("/admin/users?sort=email"));
|
/// assert_eq!(link.href().as_str(), Some("/admin/users?sort=email"));
|
||||||
/// assert_eq!(link.dir(), Some(&SortDir::Desc));
|
/// assert_eq!(link.dir(), Some(&SortDir::Desc));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ impl Child {
|
||||||
///
|
///
|
||||||
/// Si se proporciona `Some(component)`, se encapsula como [`Child`]; y si es `None`, se limpia.
|
/// Si se proporciona `Some(component)`, se encapsula como [`Child`]; y si es `None`, se limpia.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_component<C: Component>(mut self, component: impl Into<Option<C>>) -> Self {
|
pub fn with_component<C: Component>(mut self, component: Option<C>) -> Self {
|
||||||
self.0 = component.into().map(|c| Arc::new(c) as Arc<dyn Component>);
|
self.0 = component.map(|c| Arc::new(c) as Arc<dyn Component>);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,8 +166,8 @@ impl<C: Component> Embed<C> {
|
||||||
///
|
///
|
||||||
/// Si se proporciona `Some(component)`, se encapsula como [`Embed`]; y si es `None`, se limpia.
|
/// Si se proporciona `Some(component)`, se encapsula como [`Embed`]; y si es `None`, se limpia.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_component(mut self, component: impl Into<Option<C>>) -> Self {
|
pub fn with_component(mut self, component: Option<C>) -> Self {
|
||||||
self.0 = component.into().map(Arc::new);
|
self.0 = component.map(Arc::new);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ pub use logo::PageTopSvg;
|
||||||
// **< HTML ATTRIBUTES >****************************************************************************
|
// **< HTML ATTRIBUTES >****************************************************************************
|
||||||
|
|
||||||
mod attr;
|
mod attr;
|
||||||
pub use attr::{AttrName, AttrValue};
|
pub use attr::{Attr, AttrName, AttrValue};
|
||||||
|
|
||||||
mod props;
|
mod props;
|
||||||
pub use props::{Props, PropsError, PropsExtra, PropsOp};
|
pub use props::{Props, PropsError, PropsExtra, PropsOp};
|
||||||
|
|
|
||||||
184
src/html/attr.rs
184
src/html/attr.rs
|
|
@ -1,5 +1,133 @@
|
||||||
|
use crate::locale::{LangId, Lc};
|
||||||
use crate::{AutoDefault, builder_fn, util};
|
use crate::{AutoDefault, builder_fn, util};
|
||||||
|
|
||||||
|
/// Valor opcional para atributos HTML.
|
||||||
|
///
|
||||||
|
/// `Attr<T>` encapsula un `Option<T>` y sirve como tipo base para representar atributos HTML
|
||||||
|
/// opcionales, uniformes y tipados.
|
||||||
|
///
|
||||||
|
/// Este tipo **no impone ninguna normalización ni semántica concreta**; dichas reglas se definen en
|
||||||
|
/// implementaciones concretas como `Attr<Lc>` y `Attr<String>`, o en tipos específicos como
|
||||||
|
/// [`AttrName`].
|
||||||
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
|
pub struct Attr<T>(Option<T>);
|
||||||
|
|
||||||
|
impl<T> Attr<T> {
|
||||||
|
/// Crea un atributo vacío.
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Crea un atributo con valor.
|
||||||
|
pub fn some(value: T) -> Self {
|
||||||
|
Self(Some(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Attr<T> BUILDER >************************************************************************
|
||||||
|
|
||||||
|
/// Establece un valor opcional para el atributo.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_opt(mut self, opt: Option<T>) -> Self {
|
||||||
|
self.0 = opt;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Establece un valor para el atributo.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_value(mut self, value: T) -> Self {
|
||||||
|
self.0 = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Elimina el valor del atributo.
|
||||||
|
#[builder_fn]
|
||||||
|
pub fn with_none(mut self) -> Self {
|
||||||
|
self.0 = None;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Attr<T> GETTERS >************************************************************************
|
||||||
|
|
||||||
|
/// Devuelve el valor (clonado), si existe.
|
||||||
|
pub fn get(&self) -> Option<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve una referencia al valor, si existe.
|
||||||
|
pub fn as_ref(&self) -> Option<&T> {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve el valor (propiedad), si existe.
|
||||||
|
pub fn into_inner(self) -> Option<T> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `true` si no hay valor.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Attr<Lc> >***********************************************************************************
|
||||||
|
|
||||||
|
/// Extiende [`Attr`] para [texto localizado](crate::locale) en atributos HTML.
|
||||||
|
///
|
||||||
|
/// Encapsula un [`Lc`] para manejar traducciones de forma segura en atributos.
|
||||||
|
///
|
||||||
|
/// # Ejemplo
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pagetop::prelude::*;
|
||||||
|
/// // Traducción por clave en las locales por defecto de PageTop.
|
||||||
|
/// let hello = Attr::<Lc>::new(Lc::l("test_hello_world"));
|
||||||
|
///
|
||||||
|
/// // Español disponible.
|
||||||
|
/// assert_eq!(
|
||||||
|
/// hello.lookup(&Locale::resolve("es-ES")),
|
||||||
|
/// Some("¡Hola mundo!".to_string())
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Japonés no disponible, traduce al idioma de respaldo (`"en-US"`).
|
||||||
|
/// assert_eq!(
|
||||||
|
/// hello.lookup(&Locale::resolve("ja-JP")),
|
||||||
|
/// Some("Hello world!".to_string())
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // Uso típico en un atributo:
|
||||||
|
/// let title = hello.value(&Locale::resolve("es-ES"));
|
||||||
|
/// // Ejemplo: html! { a title=(title) { "Link" } }
|
||||||
|
/// ```
|
||||||
|
impl Attr<Lc> {
|
||||||
|
/// Crea una nueva instancia `Attr<Lc>`.
|
||||||
|
pub fn new(value: Lc) -> Self {
|
||||||
|
Self::some(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la traducción para `language` si puede resolverse.
|
||||||
|
pub fn lookup(&self, language: &impl LangId) -> Option<String> {
|
||||||
|
self.0.as_ref()?.lookup(language)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la traducción para `language` o una cadena vacía si no existe.
|
||||||
|
pub fn value(&self, language: &impl LangId) -> String {
|
||||||
|
self.lookup(language).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// **< Attr<String> >*******************************************************************************
|
||||||
|
|
||||||
|
/// Extiende [`Attr`] para cadenas de texto.
|
||||||
|
impl Attr<String> {
|
||||||
|
/// Devuelve el texto como `&str` si existe.
|
||||||
|
pub fn as_str(&self) -> Option<&str> {
|
||||||
|
self.0.as_deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// **< AttrName >***********************************************************************************
|
// **< AttrName >***********************************************************************************
|
||||||
|
|
||||||
/// Nombre normalizado para el atributo `name` o similar de HTML.
|
/// Nombre normalizado para el atributo `name` o similar de HTML.
|
||||||
|
|
@ -16,13 +144,13 @@ use crate::{AutoDefault, builder_fn, util};
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// let name = AttrName::new(" DISplay name ");
|
/// let name = AttrName::new(" DISplay name ");
|
||||||
/// assert_eq!(name.as_deref(), Some("display_name"));
|
/// assert_eq!(name.as_str(), Some("display_name"));
|
||||||
///
|
///
|
||||||
/// let empty = AttrName::default();
|
/// let empty = AttrName::default();
|
||||||
/// assert_eq!(empty.get(), None);
|
/// assert_eq!(empty.get(), None);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug)]
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
pub struct AttrName(Option<String>);
|
pub struct AttrName(Attr<String>);
|
||||||
|
|
||||||
impl AttrName {
|
impl AttrName {
|
||||||
/// Crea un nuevo `AttrName` normalizando el valor.
|
/// Crea un nuevo `AttrName` normalizando el valor.
|
||||||
|
|
@ -35,25 +163,33 @@ impl AttrName {
|
||||||
/// Establece un nombre nuevo normalizando el valor.
|
/// Establece un nombre nuevo normalizando el valor.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_name(mut self, name: impl AsRef<str>) -> Self {
|
pub fn with_name(mut self, name: impl AsRef<str>) -> Self {
|
||||||
self.0 = util::normalize_token(name);
|
self.0 = match util::normalize_token(name) {
|
||||||
|
Some(name) => Attr::some(name),
|
||||||
|
None => Attr::default(),
|
||||||
|
};
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< AttrName GETTERS >***********************************************************************
|
// **< AttrName GETTERS >***********************************************************************
|
||||||
|
|
||||||
/// Devuelve el nombre normalizado (sin clonar), si existe.
|
/// Devuelve el nombre normalizado, si existe.
|
||||||
pub fn as_deref(&self) -> Option<&str> {
|
pub fn get(&self) -> Option<String> {
|
||||||
self.0.as_deref()
|
self.0.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve el nombre normalizado (clonado), si existe.
|
/// Devuelve el nombre normalizado (sin clonar), si existe.
|
||||||
pub fn get(&self) -> Option<String> {
|
pub fn as_str(&self) -> Option<&str> {
|
||||||
self.0.clone()
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve el nombre normalizado (propiedad), si existe.
|
||||||
|
pub fn into_inner(self) -> Option<String> {
|
||||||
|
self.0.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `true` si no hay valor.
|
/// `true` si no hay valor.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_none()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,13 +207,13 @@ impl AttrName {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// let s = AttrValue::new(" a new string ");
|
/// let s = AttrValue::new(" a new string ");
|
||||||
/// assert_eq!(s.as_deref(), Some("a new string"));
|
/// assert_eq!(s.as_str(), Some("a new string"));
|
||||||
///
|
///
|
||||||
/// let empty = AttrValue::default();
|
/// let empty = AttrValue::default();
|
||||||
/// assert_eq!(empty.get(), None);
|
/// assert_eq!(empty.get(), None);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug)]
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
pub struct AttrValue(Option<String>);
|
pub struct AttrValue(Attr<String>);
|
||||||
|
|
||||||
impl AttrValue {
|
impl AttrValue {
|
||||||
/// Crea un nuevo `AttrValue` normalizando el valor.
|
/// Crea un nuevo `AttrValue` normalizando el valor.
|
||||||
|
|
@ -90,24 +226,32 @@ impl AttrValue {
|
||||||
/// Establece una cadena nueva normalizando el valor.
|
/// Establece una cadena nueva normalizando el valor.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_str(mut self, value: impl AsRef<str>) -> Self {
|
pub fn with_str(mut self, value: impl AsRef<str>) -> Self {
|
||||||
self.0 = util::non_blank(value.as_ref()).map(str::to_string);
|
self.0 = match util::non_blank(value.as_ref()) {
|
||||||
|
Some(value) => Attr::some(value.to_string()),
|
||||||
|
None => Attr::default(),
|
||||||
|
};
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< AttrValue GETTERS >**********************************************************************
|
// **< AttrValue GETTERS >**********************************************************************
|
||||||
|
|
||||||
/// Devuelve la cadena normalizada (sin clonar), si existe.
|
/// Devuelve la cadena normalizada, si existe.
|
||||||
pub fn as_deref(&self) -> Option<&str> {
|
pub fn get(&self) -> Option<String> {
|
||||||
self.0.as_deref()
|
self.0.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Devuelve la cadena normalizada (clonada), si existe.
|
/// Devuelve la cadena normalizada (sin clonar), si existe.
|
||||||
pub fn get(&self) -> Option<String> {
|
pub fn as_str(&self) -> Option<&str> {
|
||||||
self.0.clone()
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Devuelve la cadena normalizada (propiedad), si existe.
|
||||||
|
pub fn into_inner(self) -> Option<String> {
|
||||||
|
self.0.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `true` si no hay valor.
|
/// `true` si no hay valor.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_none()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ enum LcKind {
|
||||||
/// - Un texto puro (`n()`) que no requiere traducción.
|
/// - Un texto puro (`n()`) que no requiere traducción.
|
||||||
/// - Una clave para traducir un texto del conjunto de traducciones predefinidas de PageTop (`l()`).
|
/// - Una clave para traducir un texto del conjunto de traducciones predefinidas de PageTop (`l()`).
|
||||||
/// - Una clave para traducir de un conjunto concreto de traducciones (`t()`).
|
/// - Una clave para traducir de un conjunto concreto de traducciones (`t()`).
|
||||||
/// - Ningún contenido (`none()`), para representar la ausencia en un campo `Lc` opcional.
|
|
||||||
///
|
///
|
||||||
/// # ¿Cuál usar, `get()`, `lookup()` o `using()`?
|
/// # ¿Cuál usar, `get()`, `lookup()` o `using()`?
|
||||||
///
|
///
|
||||||
|
|
@ -123,23 +122,6 @@ impl Lc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Crea una instancia **sin contenido**: no traduce nada y no representa ningún texto.
|
|
||||||
///
|
|
||||||
/// Equivale a [`Lc::default()`](Default::default), con un nombre más explícito. Útil para
|
|
||||||
/// representar la ausencia en un campo `Lc` opcional sin requerir `Option<Lc>`:
|
|
||||||
/// [`get()`](Self::get) y [`lookup()`](Self::lookup) devuelven `None`, y
|
|
||||||
/// [`using()`](Self::using) devuelve un marcado vacío.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use pagetop::prelude::*;
|
|
||||||
/// assert_eq!(Lc::none().get(), None);
|
|
||||||
/// ```
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Lc BUILDER >*****************************************************************************
|
|
||||||
|
|
||||||
/// Añade un argumento `{$arg}` => `value` a la traducción.
|
/// Añade un argumento `{$arg}` => `value` a la traducción.
|
||||||
pub fn with_arg(mut self, arg: impl Into<CowStr>, value: impl Into<CowStr>) -> Self {
|
pub fn with_arg(mut self, arg: impl Into<CowStr>, value: impl Into<CowStr>) -> Self {
|
||||||
self.args.push((arg.into(), value.into()));
|
self.args.push((arg.into(), value.into()));
|
||||||
|
|
@ -160,8 +142,6 @@ impl Lc {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Lc GETTERS >*****************************************************************************
|
|
||||||
|
|
||||||
/// Resuelve la traducción usando el idioma por defecto o, si no procede, el de respaldo de la
|
/// Resuelve la traducción usando el idioma por defecto o, si no procede, el de respaldo de la
|
||||||
/// aplicación.
|
/// aplicación.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ use crate::core::component::{AssetsOp, ChildOp, ComponentRender};
|
||||||
use crate::core::component::{Context, ContextError, Contextual};
|
use crate::core::component::{Context, ContextError, Contextual};
|
||||||
use crate::core::theme::{CoreRegions, RegionName, RegionRef, TemplateRef, ThemeRef};
|
use crate::core::theme::{CoreRegions, RegionName, RegionRef, TemplateRef, ThemeRef};
|
||||||
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
||||||
|
use crate::html::{Attr, Props, PropsOp};
|
||||||
use crate::html::{DOCTYPE, Markup, html};
|
use crate::html::{DOCTYPE, Markup, html};
|
||||||
use crate::html::{Props, PropsOp};
|
|
||||||
use crate::locale::{CharacterDirection, LangId, LanguageIdentifier, Lc};
|
use crate::locale::{CharacterDirection, LangId, LanguageIdentifier, Lc};
|
||||||
use crate::web::HttpRequest;
|
use crate::web::HttpRequest;
|
||||||
use crate::{AutoDefault, builder_fn};
|
use crate::{AutoDefault, builder_fn};
|
||||||
|
|
@ -88,8 +88,8 @@ impl RegionName for ReservedRegions {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[derive(AutoDefault)]
|
#[derive(AutoDefault)]
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
title : Lc,
|
title : Attr<Lc>,
|
||||||
description: Lc,
|
description : Attr<Lc>,
|
||||||
metadata : Vec<(&'static str, &'static str)>,
|
metadata : Vec<(&'static str, &'static str)>,
|
||||||
properties : Vec<(&'static str, &'static str)>,
|
properties : Vec<(&'static str, &'static str)>,
|
||||||
context : Context,
|
context : Context,
|
||||||
|
|
@ -128,14 +128,14 @@ impl Page {
|
||||||
/// Establece el título de la página como un valor traducible.
|
/// Establece el título de la página como un valor traducible.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_title(mut self, title: Lc) -> Self {
|
pub fn with_title(mut self, title: Lc) -> Self {
|
||||||
self.title = title;
|
self.title.alter_value(title);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Establece la descripción de la página como un valor traducible.
|
/// Establece la descripción de la página como un valor traducible.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_description(mut self, description: Lc) -> Self {
|
pub fn with_description(mut self, description: Lc) -> Self {
|
||||||
self.description = description;
|
self.description.alter_value(description);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn label_is_rendered_when_set() {
|
|
||||||
let mut button = Button::submit(Lc::n("Save"));
|
|
||||||
let html = button.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(html.contains("Save"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn label_can_be_cleared_with_lc_none() {
|
|
||||||
let mut button = Button::submit(Lc::n("Save")).with_label(Lc::none());
|
|
||||||
let html = button.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains("Save"));
|
|
||||||
// The button itself must still render.
|
|
||||||
assert!(html.contains("<button"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn title_attribute_is_absent_by_default() {
|
|
||||||
let mut button = Button::submit(Lc::n("Save"));
|
|
||||||
let html = button.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains("title="));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn title_attribute_is_present_when_set() {
|
|
||||||
let mut button = Button::submit(Lc::n("Save")).with_title(Lc::n("Save changes"));
|
|
||||||
let html = button.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(html.contains(r#"title="Save changes""#));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn title_attribute_can_be_cleared_with_lc_none() {
|
|
||||||
let mut button = Button::submit(Lc::n("Save"))
|
|
||||||
.with_title(Lc::n("Save changes"))
|
|
||||||
.with_title(Lc::none());
|
|
||||||
let html = button.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains("title="));
|
|
||||||
}
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn label_is_absent_by_default() {
|
|
||||||
let mut field = form::input::Field::text();
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains(r#"class="form-label""#));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn label_is_rendered_when_set() {
|
|
||||||
let mut field = form::input::Field::text().with_label(Lc::n("Full name"));
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(html.contains(r#"class="form-label""#));
|
|
||||||
assert!(html.contains("Full name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn label_can_be_cleared_with_lc_none() {
|
|
||||||
let mut field = form::input::Field::text()
|
|
||||||
.with_label(Lc::n("Full name"))
|
|
||||||
.with_label(Lc::none());
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains(r#"class="form-label""#));
|
|
||||||
assert!(!html.contains("Full name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn help_text_is_absent_by_default() {
|
|
||||||
let mut field = form::input::Field::text();
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains(r#"class="form-text""#));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn help_text_is_rendered_when_set() {
|
|
||||||
let mut field = form::input::Field::text().with_help_text(Lc::n("We never share your data."));
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(html.contains(r#"class="form-text""#));
|
|
||||||
assert!(html.contains("We never share your data."));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn placeholder_attribute_is_absent_by_default() {
|
|
||||||
let mut field = form::input::Field::text();
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains("placeholder="));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn placeholder_attribute_is_present_when_set() {
|
|
||||||
let mut field = form::input::Field::text().with_placeholder(Lc::n("Enter your name"));
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(html.contains(r#"placeholder="Enter your name""#));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn placeholder_attribute_can_be_cleared_with_lc_none() {
|
|
||||||
let mut field = form::input::Field::text()
|
|
||||||
.with_placeholder(Lc::n("Enter your name"))
|
|
||||||
.with_placeholder(Lc::none());
|
|
||||||
let html = field.render(&mut Context::default()).await.into_string();
|
|
||||||
|
|
||||||
assert!(!html.contains("placeholder="));
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
use pagetop::prelude::*;
|
|
||||||
|
|
||||||
fn request() -> HttpRequest {
|
|
||||||
web::test::TestRequest::get().to_http_request()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn title_and_description_are_absent_by_default() {
|
|
||||||
let page = Page::new(request());
|
|
||||||
|
|
||||||
assert_eq!(page.title(), None);
|
|
||||||
assert_eq!(page.description(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn title_and_description_reflect_the_values_set() {
|
|
||||||
let page = Page::new(request())
|
|
||||||
.with_title(Lc::n("Dashboard"))
|
|
||||||
.with_description(Lc::n("Overview of recent activity"));
|
|
||||||
|
|
||||||
assert_eq!(page.title(), Some("Dashboard".to_string()));
|
|
||||||
assert_eq!(
|
|
||||||
page.description(),
|
|
||||||
Some("Overview of recent activity".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pagetop::test]
|
|
||||||
async fn lc_none_clears_a_previously_set_title_and_description() {
|
|
||||||
let page = Page::new(request())
|
|
||||||
.with_title(Lc::n("Dashboard"))
|
|
||||||
.with_description(Lc::n("Overview of recent activity"))
|
|
||||||
.with_title(Lc::none())
|
|
||||||
.with_description(Lc::none());
|
|
||||||
|
|
||||||
assert_eq!(page.title(), None);
|
|
||||||
assert_eq!(page.description(), None);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue