fuzzyworld

explodorama / files / src / ui / keyOverlay.ts

keyOverlay.ts

Blame: the commit that last changed each line · file · history

92cc4ee3 2026-11-02 Pavel Safronov1import { spriteUrl } from '../render/sprites'
2import { KEY_GLYPHS, type KeyPart } from './keyDemo'
3import { PAD_LIT_SECONDS } from './padOverlay'
4
5/**
6 * The keyboard demo's key overlay (Q65): a small keyboard (Kenney's Input Prompts, Keyboard &
7 * Mouse) with the keys the game uses: the arrows, Alt and Shift, M B V and Space, ? Home Enter and
8 * Esc. All white at rest, a pressed key lit for PAD_LIT_SECONDS (the controller overlay's time, so
9 * two presses never overlap). The places are a CSS grid (style.css `.key-overlay`), not art.
10 * Same shape as padOverlay.ts; shown instead of the finger.
11 */
12export interface KeyOverlay {
13 el: HTMLElement
14 /** Lights `part`, letting go again after `seconds` (default PAD_LIT_SECONDS). */
15 press(part: KeyPart, seconds?: number): void
16 /** Lets go of everything at once (and drops pending releases). */
17 releaseAll(): void
18}
19
20const PARTS = Object.keys(KEY_GLYPHS) as KeyPart[]
21
22export function createKeyOverlay(size: 'large' | 'compact'): KeyOverlay {
23 const el = document.createElement('div')
24 el.className = `key-overlay ${size}`
25 el.setAttribute('aria-hidden', 'true')
26 const imgs = new Map<KeyPart, HTMLImageElement>()
27 for (const part of PARTS) {
28 const img = document.createElement('img')
29 img.className = `key-part ${part}`
30 img.src = spriteUrl(KEY_GLYPHS[part])
31 img.alt = ''
32 img.draggable = false
33 imgs.set(part, img)
34 el.appendChild(img)
35 }
36 const timers = new Map<KeyPart, number>()
37 const release = (part: KeyPart): void => {
38 window.clearTimeout(timers.get(part))
39 timers.delete(part)
40 imgs.get(part)!.classList.remove('on')
41 }
42 return {
43 el,
44 press(part, seconds = PAD_LIT_SECONDS) {
45 window.clearTimeout(timers.get(part))
46 imgs.get(part)!.classList.add('on')
47 timers.set(part, window.setTimeout(() => release(part), seconds * 1000))
48 },
49 releaseAll() {
50 for (const part of PARTS) release(part)
51 },
52 }
53}