From d50035c1e2cec300755ccbeec05b49f2926f2e20 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Sun, 24 May 2020 18:57:11 +0800 Subject: [PATCH] Initial implementation of isKey --- addon/utils/is-key.js | 26 ++++++++++ addon/utils/key-combo.js | 29 ++++++++++++ tests/unit/utils/is-key-test.js | 84 +++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 addon/utils/is-key.js create mode 100644 addon/utils/key-combo.js create mode 100644 tests/unit/utils/is-key-test.js diff --git a/addon/utils/is-key.js b/addon/utils/is-key.js new file mode 100644 index 000000000..2031deb7a --- /dev/null +++ b/addon/utils/is-key.js @@ -0,0 +1,26 @@ +import { KeyCombo } from "./key-combo"; + +export default function isKey(keyComboOrKeyComboString, keyboardEvent) { + let keyCombo; + if (keyComboOrKeyComboString instanceof KeyCombo) { + keyCombo = keyComboOrKeyComboString; + } else if (typeof keyComboOrKeyComboString === 'string') { + keyCombo = KeyCombo.parse(keyComboOrKeyComboString); + } else { + throw new Error('Expected a `string` or `KeyCombo` as `keyComboOrKeyComboString` argument to `isKey`'); + } + + return modifiersMatch(keyCombo, keyboardEvent) + && keyOrCodeMatches(keyCombo, keyboardEvent); +} + +function modifiersMatch(keyCombo, keyboardEvent) { + return keyCombo.altKey === keyboardEvent.altKey + && keyCombo.ctrlKey === keyboardEvent.ctrlKey + && keyCombo.metaKey === keyboardEvent.metaKey + && keyCombo.shiftKey === keyboardEvent.shiftKey; +} + +function keyOrCodeMatches(keyCombo, keyboardEvent) { + return keyCombo.keyOrCode === keyboardEvent.code || keyCombo.keyOrCode === keyboardEvent.key; +} \ No newline at end of file diff --git a/addon/utils/key-combo.js b/addon/utils/key-combo.js new file mode 100644 index 000000000..61c201ae3 --- /dev/null +++ b/addon/utils/key-combo.js @@ -0,0 +1,29 @@ +export class KeyCombo { + altKey = false; + ctrlKey = false; + shiftKey = false; + metaKey = false; + keyOrCode; + static parse(s) { + let keyCombo = new KeyCombo(); + s.split('+').forEach((part) => { + switch (part) { + case 'alt': + keyCombo.altKey = true; + break; + case 'ctrl': + keyCombo.ctrlKey = true; + break; + case 'meta': + keyCombo.metaKey = true; + break; + case 'shift': + keyCombo.shiftKey = true; + break; + default: + keyCombo.keyOrCode = part; + } + }); + return keyCombo; + } +} diff --git a/tests/unit/utils/is-key-test.js b/tests/unit/utils/is-key-test.js new file mode 100644 index 000000000..88a949e8c --- /dev/null +++ b/tests/unit/utils/is-key-test.js @@ -0,0 +1,84 @@ +import { module, skip, test } from 'qunit'; +import isKey from 'ember-keyboard/utils/is-key'; + +module('Unit | Utility | isKey', function() { + let table = ` + keyCombo alt ctrl meta shift key code expected pending note + alt+c T F F F c KeyC T F + alt+c T F F F j KeyC F F simulates dvorak j + alt+c T F F F c KeyI T F simulates dvorak c + alt+c T F F F รง KeyC T T simulates Mac alt+c + alt+KeyC T F F F c KeyC T F + alt+c F F F F c KeyC F F alt not pressed + alt+c T F F T c KeyC F F alt+shift pressed + alt+KeyC F F F F c KeyC F F alt not pressed + alt+KeyC T F F T c KeyC F F alt+shift pressed + shift+c F F F T c KeyC T F + shift+KeyC F F F T c KeyC T F + ctrl+shift+t F T F T t KeyT T F + ctrl+shift+KeyT F T F T t KeyT T F + alt+Digit2 T F F F 2 Digit2 T F + shift+Digit2 F F F T @ Digit2 T F + shift+2 F F F T @ Digit2 T T + @ F F F F @ Digit2 T F + ? F F F T ? Slash T T + ctrl+? F T F T ? Slash T T + ctrl+Slash F T F F / Slash T F + ctrl+Slash F T F T ? Slash F F + / F F F F / Slash T F slash key with us language + / F F F F - Slash F F same key with german language + / F F F F / Digit7 T F slash key on german keyboard + `; + for (let line of table.split("\n").map(line => line.trim())) { + if (line === '' || line.match(/^keyCombo/)) { continue; } // blank or header row + buildTestFromLine(line); + } +}); + +function stringToBoolean(s) { + if (s === 'T') return true; + if (s === 'F') return false; + throw new Error(`Invalid boolean string value: ${s}. Must be 'T' or 'F'`); +} + +function buildTestFromLine(line) { + let [keyCombo,alt,ctrl,meta,shift,key,code,expected,pending,...note] = line.split(/\s+/); + let altKey = stringToBoolean(alt); + let ctrlKey = stringToBoolean(ctrl); + let metaKey = stringToBoolean(meta); + let shiftKey = stringToBoolean(shift); + let expectedResult = stringToBoolean(expected); + let isPending = stringToBoolean(pending); + let testDescription = `with "${keyCombo}", `; + note = note ? note.join(' ') : null; + testDescription += expectedResult ? 'should ' : 'should not '; + testDescription += `match keydown event with `; + testDescription += `key: ${key}, `; + testDescription += `code: ${code}, `; + testDescription += `with modifiers `; + let modifiers = []; + if (altKey) { + modifiers.push('alt'); + } + if (ctrlKey) { + modifiers.push('ctrl'); + } + if (metaKey) { + modifiers.push('meta'); + } + if (shiftKey) { + modifiers.push('shift'); + } + testDescription += modifiers.join('+'); + let testFunc = isPending ? skip : test; + testFunc(testDescription, async function(assert) { + let fakeEvent = new KeyboardEvent('keydown', { key, code, altKey, ctrlKey, metaKey, shiftKey }); + if (expectedResult) { + let expectedTriggerMessage = `should match${note ? ', ' + note : ''}`; + assert.ok(isKey(keyCombo, fakeEvent), expectedTriggerMessage); + } else { + let expectedNoTriggerMessage = `should not match${note ? ', ' + note : ''}`; + assert.ok(!isKey(keyCombo, fakeEvent), expectedNoTriggerMessage); + } + }); +} \ No newline at end of file