biji_ui/components/dialog/
root.rs

1use std::time::Duration;
2
3use leptos::{
4    context::Provider,
5    leptos_dom::{self, helpers::TimeoutHandle},
6    prelude::*,
7};
8
9use crate::components::dialog::context::{DialogContext, RootContext};
10
11#[component]
12pub fn Root(
13    children: Children,
14    #[prop(into, optional)] class: String,
15    /// Prevent scrolling when the dialog is open
16    #[prop(default = true)]
17    prevent_scroll: bool,
18    /// The timeout after which the component will be unmounted if `when == false`
19    #[prop(default = Duration::from_millis(200))]
20    hide_delay: Duration,
21) -> impl IntoView {
22    let root_ctx = RootContext {
23        ..RootContext::default()
24    };
25    let ctx = DialogContext {
26        root: RwSignal::new(root_ctx),
27        prevent_scroll,
28        hide_delay,
29        ..Default::default()
30    };
31
32    view! {
33        <Provider value={ctx}>
34            <RootEvents>
35                <div class={class}>
36                    <Provider value={root_ctx}>{children()}</Provider>
37                </div>
38            </RootEvents>
39        </Provider>
40    }
41}
42
43#[component]
44pub fn RootEvents(children: Children) -> impl IntoView {
45    let hide_handle: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None);
46    let dialog_ctx = expect_context::<DialogContext>();
47
48    let eff = RenderEffect::new(move |_| {
49        if dialog_ctx.prevent_scroll {
50            if dialog_ctx.open.get() {
51                if let Some(h) = hide_handle.get_value() {
52                    h.clear();
53                }
54                if let Some(doc) = document().body() {
55                    let client_width = f64::from(doc.client_width());
56                    let inner_width = window()
57                        .inner_width()
58                        .unwrap()
59                        .as_f64()
60                        .unwrap_or(client_width);
61                    let scrollbar_width = inner_width - client_width;
62
63                    let _ = doc.style().set_property("overflow", "hidden");
64                    let _ = doc
65                        .style()
66                        .set_property("--scrollbar-width", &format!("{}px", scrollbar_width));
67                    let _ = doc
68                        .style()
69                        .set_property("padding-right", &format!("calc({}px)", scrollbar_width));
70                }
71            } else {
72                let h = leptos_dom::helpers::set_timeout_with_handle(
73                    move || {
74                        if let Some(doc) = document().body() {
75                            let _ = doc.style().remove_property("overflow");
76                            let _ = doc.style().remove_property("--scrollbar-width");
77                            let _ = doc.style().remove_property("padding-right");
78                        }
79                    },
80                    dialog_ctx.hide_delay,
81                )
82                .expect("set timeout in AnimatedShow");
83                hide_handle.set_value(Some(h));
84            }
85        }
86    });
87
88    on_cleanup(move || {
89        drop(eff);
90    });
91
92    children()
93}