public inbox for yew-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH yew-widget-toolkit 1/2] touch: side dialog: prevent gestures on scrolling inner elements
@ 2026-06-03 11:18 Dominik Csapak
  2026-06-03 11:18 ` [PATCH yew-widget-toolkit 2/2] touch: fab menu: allow infinite children with the Sheet variant Dominik Csapak
  2026-06-03 12:53 ` superseded: [PATCH yew-widget-toolkit 1/2] touch: side dialog: prevent gestures on scrolling inner elements Dominik Csapak
  0 siblings, 2 replies; 3+ messages in thread
From: Dominik Csapak @ 2026-06-03 11:18 UTC (permalink / raw)
  To: yew-devel

When the SideDialog contains a child that is itself scrollable, the
GestureDetector would happily apply and call the drag/swipe etc. gesture
callbacks and e.g. drag the Sheet down while it was being scrolled.

To fix that, check the elements from the event target up to the
SideDialog container if any of these are scrolling and omit the handling
of the events.

It's currently unclear if it has any advantages of doing this in the
gesture detector itself, but it seems not necessary until now. If it
turns out it is, moving and adapting the code there should not be that
difficult.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/side_dialog.rs | 79 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 78 insertions(+), 1 deletion(-)

diff --git a/src/touch/side_dialog.rs b/src/touch/side_dialog.rs
index 9bc23c9..a379424 100644
--- a/src/touch/side_dialog.rs
+++ b/src/touch/side_dialog.rs
@@ -1,7 +1,7 @@
 use std::rc::Rc;
 
 use wasm_bindgen::JsCast;
-use web_sys::HtmlElement;
+use web_sys::{Element, EventTarget, HtmlElement};
 
 use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
@@ -262,6 +262,14 @@ impl Component for PwtSideDialog {
                 true
             }
             Msg::Drag(event) => {
+                if scrolling_element_in_range(
+                    event.target(),
+                    self.slider_ref.clone(),
+                    props.location,
+                ) {
+                    // don't do anything, children is scrolling
+                    return false;
+                }
                 let x = event.x() as f64;
                 let y = event.y() as f64;
                 match event.phase {
@@ -301,6 +309,14 @@ impl Component for PwtSideDialog {
                 }
             }
             Msg::Swipe(event) => {
+                if scrolling_element_in_range(
+                    event.target(),
+                    self.slider_ref.clone(),
+                    props.location,
+                ) {
+                    // don't do anything, children is scrolling
+                    return false;
+                }
                 let angle = event.direction; // -180 to + 180
                 let dismiss = match props.location {
                     SideDialogLocation::Left => !(-135.0..=135.0).contains(&angle),
@@ -448,3 +464,64 @@ impl From<SideDialog> for VNode {
         VNode::from(comp)
     }
 }
+
+/// Checks if there is any element in the range from `target` to `boundary` that is scrollable
+/// in the direction we would close the side dialog. (`target` must be a descendant of `boundary`).
+fn scrolling_element_in_range(
+    target: Option<EventTarget>,
+    boundary: NodeRef,
+    location: SideDialogLocation,
+) -> bool {
+    let Some(element) = target.and_then(|t| t.dyn_into::<Element>().ok()) else {
+        return false;
+    };
+
+    let Some(boundary) = boundary.cast::<Element>() else {
+        return false;
+    };
+
+    let mut element = Some(element);
+
+    while let Some(el) = element {
+        if el == boundary {
+            break;
+        }
+        if let Some(html) = el.dyn_ref::<HtmlElement>()
+            && check_scrolling(html, location)
+        {
+            return true;
+        }
+        element = el.parent_element();
+    }
+
+    false
+}
+
+/// Returns true if the element is in a state where it can scroll relative to the direction we
+/// would like to close the side dialog, e.g. for SideDialogLocation::Bottom it means returning tru
+/// if the element can scroll up, etc.
+fn check_scrolling(el: &HtmlElement, location: SideDialogLocation) -> bool {
+    match location {
+        SideDialogLocation::Bottom => {
+            if el.scroll_top() > 0 {
+                return true;
+            }
+        }
+        SideDialogLocation::Top => {
+            if el.scroll_top() != el.scroll_height() - el.offset_height() {
+                return true;
+            }
+        }
+        SideDialogLocation::Right => {
+            if el.scroll_left() > 0 {
+                return true;
+            }
+        }
+        SideDialogLocation::Left => {
+            if el.scroll_left() != el.scroll_width() - el.offset_width() {
+                return true;
+            }
+        }
+    }
+    false
+}
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-06-03 12:53 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 11:18 [PATCH yew-widget-toolkit 1/2] touch: side dialog: prevent gestures on scrolling inner elements Dominik Csapak
2026-06-03 11:18 ` [PATCH yew-widget-toolkit 2/2] touch: fab menu: allow infinite children with the Sheet variant Dominik Csapak
2026-06-03 12:53 ` superseded: [PATCH yew-widget-toolkit 1/2] touch: side dialog: prevent gestures on scrolling inner elements Dominik Csapak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal