This commit is contained in:
@@ -262,6 +262,33 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
window.devicesPageSize = devicesPageSize;
|
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 `<details>` > `<div>` inside a
|
||||||
|
// <td>). 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;
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -562,12 +562,14 @@ async fn render_table(
|
|||||||
.map_err(|e| ApiError::Internal(e.to_string()))?;
|
.map_err(|e| ApiError::Internal(e.to_string()))?;
|
||||||
let now = chrono::Utc::now();
|
let now = chrono::Utc::now();
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
// No `overflow-hidden` on the table wrapper: the per-row action menu is
|
// `overflow-x-auto` on the inner wrapper gives the wide table a
|
||||||
// an absolutely-positioned `<details>` popover inside a <td>, and the
|
// horizontal scrollbar instead of pushing past the viewport. The
|
||||||
// wrapper's clipping was hiding the bottom half of the menu.
|
// 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!(
|
let _ = write!(
|
||||||
s,
|
s,
|
||||||
r##"<div class="rounded-md border border-slate-800 bg-slate-900">
|
r##"<div class="rounded-md border border-slate-800 bg-slate-900 overflow-x-auto">
|
||||||
<table class="w-full text-sm">
|
<table class="w-full text-sm">
|
||||||
<thead class="text-xs uppercase text-slate-500 bg-slate-950">
|
<thead class="text-xs uppercase text-slate-500 bg-slate-950">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1065,9 +1067,9 @@ fn render_device_row(
|
|||||||
s,
|
s,
|
||||||
r##"
|
r##"
|
||||||
<td class="px-3 py-2">
|
<td class="px-3 py-2">
|
||||||
<details class="text-right relative">
|
<details class="text-right relative" ontoggle="actionMenuToggle(this)">
|
||||||
<summary class="cursor-pointer list-none text-xs text-slate-400 hover:text-slate-200 select-none">···</summary>
|
<summary class="cursor-pointer list-none text-xs text-slate-400 hover:text-slate-200 select-none">···</summary>
|
||||||
<div class="absolute right-2 mt-1 z-10 w-56 bg-slate-900 border border-slate-700 rounded shadow-lg p-2 space-y-1 text-left">
|
<div data-action-menu class="absolute right-2 mt-1 z-50 w-56 bg-slate-900 border border-slate-700 rounded shadow-lg p-2 space-y-1 text-left">
|
||||||
<a class="block w-full text-left px-2 py-1 text-xs text-sky-300 hover:bg-sky-900/40 rounded"
|
<a class="block w-full text-left px-2 py-1 text-xs text-sky-300 hover:bg-sky-900/40 rounded"
|
||||||
href="/admin/connect/{id}" target="_blank" rel="noopener"
|
href="/admin/connect/{id}" target="_blank" rel="noopener"
|
||||||
data-confirm="{confirm_connect}"
|
data-confirm="{confirm_connect}"
|
||||||
|
|||||||
@@ -856,12 +856,14 @@ async fn render_table(
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
// No `overflow-hidden` on the table wrapper: the per-row action menu is
|
// `overflow-x-auto` on the inner wrapper gives the wide table a
|
||||||
// an absolutely-positioned `<details>` popover inside a <td>, and the
|
// horizontal scrollbar instead of pushing past the viewport. The
|
||||||
// wrapper's clipping was hiding the bottom half of the menu.
|
// 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!(
|
let _ = write!(
|
||||||
s,
|
s,
|
||||||
r##"<div class="rounded-md border border-slate-800 bg-slate-900">
|
r##"<div class="rounded-md border border-slate-800 bg-slate-900 overflow-x-auto">
|
||||||
<table class="w-full text-sm">
|
<table class="w-full text-sm">
|
||||||
<thead class="text-xs uppercase text-slate-500 bg-slate-950">
|
<thead class="text-xs uppercase text-slate-500 bg-slate-950">
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1190,9 +1192,9 @@ fn render_user_row(
|
|||||||
s,
|
s,
|
||||||
r##"
|
r##"
|
||||||
<td class="px-3 py-2">
|
<td class="px-3 py-2">
|
||||||
<details class="text-right relative">
|
<details class="text-right relative" ontoggle="actionMenuToggle(this)">
|
||||||
<summary class="cursor-pointer list-none text-xs text-slate-400 hover:text-slate-200 select-none">···</summary>
|
<summary class="cursor-pointer list-none text-xs text-slate-400 hover:text-slate-200 select-none">···</summary>
|
||||||
<div class="absolute right-2 mt-1 z-10 w-64 bg-slate-900 border border-slate-700 rounded shadow-lg p-2 space-y-1 text-left">
|
<div data-action-menu class="absolute right-2 mt-1 z-50 w-64 bg-slate-900 border border-slate-700 rounded shadow-lg p-2 space-y-1 text-left">
|
||||||
<form class="space-y-1" hx-post="/admin/pages/users/{id}/update-info?page={page}{q_param}" hx-target="#users-region" hx-swap="innerHTML">
|
<form class="space-y-1" hx-post="/admin/pages/users/{id}/update-info?page={page}{q_param}" hx-target="#users-region" hx-swap="innerHTML">
|
||||||
<input name="display_name" value="{display_name}" placeholder="{ph_dn}" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs"/>
|
<input name="display_name" value="{display_name}" placeholder="{ph_dn}" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs"/>
|
||||||
<input name="email" type="email" value="{email}" placeholder="{ph_email}" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs"/>
|
<input name="email" type="email" value="{email}" placeholder="{ph_email}" class="w-full bg-slate-800 border border-slate-700 rounded px-2 py-1 text-xs"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user