UNPKG

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.

179 lines 6.38 kB
import { createAbortHandle } from '../abort-handler.js'; import { asArray, findElementFromEventPath, isFunction } 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 Map([ [altKey.toLowerCase(), altKey.toLowerCase()], [ctrlKey.toLowerCase(), ctrlKey.toLowerCase()], [metaKey.toLowerCase(), metaKey.toLowerCase()], [shiftKey.toLowerCase(), shiftKey.toLowerCase()], ]); const ALL_MODIFIER_VALUES = Array.from(Modifiers.values()).sort(); function normalizeKeys(keys) { return asArray(keys).map((key) => key.toLowerCase()); } 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; } function createCombinationKey(keys, modifiers) { const sortedKeys = keys.toSorted(); const sortedModifiers = ALL_MODIFIER_VALUES.filter((mod) => modifiers.includes(mod)).sort(); return sortedModifiers.concat(sortedKeys).join('+'); } class KeyBindingController { get _element() { if (this._observedElement) { return this._observedElement; } return this._ref?.value || this._host; } constructor(host, options) { this._abortHandle = createAbortHandle(); this._bindings = new Map(); this._allowedKeys = new Set(); this._pressedKeys = new Set(); this._host = host; this._ref = options?.ref; this._options = { ...KeyBindingController._defaultOptions, ...options }; if (Array.isArray(this._options.skip)) { this._skipSelector = this._options.skip.join(','); } host.addController(this); } _applyEventModifiers(binding, event) { if (binding.options?.preventDefault) { event.preventDefault(); } if (binding.options?.stopPropagation) { event.stopPropagation(); } } _bindingMatches(binding, event) { const triggers = binding.options?.triggers ?? ['keydown']; if (isKeydown(event) && isKeydownTrigger(triggers)) { return true; } if (isKeyup(event) && isKeyupTrigger(triggers)) { return true; } return false; } _shouldSkip(event) { const skip = this._options?.skip; if (!this._allowedKeys.has(event.key.toLowerCase())) { return true; } if (!findElementFromEventPath((e) => e === this._element, event)) { return true; } if (Array.isArray(skip)) { if (!this._skipSelector) { return false; } return Boolean(findElementFromEventPath(this._skipSelector, event)); } if (isFunction(skip)) { return skip.call(this._host, event.target, event); } return false; } hostConnected() { const { signal } = this._abortHandle; this._host.addEventListener('keyup', this, { signal }); this._host.addEventListener('keydown', this, { signal }); } hostDisconnected() { this._abortHandle.abort(); } handleEvent(event) { if (this._shouldSkip(event)) { return; } const key = event.key.toLowerCase(); if (!Modifiers.has(key)) { this._pressedKeys.add(key); } const activeModifiers = ALL_MODIFIER_VALUES.filter((mod) => event[`${mod}Key`]); const combination = createCombinationKey(Array.from(this._pressedKeys), activeModifiers); const binding = this._bindings.get(combination); if (binding && this._bindingMatches(binding, event)) { this._applyEventModifiers(binding, event); binding.handler.call(this._host, event); if (isKeydownRepeatTrigger(binding.options?.triggers)) { this._pressedKeys.delete(key); } } if (isKeyup(event) && !Modifiers.has(key)) { this._pressedKeys.delete(key); } } set(key, handler, bindingOptions) { const { keys, modifiers } = parseKeys(key); const combination = createCombinationKey(keys, modifiers); const options = { ...this._options?.bindingDefaults, ...bindingOptions }; for (const each of [...keys, ...modifiers]) { this._allowedKeys.add(each); } this._bindings.set(combination, { keys, handler, options, modifiers }); return this; } setActivateHandler(handler, options) { this.set(enterKey, handler, options); this.set(spaceBar, handler, options); return this; } 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; }, }; } } KeyBindingController._defaultOptions = { skip: ['input', 'textarea', 'select'], }; export function parseKeys(keys) { const normalizedKeys = normalizeKeys(keys); return { keys: normalizedKeys.filter((key) => !Modifiers.has(key)), modifiers: normalizedKeys.filter((key) => Modifiers.has(key)), }; } export function addKeybindings(element, options) { return new KeyBindingController(element, options); } //# sourceMappingURL=key-bindings.js.map