use leptos::prelude::*;
use crate::icons::{Icon, icondata};
use crate::components::shared::{MENU_ITEM_CLASS, FOCUS_RING, CONTROL_MOTION};
#[component]
pub fn Select(
#[prop(into)] value: RwSignal<String>,
#[prop(optional, into)] placeholder: String,
children: ChildrenFn,
) -> impl IntoView {
let open = RwSignal::new(false);
provide_context(open);
provide_context(value);
let placeholder = StoredValue::new(placeholder);
let children = StoredValue::new(children);
view! {
<div class="relative inline-block w-full">
<SelectTrigger value=value placeholder=placeholder.get_value() />
{children.with_value(|c| c())}
</div>
}
}
#[component]
fn SelectTrigger(value: RwSignal<String>, placeholder: String) -> impl IntoView {
let open = use_context::<RwSignal<bool>>()
.expect("SelectTrigger must be inside Select");
let trigger_class = format!(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground shadow-elev-sm hover:border-ring/60 {} {}",
CONTROL_MOTION, FOCUS_RING
);
view! {
<button
type="button"
class=trigger_class
on:click=move |_| open.update(|v| *v = !*v)
>
<span class=move || {
if value.get().is_empty() { "text-muted-foreground" } else { "text-foreground" }
}>
{move || {
let v = value.get();
if v.is_empty() { placeholder.clone() } else { v }
}}
</span>
<Icon icon=Signal::derive(|| icondata::LuChevronDown) width="16" height="16" />
</button>
}
}
#[component]
pub fn SelectContent(children: ChildrenFn) -> impl IntoView {
let open = use_context::<RwSignal<bool>>()
.expect("SelectContent must be inside Select");
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-1 w-full rounded-md border border-border bg-card p-1 shadow-md animate-scale-in">
{children.with_value(|c| c())}
</div>
</Show>
}
}
#[component]
pub fn SelectItem(#[prop(into)] value: String, children: Children) -> impl IntoView {
let open = use_context::<RwSignal<bool>>()
.expect("SelectItem must be inside Select");
let selected = use_context::<RwSignal<String>>()
.expect("SelectItem must be inside Select");
let item_value = StoredValue::new(value);
let handle_click = move |_| {
selected.set(item_value.get_value());
open.set(false);
};
view! {
<div
class=MENU_ITEM_CLASS
on:click=handle_click
>
{children()}
<Show when=move || selected.get() == item_value.get_value()>
<Icon icon=Signal::derive(|| icondata::LuCheck) width="14" height="14" />
</Show>
</div>
}
}