pagetop/pagetop/src/html/opt_classes.rs
Manuel Cillero 514a8d89bc [pagetop] Añade la librería principal de PageTop
El crate pagetop reúne algunos de los crates más estables y populares
del ecosistema Rust para proporcionar un conjunto completo de
funcionalidades que pueden extenderse y adaptarse a las necesidades
específicas de cada aplicación para crear soluciones web modulares,
extensibles y configurables.
2024-12-04 08:20:48 +01:00

111 lines
3.4 KiB
Rust

//! **OptionClasses** implements a *helper* for dynamically adding class names to components.
//!
//! This *helper* differentiates between default classes (generally associated with styles provided
//! by the theme) and user classes (for customizing components based on application styles).
//!
//! Classes can be added using [Add]. Operations to [Remove], [Replace] or [Toggle] a class, as well
//! as [Clear] all classes, are also provided.
//!
//! **OptionClasses** assumes that the order of the classes is irrelevant
//! (<https://stackoverflow.com/a/1321712>), and duplicate classes will not be allowed.
use crate::{fn_builder, AutoDefault};
pub enum ClassesOp {
Add,
Prepend,
Remove,
Replace(String),
Toggle,
Set,
}
#[derive(AutoDefault)]
pub struct OptionClasses(Vec<String>);
impl OptionClasses {
pub fn new(classes: impl Into<String>) -> Self {
OptionClasses::default().with_value(ClassesOp::Prepend, classes)
}
// OptionClasses BUILDER.
#[fn_builder]
pub fn set_value(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self {
let classes: String = classes.into();
let classes: Vec<&str> = classes.split_ascii_whitespace().collect();
if classes.is_empty() {
return self;
}
match op {
ClassesOp::Add => {
self.add(&classes, self.0.len());
}
ClassesOp::Prepend => {
self.add(&classes, 0);
}
ClassesOp::Remove => {
for class in classes {
self.0.retain(|c| c.ne(&class.to_string()));
}
}
ClassesOp::Replace(classes_to_replace) => {
let mut pos = self.0.len();
let replace: Vec<&str> = classes_to_replace.split_ascii_whitespace().collect();
for class in replace {
if let Some(replace_pos) = self.0.iter().position(|c| c.eq(class)) {
self.0.remove(replace_pos);
if pos > replace_pos {
pos = replace_pos;
}
}
}
self.add(&classes, pos);
}
ClassesOp::Toggle => {
for class in classes {
if !class.is_empty() {
if let Some(pos) = self.0.iter().position(|c| c.eq(class)) {
self.0.remove(pos);
} else {
self.0.push(class.to_string());
}
}
}
}
ClassesOp::Set => {
self.0.clear();
self.add(&classes, 0);
}
}
self
}
#[inline]
fn add(&mut self, classes: &[&str], mut pos: usize) {
for &class in classes {
if !class.is_empty() && !self.0.iter().any(|c| c == class) {
self.0.insert(pos, class.to_string());
pos += 1;
}
}
}
// OptionClasses GETTERS.
pub fn get(&self) -> Option<String> {
if self.0.is_empty() {
None
} else {
Some(self.0.join(" "))
}
}
pub fn contains(&self, class: impl Into<String>) -> bool {
let class: String = class.into();
self.0.iter().any(|c| c.eq(&class))
}
}