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.

187 lines 6.66 kB
import { createAbortHandle } from '../abort-handler.js'; import { asArray, getElementFromPath, isFunction, partition, toMerged, } 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 = 'Control'; export const metaKey = 'Meta'; export const shiftKey = 'Shift'; const MODIFIERS = new Set(['alt', 'control', 'meta', 'shift']); const ALL_MODIFIER_VALUES = Array.from(MODIFIERS).sort(); export const MODIFIER_EVENT_KEYS = { alt: 'altKey', control: 'ctrlKey', meta: 'metaKey', shift: 'shiftKey', }; 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 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 { static { this._defaultOptions = { skip: ['input', 'textarea', 'select'], bindingDefaults: { preventDefault: true }, }; } 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 = toMerged(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) && triggers.includes('keydown')) { return !event.repeat || Boolean(binding.options?.repeat); } if (isKeyup(event) && triggers.includes('keyup')) { return true; } return false; } _shouldSkip(event) { const skip = this._options.skip; if (!this._allowedKeys.has(event.key.toLowerCase())) { return true; } if (!getElementFromPath((e) => e === this._element, event)) { return true; } if (Array.isArray(skip)) { if (!this._skipSelector) { return false; } return Boolean(getElementFromPath(this._skipSelector, event)); } return isFunction(skip) ? skip.call(this._host, event.target, event) : false; } hostConnected() { const { signal } = this._abortHandle; this._host.addEventListener('keyup', this, { signal }); this._host.addEventListener('keydown', this, { signal }); globalThis.addEventListener('blur', this, { signal }); } hostDisconnected() { this._abortHandle.abort(); } _handleGlobalBlur() { this._pressedKeys.clear(); } _handleKeyEvent(event) { if (this._shouldSkip(event)) { const key = event.key.toLowerCase(); if (!MODIFIERS.has(key) && isKeyup(event)) { this._pressedKeys.delete(key); } return; } const key = event.key.toLowerCase(); if (!MODIFIERS.has(key)) { this._pressedKeys.add(key); } const activeModifiers = ALL_MODIFIER_VALUES.filter((mod) => event[MODIFIER_EVENT_KEYS[mod]]); const combination = createCombinationKey(Array.from(this._pressedKeys), activeModifiers); let binding = this._bindings.get(combination); if (!binding && this._pressedKeys.size > 1) { binding = this._bindings.get(createCombinationKey([key], activeModifiers)); } if (binding && this._bindingMatches(binding, event)) { this._applyEventModifiers(binding, event); binding.handler.call(this._host, event); } if (!MODIFIERS.has(key) && isKeyup(event)) { this._pressedKeys.delete(key); } } handleEvent(event) { switch (event.type) { case 'keydown': case 'keyup': this._handleKeyEvent(event); break; case 'blur': this._handleGlobalBlur(); break; } } set(key, handler, bindingOptions) { const { keys, modifiers } = parseKeys(key); const combination = createCombinationKey(keys, modifiers); const options = toMerged(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: () => { element.removeEventListener('keydown', this); element.removeEventListener('keyup', this); if (this._observedElement === element) { this._observedElement = undefined; } }, }; } } export function parseKeys(inputKeys) { const [modifiers, keys] = partition(normalizeKeys(inputKeys), (key) => MODIFIERS.has(key)); return { keys, modifiers }; } export function addKeybindings(element, options) { return new KeyBindingController(element, options); } //# sourceMappingURL=key-bindings.js.map