From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id B2DCE1FF136 for ; Mon, 04 May 2026 16:39:51 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 72C152605E; Mon, 4 May 2026 16:39:51 +0200 (CEST) From: Shannon Sterz To: yew-devel@lists.proxmox.com Subject: [PATCH yew-widget-toolkit 2/3] dropdown/align: make the picker render above or below a dropdown Date: Mon, 4 May 2026 16:39:10 +0200 Message-ID: <20260504143911.288747-3-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260504143911.288747-1-s.sterz@proxmox.com> References: <20260504143911.288747-1-s.sterz@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1777905450461 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.119 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 5CH72WT2N62GKWCBCSY757PUINO76GXX X-Message-ID-Hash: 5CH72WT2N62GKWCBCSY757PUINO76GXX X-MailFrom: s.sterz@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Yew framework devel list at Proxmox List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: and detect dropup mode. this allows better ux restricting the height of the picker to the maximum of the space below or above a `Dropdown`. by setting the `pwt-dropup` class the picker can also set the proper flex direction for its content. for example, rendering filter input fields closer to the `Dropdown` itself, further improving usability. this implements the fix outlined in [1]. [1]: https://git.proxmox.com/?p=proxmox-datacenter-manager.git;a=commit;f=ui/css/desktop-yew-style.scss;h=dafa21a3 Signed-off-by: Shannon Sterz --- src/dom/align.rs | 6 +++- src/widget/dropdown.rs | 62 +++++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/dom/align.rs b/src/dom/align.rs index 603a5b8d..a20cc6ed 100644 --- a/src/dom/align.rs +++ b/src/dom/align.rs @@ -456,7 +456,11 @@ where style.remove_property("overflow")?; } let padding = 2.0 * options.viewport_padding; - style.set_property("max-height", &format!("calc(100dvh - {padding}px)"))?; + + if style.get_property_value("max-height")? == "" { + style.set_property("max-height", &format!("calc(100dvh - {padding}px)"))?; + } + style.set_property("max-width", &format!("calc(100dvw - {padding}px)"))?; if options.align_width { diff --git a/src/widget/dropdown.rs b/src/widget/dropdown.rs index 1af09633..64db380a 100644 --- a/src/widget/dropdown.rs +++ b/src/widget/dropdown.rs @@ -1,7 +1,11 @@ +use std::cmp; + use html::Scope; use wasm_bindgen::JsCast; use web_sys::HtmlInputElement; +use crate::dom::IntoHtmlElement; + use yew::html::{IntoEventCallback, IntoPropValue}; use yew::prelude::*; @@ -155,15 +159,18 @@ impl PwtDropdown { } fn update_picker_placer(&mut self, _props: &Dropdown) { - let align_options = _props.align_options.clone().unwrap_or( - AlignOptions::new( - Point::BottomStart, - Point::TopStart, - GrowDirection::TopBottom, - ) - .viewport_padding(5.0) - .align_width(true), - ); + let align_options = + AlignOptions::new(Point::BottomStart, Point::TopStart, GrowDirection::None) + .viewport_padding(5.0) + .align_width(true) + .with_fallback_placement(Point::TopStart, Point::BottomStart, GrowDirection::None) + .with_fallback_placement( + Point::BottomStart, + Point::TopStart, + GrowDirection::TopBottom, + ); + + let align_options = _props.align_options.clone().unwrap_or(align_options); self.picker_placer = match AutoFloatingPlacement::new( self.dropdown_ref.clone(), self.picker_ref.clone(), @@ -497,7 +504,6 @@ impl Component for PwtDropdown { fn rendered(&mut self, ctx: &Context, first_render: bool) { if first_render { let props = ctx.props(); - self.update_picker_placer(props); if props.input_props.autofocus { @@ -525,6 +531,42 @@ impl Component for PwtDropdown { } } + // intentionally fail silently here; if any of these values aren't available here, falling + // back to the default logic is fine, this should just provide improved ui/ux. + let dropdown_rect = self + .dropdown_ref + .clone() + .into_html_element() + .map(|e| e.get_bounding_client_rect()); + + let window_height = web_sys::window() + .and_then(|w| w.inner_height().ok()) + .and_then(|h| h.as_f64().map(|h| h as i64)); + + if let Some(dropdown_rect) = dropdown_rect + && let Some(window_height) = window_height + { + let top = dropdown_rect.y() as i64; + let bottom = window_height - (top + (dropdown_rect.height() as i64)); + let height = cmp::max(top, bottom) - 5; + + if let Some(picker) = self.picker_ref.clone().into_html_element() { + let _ = picker + .style() + .set_property("max-height", &format!("{height}px")) + .ok(); + + let height = picker.get_bounding_client_rect().height() as i64; + let class_list = picker.class_list(); + + if height > bottom && height <= top { + let _ = class_list.add_1("pwt-dropup"); + } else { + let _ = class_list.remove_1("pwt-dropup"); + } + } + } + // update picker placement after we opened/closed to cope with a bug that only seems to // affect webkit based browsers like Safari if let Some(placer) = &self.picker_placer { -- 2.47.3