From 336e1c4653a20b2009eb43f60dcea54637705e13 Mon Sep 17 00:00:00 2001 From: Meysam Parvizi Date: Sun, 11 Oct 2026 17:19:22 +0200 Subject: [PATCH] feat(explorer): single download button with PDF/PNG menu --- explorer/app.js | 18 +++++++++++-- explorer/index.html | 15 ++++++----- explorer/styles.css | 65 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/explorer/app.js b/explorer/app.js index 4bd2a51..bd29c9f 100644 --- a/explorer/app.js +++ b/explorer/app.js @@ -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', () => { diff --git a/explorer/index.html b/explorer/index.html index 24a61bf..0fc4297 100644 --- a/explorer/index.html +++ b/explorer/index.html @@ -38,12 +38,15 @@ - - + diff --git a/explorer/styles.css b/explorer/styles.css index 536f786..10d2fd6 100644 --- a/explorer/styles.css +++ b/explorer/styles.css @@ -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) {