soma-ui 0.1.1

A Leptos 0.8 component library: 160+ accessible UI components, blocks, charts, and a ChartDB-style schema diagram — with a prebuilt stylesheet.
Documentation
use crate::components::shared::{MENU_ITEM_CLASS, MENU_SEPARATOR_CLASS};
use leptos::prelude::*;

// ponytail: click-catcher (fixed inset-0 z-40) handles outside-click close.
// Panel at z-50. Anchored via relative/absolute — no portal needed.

#[component]
pub fn DropdownMenu(children: ChildrenFn) -> impl IntoView {
    let open = RwSignal::new(false);
    provide_context(open);

    let children = StoredValue::new(children);

    view! {
        <div class="relative inline-block">
            {children.with_value(|c| c())}
        </div>
    }
}

#[component]
pub fn DropdownMenuTrigger(children: Children) -> impl IntoView {
    let open =
        use_context::<RwSignal<bool>>().expect("DropdownMenuTrigger must be inside DropdownMenu");

    view! {
        <div on:click=move |_| open.update(|v| *v = !*v) style="display:contents">
            {children()}
        </div>
    }
}

#[component]
pub fn DropdownMenuContent(children: ChildrenFn) -> impl IntoView {
    let open =
        use_context::<RwSignal<bool>>().expect("DropdownMenuContent must be inside DropdownMenu");
    let close = move |_| open.set(false);

    let children = StoredValue::new(children);

    view! {
        <Show when=move || open.get()>
            <div class="fixed inset-0 z-40" on:click=close />
            <div class="absolute z-50 mt-2 min-w-[12rem] rounded-md border border-border bg-card p-1 shadow-elev-md animate-scale-in">
                {children.with_value(|c| c())}
            </div>
        </Show>
    }
}

#[component]
pub fn DropdownMenuItem(
    #[prop(optional)] on_click: Option<Callback<()>>,
    children: Children,
) -> impl IntoView {
    let open =
        use_context::<RwSignal<bool>>().expect("DropdownMenuItem must be inside DropdownMenu");

    let handle_click = move |_| {
        open.set(false);
        if let Some(cb) = on_click {
            cb.run(());
        }
    };

    view! {
        <div
            class=MENU_ITEM_CLASS
            on:click=handle_click
        >
            {children()}
        </div>
    }
}

#[component]
pub fn DropdownMenuLabel(children: Children) -> impl IntoView {
    view! {
        <div class="px-2 py-1.5 text-xs font-semibold text-muted-foreground">
            {children()}
        </div>
    }
}

#[component]
pub fn DropdownMenuSeparator() -> impl IntoView {
    view! {
        <div class=MENU_SEPARATOR_CLASS />
    }
}