Make some Attrs append instead of replace

This commit is contained in:
Joscha 2024-12-02 18:06:10 +01:00
parent 3503cc7e9e
commit cb2a6da25c

View file

@ -1,4 +1,4 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::{btree_map::Entry, BTreeMap, HashMap};
/// <https://html.spec.whatwg.org/multipage/syntax.html#elements-2>
#[derive(Clone, Copy, PartialEq, Eq)]
@ -101,28 +101,76 @@ pub trait ElementComponent {
pub struct Attr {
name: String,
value: String,
append_by: Option<String>,
}
impl Attr {
/// Create or replace an attribute.
///
/// When this attribute is added to an [`Element`] through
/// [`ElementComponent::add_to_element`] and an attribute of the same name
/// already exists, it replaces that attribute's value.
pub fn new(name: impl ToString, value: impl ToString) -> Self {
Self {
name: name.to_string(),
value: value.to_string(),
append_by: None,
}
}
/// Create or append to an attribute.
///
/// When this attribute is added to an [`Element`] through
/// [`ElementComponent::add_to_element`] and an attribute of the same name
/// already exists, it appends the separator and then its own value to that
/// attribute's value.
pub fn append(name: impl ToString, value: impl ToString, separator: impl ToString) -> Self {
Self {
name: name.to_string(),
value: value.to_string(),
append_by: Some(separator.to_string()),
}
}
/// Create (or replace) a new empty attribute.
///
/// `Attr::yes(name)` is equivalent to `Attr::new(name, "").`
///
/// When rendering an empty attribute as HTML, the value can be omitted:
/// `name=""` is equivalent to just `name`.
pub fn yes(name: impl ToString) -> Self {
Self::new(name, "")
}
/// Create (or replace) an `id` attribute.
///
/// `Attr::id(id)` is equivalent to `Attr::new("id", id)`.
pub fn id(id: impl ToString) -> Self {
Self::new("id", id)
}
/// Create (or append) to a `class` attribute.
///
/// `Attr::class(class)` is equivalent to
/// `Attr::append("class", class, " ")`.
pub fn class(class: impl ToString) -> Self {
Self::new("class", class)
Self::append("class", class, " ")
}
/// Create (or append) to a `style` attribute.
///
/// `Attr::style(style)` is equivalent to
/// `Attr::append("style", style, ";")`.
pub fn style(style: impl ToString) -> Self {
Self::append("style", style, ";")
}
/// Create (or replace) a new [`data-*` attribute][mdn].
///
/// `Attr::data(name, value)` is equivalent to
/// `Attr::new(format!("data-{name}"), value)`.
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*
pub fn data(name: impl ToString, value: impl ToString) -> Self {
Self::new(format!("data-{}", name.to_string()), value)
}
@ -133,7 +181,21 @@ impl ElementComponent for Attr {
if element.kind != ElementKind::Foreign {
self.name = self.name.to_ascii_lowercase();
}
element.attributes.insert(self.name, self.value);
match element.attributes.entry(self.name) {
Entry::Vacant(entry) => {
entry.insert(self.value);
}
Entry::Occupied(mut entry) => match self.append_by {
None => {
entry.insert(self.value);
}
Some(sep) => {
let value = entry.get_mut();
value.push_str(&sep);
value.push_str(&self.value);
}
},
}
}
}