diff --git a/server/app/static/common.js b/server/app/static/common.js index 27ec4da..fc6bbe3 100644 --- a/server/app/static/common.js +++ b/server/app/static/common.js @@ -59,7 +59,13 @@ })(); function showStatus(ok, message) { - var el = document.getElementById('result'); + // While a is open, its own .dialog-result container gets the + // message instead of the page-level #result -- otherwise it lands + // behind the dialog's backdrop, invisible until the dialog closes + // (e.g. the widget config dialogs on the Layout tab, see + // frame_layout.js). Falls back to #result for everything else. + var openDialog = document.querySelector('dialog[open]'); + var el = (openDialog && openDialog.querySelector('.dialog-result')) || document.getElementById('result'); if (!el) return; el.innerHTML = '
'; el.firstChild.textContent = message; diff --git a/server/app/static/frame_layout.js b/server/app/static/frame_layout.js index a6078a8..bc33c8a 100644 --- a/server/app/static/frame_layout.js +++ b/server/app/static/frame_layout.js @@ -274,6 +274,7 @@ async function openWidgetDialog(widget) { const dialogEl = document.getElementById('widget-dialog'); const bodyEl = document.getElementById('widget-dialog-body'); bodyEl.innerHTML = '

Loading...

'; + dialogEl.querySelector('.dialog-result').innerHTML = ''; // clear any message left over from a previous dialog openDialogWidgetType = widget.widget_type; dialogEl.showModal(); try { diff --git a/server/app/static/theme.css b/server/app/static/theme.css index a3ce890..641b4dc 100644 --- a/server/app/static/theme.css +++ b/server/app/static/theme.css @@ -402,6 +402,13 @@ button.secondary:hover { background: var(--surface-alt); } background: var(--surface); color: var(--text); box-shadow: var(--shadow-hover); + /* #widget-dialog-body scrolls on its own (min-height: 0 is what lets a + flex child actually shrink/scroll instead of forcing the dialog + past max-height) so .dialog-result stays pinned as a visible footer + regardless of scroll position -- otherwise a save message can land + off-screen below a long form with no visible feedback at all. */ + display: flex; + flex-direction: column; } #widget-dialog::backdrop { background: var(--overlay); } #widget-dialog-close { @@ -414,8 +421,11 @@ button.secondary:hover { background: var(--surface-alt); } z-index: 1; } .dialog-title { margin: 0 40px 16px 0; font-size: 18px; } +#widget-dialog-body { overflow-y: auto; min-height: 0; } #widget-dialog-body .card { box-shadow: none; } #widget-dialog-body .card:first-child { margin-top: 0; } +.dialog-result { flex: none; } +.dialog-result:not(:empty) { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); } code { background: var(--surface-alt); diff --git a/server/app/templates/frame_layout.html b/server/app/templates/frame_layout.html index 6998fd0..132a410 100644 --- a/server/app/templates/frame_layout.html +++ b/server/app/templates/frame_layout.html @@ -38,6 +38,7 @@

Loading...

+