@tanstack/hotkeys
Version:
Type-safe, framework-agnostic keyboard hotkey management for the browser
79 lines (78 loc) • 2.69 kB
JavaScript
//#region src/_target-listeners.ts
/** Shares keyboard listeners within each target and capture phase. */
var TargetListeners = class {
dispatch;
#targets = /* @__PURE__ */ new Map();
#processed = /* @__PURE__ */ new WeakMap();
constructor(dispatch) {
this.dispatch = dispatch;
}
/**
* Adds a registration to the target's shared listener. With `last`, an
* existing listener moves after listeners added since it, such as one a
* framework attached later to delegate key events.
*/
add(target, capture, id, last = false) {
if (typeof document === "undefined") return;
const phases = this.#targets.get(target) ?? /* @__PURE__ */ new Map();
let group = phases.get(capture);
if (group && last) for (const type of ["keydown", "keyup"]) {
target.removeEventListener(type, group[type], capture);
target.addEventListener(type, group[type], capture);
}
if (!group) {
const ids = /* @__PURE__ */ new Set();
const listener = (eventType) => (rawEvent) => {
const event = rawEvent;
let processed = this.#processed.get(event);
const first = !processed || processed.groups.has(ids);
if (!processed || first) {
processed = {
ids: /* @__PURE__ */ new Set(),
groups: /* @__PURE__ */ new Set()
};
this.#processed.set(event, processed);
}
processed.groups.add(ids);
const pending = /* @__PURE__ */ new Set();
for (const registrationId of ids) if (!processed.ids.has(registrationId)) {
processed.ids.add(registrationId);
pending.add(registrationId);
}
this.dispatch(event, target, eventType, capture, pending, first);
};
group = {
ids,
keydown: listener("keydown"),
keyup: listener("keyup")
};
target.addEventListener("keydown", group.keydown, capture);
target.addEventListener("keyup", group.keyup, capture);
phases.set(capture, group);
this.#targets.set(target, phases);
}
group.ids.add(id);
}
remove(target, capture, id) {
const phases = this.#targets.get(target);
const group = phases?.get(capture);
if (!group) return;
group.ids.delete(id);
if (group.ids.size === 0) {
target.removeEventListener("keydown", group.keydown, capture);
target.removeEventListener("keyup", group.keyup, capture);
phases.delete(capture);
if (phases.size === 0) this.#targets.delete(target);
}
}
destroy() {
for (const [target, phases] of this.#targets) for (const [capture, group] of phases) {
target.removeEventListener("keydown", group.keydown, capture);
target.removeEventListener("keyup", group.keyup, capture);
}
this.#targets.clear();
this.#processed = /* @__PURE__ */ new WeakMap();
}
};
//#endregion
export { TargetListeners };