use crate::prelude::*;
use std::collections::{HashMap, HashSet};
use web_sys::window;
use yew::virtual_dom::VTag;
static METATYPES: [&'static str; 5] = ["name", "httpEquiv", "charSet", "itemProp", "property"];
type MetaCategories = HashMap<&'static str, HashSet<String>>;
pub fn default_head() -> Html {
rsx! { <meta charset="utf-8" /> }
}
pub fn map_components(components: Vec<Html>) -> Vec<Html> {
let flattened: Vec<Html> = components
.into_iter()
.flat_map(|c| match c {
Html::VTag(tag) => tag.children().into_iter().cloned().collect::<Vec<_>>(),
_ => vec![],
})
.collect();
let filtered: Vec<Html> = flattened.into_iter().filter(unique).collect();
let mut head = vec![default_head()];
for child in filtered.clone() {
match child {
Html::VTag(_tag) => {
}
Html::VText(text) => {
let text_str = text.text;
let mut tag = VTag::new("title");
tag.add_child(text_str.into());
head.push(tag.into());
}
Html::VComp(_component) => {
}
_ => {}
}
}
let final_result: Vec<Html> = head
.into_iter()
.map(|c| match c {
Html::VTag(mut tag) => {
let class_name = format!(
"{} {}",
"next-rs-tag",
tag.attributes
.iter()
.find(|(key, _)| *key == "class")
.map(|(_, value)| value)
.unwrap_or_default()
);
tag.add_attribute("class", class_name);
Html::VTag(tag)
}
_ => c,
})
.collect();
final_result
}
pub fn unique(head: &Html) -> bool {
match head {
Html::VTag(tag) => match tag.tag() {
"title" | "base" => tag.key.is_some(),
"meta" => {
for metatype in METATYPES.iter() {
if !tag
.attributes
.iter()
.find(|(key, _)| *key == *metatype)
.map(|(_, value)| value)
.unwrap_or_default()
.is_empty()
{
match *metatype {
"charSet" => {
if !tag
.attributes
.iter()
.find(|(key, _)| *key == "charSet")
.map(|(_, value)| value)
.unwrap_or_default()
.is_empty()
{
return false;
}
}
_ => {
let category = tag
.attributes
.iter()
.find(|(key, _)| *key == *metatype)
.map(|(_, value)| value)
.unwrap_or_default();
let mut meta_categories = MetaCategories::new();
let categories = meta_categories
.entry(metatype)
.or_insert_with(|| HashSet::new());
if categories.contains(&category.to_string()) {
return false;
}
categories.insert(category.to_string());
}
}
}
}
true
}
_ => true,
},
_ => true,
}
}
#[derive(Properties, Clone, PartialEq)]
pub struct HeadProps {
pub children: Html,
}
#[func]
pub fn Head(props: &HeadProps) -> Html {
let state: Vec<Html> = map_components(vec![props.children.clone()]);
let document = window().and_then(|win| win.document()).unwrap();
let head = document.head().expect("Failed to get head element");
create_portal(rsx! {<>{ for state.into_iter() }</> }, head.clone().into())
}