Skip to content

Commit

Permalink
Don't match shiftKey strictly, but prioritize shifted over unshifted
Browse files Browse the repository at this point in the history
Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
so we don't strictly match on the shift key, but we prioritize
shifted bindings first, and fallback to unshifted only if no match.
(This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
  • Loading branch information
bhousel committed Jun 6, 2017
1 parent 32995e5 commit 94c705e
Showing 1 changed file with 57 additions and 25 deletions.
82 changes: 57 additions & 25 deletions modules/lib/d3.keybinding.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,73 @@ import _ from 'lodash';
export function d3keybinding(namespace) {
var bindings = [];

function matches(binding, event) {
if (event.key !== undefined) {
if (binding.event.key === undefined) {
return false;
} else if (_.isArray(binding.event.key)) {
if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event.key.toLowerCase()) === -1)
return false;
} else {
if (event.key.toLowerCase() !== binding.event.key.toLowerCase())
return false;

function testBindings(isCapturing) {
var didMatch = false,
i, binding;

// Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
// so we don't strictly match on the shift key, but we prioritize
// shifted bindings first, and fallback to unshifted only if no match.
// (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')

// priority match shifted bindings first
for (i = 0; i < bindings.length; i++) {
binding = bindings[i];
if (!binding.event.modifiers.shiftKey) continue; // no shift
if (!!binding.capture !== isCapturing) continue;
if (matches(binding, true)) {
binding.callback();
didMatch = true;
}
} else {
// check keycodes if browser doesn't support KeyboardEvent.key
if (event.keyCode !== binding.event.keyCode)
return false;
}
// check modifier keys
for (var p in binding.event.modifiers) {
if (event[p] !== binding.event.modifiers[p])
return false;

// then unshifted bindings
if (didMatch) return;
for (i = 0; i < bindings.length; i++) {
binding = bindings[i];
if (binding.event.modifiers.shiftKey) continue; // shift
if (!!binding.capture !== isCapturing) continue;
if (matches(binding, false)) {
binding.callback();
}
}
return true;
}

function testBindings(isCapturing) {
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i];

if (!!binding.capture === isCapturing && matches(binding, d3.event)) {
binding.callback();
function matches(binding, testShift) {
var event = d3.event;
if (event.key !== undefined) {
if (binding.event.key === undefined) {
return false;
} else if (_.isArray(binding.event.key)) {
if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event.key.toLowerCase()) === -1)
return false;
} else {
if (event.key.toLowerCase() !== binding.event.key.toLowerCase())
return false;
}
} else {
// check keycodes if browser doesn't support KeyboardEvent.key
if (event.keyCode !== binding.event.keyCode)
return false;
}

// test modifier keys
if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
if (event.altKey !== binding.event.modifiers.altKey) return false;
if (event.metaKey !== binding.event.modifiers.metaKey) return false;
if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;

return true;
}
}


function capture() {
testBindings(true);
}


function bubble() {
var tagName = d3.select(d3.event.target).node().tagName;
if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
Expand All @@ -60,13 +89,15 @@ export function d3keybinding(namespace) {
testBindings(false);
}


function keybinding(selection) {
selection = selection || d3.select(document);
selection.on('keydown.capture' + namespace, capture, true);
selection.on('keydown.bubble' + namespace, bubble, false);
return keybinding;
}


keybinding.off = function(selection) {
bindings = [];
selection = selection || d3.select(document);
Expand All @@ -75,6 +106,7 @@ export function d3keybinding(namespace) {
return keybinding;
};


keybinding.on = function(codes, callback, capture) {
var arr = [].concat(codes);
for (var i = 0; i < arr.length; i++) {
Expand Down

0 comments on commit 94c705e

Please sign in to comment.