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 super::shared::{EscapeBackdrop, OverlayDescription, OverlayFooter, OverlayTitle};
use crate::icons::{icondata, Icon};
use leptos::portal::Portal;
use leptos::prelude::*;

/// Centered modal dialog. Caller owns the open signal.
/// ponytail: Escape-to-close via window_event_listener (leptos_dom::helpers, re-exported from
/// leptos::prelude::*). Registered in DialogOverlay — auto-removed on unmount.
#[component]
pub fn Dialog(#[prop(into)] open: RwSignal<bool>, children: ChildrenFn) -> impl IntoView {
    provide_context(open);

    // StoredValue allows repeated access from Fn closures (unlike a plain captured variable).
    let children = StoredValue::new(children);

    view! {
        <Portal>
            <Show when=move || open.get()>
                <EscapeBackdrop open=open>
                    {children.with_value(|c| c())}
                </EscapeBackdrop>
            </Show>
        </Portal>
    }
}

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

    view! {
        <div class="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-full max-w-lg rounded-lg border border-border bg-card p-6 shadow-elev-lg animate-scale-in">
            <button
                class="absolute top-4 end-4 flex items-center justify-center w-7 h-7 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
                aria-label="Close"
                on:click=close
            >
                <Icon icon=Signal::derive(|| icondata::LuX) width="16" height="16" />
            </button>
            {children()}
        </div>
    }
}

#[component]
pub fn DialogHeader(children: Children) -> impl IntoView {
    view! { <div class="mb-4">{children()}</div> }
}

#[component]
pub fn DialogTitle(children: Children) -> impl IntoView {
    view! { <OverlayTitle>{children()}</OverlayTitle> }
}

#[component]
pub fn DialogDescription(children: Children) -> impl IntoView {
    view! { <OverlayDescription>{children()}</OverlayDescription> }
}

#[component]
pub fn DialogFooter(children: Children) -> impl IntoView {
    view! { <OverlayFooter>{children()}</OverlayFooter> }
}