recipes/frontend/src/utils.rs

200 lines
4.9 KiB
Rust

use std::str::FromStr;
use chrono::Locale;
use gloo::utils::{document, window};
use wasm_bindgen::prelude::*;
use web_sys::Element;
pub trait SelectorExt {
fn selector<T>(&self, selectors: &str) -> T
where
T: JsCast;
fn selector_all<T>(&self, selectors: &str) -> Vec<T>
where
T: JsCast;
fn selector_upwards<T>(&self, selector: &str) -> Option<T>
where
T: JsCast;
fn deep_clone(&self) -> Self;
}
impl SelectorExt for Element {
fn selector<T>(&self, selectors: &str) -> T
where
T: JsCast,
{
self.query_selector(selectors)
.unwrap()
.unwrap()
.dyn_into::<T>()
.unwrap()
}
fn selector_all<T>(&self, selectors: &str) -> Vec<T>
where
T: JsCast,
{
self.query_selector_all(selectors)
.unwrap()
.values()
.into_iter()
.map(|e| e.unwrap().dyn_into::<T>().unwrap())
.collect()
}
fn selector_upwards<T>(&self, selector: &str) -> Option<T>
where
T: JsCast,
{
let mut current = self.clone();
// while let Some(parent) = current.parent_element() {
loop {
if let Ok(true) = current.matches(selector) {
return Some(current.dyn_into::<T>().unwrap());
}
if let Some(parent) = current.parent_element() {
current = parent;
} else {
return None;
}
}
}
fn deep_clone(&self) -> Self {
self.clone_node_with_deep(true)
.unwrap()
.dyn_into::<Element>()
.unwrap()
}
}
// Not used anymore.
// pub trait NodeHelperExt {
// fn is_parent(&self, child: &Node) -> bool;
// }
// impl NodeHelperExt for Node {
// fn is_parent(&self, child: &Node) -> bool {
// let mut node = child.clone();
// while let Some(parent) = node.parent_node() {
// if &parent == self {
// return true;
// }
// node = parent;
// }
// false
// }
// }
pub fn selector<T>(selectors: &str) -> T
where
T: JsCast,
{
document()
.query_selector(selectors)
.unwrap()
.unwrap()
.dyn_into::<T>()
.unwrap()
}
pub fn selector_all<T>(selectors: &str) -> Vec<T>
where
T: JsCast,
{
document()
.query_selector_all(selectors)
.unwrap()
.values()
.into_iter()
.map(|e| e.unwrap().dyn_into::<T>().unwrap())
.collect()
}
pub fn selector_and_clone<T>(selectors: &str) -> T
where
T: JsCast,
{
document()
.query_selector(selectors)
.unwrap()
.unwrap()
.clone_node_with_deep(true)
.unwrap()
.dyn_into::<T>()
.unwrap()
}
pub fn by_id<T>(element_id: &str) -> T
where
T: JsCast,
{
document()
.get_element_by_id(element_id)
.unwrap()
.dyn_into::<T>()
.unwrap()
}
pub fn get_current_lang() -> String {
selector::<Element>("html")
.get_attribute("lang")
.unwrap()
.split("-")
.next()
.unwrap()
.to_string()
}
pub fn get_locale() -> Locale {
let lang_and_territory = selector::<Element>("html")
.get_attribute("lang")
.unwrap()
.replace("-", "_");
Locale::from_str(&lang_and_territory).unwrap_or_default()
}
/// Extracts and remove some URL parameters from the current URL.
pub fn extract_get_parameters(names: &[&str]) -> Vec<(String, String)> {
let mut param_values = vec![];
let location = window().location();
if let Ok(search) = location.search() {
if !search.is_empty() && search.starts_with('?') {
let mut search_chars = search.chars();
search_chars.next(); // To remove the first character which is '?'.
let mut new_search = String::with_capacity(search.len());
for kv in search_chars.as_str().split('&') {
match kv.split('=').collect::<Vec<&str>>()[..] {
[key, value] if names.contains(&key) => {
param_values.push((key.to_string(), value.to_string()));
}
_ => {
if !new_search.is_empty() {
new_search.push('&');
}
new_search.push_str(kv);
}
}
}
if !param_values.is_empty() {
let mut new_url = location.pathname().unwrap();
if !new_search.is_empty() {
new_url.push('?');
new_url.push_str(&new_search);
}
window()
.history()
.unwrap()
.push_state_with_url(&JsValue::null(), "", Some(&new_url))
.unwrap();
}
}
}
param_values
}