use super::dom::{attr_value, strip_text, tags_where};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Tab {
pub label: String,
pub badge: Option<String>,
pub active: bool,
pub errored: bool,
pub keys: Vec<String>,
}
pub fn tabs(html: &str) -> Vec<Tab> {
tags_where(html, |tag, attrs| {
tag == "button" && attr_value(attrs, "role").as_deref() == Some("tab")
})
.into_iter()
.map(|(_, attrs, inner)| {
let panel = attr_value(&attrs, "aria-controls").and_then(|id| {
tags_where(html, |_, attrs| {
attr_value(attrs, "id").as_deref() == Some(id.as_str())
})
.into_iter()
.next()
});
let shown = panel
.as_ref()
.is_some_and(|(_, attrs, _)| !attrs.iter().any(|(name, _)| name == "hidden"));
let mut keys: Vec<String> = Vec::new();
if let Some((_, _, panel)) = &panel {
for (_, attrs, _) in tags_where(panel, |_, attrs| attr_value(attrs, "name").is_some()) {
let key = attr_value(&attrs, "name").unwrap_or_default();
if !keys.contains(&key) {
keys.push(key);
}
}
}
let (label, badge) = match inner.find("data-tab-badge") {
Some(at) => {
let start = inner[..at].rfind('<').unwrap_or(at);
(&inner[..start], Some(strip_text(&inner[start..])))
}
None => (inner.as_str(), None),
};
Tab {
label: strip_text(label),
badge,
active: shown && attr_value(&attrs, "aria-selected").as_deref() == Some("true"),
errored: attrs.iter().any(|(name, _)| name == "data-tab-error"),
keys,
}
})
.collect()
}