orbital_base_components/overlay/modal/dialog/
base.rs1use leptos::prelude::*;
2use orbital_motion::{resolve_presence_motion, MotionSlot, OrbitalPresence, PresenceMotion};
3
4use crate::overlay::{
5 backdrop::BaseBackdrop, focus_trap::FocusTrap, open_bind::OpenBind, themed_portal::ThemedPortal,
6};
7use crate::Handler;
8
9#[derive(Clone, Copy, Default)]
10pub struct DialogDismiss {
11 pub mask_closeable: bool,
12 pub close_on_esc: bool,
13}
14
15#[derive(Clone, Copy)]
16pub struct DialogInjection {
17 pub open: OpenBind,
18}
19
20impl DialogInjection {
21 pub fn expect_context() -> Self {
22 expect_context::<Self>()
23 }
24}
25
26#[component]
27pub fn BaseDialog(
28 open: OpenBind,
29 #[prop(default = DialogDismiss {
30 mask_closeable: true,
31 close_on_esc: true,
32 })]
33 dismiss: DialogDismiss,
34 #[prop(optional, into)] class: MaybeProp<String>,
35 #[prop(optional)] motion: MotionSlot,
36 children: Children,
37) -> impl IntoView {
38 let on_mask_click = move |_| {
39 if dismiss.mask_closeable {
40 open.set(false);
41 }
42 };
43 let on_esc = Handler::with(move |_: leptos::ev::KeyboardEvent| {
44 if dismiss.close_on_esc {
45 open.set(false);
46 }
47 });
48 let open_signal = open.signal();
49 let motion = resolve_presence_motion(motion, PresenceMotion::fade());
50
51 provide_context(DialogInjection { open });
52
53 view! {
54 <ThemedPortal immediate=open_signal class=class>
55 <FocusTrap disabled=!dismiss.close_on_esc active=open_signal on_esc>
56 <div
57 class="orbital-dialog"
58 style:display=move || if open.get() { "" } else { "none" }
59 >
60 <OrbitalPresence appear=true show=open_signal motion=motion>
61 <BaseBackdrop on_click=Callback::new(on_mask_click) />
62 </OrbitalPresence>
63 {children()}
64 </div>
65 </FocusTrap>
66 </ThemedPortal>
67 }
68}