help.js (2165B)
1 import { isEditableTarget } from "./dom.js"; 2 3 const SHORTCUTS = [ 4 { key: "◀ ▶", description: "navigate tabs" }, 5 { key: "▲ ▼", description: "switch list items" }, 6 { key: "shift + ▲ ▼", description: "scroll item details" }, 7 { key: "tab", description: "cycle tabs" }, 8 { key: "1-4", description: "switch tabs" }, 9 { key: "enter", description: "open item" }, 10 { key: "?", description: "toggle help" }, 11 ]; 12 13 const helpHandlers = { 14 keydown: null, 15 }; 16 17 export function initHelp() { 18 const overlay = createHelpOverlay(); 19 20 if (helpHandlers.keydown) { 21 document.removeEventListener("keydown", helpHandlers.keydown); 22 } 23 24 helpHandlers.keydown = (e) => { 25 if (isEditableTarget(e.target)) return; 26 27 switch (e.key) { 28 case "?": 29 e.preventDefault(); 30 overlay.classList.toggle("visible"); 31 break; 32 case "Escape": 33 if (overlay.classList.contains("visible")) { 34 e.preventDefault(); 35 overlay.classList.remove("visible"); 36 } 37 break; 38 default: 39 break; 40 } 41 }; 42 43 document.addEventListener("keydown", helpHandlers.keydown); 44 } 45 46 function createHelpOverlay() { 47 const existing = document.getElementById("help-overlay"); 48 if (existing) return existing; 49 50 const overlay = document.createElement("div"); 51 overlay.id = "help-overlay"; 52 overlay.setAttribute("aria-hidden", "true"); 53 54 overlay.innerHTML = ` 55 <div class="help-modal" role="dialog" aria-modal="true" aria-label="keyboard shortcuts"> 56 <div class="help-header"> 57 <span class="help-title">help</span> 58 <span class="help-close">press ? to close</span> 59 </div> 60 <div class="help-content"> 61 ${SHORTCUTS.map( 62 (shortcut) => ` 63 <div class="help-row"> 64 <span class="help-key">${shortcut.key}</span> 65 <span class="help-desc">${shortcut.description}</span> 66 </div> 67 `, 68 ).join("")} 69 </div> 70 </div> 71 `; 72 73 overlay.addEventListener("click", (e) => { 74 if (e.target === overlay) { 75 overlay.classList.remove("visible"); 76 } 77 }); 78 79 document.body.appendChild(overlay); 80 return overlay; 81 }