thaw 0.4.8

An easy to use leptos component library
Documentation
use super::utils::{get_dropdown_action_from_key, DropdownAction};
use crate::_aria::ActiveDescendantController;
use leptos::{context::Provider, ev, html, prelude::*};
use std::sync::Arc;
use thaw_utils::mount_style;
use web_sys::{HtmlElement, Node};

#[component]
pub fn Listbox(
    class: &'static str,
    set_listbox: Arc<dyn Fn(Node) + Send + Sync>,
    listbox_ref: NodeRef<html::Div>,
    children: Children,
) -> impl IntoView {
    mount_style("listbox", include_str!("./listbox.css"));

    let trigger = ArcTrigger::new();
    let effect = RenderEffect::new({
        let trigger = trigger.clone();
        move |_| {
            trigger.track();
            if let Some(listbox_el) = listbox_ref.get() {
                set_listbox(listbox_el.into());
            }
        }
    });

    on_cleanup(move || {
        drop(effect);
    });

    view! {
        <div
            class=format!("thaw-listbox {class}")
            node_ref=listbox_ref
            role="listbox"
            on:mousedown=|e| e.prevent_default()
        >
            <Provider value=ListboxInjection(trigger)>{children()}</Provider>
        </div>
    }
}

#[derive(Clone)]
pub(crate) struct ListboxInjection(ArcTrigger);

impl ListboxInjection {
    #[inline]
    pub fn expect_context() -> Self {
        expect_context()
    }

    #[inline]
    pub fn trigger(&self) {
        self.0.notify();
    }
}

pub fn listbox_keyboard_event(
    e: ev::KeyboardEvent,
    open: RwSignal<bool>,
    multiselect: bool,
    active_descendant_controller: &ActiveDescendantController,
    select_option: impl Fn(HtmlElement),
) {
    let (open, set_open) = open.split();
    let open = open.get_untracked();
    let action = get_dropdown_action_from_key(&e, open, multiselect);
    let active_option = active_descendant_controller.active();

    match action {
        DropdownAction::Type | DropdownAction::Open => {
            if !open {
                set_open.set(true);
            }
            if action == DropdownAction::Open {
                e.prevent_default();
            }
        }
        DropdownAction::CloseSelect | DropdownAction::Select => {
            if let Some(option) = active_option {
                e.prevent_default();
                select_option(option);
            }
        }
        DropdownAction::Next => {
            e.prevent_default();
            if active_option.is_some() {
                active_descendant_controller.next();
            } else {
                active_descendant_controller.first();
            }
        }
        DropdownAction::Previous => {
            e.prevent_default();
            if active_option.is_some() {
                active_descendant_controller.prev();
            } else {
                active_descendant_controller.first();
            }
        }
        DropdownAction::First | DropdownAction::PageUp => {
            e.prevent_default();
            active_descendant_controller.first();
        }
        DropdownAction::Last | DropdownAction::PageDown => {
            e.prevent_default();
            active_descendant_controller.last();
        }
        DropdownAction::Tab | DropdownAction::Close | DropdownAction::None => {}
    };
}