use icons::X; use leptos::context::Provider; use leptos::prelude::*; use leptos_ui::clx; use tw_merge::*; use crate::components::hooks::use_random::use_random_id_for; use crate::components::ui::button::{Button, ButtonSize, ButtonVariant}; mod components { use super::*; clx! {DialogBody, div, "flex flex-col gap-4"} clx! {DialogHeader, div, "flex flex-col gap-2 text-center sm:text-left"} clx! {DialogTitle, h3, "text-lg leading-none font-semibold"} clx! {DialogDescription, p, "text-muted-foreground text-sm"} clx! {DialogFooter, footer, "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"} } pub use components::*; /* ========================================================== */ /* ✨ FUNCTIONS ✨ */ /* ========================================================== */ #[derive(Clone)] struct DialogContext { target_id: String, } #[component] pub fn Dialog(children: Children, #[prop(optional, into)] class: String) -> impl IntoView { let dialog_target_id = use_random_id_for("dialog"); let ctx = DialogContext { target_id: dialog_target_id.clone() }; let merged_class = tw_merge!("w-fit", class); view! {
{children()}
} } #[component] pub fn DialogTrigger( children: Children, #[prop(optional, into)] class: String, #[prop(default = ButtonVariant::Outline)] variant: ButtonVariant, #[prop(default = ButtonSize::Default)] size: ButtonSize, ) -> impl IntoView { let ctx = expect_context::(); let trigger_id = format!("trigger_{}", ctx.target_id); view! { } } #[component] pub fn DialogContent( children: Children, #[prop(optional, into)] class: String, #[prop(into, optional)] hide_close_button: Option, #[prop(default = true)] close_on_backdrop_click: bool, #[prop(default = "Dialog")] data_name_prefix: &'static str, ) -> impl IntoView { let ctx = expect_context::(); let merged_class = tw_merge!( // "flex flex-col gap-4", // TODO 🐛 Bug when I try to have this.. Using DialogBody instead. "relative bg-background border rounded-2xl shadow-lg p-6 w-full max-w-[calc(100%-2rem)] max-h-[85vh] fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-100 transition-all duration-200 data-[state=closed]:opacity-0 data-[state=closed]:scale-95 data-[state=open]:opacity-100 data-[state=open]:scale-100", class ); let backdrop_data_name = format!("{}Backdrop", data_name_prefix); let content_data_name = format!("{}Content", data_name_prefix); let target_id_clone = ctx.target_id.clone(); let backdrop_id = format!("{}_backdrop", ctx.target_id); let target_id_for_script = ctx.target_id.clone(); let backdrop_id_for_script = backdrop_id.clone(); let backdrop_behavior = if close_on_backdrop_click { "auto" } else { "manual" }; view! {
{children()}
} } #[component] pub fn DialogClose( children: Children, #[prop(optional, into)] class: String, #[prop(default = ButtonVariant::Outline)] variant: ButtonVariant, #[prop(default = ButtonSize::Default)] size: ButtonSize, ) -> impl IntoView { let ctx = expect_context::(); view! { } } #[component] pub fn DialogAction( children: Children, #[prop(optional, into)] class: String, #[prop(default = ButtonVariant::Default)] variant: ButtonVariant, #[prop(default = ButtonSize::Default)] size: ButtonSize, ) -> impl IntoView { let ctx = expect_context::(); view! { } }