ui: tidy job-page download into a single dropdown
Replace the floating 'Download log' button + bare '.ndjson' link with one cohesive dropdown menu — same affordance as the rest of the header, opens to two well-described options. - Native <details><summary> for keyboard + no-JS support; only the click-outside-to-close handler is JS (a few lines). - New .dropdown / .dropdown-menu / .dropdown-item tokens in web/styles/input.css. Reusable for future header menus (host-detail overflow, source-group action menus, etc). - Chevron flips 180 degrees when open via .dropdown[open] selector. - Each option has a label + a mono hint line explaining when to pick it (.txt for humans / paste into a ticket; .ndjson for jq / tooling).
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -206,6 +206,59 @@
|
|||||||
.src-row.clickable > .row-link { pointer-events: auto; }
|
.src-row.clickable > .row-link { pointer-events: auto; }
|
||||||
.src-row.clickable > .row-action { pointer-events: auto; }
|
.src-row.clickable > .row-action { pointer-events: auto; }
|
||||||
|
|
||||||
|
/* ---------- dropdown menu (header actions) ----------
|
||||||
|
* Uses native <details><summary> for keyboard + no-JS support.
|
||||||
|
* The summary is styled like a .btn, the panel sits absolute below.
|
||||||
|
* Click-outside-to-close handled by CSS via :has() — no JS.
|
||||||
|
*/
|
||||||
|
.dropdown { position: relative; display: inline-block; }
|
||||||
|
.dropdown summary {
|
||||||
|
list-style: none; cursor: pointer;
|
||||||
|
/* match .btn shape */
|
||||||
|
font-size: 12px; font-weight: 500;
|
||||||
|
padding: 6px 11px; border-radius: 5px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
color: var(--ink-mid);
|
||||||
|
transition: all 120ms ease;
|
||||||
|
display: inline-flex; align-items: center; gap: 6px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.dropdown summary::-webkit-details-marker { display: none; }
|
||||||
|
.dropdown summary::marker { content: ""; }
|
||||||
|
.dropdown summary:hover { background: var(--panel-hi); color: var(--ink); }
|
||||||
|
.dropdown summary .chev {
|
||||||
|
font-size: 9px; color: var(--ink-fade);
|
||||||
|
transition: transform 120ms ease;
|
||||||
|
}
|
||||||
|
.dropdown[open] summary .chev { transform: rotate(180deg); }
|
||||||
|
.dropdown[open] summary { background: var(--panel-hi); color: var(--ink); }
|
||||||
|
.dropdown-menu {
|
||||||
|
position: absolute; top: calc(100% + 4px); right: 0;
|
||||||
|
z-index: 30;
|
||||||
|
min-width: 220px;
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 6px 24px -8px rgba(0,0,0,0.55);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.dropdown-item {
|
||||||
|
display: block;
|
||||||
|
padding: 8px 11px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--ink-mid);
|
||||||
|
font-size: 12.5px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.dropdown-item:hover { background: var(--panel-hi); color: var(--ink); }
|
||||||
|
.dropdown-item .label { display: block; color: var(--ink); font-weight: 500; }
|
||||||
|
.dropdown-item .hint {
|
||||||
|
display: block; font-size: 11px; color: var(--ink-mute); margin-top: 2px;
|
||||||
|
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- snapshot picker rows (Restore wizard step 1) ---------- */
|
/* ---------- snapshot picker rows (Restore wizard step 1) ---------- */
|
||||||
.snap-row {
|
.snap-row {
|
||||||
display: grid; align-items: center;
|
display: grid; align-items: center;
|
||||||
|
|||||||
@@ -63,11 +63,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<a href="/api/jobs/{{$job.ID}}/log.txt" class="btn"
|
<details class="dropdown" id="download-menu">
|
||||||
title="Download the full log up to this moment (works while running too).">
|
<summary>
|
||||||
Download log
|
Download log
|
||||||
</a>
|
<span class="chev">▾</span>
|
||||||
<a href="/api/jobs/{{$job.ID}}/log.ndjson" class="btn btn-ghost" title="Same log as NDJSON for jq / tooling">.ndjson</a>
|
</summary>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="/api/jobs/{{$job.ID}}/log.txt">
|
||||||
|
<span class="label">Plain text</span>
|
||||||
|
<span class="hint">.txt · for humans / paste into a ticket</span>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="/api/jobs/{{$job.ID}}/log.ndjson">
|
||||||
|
<span class="label">JSON Lines</span>
|
||||||
|
<span class="hint">.ndjson · pipe into jq / tooling</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
{{if $page.IsActive}}
|
{{if $page.IsActive}}
|
||||||
<button class="btn btn-danger" id="cancel-btn"
|
<button class="btn btn-danger" id="cancel-btn"
|
||||||
hx-post="/api/jobs/{{$job.ID}}/cancel"
|
hx-post="/api/jobs/{{$job.ID}}/cancel"
|
||||||
@@ -76,6 +87,16 @@
|
|||||||
<a href="/hosts/{{$host.ID}}" class="btn">Back to host</a>
|
<a href="/hosts/{{$host.ID}}" class="btn">Back to host</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
// Close the download dropdown when clicking outside it.
|
||||||
|
(function() {
|
||||||
|
var dd = document.getElementById('download-menu');
|
||||||
|
if (!dd) return;
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (dd.open && !dd.contains(e.target)) dd.open = false;
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/* ---------- progress (running only) ---------- */}}
|
{{/* ---------- progress (running only) ---------- */}}
|
||||||
|
|||||||
Reference in New Issue
Block a user