feat(explorer): single download button with PDF/PNG menu

This commit is contained in:
Meysam Parvizi
2026-10-11 17:19:22 +02:00
parent 92a37ddac9
commit 336e1c4653
3 changed files with 75 additions and 23 deletions
+16 -2
View File
@@ -629,10 +629,24 @@ function wireGlobal() {
for (const tab of els.tabs) tab.addEventListener('click', () => setView(tab.dataset.view));
document.getElementById('print').addEventListener('click', () => window.print());
// The PDF and PNG are rendered by CI next to the deployed site; local previews don't have them.
for (const link of document.querySelectorAll('[data-download]')) {
const download = document.getElementById('download');
for (const link of download.querySelectorAll('[data-download]')) {
fetch(link.getAttribute('href'), { method: 'HEAD', cache: 'no-cache' })
.then((response) => { link.hidden = !response.ok; }, () => {});
.then((response) => {
link.hidden = !response.ok;
if (response.ok) download.hidden = false;
}, () => {});
link.addEventListener('click', () => { download.open = false; });
}
document.addEventListener('pointerdown', (e) => {
if (download.open && !download.contains(e.target)) download.open = false;
});
download.addEventListener('keydown', (e) => {
if (e.key !== 'Escape' || !download.open) return;
e.stopPropagation();
download.open = false;
download.querySelector('summary').focus();
});
// Printing always produces the diagram, whatever view is open.
let viewBeforePrint = null;
window.addEventListener('beforeprint', () => {
+9 -6
View File
@@ -38,12 +38,15 @@
<button type="button" id="print" class="icon-btn" aria-label="Print or save the roadmap as PDF" title="Print or save as PDF (A4)">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M7 9V3h10v6M7 17H5a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2M7 14h10v7H7z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>
</button>
<a class="icon-btn text-btn" href="Embedded-Engineering-Roadmap.pdf" download data-download hidden title="Download the roadmap as PDF (A4)">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path d="M12 4v11m0 0-4.5-4.5M12 15l4.5-4.5M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>PDF
</a>
<a class="icon-btn text-btn" href="Embedded-Engineering-Roadmap.png" download data-download hidden title="Download the roadmap as PNG image">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path d="M12 4v11m0 0-4.5-4.5M12 15l4.5-4.5M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>PNG
</a>
<details class="download-menu" id="download" hidden>
<summary class="icon-btn" aria-label="Download the roadmap" title="Download the roadmap">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M12 4v11m0 0-4.5-4.5M12 15l4.5-4.5M5 19h14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>
</summary>
<div class="download-panel">
<a class="download-item" href="Embedded-Engineering-Roadmap.pdf" download data-download hidden><b>PDF</b><span>A4, for printing</span></a>
<a class="download-item" href="Embedded-Engineering-Roadmap.png" download data-download hidden><b>PNG</b><span>Image</span></a>
</div>
</details>
<button type="button" id="theme" class="icon-btn" aria-label="Switch color theme" title="Switch color theme">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M12 3a9 9 0 1 0 9 9A7 7 0 0 1 12 3Z" fill="currentColor"/></svg>
</button>
+50 -15
View File
@@ -351,16 +351,59 @@ button {
background: var(--chip-hover);
}
.text-btn {
width: auto;
grid-auto-flow: column;
gap: 6px;
padding: 0 11px;
font-size: 13px;
font-weight: 600;
.download-menu {
position: relative;
}
.download-menu > summary {
list-style: none;
}
.download-menu > summary::-webkit-details-marker {
display: none;
}
.download-menu[open] > summary {
border-color: var(--accent);
}
.download-panel {
position: absolute;
top: calc(100% + 8px);
right: 0;
display: grid;
gap: 2px;
min-width: 190px;
padding: 6px;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface-solid);
box-shadow: var(--shadow);
}
.download-item {
display: grid;
gap: 1px;
padding: 8px 10px;
border-radius: 8px;
color: var(--text);
text-decoration: none;
}
.download-item:hover,
.download-item:focus-visible {
background: var(--chip-hover);
}
.download-item b {
font-size: 14px;
}
.download-item span {
color: var(--muted);
font-size: 12px;
}
/* Layout */
.layout {
@@ -1128,14 +1171,6 @@ button {
#print {
display: none;
}
.text-btn {
padding: 0 9px;
}
.text-btn svg {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {