Implementing keyboard shortcuts
We use Mousetrap to implement keyboard shortcuts in GitLab.
Mousetrap provides an API that allows keyboard shortcut strings (like
mod+shift+p
or p b
) to be bound to a JavaScript handler:
// Don't do this; see note below
Mousetrap.bind('p b', togglePerformanceBar)
However, associating a hard-coded key sequence to a handler (as shown above) prevents these keyboard shortcuts from being customized or disabled by users.
To allow keyboard shortcuts to be customized, commands are defined in
~/behaviors/shortcuts/keybindings.js
. The keysFor
method is responsible for
returning the correct key sequence for the provided command:
import { keysFor, TOGGLE_PERFORMANCE_BAR } from '~/behaviors/shortcuts/keybindings'
Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), togglePerformanceBar);
Shortcut customization
keybindings.js
stores keyboard shortcut customizations as a JSON string in
localStorage
. When keysFor
is called, it uses the provided command object's
id
to lookup any customizations found in localStorage
and returns the custom
keybindings, or the default keybindings if the command has not been customized.
There is no UI to edit these customizations.
Adding new shortcuts
Because keyboard shortcuts can be customized or disabled by end users, developers are encouraged to build lots of keyboard shortcuts into GitLab. Shortcuts that are less likely to be used should be disabled by default.
To add a new shortcut, define and export a new command object in
keybindings.js
:
export const MAKE_COFFEE = {
id: 'foodAndBeverage.makeCoffee',
description: s__('KeyboardShortcuts|Make coffee'),
defaultKeys: ['mod+shift+c'],
};
Next, add a new command to the appropriate keybinding group object:
const COFFEE_GROUP = {
id: 'foodAndBeverage',
name: s__('KeyboardShortcuts|Food and Beverage'),
keybindings: [
MAKE_ESPRESSO,
MAKE_LATTE,
MAKE_COFFEE
];
}
Finally, in the application code, import the keysFor
function and the new
command object and bind the shortcut to the handler using Mousetrap:
import { keysFor, MAKE_COFFEE } from '~/behaviors/shortcuts/keybindings'
Mousetrap.bind(keysFor(MAKE_COFFEE), makeCoffee);
See the existing the command definitions in keybindings.js
for more examples.
Disabling shortcuts
A shortcut can be disabled, also known as unassigned, by assigning the
shortcut to an empty array []
. For example, to introduce a new shortcut that
is disabled by default, a command can be defined like this:
export const MAKE_MOCHA = {
id: 'foodAndBeverage.makeMocha',
description: s__('KeyboardShortcuts|Make a mocha'),
defaultKeys: [],
};
Making shortcuts non-customizable
Occasionally, it's important that a keyboard shortcut not be customizable (although this should be a rare occurrence).
In this case, a shortcut can be defined with customizable: false
, which
disables customization of the keybinding:
export const MAKE_AMERICANO = {
id: 'foodAndBeverage.makeAmericano',
description: s__('KeyboardShortcuts|Make an Americano'),
defaultKeys: ['mod+shift+a'],
// this disables customization of this shortcut
customizable: false
};
This shortcut will always be bound to its defaultKeys
.
Make cross-platform shortcuts
It's difficult to make shortcuts that work well in all platforms and browsers. This is one of the reasons that being able to customize and disable shortcuts is so important.
One important way to make keyboard shortcuts more portable is to use the mod
shortcut string, which resolves to command
on Mac and ctrl
otherwise.
See Mousetrap's documentation for more information.