diff --git a/src/compiler/codegen/events.js b/src/compiler/codegen/events.js index 7bfe5ec1c5f..7f7954dd96d 100644 --- a/src/compiler/codegen/events.js +++ b/src/compiler/codegen/events.js @@ -3,6 +3,12 @@ const fnExpRE = /^\s*([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/ const simplePathRE = /^\s*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?']|\[".*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*\s*$/ +const keyboardEvents = [ + 'keydown', + 'keyup', + 'keypress' +] + // keyCode aliases const keyCodes: { [key: string]: number | Array } = { esc: 27, @@ -29,9 +35,15 @@ const modifierCode: { [key: string]: string } = { shift: genGuard(`!$event.shiftKey`), alt: genGuard(`!$event.altKey`), meta: genGuard(`!$event.metaKey`), - left: genGuard(`'button' in $event && $event.button !== 0`), - middle: genGuard(`'button' in $event && $event.button !== 1`), - right: genGuard(`'button' in $event && $event.button !== 2`) + primary: genGuard(`'button' in $event && $event.button !== 0`), + auxiliary: genGuard(`'button' in $event && $event.button !== 1`), + secondary: genGuard(`'button' in $event && $event.button !== 2`) +} + +const deprecatedPointerModifierAliases: { [key: string]: string } = { + left: 'primary', + middle: 'auxiliary', + right: 'secondary' } export function genHandlers ( @@ -41,21 +53,22 @@ export function genHandlers ( ): string { let res = isNative ? 'nativeOn:{' : 'on:{' for (const name in events) { - res += `"${name}":${genHandler(name, events[name])},` + res += `"${name}":${genHandler(name, events[name], warn)},` } return res.slice(0, -1) + '}' } function genHandler ( name: string, - handler: ASTElementHandler | Array + handler: ASTElementHandler | Array, + warn: Function ): string { if (!handler) { return 'function(){}' } if (Array.isArray(handler)) { - return `[${handler.map(handler => genHandler(name, handler)).join(',')}]` + return `[${handler.map(handler => genHandler(name, handler, warn)).join(',')}]` } const isMethodPath = simplePathRE.test(handler.value) @@ -70,12 +83,18 @@ function genHandler ( let genModifierCode = '' const keys = [] for (const key in handler.modifiers) { - if (modifierCode[key]) { - genModifierCode += modifierCode[key] - // left/right - if (keyCodes[key]) { + if (deprecatedPointerModifierAliases[key]) { + // left/right modifierCodes (for mouse) collide with left/right keyCodes + if (keyCodes[key] && keyboardEvents.indexOf(name) >= 0) { keys.push(key) + } else { + process.env.NODE_ENV !== 'production' && warn( + `Pointer modifier "${key}" is deprecated. Use "${deprecatedPointerModifierAliases[key]}" instead.` + ) + genModifierCode += modifierCode[deprecatedPointerModifierAliases[key]] } + } else if (modifierCode[key]) { + genModifierCode += modifierCode[key] } else if (key === 'exact') { const modifiers: ASTModifiers = (handler.modifiers: any) genModifierCode += genGuard( diff --git a/src/compiler/helpers.js b/src/compiler/helpers.js index 1349fa364f4..3e2d6015f4f 100644 --- a/src/compiler/helpers.js +++ b/src/compiler/helpers.js @@ -71,14 +71,15 @@ export function addHandler ( name = '&' + name // mark the event as passive } - // normalize click.right and click.middle since they don't actually fire + // normalize click.secondary (click.right) and click.auxiliary (click.middle) + // since they don't actually fire // this is technically browser-specific, but at least for now browsers are // the only target envs that have right/middle clicks. if (name === 'click') { - if (modifiers.right) { + if (modifiers.secondary || modifiers.right) { name = 'contextmenu' - delete modifiers.right - } else if (modifiers.middle) { + delete (modifiers.secondary || modifiers.right) + } else if (modifiers.auxiliary || modifiers.middle) { name = 'mouseup' } } diff --git a/test/unit/features/directives/on.spec.js b/test/unit/features/directives/on.spec.js index 4ef3c699fe0..a485aeefa45 100644 --- a/test/unit/features/directives/on.spec.js +++ b/test/unit/features/directives/on.spec.js @@ -319,47 +319,72 @@ describe('Directive v-on', () => { expect(spy).toHaveBeenCalled() }) - it('should support mouse modifier', () => { - const left = 0 - const middle = 1 - const right = 2 - const spyLeft = jasmine.createSpy() - const spyMiddle = jasmine.createSpy() - const spyRight = jasmine.createSpy() + it('should support mouse (pointer) modifiers', () => { + const primary = 0 + const auxiliary = 1 + const secondary = 2 + const spyPrimary = jasmine.createSpy() + const spyAuxiliary = jasmine.createSpy() + const spySecondary = jasmine.createSpy() vm = new Vue({ el, template: `
-
left
-
right
-
right
+
primary
+
secondary
+
auxiliary
`, methods: { - foo: spyLeft, - foo1: spyRight, - foo2: spyMiddle + foo: spyPrimary, + foo1: spySecondary, + foo2: spyAuxiliary } }) - triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = right }) - triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = middle }) - expect(spyLeft).not.toHaveBeenCalled() - triggerEvent(vm.$refs.left, 'mousedown', e => { e.button = left }) - expect(spyLeft).toHaveBeenCalled() + // unexpected warnings fail the test + expect(`Pointer modifier "left" is deprecated. Use "primary" instead.`).toHaveBeenWarned() + expect(`Pointer modifier "right" is deprecated. Use "secondary" instead.`).toHaveBeenWarned() + expect(`Pointer modifier "middle" is deprecated. Use "auxiliary" instead.`).toHaveBeenWarned() + + triggerEvent(vm.$refs.primary, 'mousedown', e => { e.button = secondary }) + triggerEvent(vm.$refs.primary, 'mousedown', e => { e.button = auxiliary }) + expect(spyPrimary).not.toHaveBeenCalled() + triggerEvent(vm.$refs.primary, 'mousedown', e => { e.button = primary }) + expect(spyPrimary.calls.count()).toBe(2) - triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = left }) - triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = middle }) - expect(spyRight).not.toHaveBeenCalled() - triggerEvent(vm.$refs.right, 'mousedown', e => { e.button = right }) - expect(spyRight).toHaveBeenCalled() + triggerEvent(vm.$refs.secondary, 'mousedown', e => { e.button = primary }) + triggerEvent(vm.$refs.secondary, 'mousedown', e => { e.button = auxiliary }) + expect(spySecondary).not.toHaveBeenCalled() + triggerEvent(vm.$refs.secondary, 'mousedown', e => { e.button = secondary }) + expect(spySecondary.calls.count()).toBe(2) - triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = left }) - triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = right }) - expect(spyMiddle).not.toHaveBeenCalled() - triggerEvent(vm.$refs.middle, 'mousedown', e => { e.button = middle }) - expect(spyMiddle).toHaveBeenCalled() + triggerEvent(vm.$refs.auxiliary, 'mousedown', e => { e.button = primary }) + triggerEvent(vm.$refs.auxiliary, 'mousedown', e => { e.button = secondary }) + expect(spyAuxiliary).not.toHaveBeenCalled() + triggerEvent(vm.$refs.auxiliary, 'mousedown', e => { e.button = auxiliary }) + expect(spyAuxiliary.calls.count()).toBe(2) + }) + + it('warn deprecated mouse (pointer) modifiers', () => { + new Vue({ + template: `
primary
`, + methods: { foo: spy } + }).$mount() + expect(`Pointer modifier "left" is deprecated. Use "primary" instead.`).toHaveBeenWarned() + + new Vue({ + template: `
secondary
`, + methods: { foo: spy } + }).$mount() + expect(`Pointer modifier "right" is deprecated. Use "secondary" instead.`).toHaveBeenWarned() + + new Vue({ + template: `
auxiliary
`, + methods: { foo: spy } + }).$mount() + expect(`Pointer modifier "middle" is deprecated. Use "auxiliary" instead.`).toHaveBeenWarned() }) it('should support custom keyCode', () => { @@ -693,27 +718,29 @@ describe('Directive v-on', () => { expect(prevented).toBe(true) }) - it('should transform click.right to contextmenu', () => { - const spy = jasmine.createSpy('click.right') + it('should transform click.right (click.secondary) to contextmenu', () => { const vm = new Vue({ - template: `
`, + template: `
`, methods: { foo: spy } }).$mount() + expect(`Pointer modifier "right" is deprecated. Use "secondary" instead.`).toHaveBeenWarned() triggerEvent(vm.$el, 'contextmenu') - expect(spy).toHaveBeenCalled() + expect(spy.calls.count()).toBe(2) }) - it('should transform click.middle to mouseup', () => { + it('should transform click.middle (click.auxiliary) to mouseup', () => { const spy = jasmine.createSpy('click.middle') const vm = new Vue({ - template: `
`, + template: `
`, methods: { foo: spy } }).$mount() + expect(`Pointer modifier "middle" is deprecated. Use "auxiliary" instead.`).toHaveBeenWarned() + triggerEvent(vm.$el, 'mouseup', e => { e.button = 0 }) expect(spy).not.toHaveBeenCalled() triggerEvent(vm.$el, 'mouseup', e => { e.button = 1 }) - expect(spy).toHaveBeenCalled() + expect(spy.calls.count()).toBe(2) }) it('object syntax (no argument)', () => {