Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion thaw/src/dialog/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::ConfigInjection;
use leptos::{context::Provider, ev, prelude::*};
use thaw_components::{FocusTrap, Teleport};
use leptos_transition_group::CSSTransition;
use thaw_utils::{class_list, mount_style, Model};
use thaw_utils::{class_list, mount_style, use_lock_html_scroll, Model};

#[component]
pub fn Dialog(
Expand All @@ -21,6 +21,11 @@ pub fn Dialog(
mount_style("dialog", include_str!("./dialog.css"));
let config_provider = ConfigInjection::expect_context();

// Prevent the page behind the backdrop from scrolling while the dialog
// is open. Same as OverlayDrawer — the Dialog was the only overlay
// component missing this.
use_lock_html_scroll(open.signal());

let on_mask_click = move |_| {
if mask_closeable.get_untracked() {
open.set(false);
Expand Down
32 changes: 30 additions & 2 deletions thaw_utils/src/hooks/use_lock_html_scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ pub fn use_lock_html_scroll(is_lock: Signal<bool>) {
let style = document()
.create_element("style")
.expect("create style element error");
_ = style.set_attribute("data-id", &format!("thaw-lock-html-scroll"));
style.set_text_content(Some("html { overflow: hidden; }"));
_ = style.set_attribute("data-id", "thaw-lock-html-scroll");
style.set_text_content(Some(&lock_scroll_css()));
_ = head.append_child(&style);
style_el.update_value(move |el| {
*el = SendWrapper::new(Some(style));
Expand All @@ -48,3 +48,31 @@ pub fn use_lock_html_scroll(is_lock: Signal<bool>) {
_ = is_lock;
}
}

/// Build the CSS rule that locks scrolling and compensates for the scrollbar.
///
/// When `overflow: hidden` removes the scrollbar, the viewport widens by
/// the scrollbar's width, causing `margin: auto` centered content to shift.
/// Adding `padding-right` equal to the scrollbar width keeps the layout stable.
#[cfg(any(feature = "csr", feature = "hydrate"))]
fn lock_scroll_css() -> String {
use leptos::prelude::document;

let scrollbar_width = document()
.document_element()
.map(|html| {
let client_w = html.client_width();
let window_w = web_sys::window()
.and_then(|w| w.inner_width().ok())
.and_then(|v| v.as_f64())
.unwrap_or(0.0) as i32;
window_w - client_w
})
.unwrap_or(0);

if scrollbar_width > 0 {
format!("html {{ overflow: hidden; padding-right: {scrollbar_width}px; }}")
} else {
"html { overflow: hidden; }".to_string()
}
}
Loading