thaw 0.4.8

An easy to use leptos component library
Documentation
use leptos::prelude::{document, SetValue, StoredValue, WithValue};
use send_wrapper::SendWrapper;
use std::sync::Arc;
use wasm_bindgen::{closure::Closure, JsCast, UnwrapThrowExt};
use web_sys::{HtmlElement, Node, NodeFilter, TreeWalker};

pub fn use_option_walker<MF>(match_option: MF) -> (Box<dyn Fn(&Node) + Send + Sync>, OptionWalker)
where
    MF: Fn(HtmlElement) -> bool + Send + Sync + 'static,
{
    let tree_walker = StoredValue::new(
        None::<(
            SendWrapper<TreeWalker>,
            SendWrapper<Closure<dyn Fn(Node) -> u32>>,
        )>,
    );
    let option_walker = OptionWalker(tree_walker);
    let match_option = Arc::new(match_option);
    let set_listbox = move |el: &Node| {
        let match_option = match_option.clone();
        let cb: Closure<dyn Fn(Node) -> u32> = Closure::new(move |node: Node| {
            if let Ok(html_element) = node.dyn_into() {
                if match_option(html_element) {
                    return 1u32;
                }
            }

            3u32
        });
        let node_filter = NodeFilter::new();
        node_filter.set_accept_node(cb.as_ref().unchecked_ref());

        let tw = document()
            .create_tree_walker_with_what_to_show_and_filter(el, 0x1, Some(&node_filter))
            .unwrap_throw();
        tree_walker.set_value(Some((SendWrapper::new(tw), SendWrapper::new(cb))));
    };

    (Box::new(set_listbox), option_walker)
}

#[derive(Clone)]
pub struct OptionWalker(
    StoredValue<
        Option<(
            SendWrapper<TreeWalker>,
            SendWrapper<Closure<dyn Fn(Node) -> u32>>,
        )>,
    >,
);

impl OptionWalker {
    pub fn first(&self) -> Option<HtmlElement> {
        self.0.with_value(|tree_walker| {
            let Some((tree_walker, _)) = tree_walker.as_ref() else {
                return None;
            };
            tree_walker.set_current_node(&tree_walker.root());
            tree_walker.first_child().unwrap_throw()?.dyn_into().ok()
        })
    }

    pub fn last(&self) -> Option<HtmlElement> {
        self.0.with_value(|tree_walker| {
            let Some((tree_walker, _)) = tree_walker.as_ref() else {
                return None;
            };
            tree_walker.set_current_node(&tree_walker.root());
            tree_walker.last_child().unwrap_throw()?.dyn_into().ok()
        })
    }

    pub fn next(&self) -> Option<HtmlElement> {
        self.0.with_value(|tree_walker| {
            let Some((tree_walker, _)) = tree_walker.as_ref() else {
                return None;
            };
            tree_walker.next_node().unwrap_throw()?.dyn_into().ok()
        })
    }

    pub fn prev(&self) -> Option<HtmlElement> {
        self.0.with_value(|tree_walker| {
            let Some((tree_walker, _)) = tree_walker.as_ref() else {
                return None;
            };
            tree_walker.previous_node().unwrap_throw()?.dyn_into().ok()
        })
    }

    pub fn find(&self, predicate: impl Fn(String) -> bool) -> Option<HtmlElement> {
        self.0.with_value(|tree_walker| {
            let Some((tree_walker, _)) = tree_walker.as_ref() else {
                return None;
            };
            tree_walker.set_current_node(&tree_walker.root());
            let mut current: Option<HtmlElement> =
                tree_walker.first_child().unwrap_throw()?.dyn_into().ok();

            while let Some(cur) = current.as_ref() {
                if predicate(cur.id()) {
                    break;
                }
                current = tree_walker.next_node().unwrap_throw()?.dyn_into().ok();
            }

            current
        })
    }
}