@nextcloud/vue
Version:
Nextcloud vue components
1 lines • 8.46 kB
Source Map (JSON)
{"version":3,"file":"useHotKey.mjs","sources":["../../src/composables/useHotKey/index.ts"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { onKeyStroke } from '@vueuse/core'\nimport { isMac } from '../../utils/platform.ts'\n\nconst disableKeyboardShortcuts = window.OCP?.Accessibility?.disableKeyboardShortcuts?.()\nconst derivedKeysRegex = /^[a-zA-Z0-9]$/\nconst nonAsciiPrintableRegex = /^[^\\x20-\\x7F]$/\n\nexport interface UseHotKeyOptions {\n\t/** Make key filter case sensitive */\n\tcaseSensitive?: boolean\n\n\t/** Prevent default behavior of key stroke */\n\tprevent?: boolean\n\n\t/** Stop the event bubbling */\n\tstop?: boolean\n\n\t/** Also listen for keyup event */\n\tpush?: boolean\n\n\t/**\n\t * If set then the callback is only called when the shift key is (not) pressed.\n\t * When left `undefined` a pressed shift key is ignored (callback is run with and without shift pressed).\n\t */\n\tshift?: boolean\n\n\t/**\n\t * Only run the callback if the control key is (not-)pressed.\n\t * Undefined will be handled the same as `false` and will only run the callback if the 'ctrl' key is NOT pressed.\n\t */\n\tctrl?: boolean\n\n\t/**\n\t * If set the callback is only executed if the alt key is (not-)pressed\n\t * Undefined will be handled the same as `false` and will only run the callback if the 'alt' key is NOT pressed.\n\t */\n\talt?: boolean\n}\n\n/**\n * Check if event target (active element) is editable (allows input from keyboard) or NcModal is open\n * If true, a hot key should not trigger the callback\n *\n * @todo Discuss if we should abort on another interactive elements (button, a, e.t.c)\n *\n * @param event keyboard event\n * @return Whether it should prevent callback\n */\nfunction shouldIgnoreEvent(event: KeyboardEvent): boolean {\n\tif (!(event.target instanceof HTMLElement)\n\t\t|| event.target instanceof HTMLInputElement\n\t\t|| event.target instanceof HTMLTextAreaElement\n\t\t|| event.target instanceof HTMLSelectElement\n\t\t|| event.target.isContentEditable) {\n\t\treturn true\n\t}\n\t/** Abort if any modal/dialog opened */\n\treturn document.getElementsByClassName('modal-mask').length !== 0\n}\n\ntype KeyboardEventHandler = (event: KeyboardEvent) => void\n\n/**\n * Implementation of the event handler.\n *\n * @param callback The callback to run\n * @param options hot key options\n */\nfunction eventHandler(callback: KeyboardEventHandler, options: UseHotKeyOptions): KeyboardEventHandler {\n\treturn (event: KeyboardEvent) => {\n\t\tconst ctrlKeyPressed = isMac ? event.metaKey : event.ctrlKey\n\t\tif (ctrlKeyPressed !== Boolean(options.ctrl)) {\n\t\t\t/**\n\t\t\t * Ctrl is required and not pressed, or the opposite\n\t\t\t * As on macOS 'cmd' key is used instead of 'ctrl' key for most key combinations,\n\t\t\t * 'event.metaKey' should be checked\n\t\t\t */\n\t\t\treturn\n\t\t} else if (event.altKey !== Boolean(options.alt)) {\n\t\t\t// Alt is required and not pressed, or the opposite\n\t\t\treturn\n\t\t} else if (options.shift !== undefined && event.shiftKey !== Boolean(options.shift)) {\n\t\t\t/**\n\t\t\t * Shift is required and not pressed, or the opposite\n\t\t\t * As shift key is used to type capital letters and alternate characters,\n\t\t\t * option should be explicitly defined\n\t\t\t */\n\t\t\treturn\n\t\t} else if (shouldIgnoreEvent(event)) {\n\t\t\t// Keyboard shortcuts are disabled, because active element assumes input\n\t\t\treturn\n\t\t}\n\n\t\tif (options.prevent) {\n\t\t\tevent.preventDefault()\n\t\t}\n\t\tif (options.stop) {\n\t\t\tevent.stopPropagation()\n\t\t}\n\t\tcallback(event)\n\t}\n}\n\n/**\n * Composable to use keyboard shortcuts in the application.\n * It respects the users accessibility configuration (opt-out shortcuts).\n *\n * @param keysOrFilter - keyboard key(s) to listen to, or filter function or pass `true` for listening to all keys\n * @param callback - callback function\n * @param options - composable options\n * @see docs/composables/usekeystroke.md\n */\nexport function useHotKey(\n\tkeysOrFilter: true | string | string[] | ((e: KeyboardEvent) => boolean),\n\tcallback = () => {},\n\toptions: UseHotKeyOptions = {},\n) {\n\tif (disableKeyboardShortcuts) {\n\t\t// Keyboard shortcuts are disabled\n\t\treturn () => {}\n\t}\n\n\t/**\n\t * Validates event key to expected key\n\t *\n\t * @param event keyboard event\n\t * @param key expected key\n\t * @return whether it satisfies expected value or not\n\t */\n\tconst validateKeyEvent = (event: KeyboardEvent, key: string): boolean => {\n\t\t// If key exactly matches event.key, valid with any caseSensitive option. Do not perform further checks\n\t\tif (event.key === key) {\n\t\t\treturn true\n\t\t}\n\n\t\t// If key and event.key are in different cases, invalid with caseSensitive = true. Do not perform further checks\n\t\tif (options.caseSensitive) {\n\t\t\tconst isKeyInLowerCase = key === key.toLowerCase()\n\t\t\tconst isEventKeyInLowerCase = event.key === event.key.toLowerCase()\n\t\t\tif (isKeyInLowerCase !== isEventKeyInLowerCase) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\t// If received event.key is not a printable ASCII character code (character code 32-127),\n\t\t// try to derive it from event.code and match with expected key\n\t\tif (derivedKeysRegex.test(key) && nonAsciiPrintableRegex.test(event.key)) {\n\t\t\treturn event.code.replace(/^(?:Key|Digit|Numpad)/, '') === key.toUpperCase()\n\t\t}\n\n\t\treturn event.key.toLowerCase() === key.toLowerCase()\n\t}\n\n\t/**\n\t * Filter function for the listener\n\t * see https://github.com/vueuse/vueuse/blob/v11.3.0/packages/core/onKeyStroke/index.ts#L21-L32\n\t *\n\t * @param event keyboard event\n\t * @return Whether it satisfies expected value or not\n\t */\n\tconst keyFilter = (event: KeyboardEvent): boolean => {\n\t\tif (typeof keysOrFilter === 'function') {\n\t\t\treturn keysOrFilter(event)\n\t\t} else if (typeof keysOrFilter === 'string') {\n\t\t\treturn validateKeyEvent(event, keysOrFilter)\n\t\t} else if (Array.isArray(keysOrFilter)) {\n\t\t\treturn keysOrFilter.some((key) => validateKeyEvent(event, key))\n\t\t} else {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tconst stopKeyDown = onKeyStroke(keyFilter, eventHandler(callback, options), {\n\t\teventName: 'keydown',\n\t\tdedupe: true,\n\t\tpassive: !options.prevent,\n\t})\n\n\tconst stopKeyUp = options.push\n\t\t? onKeyStroke(keyFilter, eventHandler(callback, options), {\n\t\t\t\teventName: 'keyup',\n\t\t\t\tpassive: !options.prevent,\n\t\t\t})\n\t\t: () => {}\n\n\treturn () => {\n\t\tstopKeyDown()\n\t\tstopKeyUp()\n\t}\n}\n"],"names":[],"mappings":";;AAOA,MAAM,2BAA2B,OAAO,KAAK,eAAe,2BAAA;AAC5D,MAAM,mBAAmB;AACzB,MAAM,yBAAyB;AA2C/B,SAAS,kBAAkB,OAA+B;AACzD,MAAI,EAAE,MAAM,kBAAkB,gBAC1B,MAAM,kBAAkB,oBACxB,MAAM,kBAAkB,uBACxB,MAAM,kBAAkB,qBACxB,MAAM,OAAO,mBAAmB;AACnC,WAAO;AAAA,EACR;AAEA,SAAO,SAAS,uBAAuB,YAAY,EAAE,WAAW;AACjE;AAUA,SAAS,aAAa,UAAgC,SAAiD;AACtG,SAAO,CAAC,UAAyB;AAChC,UAAM,iBAAiB,QAAQ,MAAM,UAAU,MAAM;AACrD,QAAI,mBAAmB,QAAQ,QAAQ,IAAI,GAAG;AAM7C;AAAA,IACD,WAAW,MAAM,WAAW,QAAQ,QAAQ,GAAG,GAAG;AAEjD;AAAA,IACD,WAAW,QAAQ,UAAU,UAAa,MAAM,aAAa,QAAQ,QAAQ,KAAK,GAAG;AAMpF;AAAA,IACD,WAAW,kBAAkB,KAAK,GAAG;AAEpC;AAAA,IACD;AAEA,QAAI,QAAQ,SAAS;AACpB,YAAM,eAAA;AAAA,IACP;AACA,QAAI,QAAQ,MAAM;AACjB,YAAM,gBAAA;AAAA,IACP;AACA,aAAS,KAAK;AAAA,EACf;AACD;AAWO,SAAS,UACf,cACA,WAAW,MAAM;AAAC,GAClB,UAA4B,CAAA,GAC3B;AACD,MAAI,0BAA0B;AAE7B,WAAO,MAAM;AAAA,IAAC;AAAA,EACf;AASA,QAAM,mBAAmB,CAAC,OAAsB,QAAyB;AAExE,QAAI,MAAM,QAAQ,KAAK;AACtB,aAAO;AAAA,IACR;AAGA,QAAI,QAAQ,eAAe;AAC1B,YAAM,mBAAmB,QAAQ,IAAI,YAAA;AACrC,YAAM,wBAAwB,MAAM,QAAQ,MAAM,IAAI,YAAA;AACtD,UAAI,qBAAqB,uBAAuB;AAC/C,eAAO;AAAA,MACR;AAAA,IACD;AAIA,QAAI,iBAAiB,KAAK,GAAG,KAAK,uBAAuB,KAAK,MAAM,GAAG,GAAG;AACzE,aAAO,MAAM,KAAK,QAAQ,yBAAyB,EAAE,MAAM,IAAI,YAAA;AAAA,IAChE;AAEA,WAAO,MAAM,IAAI,YAAA,MAAkB,IAAI,YAAA;AAAA,EACxC;AASA,QAAM,YAAY,CAAC,UAAkC;AACpD,QAAI,OAAO,iBAAiB,YAAY;AACvC,aAAO,aAAa,KAAK;AAAA,IAC1B,WAAW,OAAO,iBAAiB,UAAU;AAC5C,aAAO,iBAAiB,OAAO,YAAY;AAAA,IAC5C,WAAW,MAAM,QAAQ,YAAY,GAAG;AACvC,aAAO,aAAa,KAAK,CAAC,QAAQ,iBAAiB,OAAO,GAAG,CAAC;AAAA,IAC/D,OAAO;AACN,aAAO;AAAA,IACR;AAAA,EACD;AAEA,QAAM,cAAc,YAAY,WAAW,aAAa,UAAU,OAAO,GAAG;AAAA,IAC3E,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,SAAS,CAAC,QAAQ;AAAA,EAAA,CAClB;AAED,QAAM,YAAY,QAAQ,OACvB,YAAY,WAAW,aAAa,UAAU,OAAO,GAAG;AAAA,IACxD,WAAW;AAAA,IACX,SAAS,CAAC,QAAQ;AAAA,EAAA,CAClB,IACA,MAAM;AAAA,EAAC;AAEV,SAAO,MAAM;AACZ,gBAAA;AACA,cAAA;AAAA,EACD;AACD;"}