use std::rc::Rc;
use crate::{
Css, DomId, css::get_css_manager, dom::attr_value::AttrText, driver_module::get_driver_dom,
struct_mut::ValueMut,
};
struct CssSource {
resolved: String,
hint: Option<String>,
hint_sent: Option<String>,
}
struct ClassMergeInner {
id_dom: DomId,
attr: Option<AttrText>,
css: Option<Box<CssSource>>,
last_sent: Option<AttrText>,
}
impl ClassMergeInner {
fn new(id_dom: DomId, already_sent: Option<AttrText>) -> Self {
Self {
id_dom,
attr: already_sent.clone(),
css: None,
last_sent: already_sent,
}
}
fn merged(&self) -> Option<AttrText> {
match (&self.attr, &self.css) {
(None, None) => None,
(Some(attr), None) => Some(attr.clone()),
(None, Some(css)) => Some(AttrText::from(css.resolved.clone())),
(Some(attr), Some(css)) => Some(AttrText::from(format!(
"{} {}",
attr.as_str(),
css.resolved
))),
}
}
fn refresh_dom(&mut self) {
let merged = self.merged();
if self.last_sent != merged {
let value = merged.as_ref().map(AttrText::as_str).unwrap_or("");
get_driver_dom().set_attr(self.id_dom, "class", value);
self.last_sent = merged;
}
let id_dom = self.id_dom;
if let Some(css) = &mut self.css
&& let Some(hint) = css.hint.clone()
&& css.hint_sent.as_ref() != Some(&hint)
{
get_driver_dom().set_attr(id_dom, "v-css", &hint);
css.hint_sent = Some(hint);
}
}
}
#[derive(Clone)]
pub struct DomElementClassMerge {
inner: Rc<ValueMut<ClassMergeInner>>,
}
impl DomElementClassMerge {
fn new(id_dom: DomId, already_sent: Option<AttrText>) -> Self {
Self {
inner: Rc::new(ValueMut::new(ClassMergeInner::new(id_dom, already_sent))),
}
}
pub fn set_attribute(&self, new_value: AttrText) {
self.inner.change(|state| {
state.attr = Some(new_value);
state.refresh_dom();
});
}
pub fn remove_attribute(&self) {
self.inner.change(|state| {
state.attr = None;
state.refresh_dom();
});
}
pub fn set_css(&self, new_value: Css, debug_class_name: Option<String>) {
self.inner.change(|state| {
let resolved = get_css_manager().get_class_name(&new_value);
let hint_sent = state.css.as_mut().and_then(|css| css.hint_sent.take());
state.css = Some(Box::new(CssSource {
resolved,
hint: debug_class_name,
hint_sent,
}));
state.refresh_dom();
});
}
}
pub enum ClassState {
Empty,
Plain(AttrText),
Merged(DomElementClassMerge),
}
impl ClassState {
pub fn set_attribute(&mut self, id_dom: DomId, value: AttrText) {
match self {
ClassState::Merged(merge) => merge.set_attribute(value),
ClassState::Plain(sent) if *sent == value => {}
ClassState::Empty | ClassState::Plain(_) => {
get_driver_dom().set_attr(id_dom, "class", value.as_str());
*self = ClassState::Plain(value);
}
}
}
pub fn merger(&mut self, id_dom: DomId) -> DomElementClassMerge {
if let ClassState::Merged(merge) = self {
return merge.clone();
}
let already_sent = match std::mem::replace(self, ClassState::Empty) {
ClassState::Plain(sent) => Some(sent),
_ => None,
};
let merge = DomElementClassMerge::new(id_dom, already_sent);
*self = ClassState::Merged(merge.clone());
merge
}
}