use crate::*;
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(AttributeValue::Text(old_val), AttributeValue::Text(new_val)) => old_val == new_val,
(AttributeValue::Signal(old_sig), AttributeValue::Signal(new_sig)) => {
old_sig.get() == new_sig.get()
}
(AttributeValue::Signal(old_sig), AttributeValue::Text(new_val)) => {
old_sig.get() == *new_val
}
(AttributeValue::Text(old_val), AttributeValue::Signal(new_sig)) => {
*old_val == new_sig.get()
}
(AttributeValue::Event(_), AttributeValue::Event(_)) => true,
(AttributeValue::Css(old_css), AttributeValue::Css(new_css)) => {
old_css.get_name() == new_css.get_name()
}
(AttributeValue::Dynamic(old_dyn), AttributeValue::Dynamic(new_dyn)) => {
old_dyn == new_dyn
}
_ => false,
}
}
}
impl PartialEq for AttributeEntry {
fn eq(&self, other: &Self) -> bool {
self.get_name() == other.get_name() && self.get_value() == other.get_value()
}
}
impl PartialEq for CssClass {
fn eq(&self, other: &Self) -> bool {
self.get_name() == other.get_name()
}
}
impl Style {
pub fn property<N, V>(mut self, name: N, value: V) -> Self
where
N: AsRef<str>,
V: AsRef<str>,
{
self.get_mut_properties().push(StyleProperty::new(
name.as_ref().replace('_', "-"),
value.as_ref().to_string(),
));
self
}
pub fn to_css_string(&self) -> String {
self.get_properties()
.iter()
.map(|style: &StyleProperty| format!("{}: {};", style.get_name(), style.get_value()))
.collect::<Vec<String>>()
.join(" ")
}
pub fn create_style_string(props: &[(&str, &str)]) -> String {
let mut result: String = String::new();
for (key, value) in props {
if !result.is_empty() {
result.push(' ');
}
result.push_str(&key.replace('_', "-"));
result.push_str(": ");
result.push_str(value);
result.push(';');
}
result
}
}
impl Default for Style {
fn default() -> Self {
Self::new(Vec::new())
}
}
impl CssClass {
pub fn new(name: String, style: String) -> Self {
let mut css_class: CssClass = CssClass::default();
css_class.set_name(name);
css_class.set_style(style);
css_class.inject_style();
css_class
}
pub fn new_with_rules(
name: String,
style: String,
pseudo_rules: Vec<PseudoRule>,
media_rules: Vec<MediaRule>,
) -> Self {
let mut css_class: CssClass = CssClass::default();
css_class.set_name(name);
css_class.set_style(style);
css_class.set_pseudo_rules(pseudo_rules);
css_class.set_media_rules(media_rules);
css_class.inject_style();
css_class
}
pub fn parse_pseudo_rules(input: &str) -> Vec<PseudoRule> {
let mut rules: Vec<PseudoRule> = Vec::new();
let mut remaining: &str = input;
while !remaining.is_empty() {
let selector_end: Option<usize> = remaining.find(" { ");
let Some(sel_end) = selector_end else {
break;
};
let selector: &str = &remaining[..sel_end];
let after_selector: &str = remaining[sel_end..].strip_prefix(" { ").unwrap_or("");
let style_end: Option<usize> = after_selector.find('}');
let Some(st_end) = style_end else {
break;
};
let style: &str = &after_selector[..st_end];
if !selector.is_empty() && !style.is_empty() {
rules.push(PseudoRule::new(selector.to_string(), style.to_string()));
}
remaining = after_selector[st_end..].strip_prefix('}').unwrap_or("");
}
rules
}
pub fn parse_media_rules(input: &str) -> Vec<MediaRule> {
let mut rules: Vec<MediaRule> = Vec::new();
let mut remaining: &str = input;
while !remaining.is_empty() {
if !remaining.starts_with("@media ") {
break;
}
let after_prefix: &str = remaining.strip_prefix("@media ").unwrap_or("");
let query_end: Option<usize> = after_prefix.find(" { ");
let Some(q_end) = query_end else {
break;
};
let query: &str = &after_prefix[..q_end];
let after_query: &str = after_prefix[q_end..].strip_prefix(" { ").unwrap_or("");
let style_end: Option<usize> = after_query.find('}');
let Some(st_end) = style_end else {
break;
};
let style: &str = &after_query[..st_end];
if !query.is_empty() && !style.is_empty() {
rules.push(MediaRule::new(query.to_string(), style.to_string()));
}
remaining = after_query[st_end..].strip_prefix('}').unwrap_or("");
}
rules
}
pub fn inject_style(&self) {
let class_rule: String = format!(".{} {{ {} }}", self.get_name(), self.get_style());
let mut css: String = class_rule;
for pseudo_rule in self.get_pseudo_rules() {
if !pseudo_rule.get_style().is_empty() {
let pseudo_rule_str: String = format!(
".{}{} {{ {} }}",
self.get_name(),
pseudo_rule.get_selector(),
pseudo_rule.get_style()
);
css = format!("{}\n{}", css, pseudo_rule_str);
}
}
for media_rule in self.get_media_rules() {
if !media_rule.get_query().is_empty() {
let media_rule_str: String = format!(
"@media {} {{ .{} {{ {} }} }}",
media_rule.get_query(),
self.get_name(),
media_rule.get_style()
);
css = format!("{}\n{}", css, media_rule_str);
}
}
Self::inject_css(&css);
}
pub fn inject_css(css: &str) {
let _ = css;
#[cfg(target_arch = "wasm32")]
{
let style_id: &str = "euv-css-injected";
let document: Document = window()
.expect("no global window exists")
.document()
.expect("no document exists");
let style_element: HtmlStyleElement = match document.get_element_by_id(style_id) {
Some(el) => el.dyn_into::<HtmlStyleElement>().unwrap(),
None => {
let el: HtmlStyleElement = document
.create_element("style")
.unwrap()
.dyn_into::<HtmlStyleElement>()
.unwrap();
el.set_id(style_id);
document.head().unwrap().append_child(&el).unwrap();
el
}
};
let existing_css: String = style_element.inner_text();
if !css.is_empty() && !existing_css.contains(css) {
let new_css: String = if existing_css.is_empty() {
css.to_string()
} else {
format!("{}\n{}", existing_css, css)
};
style_element.set_inner_text(&new_css);
}
}
}
}
impl std::fmt::Display for CssClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.get_name())
}
}