From 2b3be2798e4e2baae2f1a3669bfb813408414890 Mon Sep 17 00:00:00 2001 From: ollieri3 Date: Tue, 1 Oct 2024 10:56:04 +0100 Subject: [PATCH] Add support for event capturing when registering listeners --- README.md | 1 + src/types.ts | 3 ++- src/useHotkeys.ts | 8 ++++---- tests/useHotkeys.test.tsx | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6acb38d6..dff17b2f 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ All options are optional and have a default value which you can override to chan | `keydown` | `boolean` | `true` | Determines whether to listen to the browsers `keydown` event for triggering the callback. If you set both `keyup`and `keydown` to true, the callback will trigger on both events. | | `preventDefault` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `false` | Set this to a `true` if you want the hook to prevent the browsers default behavior on certain keystrokes like `meta+s` to save a page. NOTE: Certain keystrokes are not preventable, like `meta+w` to close a tab in chrome. | | `description` | `string` | `undefined` | Use this option to describe what the hotkey does. this is helpful if you want to display a list of active hotkeys to the user. | +| `capture` | `boolean` | `undefined` | Set this option to have the hook use event capturing instead of event bubbling when registering event listeners. | #### Overloads diff --git a/src/types.ts b/src/types.ts index a5181c29..f8e72b13 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,7 +39,8 @@ export type Options = { preventDefault?: Trigger // Prevent default browser behavior? (Default: false) description?: string // Use this option to describe what the hotkey does. (Default: undefined) document?: Document // Listen to events on the document instead of the window. (Default: false) - ignoreModifiers?: boolean // Ignore modifiers when matching hotkeys. (Default: false) + ignoreModifiers?: boolean // Ignore modifiers when matching hotkeys. (Default: false), + capture?: boolean // Sets key event handlers to trigger on capturing phase. (Default: false) } export type OptionsOrDependencyArray = Options | DependencyList diff --git a/src/useHotkeys.ts b/src/useHotkeys.ts index a6d9600c..ad1c3909 100644 --- a/src/useHotkeys.ts +++ b/src/useHotkeys.ts @@ -143,9 +143,9 @@ export default function useHotkeys( const domNode = ref || _options?.document || document // @ts-ignore - domNode.addEventListener('keyup', handleKeyUp) + domNode.addEventListener('keyup', handleKeyUp, { capture: memoisedOptions?.capture }) // @ts-ignore - domNode.addEventListener('keydown', handleKeyDown) + domNode.addEventListener('keydown', handleKeyDown, { capture: memoisedOptions?.capture }) if (proxy) { parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) => @@ -155,9 +155,9 @@ export default function useHotkeys( return () => { // @ts-ignore - domNode.removeEventListener('keyup', handleKeyUp) + domNode.removeEventListener('keyup', handleKeyUp, { capture: memoisedOptions?.capture }) // @ts-ignore - domNode.removeEventListener('keydown', handleKeyDown) + domNode.removeEventListener('keydown', handleKeyDown, { capture: memoisedOptions?.capture }) if (proxy) { parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) => diff --git a/tests/useHotkeys.test.tsx b/tests/useHotkeys.test.tsx index c6f67894..59d0ab00 100644 --- a/tests/useHotkeys.test.tsx +++ b/tests/useHotkeys.test.tsx @@ -1346,4 +1346,23 @@ test('Should listen to special chars with modifiers', async () => { await user.keyboard(`{Shift>}-{/Shift}`) expect(callback).toHaveBeenCalledTimes(1) +}) + +test('should use event capturing if option is set', async () => { + const user = userEvent.setup() + const callback = jest.fn() + + const Component = ({ cb }: { cb: HotkeyCallback }) => { + useHotkeys('a', cb, { capture: true, enableOnFormTags: true }) + + return e.stopPropagation()} /> + } + + const { getByTestId } = render() + + await user.click(getByTestId('form-tag')) + await user.keyboard('A') + + expect(callback).toHaveBeenCalledTimes(1) + expect(getByTestId('form-tag')).toHaveValue('A') }) \ No newline at end of file