Repository: vuejs/vue
Pull request: Modifiers key modifier for directive on

add ctrl, shift and alt modifiers for v-on


Changed files:
filename: src/directives/public/on.js
patch: @@ -14,6 +14,12 @@ const keyCodes = {
   down: 40
 }
 
+const eventKeys = ['stop', 'prevent', 'self', 'capture']
+const modifierKeys = ['ctrl', 'shift', 'alt', 'meta']
+const namedKeys = eventKeys.concat(modifierKeys).sort()
+
+var modifierKeyHandlers = {}
+
 function keyFilter (handler, keys) {
   var codes = keys.map(function (key) {
     var charCode = key.charCodeAt(0)
@@ -58,6 +64,17 @@ function selfFilter (handler) {
   }
 }
 
+modifierKeys.forEach((key) => {
+  var keyName = key + 'Key'
+  modifierKeyHandlers[key] = function (handler) {
+    return function (e) {
+      if (e[keyName]) {
+        return handler.call(this, e)
+      }
+    }
+  }
+})
+
 export default {
 
   priority: ON,
@@ -110,13 +127,15 @@ export default {
     if (this.modifiers.self) {
       handler = selfFilter(handler)
     }
+    modifierKeys.forEach((key) => {
+      if (this.modifiers[key]) {
+        handler = modifierKeyHandlers[key](handler)
+      }
+    })
     // key filter
     var keys = Object.keys(this.modifiers)
       .filter(function (key) {
-        return key !== 'stop' &&
-          key !== 'prevent' &&
-          key !== 'self' &&
-          key !== 'capture'
+        return namedKeys.indexOf(key) === -1
       })
     if (keys.length) {
       handler = keyFilter(handler, keys)

filename: test/unit/specs/directives/public/on_spec.js
patch: @@ -86,6 +86,353 @@ describe('v-on', function () {
     })
   })
 
+  it('with ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + alt + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.alt.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.altKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + alt + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.alt.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.altKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
   it('with delete modifier capturing DEL', function (done) {
     new Vue({
       el: el,