From d90ce8a4db8d4d159f4c7d4a139b09bdb0f40a8d Mon Sep 17 00:00:00 2001 From: jkzing Date: Thu, 23 Nov 2017 23:57:22 +0800 Subject: [PATCH 1/6] feat: support KeyboardEvent.key for built in key modifiers --- src/compiler/codegen/events.js | 15 ++++++++++++++- .../instance/render-helpers/check-keycodes.js | 11 +++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/compiler/codegen/events.js b/src/compiler/codegen/events.js index 2b53c2333fd..dc97c03a801 100644 --- a/src/compiler/codegen/events.js +++ b/src/compiler/codegen/events.js @@ -16,6 +16,18 @@ const keyCodes: { [key: string]: number | Array } = { 'delete': [8, 46] } +const keyNames: { [key: string]: string | Array } = { + esc: 'Escape', + tab: 'Tab', + enter: 'Enter', + space: ' ', + up: 'ArrowUp', + left: 'ArrowLeft', + right: 'ArrowRight', + down: 'ArrowDown', + 'delete': ['Backspace', 'Delete'] +} + // #4868: modifiers that prevent the execution of the listener // need to explicitly return null so that we can determine whether to remove // the listener for .once @@ -145,6 +157,7 @@ function genFilterCode (key: string): string { `_k($event.keyCode,` + `${JSON.stringify(key)},` + `${JSON.stringify(code)},` + - `$event.key)` + `$event.key,` + + `${JSON.stringify(keyNames[key])})` ) } diff --git a/src/core/instance/render-helpers/check-keycodes.js b/src/core/instance/render-helpers/check-keycodes.js index 8e77f29b9e4..f22e9920567 100644 --- a/src/core/instance/render-helpers/check-keycodes.js +++ b/src/core/instance/render-helpers/check-keycodes.js @@ -12,10 +12,17 @@ export function checkKeyCodes ( eventKeyCode: number, key: string, builtInAlias?: number | Array, - eventKeyName?: string + eventKeyName?: string, + builtInName?: string | Array ): ?boolean { const keyCodes = config.keyCodes[key] || builtInAlias - if (keyCodes) { + if (keyCodes === builtInAlias && eventKeyName) { + if (Array.isArray(builtInName)) { + return builtInName.indexOf(eventKeyName) === -1 + } else { + return builtInName !== eventKeyName + } + } else if (keyCodes) { if (Array.isArray(keyCodes)) { return keyCodes.indexOf(eventKeyCode) === -1 } else { From 75d0f1d5bc7e010b3ea61f4aeab66b457a5a9e1a Mon Sep 17 00:00:00 2001 From: jkzing Date: Fri, 24 Nov 2017 00:17:30 +0800 Subject: [PATCH 2/6] test: fix failing tests --- src/core/instance/render-helpers/check-keycodes.js | 2 +- test/unit/modules/compiler/codegen.spec.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/instance/render-helpers/check-keycodes.js b/src/core/instance/render-helpers/check-keycodes.js index f22e9920567..60e6979aa46 100644 --- a/src/core/instance/render-helpers/check-keycodes.js +++ b/src/core/instance/render-helpers/check-keycodes.js @@ -16,7 +16,7 @@ export function checkKeyCodes ( builtInName?: string | Array ): ?boolean { const keyCodes = config.keyCodes[key] || builtInAlias - if (keyCodes === builtInAlias && eventKeyName) { + if (builtInAlias && keyCodes === builtInAlias && eventKeyName) { if (Array.isArray(builtInName)) { return builtInName.indexOf(eventKeyName) === -1 } else { diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index b1085a571df..e080a0e5aa9 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -265,17 +265,17 @@ describe('codegen', () => { it('generate events with keycode', () => { assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;onInput($event)}}})}` ) // multiple keycodes (delete) assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}` ) // multiple keycodes (chained) assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter")&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}` ) // number keycode assertCodegen( @@ -285,7 +285,7 @@ describe('codegen', () => { // custom keycode assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key,undefined))return null;onInput($event)}}})}` ) }) @@ -308,12 +308,12 @@ describe('codegen', () => { it('generate events with generic modifiers and keycode correct order', () => { assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.preventDefault();onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.preventDefault();onInput($event)}}})}` ) assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.stopPropagation();onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.stopPropagation();onInput($event)}}})}` ) }) @@ -420,7 +420,7 @@ describe('codegen', () => { // with modifiers assertCodegen( ``, - `with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;(e=>current++)($event)}}})}` + `with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;(e=>current++)($event)}}})}` ) }) From e7e71f065d32556eb42ee9f678363c609b1a27d9 Mon Sep 17 00:00:00 2001 From: jkzing Date: Fri, 24 Nov 2017 00:19:51 +0800 Subject: [PATCH 3/6] test: add unit test for new code --- test/unit/features/directives/on.spec.js | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/unit/features/directives/on.spec.js b/test/unit/features/directives/on.spec.js index 4ef3c699fe0..3973c41cb26 100644 --- a/test/unit/features/directives/on.spec.js +++ b/test/unit/features/directives/on.spec.js @@ -362,6 +362,35 @@ describe('Directive v-on', () => { expect(spyMiddle).toHaveBeenCalled() }) + it('should support KeyboardEvent.key for built in aliases', () => { + vm = new Vue({ + el, + template: ` +
+ + + + + +
+ `, + methods: { foo: spy } + }) + + triggerEvent(vm.$refs.enter, 'keyup', e => { e.key = 'Enter' }) + expect(spy.calls.count()).toBe(1) + triggerEvent(vm.$refs.space, 'keyup', e => { e.key = ' ' }) + expect(spy.calls.count()).toBe(2) + triggerEvent(vm.$refs.esc, 'keyup', e => { e.key = 'Escape' }) + expect(spy.calls.count()).toBe(3) + triggerEvent(vm.$refs.left, 'keyup', e => { e.key = 'ArrowLeft' }) + expect(spy.calls.count()).toBe(4) + triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Backspace' }) + expect(spy.calls.count()).toBe(5) + triggerEvent(vm.$refs.delete, 'keyup', e => { e.key = 'Delete' }) + expect(spy.calls.count()).toBe(6) + }) + it('should support custom keyCode', () => { Vue.config.keyCodes.test = 1 vm = new Vue({ From 99e27df9536f9a946591411b7adc1ae26c8f78d7 Mon Sep 17 00:00:00 2001 From: jkzing Date: Fri, 24 Nov 2017 11:05:15 +0800 Subject: [PATCH 4/6] refactor: extract isKeyNotMatch --- .../instance/render-helpers/check-keycodes.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/instance/render-helpers/check-keycodes.js b/src/core/instance/render-helpers/check-keycodes.js index 60e6979aa46..9223c8ff89e 100644 --- a/src/core/instance/render-helpers/check-keycodes.js +++ b/src/core/instance/render-helpers/check-keycodes.js @@ -3,6 +3,14 @@ import config from 'core/config' import { hyphenate } from 'shared/util' +function isKeyNotMatch (expect: T | Array, actual: T): boolean { + if (Array.isArray(expect)) { + return expect.indexOf(actual) === -1 + } else { + return expect !== actual + } +} + /** * Runtime helper for checking keyCodes from config. * exposed as Vue.prototype._k @@ -16,18 +24,10 @@ export function checkKeyCodes ( builtInName?: string | Array ): ?boolean { const keyCodes = config.keyCodes[key] || builtInAlias - if (builtInAlias && keyCodes === builtInAlias && eventKeyName) { - if (Array.isArray(builtInName)) { - return builtInName.indexOf(eventKeyName) === -1 - } else { - return builtInName !== eventKeyName - } + if (builtInName && keyCodes === builtInAlias && eventKeyName) { + return isKeyNotMatch(builtInName, eventKeyName) } else if (keyCodes) { - if (Array.isArray(keyCodes)) { - return keyCodes.indexOf(eventKeyCode) === -1 - } else { - return keyCodes !== eventKeyCode - } + return isKeyNotMatch(keyCodes, eventKeyCode) } else if (eventKeyName) { return hyphenate(eventKeyName) !== key } From d4ce049f5c96839bdf69f3f3e1b565dc29187bf8 Mon Sep 17 00:00:00 2001 From: JingkaiZhao Date: Tue, 2 Jan 2018 23:32:58 +0800 Subject: [PATCH 5/6] refactor: move keyNames related code into runtime --- src/compiler/codegen/events.js | 15 +-------------- .../instance/render-helpers/check-keycodes.js | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/compiler/codegen/events.js b/src/compiler/codegen/events.js index dc97c03a801..2b53c2333fd 100644 --- a/src/compiler/codegen/events.js +++ b/src/compiler/codegen/events.js @@ -16,18 +16,6 @@ const keyCodes: { [key: string]: number | Array } = { 'delete': [8, 46] } -const keyNames: { [key: string]: string | Array } = { - esc: 'Escape', - tab: 'Tab', - enter: 'Enter', - space: ' ', - up: 'ArrowUp', - left: 'ArrowLeft', - right: 'ArrowRight', - down: 'ArrowDown', - 'delete': ['Backspace', 'Delete'] -} - // #4868: modifiers that prevent the execution of the listener // need to explicitly return null so that we can determine whether to remove // the listener for .once @@ -157,7 +145,6 @@ function genFilterCode (key: string): string { `_k($event.keyCode,` + `${JSON.stringify(key)},` + `${JSON.stringify(code)},` + - `$event.key,` + - `${JSON.stringify(keyNames[key])})` + `$event.key)` ) } diff --git a/src/core/instance/render-helpers/check-keycodes.js b/src/core/instance/render-helpers/check-keycodes.js index 9223c8ff89e..27596fde8bf 100644 --- a/src/core/instance/render-helpers/check-keycodes.js +++ b/src/core/instance/render-helpers/check-keycodes.js @@ -3,6 +3,18 @@ import config from 'core/config' import { hyphenate } from 'shared/util' +const keyNames: { [key: string]: string | Array } = { + esc: 'Escape', + tab: 'Tab', + enter: 'Enter', + space: ' ', + up: 'ArrowUp', + left: 'ArrowLeft', + right: 'ArrowRight', + down: 'ArrowDown', + 'delete': ['Backspace', 'Delete'] +} + function isKeyNotMatch (expect: T | Array, actual: T): boolean { if (Array.isArray(expect)) { return expect.indexOf(actual) === -1 @@ -20,10 +32,10 @@ export function checkKeyCodes ( eventKeyCode: number, key: string, builtInAlias?: number | Array, - eventKeyName?: string, - builtInName?: string | Array + eventKeyName?: string ): ?boolean { const keyCodes = config.keyCodes[key] || builtInAlias + const builtInName: string | Array = keyNames[key] if (builtInName && keyCodes === builtInAlias && eventKeyName) { return isKeyNotMatch(builtInName, eventKeyName) } else if (keyCodes) { From 763df6740ff35228f3dc4167ed2328c2eab59377 Mon Sep 17 00:00:00 2001 From: JingkaiZhao Date: Tue, 2 Jan 2018 23:40:47 +0800 Subject: [PATCH 6/6] revert: codegen unit tests --- test/unit/modules/compiler/codegen.spec.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index e080a0e5aa9..b1085a571df 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -265,17 +265,17 @@ describe('codegen', () => { it('generate events with keycode', () => { assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;onInput($event)}}})}` ) // multiple keycodes (delete) assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` ) // multiple keycodes (chained) assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter")&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete"]))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key)&&_k($event.keyCode,"delete",[8,46],$event.key))return null;onInput($event)}}})}` ) // number keycode assertCodegen( @@ -285,7 +285,7 @@ describe('codegen', () => { // custom keycode assertCodegen( '', - `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key,undefined))return null;onInput($event)}}})}` + `with(this){return _c('input',{on:{"input":function($event){if(!('button' in $event)&&_k($event.keyCode,"custom",undefined,$event.key))return null;onInput($event)}}})}` ) }) @@ -308,12 +308,12 @@ describe('codegen', () => { it('generate events with generic modifiers and keycode correct order', () => { assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.preventDefault();onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.preventDefault();onInput($event)}}})}` ) assertCodegen( '', - `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.stopPropagation();onInput($event)}}})}` + `with(this){return _c('input',{on:{"keydown":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;$event.stopPropagation();onInput($event)}}})}` ) }) @@ -420,7 +420,7 @@ describe('codegen', () => { // with modifiers assertCodegen( ``, - `with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;(e=>current++)($event)}}})}` + `with(this){return _c('input',{on:{"keyup":function($event){if(!('button' in $event)&&_k($event.keyCode,"enter",13,$event.key))return null;(e=>current++)($event)}}})}` ) })