Optional
listenerAn event listener for all events the manager handles including virtualPress/virtualRelease.
The listener is called right after keys are found so you can have access to the keys as the manager understood them, but before everything else (setting the state, labeling, adding/removing from the chain). Note that isKeydown
is always true for wheel events since their keyup is emulated within the same event handler.
This exists because not all events trigger shortcuts (which can access only the event that triggered them) but there are many times when you might still want access to the event to do things like e.preventdefault()
:
e.preventDefault()
except in some rare cases:
e.preventDefault()
for any chords that are also browser shortcuts (though note not all browser shortcuts can be overriden). If you really need to there is the Keyboard API which allows requesting some keyboard shortcuts be locked (see Keyboard Locking).The following should give you a good starting point:
manager.listener = (({event, isKeydown, keys}) => {
if (
(manager.isRecording && !(event instanceof MouseEvent))
// || TODO browser shortcuts filter
) {
event.preventDefault()
}
})
A name for the manager.
Most options can be set directly, except any readonly options which must go through setManagerProperties to ensure state remains consistent.
The error callback for recoverable errors such as multiple shortcuts matching, no shortcut matching once a chord chain has been "started", or an unknown key event because no matching key was found (only for keyboard events).
Usually you will want to clear the manager's chain when this happens and display the error to the user. The default callback logs the error and clears the chain.
In the case of multiple valid shortcuts, if you trigger any of the shortcuts "manually" note that you will need to simulate both the keydown/keyup calls if you differentiate between them.
Also the input event can be undefined if you set the manager chain directly since it will check if it should trigger shortcuts.
Determines if two conditions are equal.
This is actually not a good idea to implement if you use boolean conditions. See ConditionComparer for why.
Enable/disable listeners. Listeners will remain attached but do nothing.
Enable/disable triggering of shortcuts. The manager otherwise works as normal.
Determines how conditions are evaluated.
Whether to check the state of modifier or toggle keys using event.getModifierState
.
This is set to true by default when using createManager
because it is usually what you want. It tracks the state with the most accuracy when, for example, the user focuses out, toggles a key, then focuses back.
But, if you are allowing the user to change key states in some way (e.g. clicking on keys in the settings to visualize shortcuts), you will want to disable this temporarily so that they can click modifier keys. Otherwise they'd be immediately toggled off again (by the state check during the click) and nothing would happen.
Keys also have their own individual Key.updateStateOnAllEvents in case you need more fine grained control.
When this is set to false, the default mouseenter handler will ignore the event.
If you will always have this set to false, you can forego the mouseenter event listener.
The manager requires some state to function and be a little bit more efficient. All properties are readonly because they should not be modified unless they are allowed to be by setManagerProp.
Readonly
chain: string[][]The current chain of chords.
Note that the manager's chain is not neccesarily valid and should be checked before assigning to a shortcut.
Readonly
isAwaitingKeyup: booleanWhether the manager is waiting for non-modifier keys to be release.
Readonly
isRecording: booleanWhether the manager is in recording mode.
When enabling/disabling this property, you should clear the chain first with safeSetSetManagerChain.
safeSetManagerChain(manager, [])
setManagerProp(manager, "state.isRecording", true)
//...
safeSetManagerChain(manager, [])
setManagerProp(manager, "state.isRecording", false)
To allow users to record shortcuts, you can do something like have an input element that on focus starts recording and stops when focused is blurred (this has the advantage of working with keyboard navigation). In such a case, you can use the Manager.listener to e.preventDefault()
all events except clicks outside the input. See it for more details.
Readonly
nextIsChord: booleanThe manager keeps track of whether the next key press should start a new chord or not.
Readonly
untrigger: false | TriggerableShortcutThere are times, such as after a keydown event, that a shortcut command will trigger, but we also need to "untrigger" it later on key up. If there is a triggerable shortcut it is saved here. See Manager.state.chain.
Hooks provide a way to set further limits on what can be changed (can hooks), and provide listeners for when something is about to change (on* hooks).
on* hooks allow hooking into changes for certain properties (such as key size/pos changed) to trigger recalculations (e.g. of layouts).
They could also technically be used as an escape hatch for frameworks that don't support working mutable data, but I would advice against it. Instead I would recommend using a proxy based state management solution like valtio that allows for mutations and optimized rendering.