1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! 气泡确认框。
//!
//! 点击触发器弹出小浮层二次确认(确定/取消),比 `AlertDialog` 更轻。
//! 开关状态由 `Popover` 内部管理,确认/取消后自动关闭。
use crate::{prelude::*, *};
use std::sync::Arc;
/// 气泡确认框。
#[derive(IntoElement)]
pub struct Popconfirm {
/// 实例 ID(元素 ID 前缀,跨帧稳定;默认调用点生成)。
instance: SharedString,
/// 触发器按钮文本。
trigger_label: SharedString,
/// 确认提示文本。
message: SharedString,
/// 确认回调。
on_confirm: Option<Arc<dyn Fn(&mut Window, &mut App) + Send + Sync + 'static>>,
/// 取消回调。
on_cancel: Option<Arc<dyn Fn(&mut Window, &mut App) + Send + Sync + 'static>>,
/// 用户样式。
style: StyleRefinement,
}
impl Popconfirm {
/// 创建气泡确认框(默认 ID 取调用点,跨帧稳定,弹层状态存得住;
/// 循环内多实例必须显式 `.id()`)。
#[track_caller]
pub fn new(trigger_label: impl Into<SharedString>, message: impl Into<SharedString>) -> Self {
Self {
instance: crate::caller_element_id("popconfirm"),
trigger_label: trigger_label.into(),
message: message.into(),
on_confirm: None,
on_cancel: None,
style: StyleRefinement::default(),
}
}
/// 设置实例 ID(默认调用点生成;循环内多实例必须显式设置)。
pub fn id(mut self, id: impl Into<SharedString>) -> Self {
self.instance = id.into();
self
}
/// 设置确认回调。
pub fn on_confirm<F>(mut self, f: F) -> Self
where
F: Fn(&mut Window, &mut App) + Send + Sync + 'static,
{
self.on_confirm = Some(Arc::new(f));
self
}
/// 设置取消回调。
pub fn on_cancel<F>(mut self, f: F) -> Self
where
F: Fn(&mut Window, &mut App) + Send + Sync + 'static,
{
self.on_cancel = Some(Arc::new(f));
self
}
}
impl Styled for Popconfirm {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl RenderOnce for Popconfirm {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
let border = theme.tokens.border;
let popover_bg = theme.tokens.popover;
let muted_foreground = theme.tokens.muted_foreground.color;
let user_style = self.style;
let instance = self.instance;
let message = self.message;
let on_confirm = self.on_confirm;
let on_cancel = self.on_cancel;
Popover::new(SharedString::from(format!("popconfirm-{instance}")))
.trigger(
Button::new(SharedString::from(format!("popconfirm-{instance}-trigger")))
.label(self.trigger_label),
)
.content(move |_, _window, cx| {
let popover = cx.entity();
let on_confirm = on_confirm.clone();
let on_cancel = on_cancel.clone();
div()
.flex()
.flex_col()
.w(px(220.0))
.bg(popover_bg)
.border_1()
.border_color(border)
.rounded_md()
.p(px(12.0))
.gap(px(10.0))
.child(
div()
.text_sm()
.text_color(muted_foreground)
.child(message.clone()),
)
.child(
div()
.flex()
.flex_row()
.justify_end()
.gap(px(8.0))
.child({
let popover = popover.clone();
Button::new(SharedString::from(format!(
"popconfirm-{instance}-cancel"
)))
.ghost()
.small()
.label("取消")
.on_click(
move |_, window, cx| {
let popover = popover.clone();
popover.update(cx, |state, cx| {
state.dismiss(window, cx);
});
if let Some(ref cb) = on_cancel {
cb(window, cx);
}
},
)
})
.child(
Button::new(SharedString::from(format!(
"popconfirm-{instance}-ok"
)))
.small()
.label("确定")
.on_click(
move |_, window, cx| {
popover.update(cx, |state, cx| {
state.dismiss(window, cx);
});
if let Some(ref cb) = on_confirm {
cb(window, cx);
}
},
),
),
)
})
.map(|mut this| {
this.style().refine(&user_style);
this
})
}
}