UNPKG

@tanstack/hotkeys

Version:

Type-safe, framework-agnostic keyboard hotkey management for the browser

55 lines (53 loc) • 2.08 kB
import { MODIFIER_KEYS, normalizeKeyName } from "./constants.js"; //#region src/_recording-guard.ts const sessions = /* @__PURE__ */ new Set(); const captured = /* @__PURE__ */ new WeakSet(); const pending = /* @__PURE__ */ new Map(); let listening = false; const identity = (event) => event.code || `key:${event.key}`; /** Consumes recorded key releases and removes guards for completed chords. */ function release(event) { if (pending.delete(identity(event))) captured.add(event); if (MODIFIER_KEYS.has(normalizeKeyName(event.key))) { for (const [id, key] of pending) if (!MODIFIER_KEYS.has(key)) pending.delete(id); } cleanup(); } /** Clears pending releases when focus loss prevents the browser from delivering them. */ function blur() { pending.clear(); cleanup(); } /** Detaches shared listeners once neither active sessions nor pending releases need them. */ function cleanup() { if (listening && sessions.size === 0 && pending.size === 0) { document.removeEventListener("keyup", release, true); window.removeEventListener("blur", blur); listening = false; } } /** Pauses application shortcut dispatch and starts tracking releases for this recorder. */ function beginRecording(owner) { sessions.add(owner); if (!listening && typeof document !== "undefined") { document.addEventListener("keyup", release, true); window.addEventListener("blur", blur); listening = true; } } /** Ends a session while retaining guards for keys still held after commit or cancellation. */ function endRecording(owner) { sessions.delete(owner); cleanup(); } /** Marks a consumed keydown and guards subsequent repeats and its release. */ function captureRecordingEvent(event) { captured.add(event); pending.set(identity(event), normalizeKeyName(event.key)); } /** Whether managers should suppress callbacks for this recording session or captured chord. */ function isRecordingEvent(event) { return sessions.size > 0 || captured.has(event) || pending.has(identity(event)); } //#endregion export { beginRecording, captureRecordingEvent, endRecording, isRecordingEvent };