✨ Añade trait JoinClasses para unir clases CSS
También elimina macros sin uso `join_op!` y `join_strict!` (KISS).
This commit is contained in:
parent
6a4ad213d8
commit
6365e1a077
6 changed files with 90 additions and 97 deletions
|
|
@ -87,6 +87,9 @@ use crate::{core, AutoDefault};
|
|||
#[allow(type_alias_bounds)]
|
||||
pub type OptionComponent<C: core::component::Component> = core::component::Typed<C>;
|
||||
|
||||
mod join_classes;
|
||||
pub use join_classes::JoinClasses;
|
||||
|
||||
mod unit;
|
||||
pub use unit::UnitValue;
|
||||
|
||||
|
|
|
|||
|
|
@ -215,13 +215,13 @@ impl Asset for JavaScript {
|
|||
fn render(&self, cx: &mut Context) -> Markup {
|
||||
match &self.source {
|
||||
Source::From(path) => html! {
|
||||
script src=(join_pair!(path, "?v=", self.version.as_str())) {};
|
||||
script src=(join_pair!(path, "?v=", &self.version)) {};
|
||||
},
|
||||
Source::Defer(path) => html! {
|
||||
script src=(join_pair!(path, "?v=", self.version.as_str())) defer {};
|
||||
script src=(join_pair!(path, "?v=", &self.version)) defer {};
|
||||
},
|
||||
Source::Async(path) => html! {
|
||||
script src=(join_pair!(path, "?v=", self.version.as_str())) async {};
|
||||
script src=(join_pair!(path, "?v=", &self.version)) async {};
|
||||
},
|
||||
Source::Inline(_, f) => html! {
|
||||
script { (PreEscaped((f)(cx))) };
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ impl Asset for StyleSheet {
|
|||
Source::From(path) => html! {
|
||||
link
|
||||
rel="stylesheet"
|
||||
href=(join_pair!(path, "?v=", self.version.as_str()))
|
||||
href=(join_pair!(path, "?v=", &self.version))
|
||||
media=[self.media.as_str_opt()];
|
||||
},
|
||||
Source::Inline(_, f) => html! {
|
||||
|
|
|
|||
67
src/html/join_classes.rs
Normal file
67
src/html/join_classes.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/// Añade a los *slices* de elementos [`AsRef<str>`] un método para unir clases CSS.
|
||||
///
|
||||
/// El método es [`join_classes()`](JoinClasses::join_classes), que une las cadenas **no vacías**
|
||||
/// del *slice* usando un espacio como separador.
|
||||
pub trait JoinClasses {
|
||||
/// Une las cadenas **no vacías** de un *slice* usando un espacio como separador.
|
||||
///
|
||||
/// Son cadenas vacías únicamente los elementos del *slice* cuya longitud es `0` (p. ej., `""`);
|
||||
/// no se realiza recorte ni normalización, por lo que elementos como `" "` no se consideran
|
||||
/// vacíos.
|
||||
///
|
||||
/// Si todas las cadenas están vacías, devuelve una cadena vacía. Acepta elementos que
|
||||
/// implementen [`AsRef<str>`] como `&str`, [`String`] o `Cow<'_, str>`.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let classes = ["btn", "", "btn-primary"];
|
||||
/// assert_eq!(classes.join_classes(), "btn btn-primary");
|
||||
///
|
||||
/// let empty: [&str; 3] = ["", "", ""];
|
||||
/// assert_eq!(empty.join_classes(), "");
|
||||
///
|
||||
/// let border = String::from("border");
|
||||
/// let border_top = String::from("border-top-0");
|
||||
/// let v = vec![&border, "", "", "", &border_top];
|
||||
/// assert_eq!(v.as_slice().join_classes(), "border border-top-0");
|
||||
///
|
||||
/// // Elementos con espacios afectan al resultado.
|
||||
/// let spaced = ["btn", " ", "primary "];
|
||||
/// assert_eq!(spaced.join_classes(), "btn primary ");
|
||||
/// ```
|
||||
fn join_classes(&self) -> String;
|
||||
}
|
||||
|
||||
impl<T> JoinClasses for [T]
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
#[inline]
|
||||
fn join_classes(&self) -> String {
|
||||
let mut count = 0usize;
|
||||
let mut total = 0usize;
|
||||
for s in self.iter().map(T::as_ref).filter(|s| !s.is_empty()) {
|
||||
count += 1;
|
||||
total += s.len();
|
||||
}
|
||||
if count == 0 {
|
||||
return String::new();
|
||||
}
|
||||
let separator = " ";
|
||||
let mut result = String::with_capacity(total + separator.len() * count.saturating_sub(1));
|
||||
for (i, s) in self
|
||||
.iter()
|
||||
.map(T::as_ref)
|
||||
.filter(|s| !s.is_empty())
|
||||
.enumerate()
|
||||
{
|
||||
if i > 0 {
|
||||
result.push_str(separator);
|
||||
}
|
||||
result.push_str(s);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ pub use crate::{AutoDefault, StaticResources, UniqueId, Weight};
|
|||
// MACROS.
|
||||
|
||||
// crate::util
|
||||
pub use crate::{hm, join, join_opt, join_pair, join_strict};
|
||||
pub use crate::{hm, join, join_pair};
|
||||
// crate::config
|
||||
pub use crate::include_config;
|
||||
// crate::locale
|
||||
|
|
@ -33,8 +33,8 @@ pub use crate::trace;
|
|||
// alias obsoletos se volverá a declarar como `pub use crate::html::*;`.
|
||||
pub use crate::html::{
|
||||
display, html_private, Asset, Assets, AttrClasses, AttrId, AttrL10n, AttrName, AttrValue,
|
||||
ClassesOp, Escaper, Favicon, JavaScript, Markup, PageTopSvg, PreEscaped, PrepareMarkup,
|
||||
StyleSheet, TargetMedia, UnitValue, DOCTYPE,
|
||||
ClassesOp, Escaper, Favicon, JavaScript, JoinClasses, Markup, PageTopSvg, PreEscaped,
|
||||
PrepareMarkup, StyleSheet, TargetMedia, UnitValue, DOCTYPE,
|
||||
};
|
||||
|
||||
pub use crate::locale::*;
|
||||
|
|
|
|||
103
src/util.rs
103
src/util.rs
|
|
@ -65,53 +65,11 @@ macro_rules! hm {
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! join {
|
||||
($($arg:tt)*) => {
|
||||
$crate::util::concat_string!($($arg)*)
|
||||
($($arg:expr),+) => {
|
||||
$crate::util::concat_string!($($arg),+)
|
||||
};
|
||||
}
|
||||
|
||||
/// Concatena los fragmentos **no vacíos** en un [`Option<String>`] con un separador opcional.
|
||||
///
|
||||
/// Acepta cualquier número de fragmentos que implementen [`AsRef<str>`]. Si todos los fragmentos
|
||||
/// están vacíos, devuelve `None`.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// // Concatena los fragmentos no vacíos con un espacio como separador.
|
||||
/// let result_with_separator = join_opt!(["Hello", "", "World"]; " ");
|
||||
/// assert_eq!(result_with_separator, Some("Hello World".to_string()));
|
||||
///
|
||||
/// // Concatena los fragmentos no vacíos sin un separador.
|
||||
/// let result_without_separator = join_opt!(["Hello", "", "World"]);
|
||||
/// assert_eq!(result_without_separator, Some("HelloWorld".to_string()));
|
||||
///
|
||||
/// // Devuelve `None` si todos los fragmentos están vacíos.
|
||||
/// let result_empty = join_opt!(["", "", ""]; ",");
|
||||
/// assert_eq!(result_empty, None);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! join_opt {
|
||||
([$($arg:expr),* $(,)?]) => {{
|
||||
let s = $crate::util::concat_string!($($arg),*);
|
||||
(!s.is_empty()).then_some(s)
|
||||
}};
|
||||
([$($arg:expr),* $(,)?]; $separator:expr) => {{
|
||||
let sep = ($separator).as_ref();
|
||||
let mut s = String::new();
|
||||
for part in [ $( ($arg).as_ref() ),* ] {
|
||||
if !(part as &str).is_empty() {
|
||||
if !s.is_empty() {
|
||||
s.push_str(sep);
|
||||
}
|
||||
s.push_str(part);
|
||||
}
|
||||
}
|
||||
(!s.is_empty()).then_some(s)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Concatena dos fragmentos en un [`String`] usando un separador.
|
||||
///
|
||||
/// Une los dos fragmentos, que deben implementar [`AsRef<str>`], usando el separador proporcionado.
|
||||
|
|
@ -145,54 +103,19 @@ macro_rules! join_opt {
|
|||
#[macro_export]
|
||||
macro_rules! join_pair {
|
||||
($first:expr, $separator:expr, $second:expr) => {{
|
||||
if $first.is_empty() {
|
||||
String::from($second)
|
||||
} else if $second.is_empty() {
|
||||
String::from($first)
|
||||
} else {
|
||||
$crate::util::concat_string!($first, $separator, $second)
|
||||
}
|
||||
}};
|
||||
}
|
||||
let first_val = $first;
|
||||
let second_val = $second;
|
||||
let separator_val = $separator;
|
||||
|
||||
/// Concatena varios fragmentos en un [`Option<String>`] **si ninguno está vacío**.
|
||||
///
|
||||
/// Si alguno de los fragmentos, que deben implementar [`AsRef<str>`], está vacío, devuelve
|
||||
/// [`None`]. Opcionalmente se puede indicar un separador entre los fragmentos concatenados.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust
|
||||
/// # use pagetop::prelude::*;
|
||||
/// // Concatena los fragmentos.
|
||||
/// let result = join_strict!(["Hello", "World"]);
|
||||
/// assert_eq!(result, Some("HelloWorld".to_string()));
|
||||
///
|
||||
/// // Concatena los fragmentos con un separador.
|
||||
/// let result_with_separator = join_strict!(["Hello", "World"]; " ");
|
||||
/// assert_eq!(result_with_separator, Some("Hello World".to_string()));
|
||||
///
|
||||
/// // Devuelve `None` si alguno de los fragmentos está vacío.
|
||||
/// let result_with_empty = join_strict!(["Hello", "", "World"]);
|
||||
/// assert_eq!(result_with_empty, None);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! join_strict {
|
||||
([$($arg:expr),* $(,)?]) => {{
|
||||
let fragments = [$($arg),*];
|
||||
if fragments.iter().any(|&item| item.is_empty()) {
|
||||
None
|
||||
let first = AsRef::<str>::as_ref(&first_val);
|
||||
let second = AsRef::<str>::as_ref(&second_val);
|
||||
let separator = if first.is_empty() || second.is_empty() {
|
||||
""
|
||||
} else {
|
||||
Some(fragments.concat())
|
||||
}
|
||||
}};
|
||||
([$($arg:expr),* $(,)?]; $separator:expr) => {{
|
||||
let fragments = [$($arg),*];
|
||||
if fragments.iter().any(|&item| item.is_empty()) {
|
||||
None
|
||||
} else {
|
||||
Some(fragments.join($separator))
|
||||
}
|
||||
AsRef::<str>::as_ref(&separator_val)
|
||||
};
|
||||
|
||||
$crate::util::concat_string!(first, separator, second)
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue