use topcoat::{
Result,
icon::icon,
view::{Attributes, Child, StaticClass, View, attributes, class, component, view},
};
use crate::icons;
const TOASTER: StaticClass = class!(
"pointer-events-none fixed right-4 bottom-4 z-50 flex w-[356px] \
max-w-[calc(100vw-2rem)] flex-col gap-3.5 sm:right-6 sm:bottom-6",
);
const TOAST: StaticClass = class!(
"pointer-events-auto relative flex w-full items-center gap-1.5 rounded-lg \
border border-border bg-background p-4 text-[13px] text-foreground shadow-lg \
[overflow-wrap:anywhere] \
invisible opacity-0 starting:visible starting:translate-y-full starting:opacity-100 \
[transition:translate_400ms_cubic-bezier(0.25,0.1,0.25,1),opacity_400ms_ease_4s,visibility_0s_linear_4.4s] \
hover:visible hover:opacity-100 hover:[transition-delay:0s] \
focus-within:visible focus-within:opacity-100 focus-within:[transition-delay:0s] \
has-[[data-dismiss]:checked]:hidden \
focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:outline-none",
);
const ICON: StaticClass = class!("flex size-4 shrink-0 items-center justify-center");
const CONTENT: StaticClass = class!("flex min-w-0 flex-1 flex-col gap-0.5");
const TITLE: StaticClass = class!("font-medium leading-normal");
const DESCRIPTION: StaticClass = class!("leading-snug text-muted-foreground");
const CLOSE: StaticClass = class!(
"absolute top-0 left-0 flex size-5 -translate-x-[35%] -translate-y-[35%] \
cursor-pointer items-center justify-center rounded-full border border-border \
bg-background text-foreground transition-colors hover:bg-muted",
);
#[component]
pub async fn toaster(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<section
aria-label="Notifications"
tabindex="-1"
aria-live="polite"
aria-relevant="additions text"
aria-atomic="false"
>
<ol
class=(class!(TOASTER, attrs.remove("class")))
data-sonner-toaster=""
data-y-position="bottom"
data-x-position="right"
(attrs)
>
(child)
</ol>
</section>
})
}
#[component]
pub async fn toast(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<li
tabindex="0"
class=(class!(TOAST, attrs.remove("class")))
data-sonner-toast=""
data-styled="true"
data-visible="true"
data-y-position="bottom"
data-x-position="right"
data-front="true"
(attrs)
>
(child)
</li>
})
}
#[component]
pub async fn toast_icon(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
class=(class!(ICON, attrs.remove("class")))
data-icon=""
aria-hidden="true"
(attrs)
>
(child)
</div>
})
}
#[component]
pub async fn toast_content(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div class=(class!(CONTENT, attrs.remove("class"))) data-content="" (attrs)>
(child)
</div>
})
}
#[component]
pub async fn toast_title(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div class=(class!(TITLE, attrs.remove("class"))) data-title="" (attrs)>
(child)
</div>
})
}
#[component]
pub async fn toast_description(
#[default] mut attrs: Attributes,
#[default] child: Child<'_>,
) -> Result<impl View> {
Ok(view! {
<div
class=(class!(DESCRIPTION, attrs.remove("class")))
data-description=""
(attrs)
>
(child)
</div>
})
}
#[component]
pub async fn toast_close(#[default] mut attrs: Attributes) -> Result<impl View> {
Ok(view! {
<label
class=(class!(
CLOSE,
"has-focus-visible:ring-2 has-focus-visible:ring-ring/50",
attrs.remove("class"),
))
data-close-button=""
(attrs)
>
<input
type="checkbox"
class="sr-only"
aria-label="Close toast"
data-dismiss=""
>
icon(data: icons::X, attrs: attributes! { class="size-3" })
</label>
})
}