igniteui-webcomponents
Version:
Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.
166 lines • 5.49 kB
JavaScript
import { isElement } from '../util.js';
export const arrowLeft = 'ArrowLeft';
export const arrowRight = 'ArrowRight';
export const arrowUp = 'ArrowUp';
export const arrowDown = 'ArrowDown';
export const enterKey = 'Enter';
export const spaceBar = ' ';
export const escapeKey = 'Escape';
export const homeKey = 'Home';
export const endKey = 'End';
export const pageUpKey = 'PageUp';
export const pageDownKey = 'PageDown';
export const tabKey = 'Tab';
export const altKey = 'Alt';
export const ctrlKey = 'Ctrl';
export const metaKey = 'Meta';
export const shiftKey = 'Shift';
const __modifiers = new Set([altKey, ctrlKey, metaKey, shiftKey].map((key) => key.toLowerCase()));
const defaultOptions = {
skip: ['input', 'textarea', 'select'],
};
function normalizeKeys(keys) {
return (Array.isArray(keys) ? keys : [keys]).map((key) => key.toLowerCase());
}
function isModifier(key) {
return __modifiers.has(key);
}
function isKeydown(event) {
return event.type === 'keydown';
}
function isKeyup(event) {
return event.type === 'keyup';
}
function isKeydownTrigger(triggers) {
return triggers
? triggers.includes('keydown') || isKeydownRepeatTrigger(triggers)
: false;
}
function isKeyupTrigger(triggers) {
return triggers ? triggers.includes('keyup') : false;
}
function isKeydownRepeatTrigger(triggers) {
return triggers ? triggers.includes('keydownRepeat') : false;
}
export function parseKeys(keys) {
const parsed = normalizeKeys(keys);
return {
keys: parsed.filter((key) => !isModifier(key)),
modifiers: parsed.filter((key) => isModifier(key)),
};
}
class KeyBindingController {
get _element() {
if (this._observedElement) {
return this._observedElement;
}
return this._ref ? this._ref.value : this._host;
}
observeElement(element) {
element.addEventListener('keydown', this);
element.addEventListener('keyup', this);
this._observedElement = element;
return {
unsubscribe: () => {
this._observedElement?.removeEventListener('keydown', this);
this._observedElement?.removeEventListener('keyup', this);
this._observedElement = undefined;
},
};
}
constructor(host, options) {
this.bindings = new Set();
this.pressedKeys = new Set();
this._host = host;
this._ref = options?.ref;
this._options = { ...defaultOptions, ...options };
host.addController(this);
}
eventModifiersMatch(binding, event) {
if (binding.options?.preventDefault) {
event.preventDefault();
}
if (binding.options?.stopPropagation) {
event.stopPropagation();
}
}
keysMatch(binding, event) {
const modifiers = binding.modifiers ?? [];
return (binding.keys.every((key) => this.pressedKeys.has(key)) &&
modifiers.every((mod) => !!event[`${mod}Key`]));
}
bindingMatches(binding, event) {
const triggers = binding.options?.triggers ?? ['keydown'];
if (!this.keysMatch(binding, event)) {
return false;
}
if (isKeydown(event) && isKeydownTrigger(triggers)) {
return true;
}
if (isKeyup(event) && isKeyupTrigger(triggers)) {
return true;
}
return false;
}
handleEvent(event) {
const key = event.key.toLowerCase();
const path = event.composedPath();
const skip = this._options?.skip;
if (!this._element || !path.includes(this._element)) {
return;
}
if (skip) {
const shouldSkip = Array.isArray(skip)
? path.some((target) => isElement(target) &&
skip.some((selector) => target.matches(selector)))
: skip.call(this._host, event.target, event);
if (shouldSkip) {
return;
}
}
if (isModifier(key)) {
this.pressedKeys.clear();
}
if (isKeydown(event) && !isModifier(key)) {
this.pressedKeys.add(key);
}
for (const binding of this.bindings) {
if (this.bindingMatches(binding, event)) {
this.eventModifiersMatch(binding, event);
binding.handler.call(this._host, event);
if (isKeydownRepeatTrigger(binding.options?.triggers)) {
this.pressedKeys.delete(key);
}
}
}
if (isKeyup(event) && !isModifier(key)) {
this.pressedKeys.delete(key);
}
}
set(key, fn, options) {
this.bindings.add({
...parseKeys(key),
handler: fn,
options: { ...this._options?.bindingDefaults, ...options },
});
return this;
}
setActivateHandler(fn, options) {
for (const key of [enterKey, spaceBar]) {
this.set(key, fn, options);
}
return this;
}
hostConnected() {
this._host.addEventListener('keyup', this);
this._host.addEventListener('keydown', this);
}
hostDisconnected() {
this._host.removeEventListener('keyup', this);
this._host.removeEventListener('keydown', this);
}
}
export function addKeybindings(element, options) {
return new KeyBindingController(element, options);
}
//# sourceMappingURL=key-bindings.js.map