use crate::components::{Button, ButtonType, Icon, IconType};
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum DialogType {
Default,
Info,
Success,
Warning,
Error,
}
impl DialogType {
pub fn icon(&self) -> IconType {
match self {
DialogType::Default => IconType::Info,
DialogType::Info => IconType::Info,
DialogType::Success => IconType::Success,
DialogType::Warning => IconType::Warning,
DialogType::Error => IconType::Error,
}
}
pub fn color_class(&self) -> &'static str {
match self {
DialogType::Default => "text-gray-600 dark:text-gray-300",
DialogType::Info => "text-blue-600 dark:text-blue-400",
DialogType::Success => "text-green-600 dark:text-green-400",
DialogType::Warning => "text-orange-600 dark:text-orange-400",
DialogType::Error => "text-red-600 dark:text-red-400",
}
}
}
#[derive(Props, Clone, PartialEq)]
pub struct DialogProps {
#[props(default = false)]
pub visible: bool,
#[props(default)]
pub title: Option<String>,
pub content: String,
#[props(default = DialogType::Default)]
pub dialog_type: DialogType,
#[props(default = false)]
pub mask_closable: bool,
#[props(default = true)]
pub closable: bool,
#[props(default = true)]
pub show_confirm: bool,
#[props(default = true)]
pub show_cancel: bool,
#[props(default = "确认".to_string())]
pub confirm_text: String,
#[props(default = "取消".to_string())]
pub cancel_text: String,
#[props(default = ButtonType::Primary)]
pub confirm_type: ButtonType,
#[props(default = ButtonType::Default)]
pub cancel_type: ButtonType,
#[props(default)]
pub on_confirm: Option<EventHandler<()>>,
#[props(default)]
pub on_cancel: Option<EventHandler<()>>,
#[props(default)]
pub on_close: Option<EventHandler<()>>,
#[props(default)]
pub class: Option<String>,
}
#[component]
pub fn Dialog(props: DialogProps) -> Element {
let DialogProps {
visible,
title,
content,
dialog_type,
mask_closable,
closable,
show_confirm,
show_cancel,
confirm_text,
cancel_text,
confirm_type,
cancel_type,
on_confirm,
on_cancel,
on_close,
class,
} = props;
let handle_mask_click = move |_| {
if mask_closable {
if let Some(handler) = &on_close {
handler.call(());
}
}
};
let handle_confirm = move |_| {
if let Some(handler) = &on_confirm {
handler.call(());
}
if let Some(handler) = &on_close {
handler.call(());
}
};
let handle_cancel = move |_| {
if let Some(handler) = &on_cancel {
handler.call(());
}
if let Some(handler) = &on_close {
handler.call(());
}
};
let handle_close = move |_| {
if let Some(handler) = &on_close {
handler.call(());
}
};
if !visible {
return rsx! { div {} };
}
let dialog_class = if let Some(custom_class) = class {
format!(
"fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-auto {}",
custom_class
)
} else {
"fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-auto".to_string()
};
rsx! {
div {
class: dialog_class,
div {
class: "absolute inset-0 bg-black bg-opacity-30 transition-opacity pointer-events-auto",
onclick: handle_mask_click,
}
div {
class: "relative bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md max-h-[90vh] overflow-hidden pointer-events-auto",
div {
class: "p-6",
if title.is_some() || dialog_type != DialogType::Default {
div {
class: "flex items-center gap-3 mb-4",
if dialog_type != DialogType::Default {
Icon {
icon: dialog_type.icon(),
class: format!("w-5 h-5 {}", dialog_type.color_class()),
}
}
if let Some(title_text) = title {
h3 {
class: "text-lg font-semibold text-gray-900 dark:text-white",
"{title_text}"
}
}
}
}
div {
class: "text-sm text-gray-700 dark:text-gray-300 leading-relaxed mb-6",
"{content}"
}
if show_confirm || show_cancel {
div {
class: "flex justify-end gap-3",
if show_cancel {
Button {
button_type: cancel_type,
onclick: handle_cancel,
"{cancel_text}"
}
}
if show_confirm {
Button {
button_type: confirm_type,
onclick: handle_confirm,
"{confirm_text}"
}
}
}
}
if closable {
button {
class: "absolute top-4 right-4 p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors",
onclick: handle_close,
Icon {
icon: IconType::Close,
class: "w-5 h-5".to_string(),
}
}
}
}
}
}
}
}
#[derive(Clone)]
pub struct DialogManager {
dialogs: Signal<Vec<DialogData>>,
next_id: Signal<u32>,
}
#[derive(Clone, PartialEq)]
pub struct DialogData {
pub id: u32,
pub visible: bool,
pub title: Option<String>,
pub content: String,
pub dialog_type: DialogType,
pub mask_closable: bool,
pub closable: bool,
pub show_confirm: bool,
pub show_cancel: bool,
pub confirm_text: String,
pub cancel_text: String,
pub confirm_type: ButtonType,
pub cancel_type: ButtonType,
pub on_confirm: Option<EventHandler<()>>,
pub on_cancel: Option<EventHandler<()>>,
pub on_close: Option<EventHandler<()>>,
}
impl DialogManager {
pub fn new() -> Self {
Self {
dialogs: Signal::new(vec![]),
next_id: Signal::new(1),
}
}
pub fn show(&mut self, dialog: DialogData) -> u32 {
let id = *self.next_id.read();
*self.next_id.write() = id + 1;
let mut dialogs = self.dialogs.write();
dialogs.push(dialog);
id
}
pub fn close(&mut self, id: u32) {
let mut dialogs = self.dialogs.write();
dialogs.retain(|d| d.id != id);
}
pub fn close_all(&mut self) {
let mut dialogs = self.dialogs.write();
for dialog in dialogs.iter_mut() {
dialog.visible = false;
}
}
pub fn remove(&mut self, id: u32) {
let mut dialogs = self.dialogs.write();
dialogs.retain(|d| d.id != id);
}
pub fn get_dialogs(&self) -> Vec<DialogData> {
self.dialogs.read().clone()
}
}
pub static DIALOG_MANAGER: GlobalSignal<DialogManager> = Signal::global(DialogManager::new);
#[component]
pub fn GlobalDialogContainer() -> Element {
let manager = DIALOG_MANAGER.signal();
let dialogs = manager.read().get_dialogs();
rsx! {
div {
class: "fixed inset-0 z-50 pointer-events-none",
for dialog in dialogs {
if dialog.visible {
Dialog {
visible: dialog.visible,
title: dialog.title.clone(),
content: dialog.content.clone(),
dialog_type: dialog.dialog_type,
mask_closable: dialog.mask_closable,
closable: dialog.closable,
show_confirm: dialog.show_confirm,
show_cancel: dialog.show_cancel,
confirm_text: dialog.confirm_text.clone(),
cancel_text: dialog.cancel_text.clone(),
confirm_type: dialog.confirm_type,
cancel_type: dialog.cancel_type,
on_confirm: dialog.on_confirm.clone(),
on_cancel: dialog.on_cancel.clone(),
on_close: {
let dialog_id = dialog.id;
move |_| {
DIALOG_MANAGER.write().close(dialog_id);
}
},
}
}
}
}
}
}
pub fn show_confirm_dialog(
title: Option<String>,
content: String,
on_confirm: Option<EventHandler<()>>,
on_cancel: Option<EventHandler<()>>,
) -> u32 {
let dialog = DialogData {
id: 0, visible: true,
title,
content,
dialog_type: DialogType::Default,
mask_closable: false,
closable: true,
show_confirm: true,
show_cancel: true,
confirm_text: "确认".to_string(),
cancel_text: "取消".to_string(),
confirm_type: ButtonType::Primary,
cancel_type: ButtonType::Default,
on_confirm,
on_cancel,
on_close: None, };
DIALOG_MANAGER.write().show(dialog)
}
pub fn show_info_dialog(
title: Option<String>,
content: String,
on_confirm: Option<EventHandler<()>>,
) -> u32 {
let dialog = DialogData {
id: 0,
visible: true,
title,
content,
dialog_type: DialogType::Info,
mask_closable: false,
closable: true,
show_confirm: true,
show_cancel: false,
confirm_text: "知道了".to_string(),
cancel_text: "取消".to_string(),
confirm_type: ButtonType::Primary,
cancel_type: ButtonType::Default,
on_confirm,
on_cancel: None,
on_close: None,
};
DIALOG_MANAGER.write().show(dialog)
}
pub fn show_success_dialog(
title: Option<String>,
content: String,
on_confirm: Option<EventHandler<()>>,
) -> u32 {
let dialog = DialogData {
id: 0,
visible: true,
title,
content,
dialog_type: DialogType::Success,
mask_closable: false,
closable: true,
show_confirm: true,
show_cancel: false,
confirm_text: "知道了".to_string(),
cancel_text: "取消".to_string(),
confirm_type: ButtonType::Success,
cancel_type: ButtonType::Default,
on_confirm,
on_cancel: None,
on_close: None,
};
DIALOG_MANAGER.write().show(dialog)
}
pub fn show_warning_dialog(
title: Option<String>,
content: String,
on_confirm: Option<EventHandler<()>>,
on_cancel: Option<EventHandler<()>>,
) -> u32 {
let dialog = DialogData {
id: 0,
visible: true,
title,
content,
dialog_type: DialogType::Warning,
mask_closable: false,
closable: true,
show_confirm: true,
show_cancel: true,
confirm_text: "确认".to_string(),
cancel_text: "取消".to_string(),
confirm_type: ButtonType::Warning,
cancel_type: ButtonType::Default,
on_confirm,
on_cancel,
on_close: None,
};
DIALOG_MANAGER.write().show(dialog)
}
pub fn show_error_dialog(
title: Option<String>,
content: String,
on_confirm: Option<EventHandler<()>>,
) -> u32 {
let dialog = DialogData {
id: 0,
visible: true,
title,
content,
dialog_type: DialogType::Error,
mask_closable: false,
closable: true,
show_confirm: true,
show_cancel: false,
confirm_text: "知道了".to_string(),
cancel_text: "取消".to_string(),
confirm_type: ButtonType::Error,
cancel_type: ButtonType::Default,
on_confirm,
on_cancel: None,
on_close: None,
};
DIALOG_MANAGER.write().show(dialog)
}
pub fn close_dialog(id: u32) {
DIALOG_MANAGER.write().close(id);
}
pub fn close_all_dialogs() {
DIALOG_MANAGER.write().close_all();
}