From 94c705ec685bc4048600f352dadfee76597bd13e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 5 Jun 2017 20:50:58 -0400 Subject: [PATCH] Don't match shiftKey strictly, but prioritize shifted over unshifted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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') --- modules/lib/d3.keybinding.js | 84 +++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/modules/lib/d3.keybinding.js b/modules/lib/d3.keybinding.js index db5462ae6..51c32fe7c 100644 --- a/modules/lib/d3.keybinding.js +++ b/modules/lib/d3.keybinding.js @@ -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; - } - } 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; - } - return true; - } function testBindings(isCapturing) { - for (var i = 0; i < bindings.length; i++) { - var binding = bindings[i]; + var didMatch = false, + i, binding; - if (!!binding.capture === isCapturing && matches(binding, d3.event)) { + // 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; + } + } + + // 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(); } } + + + 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') { @@ -60,6 +89,7 @@ export function d3keybinding(namespace) { testBindings(false); } + function keybinding(selection) { selection = selection || d3.select(document); selection.on('keydown.capture' + namespace, capture, true); @@ -67,6 +97,7 @@ export function d3keybinding(namespace) { return keybinding; } + keybinding.off = function(selection) { bindings = []; selection = selection || d3.select(document); @@ -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++) {