diff --git a/admin_ui/index.html b/admin_ui/index.html index e48119c..90000a2 100644 --- a/admin_ui/index.html +++ b/admin_ui/index.html @@ -262,6 +262,33 @@ }); } window.devicesPageSize = devicesPageSize; + + // The devices table lives inside an `overflow-x-auto` wrapper so wide + // column sets get a horizontal scrollbar instead of pushing the page + // out. CSS forces overflow-y to auto on the same axis, which would + // clip the per-row action popover (a `
` > `
` inside a + // ). On toggle we flip the popover to `position: fixed` and pin + // it to the summary's viewport rect so it escapes the scroll context. + // Inline `ontoggle=` is preserved across htmx swaps without re-binding. + function actionMenuToggle(details) { + const popover = details.querySelector('[data-action-menu]'); + if (!popover) return; + if (!details.open) { + popover.style.position = ''; + popover.style.top = ''; + popover.style.left = ''; + popover.style.right = ''; + return; + } + const summary = details.querySelector('summary'); + if (!summary) return; + const rect = summary.getBoundingClientRect(); + popover.style.position = 'fixed'; + popover.style.top = rect.bottom + 4 + 'px'; + popover.style.right = (window.innerWidth - rect.right - 8) + 'px'; + popover.style.left = 'auto'; + } + window.actionMenuToggle = actionMenuToggle; diff --git a/src/api/admin/pages/devices.rs b/src/api/admin/pages/devices.rs index f4e773d..5551c62 100644 --- a/src/api/admin/pages/devices.rs +++ b/src/api/admin/pages/devices.rs @@ -562,12 +562,14 @@ async fn render_table( .map_err(|e| ApiError::Internal(e.to_string()))?; let now = chrono::Utc::now(); let mut s = String::new(); - // No `overflow-hidden` on the table wrapper: the per-row action menu is - // an absolutely-positioned `
` popover inside a , and the - // wrapper's clipping was hiding the bottom half of the menu. + // `overflow-x-auto` on the inner wrapper gives the wide table a + // horizontal scrollbar instead of pushing past the viewport. The + // action-menu popover compensates for the implicit `overflow-y: auto` + // (CSS spec) by switching to `position: fixed` on toggle — see + // `actionMenuToggle` in admin_ui/index.html. let _ = write!( s, - r##"
+ r##"
@@ -1065,9 +1067,9 @@ fn render_device_row( s, r##"
-
+
··· -
, and the - // wrapper's clipping was hiding the bottom half of the menu. + // `overflow-x-auto` on the inner wrapper gives the wide table a + // horizontal scrollbar instead of pushing past the viewport. The + // action-menu popover compensates for the implicit `overflow-y: auto` + // (CSS spec) by switching to `position: fixed` on toggle — see + // `actionMenuToggle` in admin_ui/index.html. let _ = write!( s, - r##"
+ r##"
@@ -1190,9 +1192,9 @@ fn render_user_row( s, r##"
-
+
··· -
+