use smallvec::SmallVec;
use std::{borrow::Cow, fmt};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Class<'a> {
namespace: Option<&'a str>,
classes: SmallVec<[&'a str; 5]>,
}
impl<'a> Class<'a> {
#[inline]
pub fn new(class: &'a str) -> Self {
Self {
namespace: None,
classes: class.split_whitespace().collect(),
}
}
#[inline]
pub fn with_namespace(namespace: &'a str, class: &'a str) -> Self {
Self {
namespace: (!namespace.is_empty()).then_some(namespace),
classes: class.split_whitespace().collect(),
}
}
#[inline]
pub fn add(&mut self, class: &'a str) {
if !(class.is_empty() || self.contains(class)) {
self.classes.push(class);
}
}
#[inline]
pub fn remove(&mut self, class: &str) {
self.classes.retain(|s| s != &class)
}
#[inline]
pub fn toggle(&mut self, class: &'a str) {
if let Some(index) = self.classes.iter().position(|&s| s == class) {
self.classes.remove(index);
} else {
self.classes.push(class);
}
}
#[inline]
pub fn replace(&mut self, class: &str, new_class: &'a str) {
if let Some(index) = self.classes.iter().position(|&s| s == class) {
self.classes[index] = new_class;
}
}
#[inline]
pub fn contains(&self, class: &str) -> bool {
self.classes.iter().any(|&s| s == class)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.classes.is_empty()
}
#[inline]
pub fn namespace(&self) -> Option<&str> {
self.namespace.filter(|s| !s.is_empty())
}
pub fn format(&self) -> Cow<'_, str> {
let classes = self.classes.as_slice();
if let Some(namespace) = self.namespace() {
let class = if let [class] = classes {
[namespace, class].join("-")
} else {
classes
.iter()
.filter(|s| !s.is_empty())
.map(|s| [namespace, s].join("-"))
.collect::<Vec<_>>()
.join(" ")
};
Cow::Owned(class)
} else if let [class] = classes {
Cow::Borrowed(class)
} else {
Cow::Owned(classes.join(" "))
}
}
}
impl<'a> From<&'a str> for Class<'a> {
#[inline]
fn from(class: &'a str) -> Self {
Self::new(class)
}
}
impl<'a> From<Vec<&'a str>> for Class<'a> {
#[inline]
fn from(classes: Vec<&'a str>) -> Self {
Self {
namespace: None,
classes: SmallVec::from_vec(classes),
}
}
}
impl<'a, const N: usize> From<[&'a str; N]> for Class<'a> {
#[inline]
fn from(classes: [&'a str; N]) -> Self {
Self {
namespace: None,
classes: SmallVec::from_slice(&classes),
}
}
}
impl<'a> fmt::Display for Class<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let format = self.format();
write!(f, "{format}")
}
}
#[macro_export]
macro_rules! format_class {
($cx:ident, $default_class:expr) => {
$cx.props
.class
.as_ref()
.map(Class::format)
.unwrap_or_else(|| $default_class.into())
};
($cx:ident, $class_prop:ident, $default_class:expr) => {
$cx.props
.$class_prop
.as_ref()
.map(Class::format)
.unwrap_or_else(|| $default_class.into())
};
}