diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b20f60..ab2a26be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ _8 nov 2024_ - Fixed issue with watching nested state variables and global state - Upgraded to renderer 2.6.2 - Fixed issue with white background for Elements with falsy src attribute -- Fixed issue with calling focus on component that already is focussed +- Fixed issue with calling focus on component that already is focused ## v1.9.2 diff --git a/docs/components/user_input.md b/docs/components/user_input.md index 68d580e8..75c6447f 100644 --- a/docs/components/user_input.md +++ b/docs/components/user_input.md @@ -72,6 +72,16 @@ When a component handles a key press by having a corresponding function specifie } ``` +## Intercepting key input + +In addition to the Event handling chain explained above. Blits offers the option to _intercept_ key presses at the root level of the Application, before they reach the currently focused Component. This can be useful in certain situation where you want to globally disable all key presses, or when implementing an override key press handler. + +The `intercept()` input-method can only be implemented in the `Blits.Application`-component. When present, the method acts as a _catch-all_ method, and will be executed for _all_ key presses. It receives the `KeyboardEvent` as its argument, allowing you to execute logic based on the key being pressed. + +Only when the `intercept()` input-method returns the `KeyboardEvent` (possibly modified), the keypress will continue to be handled (by the currently focused Component). + +The `intercept`-method can also be an asynchronous method. + ## Key-up handling The functions specified in the `input` configuration are invoked when a key is _pressed down_ (i.e. the `keydown` event listener). But sometimes you may also want to execute some logic when a key is _released_ (i.e. the `keyup` event listener). diff --git a/index.d.ts b/index.d.ts index 931f41c0..00f056b6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -81,14 +81,26 @@ declare module '@lightningjs/blits' { } export interface Input { - [key: string]: (event: KeyboardEvent) => void | undefined, + [key: string]: (event: KeyboardEvent) => void | undefined | unknown, /** * Catch all input function * * Will be invoked when there is no dedicated function for a certain key */ // @ts-ignore - any?: (event: KeyboardEvent) => void + any?: (event: KeyboardEvent) => void, + /** + * Intercept key presses on the root Application component before being handled + * by the currently focused component. + * + * Only when a KeyboardEvent (the original one, or a modified one) is returned from the + * intercept function, the Input event is passed on to the Component with focus. + * + * The intercept function can be asynchronous. + * + * Note: the intercept input handler is only available on the Root App component (i.e. Blits.Application) + */ + intercept?: (event: KeyboardEvent) => KeyboardEvent | Promise | any } export interface Log { diff --git a/src/application.js b/src/application.js index 6641b63c..b53bae73 100644 --- a/src/application.js +++ b/src/application.js @@ -56,8 +56,15 @@ const Application = (config) => { config.hooks[symbols.init] = function () { const keyMap = { ...defaultKeyMap, ...Settings.get('keymap', {}) } - keyDownHandler = (e) => { + keyDownHandler = async (e) => { const key = keyMap[e.key] || keyMap[e.keyCode] || e.key || e.keyCode + // intercept key press if specified in main Application component + if (this[symbols.inputEvents].intercept !== undefined) { + e = await this[symbols.inputEvents].intercept.call(this, e) + // only pass on the key press to focused component when keyboard event is returned + if (e instanceof KeyboardEvent === false) return + } + Focus.input(key, e) clearTimeout(holdTimeout) holdTimeout = setTimeout(() => {