use topcoat::{
Result,
icon::{icon, iconify::iconify_icon},
view::{Attributes, Child, StaticClass, View, attributes, class, component, view},
};
use super::floating::{FloatingAlign, FloatingAnchor, FloatingSide, floating_variants};
#[component]
pub async fn dropdown_menu(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
class=(class!("group/dropdown-menu contents", attrs.remove("class")))
(attrs)
>
(child)
</div>
})
}
#[component]
pub async fn dropdown_menu_trigger(
target: &str,
#[default]
mut attrs: Attributes,
#[default]
child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<button
type="button"
popovertarget=(target)
class=(class!("cursor-pointer", attrs.remove("class")))
(attrs)
>
(child)
</button>
})
}
const PANEL: StaticClass = class!(
"min-w-40 rounded-lg border border-border bg-popover p-1",
"text-popover-foreground shadow-sm outline-none",
);
#[component]
pub async fn dropdown_menu_content(
id: &str,
#[default]
side: FloatingSide,
#[default]
align: FloatingAlign,
#[default]
mut attrs: Attributes,
#[default]
child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
id=(id)
popover="auto"
class=(class!(
floating_variants(FloatingAnchor::Invoker, side, align),
PANEL,
attrs.remove("class"),
))
(attrs)
>
(child)
</div>
})
}
const ITEM: StaticClass = class!(
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm",
"whitespace-nowrap outline-none hover:bg-foreground/5 focus-visible:bg-foreground/5",
"active:bg-foreground/10 disabled:pointer-events-none disabled:opacity-50",
"[&_svg]:size-4 [&_svg]:shrink-0",
);
#[component]
pub async fn dropdown_menu_item(
#[default]
close: Option<&str>,
#[default]
mut attrs: Attributes,
#[default]
child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<button
type="button"
popovertarget=(close)
popovertargetaction=(close.map(|_| "hide"))
class=(class!(ITEM, attrs.remove("class")))
(attrs)
>
(child)
</button>
})
}
#[component]
pub async fn dropdown_menu_sub(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
class=(class!("group/dropdown-menu-sub contents", attrs.remove("class")))
(attrs)
>
(child)
</div>
})
}
#[component]
pub async fn dropdown_menu_sub_trigger(
target: &str,
#[default]
mut attrs: Attributes,
#[default]
child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<button
type="button"
popovertarget=(target)
class=(class!(
ITEM,
"group-has-[:popover-open]/dropdown-menu-sub:bg-foreground/5",
attrs.remove("class"),
))
(attrs)
>
(child)
icon(
data: iconify_icon!("lucide:chevron-right"),
attrs: attributes! { class="ml-auto" }
)
</button>
})
}
#[component]
pub async fn dropdown_menu_sub_content(
id: &str,
#[default]
mut attrs: Attributes,
#[default]
child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
id=(id)
popover="auto"
class=(class!(
floating_variants(
FloatingAnchor::Invoker,
FloatingSide::Right,
FloatingAlign::Start,
),
PANEL,
attrs.remove("class"),
))
(attrs)
>
(child)
</div>
})
}
#[component]
pub async fn dropdown_menu_label(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<p
class=(class!(
"px-2 py-1.5 text-xs font-medium text-muted-foreground",
attrs.remove("class"),
))
(attrs)
>
(child)
</p>
})
}
#[component]
pub async fn dropdown_menu_separator(#[default] mut attrs: Attributes) -> Result<impl View> {
Ok(view! {
<hr class=(class!("-mx-1 my-1 border-border", attrs.remove("class"))) (attrs)>
})
}