From 4379bd4906a8450c59768255b4e9e84bcde2176b Mon Sep 17 00:00:00 2001 From: HyunJun CHOI Date: Fri, 6 Feb 2026 12:07:12 +0900 Subject: [PATCH 1/3] feat: Add hotkey conflict detection with configurable behavior --- .changeset/conflict-detection.md | 14 +++++ packages/keys/src/hotkey-manager.ts | 75 ++++++++++++++++++++++ packages/keys/tests/manager.test.ts | 97 +++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 .changeset/conflict-detection.md diff --git a/.changeset/conflict-detection.md b/.changeset/conflict-detection.md new file mode 100644 index 0000000..1e9d614 --- /dev/null +++ b/.changeset/conflict-detection.md @@ -0,0 +1,14 @@ +--- +'@tanstack/keys': minor +--- + +Add hotkey conflict detection with configurable behavior + +Implements conflict detection when registering hotkeys with the same combination on the same target. Adds a new `conflictBehavior` option to `HotkeyOptions`: + +- `'warn'` (default) - Log a warning to console but allow both registrations +- `'error'` - Throw an error and prevent the new registration +- `'replace'` - Unregister the existing hotkey and register the new one +- `'allow'` - Allow multiple registrations without warning + +This addresses the "Warn/error on conflicting shortcuts (TBD)" item from the README. diff --git a/packages/keys/src/hotkey-manager.ts b/packages/keys/src/hotkey-manager.ts index 5157435..44c626e 100644 --- a/packages/keys/src/hotkey-manager.ts +++ b/packages/keys/src/hotkey-manager.ts @@ -8,6 +8,16 @@ import type { ParsedHotkey, } from './hotkey' +/** + * Behavior when registering a hotkey that conflicts with an existing registration. + * + * - `'warn'` - Log a warning to the console but allow both registrations (default) + * - `'error'` - Throw an error and prevent the new registration + * - `'replace'` - Unregister the existing hotkey and register the new one + * - `'allow'` - Allow multiple registrations of the same hotkey without warning + */ +export type ConflictBehavior = 'warn' | 'error' | 'replace' | 'allow' + /** * Options for registering a hotkey. */ @@ -28,6 +38,8 @@ export interface HotkeyOptions { stopPropagation?: boolean /** The DOM element to attach the event listener to. Defaults to document. */ target?: HTMLElement | Document | Window | null + /** Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' */ + conflictBehavior?: ConflictBehavior } /** @@ -112,6 +124,7 @@ const defaultHotkeyOptions: Omit< requireReset: false, enabled: true, ignoreInputs: true, + conflictBehavior: 'warn', } let registrationIdCounter = 0 @@ -220,6 +233,19 @@ export class HotkeyManager { options.target ?? (typeof document !== 'undefined' ? document : ({} as Document)) + // Resolve conflict behavior + const conflictBehavior = options.conflictBehavior ?? 'warn' + + // Check for existing registrations with the same hotkey and target + const conflictingRegistration = this.#findConflictingRegistration( + hotkey, + target, + ) + + if (conflictingRegistration) { + this.#handleConflict(conflictingRegistration, hotkey, conflictBehavior) + } + const registration: HotkeyRegistration = { id, hotkey, @@ -508,6 +534,55 @@ export class HotkeyManager { return false } + /** + * Finds an existing registration with the same hotkey and target. + */ + #findConflictingRegistration( + hotkey: Hotkey, + target: HTMLElement | Document | Window, + ): HotkeyRegistration | null { + for (const registration of this.#registrations.values()) { + if (registration.hotkey === hotkey && registration.target === target) { + return registration + } + } + return null + } + + /** + * Handles conflicts between hotkey registrations based on conflict behavior. + */ + #handleConflict( + conflictingRegistration: HotkeyRegistration, + hotkey: Hotkey, + conflictBehavior: ConflictBehavior, + ): void { + if (conflictBehavior === 'allow') { + return + } + + if (conflictBehavior === 'warn') { + console.warn( + `Hotkey '${hotkey}' is already registered. Multiple handlers will be triggered. ` + + `Use conflictBehavior: 'replace' to replace the existing handler, ` + + `or conflictBehavior: 'allow' to suppress this warning.`, + ) + return + } + + if (conflictBehavior === 'error') { + throw new Error( + `Hotkey '${hotkey}' is already registered. ` + + `Use conflictBehavior: 'replace' to replace the existing handler, ` + + `or conflictBehavior: 'allow' to allow multiple registrations.`, + ) + } + + if (conflictBehavior === 'replace') { + this.#unregister(conflictingRegistration.id) + } + } + /** * Checks if an element is an input-like element that should be ignored. * diff --git a/packages/keys/tests/manager.test.ts b/packages/keys/tests/manager.test.ts index b502ac2..b33dd5e 100644 --- a/packages/keys/tests/manager.test.ts +++ b/packages/keys/tests/manager.test.ts @@ -717,4 +717,101 @@ describe('HotkeyManager', () => { } }) }) + + describe('conflict detection', () => { + it('should warn by default when registering a conflicting hotkey', () => { + const manager = HotkeyManager.getInstance() + const callback1 = vi.fn() + const callback2 = vi.fn() + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + manager.register('Mod+S', callback1) + manager.register('Mod+S', callback2) + + expect(warnSpy).toHaveBeenCalled() + expect(warnSpy.mock.calls[0]?.[0]).toContain('already registered') + expect(manager.getRegistrationCount()).toBe(2) + + warnSpy.mockRestore() + }) + + it('should throw error when conflictBehavior is "error"', () => { + const manager = HotkeyManager.getInstance() + const callback1 = vi.fn() + const callback2 = vi.fn() + + manager.register('Mod+S', callback1) + + expect(() => { + manager.register('Mod+S', callback2, { conflictBehavior: 'error' }) + }).toThrow('already registered') + + expect(manager.getRegistrationCount()).toBe(1) + }) + + it('should replace existing registration when conflictBehavior is "replace"', () => { + const manager = HotkeyManager.getInstance() + const callback1 = vi.fn() + const callback2 = vi.fn() + + manager.register('Mod+S', callback1, { platform: 'mac' }) + expect(manager.getRegistrationCount()).toBe(1) + + manager.register('Mod+S', callback2, { + conflictBehavior: 'replace', + platform: 'mac', + }) + expect(manager.getRegistrationCount()).toBe(1) + + document.dispatchEvent( + createKeyboardEvent('keydown', 's', { metaKey: true }), + ) + + expect(callback1).not.toHaveBeenCalled() + expect(callback2).toHaveBeenCalledOnce() + }) + + it('should allow multiple registrations when conflictBehavior is "allow"', () => { + const manager = HotkeyManager.getInstance() + const callback1 = vi.fn() + const callback2 = vi.fn() + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + manager.register('Mod+S', callback1, { platform: 'mac' }) + manager.register('Mod+S', callback2, { + conflictBehavior: 'allow', + platform: 'mac', + }) + + expect(warnSpy).not.toHaveBeenCalled() + expect(manager.getRegistrationCount()).toBe(2) + + document.dispatchEvent( + createKeyboardEvent('keydown', 's', { metaKey: true }), + ) + + expect(callback1).toHaveBeenCalledOnce() + expect(callback2).toHaveBeenCalledOnce() + + warnSpy.mockRestore() + }) + + it('should not conflict when same hotkey is registered on different targets', () => { + const manager = HotkeyManager.getInstance() + const callback1 = vi.fn() + const callback2 = vi.fn() + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + const div1 = document.createElement('div') + const div2 = document.createElement('div') + + manager.register('Mod+S', callback1, { target: div1 }) + manager.register('Mod+S', callback2, { target: div2 }) + + expect(warnSpy).not.toHaveBeenCalled() + expect(manager.getRegistrationCount()).toBe(2) + + warnSpy.mockRestore() + }) + }) }) From 7febeb49b5b36e42fccfcaf706727d2670ff3ecb Mon Sep 17 00:00:00 2001 From: HyunJun CHOI Date: Fri, 6 Feb 2026 13:27:13 +0900 Subject: [PATCH 2/3] fix: Remove unnecessary conditional in conflict handler ESLint was correctly identifying that the final if statement was unnecessary since all other cases (allow, warn, error) return early. At this point in the code, conflictBehavior must be 'replace'. --- packages/keys/src/hotkey-manager.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/keys/src/hotkey-manager.ts b/packages/keys/src/hotkey-manager.ts index 44c626e..62de2b5 100644 --- a/packages/keys/src/hotkey-manager.ts +++ b/packages/keys/src/hotkey-manager.ts @@ -578,9 +578,8 @@ export class HotkeyManager { ) } - if (conflictBehavior === 'replace') { - this.#unregister(conflictingRegistration.id) - } + // At this point, conflictBehavior must be 'replace' + this.#unregister(conflictingRegistration.id) } /** From 081f91f5aedf82f7e852bc18004040ea154f9ab9 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 03:30:22 +0000 Subject: [PATCH 3/3] ci: apply automated fixes --- docs/reference/classes/HotkeyManager.md | 14 ++++---- docs/reference/functions/getHotkeyManager.md | 2 +- docs/reference/index.md | 1 + docs/reference/interfaces/HotkeyOptions.md | 30 +++++++++++------ .../interfaces/HotkeyRegistration.md | 16 +++++----- .../interfaces/HotkeyRegistrationHandle.md | 12 +++---- docs/reference/interfaces/SequenceOptions.md | 32 ++++++++++++++----- .../type-aliases/ConflictBehavior.md | 19 +++++++++++ packages/keys/tests/match.test.ts | 4 ++- 9 files changed, 90 insertions(+), 40 deletions(-) create mode 100644 docs/reference/type-aliases/ConflictBehavior.md diff --git a/docs/reference/classes/HotkeyManager.md b/docs/reference/classes/HotkeyManager.md index 9ae621c..6d7b7e5 100644 --- a/docs/reference/classes/HotkeyManager.md +++ b/docs/reference/classes/HotkeyManager.md @@ -5,7 +5,7 @@ title: HotkeyManager # Class: HotkeyManager -Defined in: [hotkey-manager.ts:145](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L145) +Defined in: [hotkey-manager.ts:158](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L158) Singleton manager for hotkey registrations. @@ -34,7 +34,7 @@ unregister() destroy(): void; ``` -Defined in: [hotkey-manager.ts:609](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L609) +Defined in: [hotkey-manager.ts:683](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L683) Destroys the manager and removes all listeners. @@ -50,7 +50,7 @@ Destroys the manager and removes all listeners. getRegistrationCount(): number; ``` -Defined in: [hotkey-manager.ts:580](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L580) +Defined in: [hotkey-manager.ts:654](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L654) Gets the number of registered hotkeys. @@ -66,7 +66,7 @@ Gets the number of registered hotkeys. isRegistered(hotkey, target?): boolean; ``` -Defined in: [hotkey-manager.ts:591](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L591) +Defined in: [hotkey-manager.ts:665](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L665) Checks if a specific hotkey is registered. @@ -101,7 +101,7 @@ register( options): HotkeyRegistrationHandle; ``` -Defined in: [hotkey-manager.ts:209](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L209) +Defined in: [hotkey-manager.ts:222](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L222) Registers a hotkey handler and returns a handle for updating the registration. @@ -157,7 +157,7 @@ handle.unregister() static getInstance(): HotkeyManager; ``` -Defined in: [hotkey-manager.ts:167](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L167) +Defined in: [hotkey-manager.ts:180](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L180) Gets the singleton instance of HotkeyManager. @@ -173,7 +173,7 @@ Gets the singleton instance of HotkeyManager. static resetInstance(): void; ``` -Defined in: [hotkey-manager.ts:177](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L177) +Defined in: [hotkey-manager.ts:190](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L190) Resets the singleton instance. Useful for testing. diff --git a/docs/reference/functions/getHotkeyManager.md b/docs/reference/functions/getHotkeyManager.md index 22b56fd..0deade3 100644 --- a/docs/reference/functions/getHotkeyManager.md +++ b/docs/reference/functions/getHotkeyManager.md @@ -9,7 +9,7 @@ title: getHotkeyManager function getHotkeyManager(): HotkeyManager; ``` -Defined in: [hotkey-manager.ts:625](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L625) +Defined in: [hotkey-manager.ts:699](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L699) Gets the singleton HotkeyManager instance. Convenience function for accessing the manager. diff --git a/docs/reference/index.md b/docs/reference/index.md index 1d2f154..6b95e4a 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -30,6 +30,7 @@ title: "@tanstack/keys" ## Type Aliases - [CanonicalModifier](type-aliases/CanonicalModifier.md) +- [ConflictBehavior](type-aliases/ConflictBehavior.md) - [EditingKey](type-aliases/EditingKey.md) - [FunctionKey](type-aliases/FunctionKey.md) - [HeldKey](type-aliases/HeldKey.md) diff --git a/docs/reference/interfaces/HotkeyOptions.md b/docs/reference/interfaces/HotkeyOptions.md index 61362cc..a964631 100644 --- a/docs/reference/interfaces/HotkeyOptions.md +++ b/docs/reference/interfaces/HotkeyOptions.md @@ -5,7 +5,7 @@ title: HotkeyOptions # Interface: HotkeyOptions -Defined in: [hotkey-manager.ts:14](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L14) +Defined in: [hotkey-manager.ts:24](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L24) Options for registering a hotkey. @@ -15,13 +15,25 @@ Options for registering a hotkey. ## Properties +### conflictBehavior? + +```ts +optional conflictBehavior: ConflictBehavior; +``` + +Defined in: [hotkey-manager.ts:42](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L42) + +Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' + +*** + ### enabled? ```ts optional enabled: boolean; ``` -Defined in: [hotkey-manager.ts:16](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L16) +Defined in: [hotkey-manager.ts:26](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L26) Whether the hotkey is enabled. Defaults to true @@ -33,7 +45,7 @@ Whether the hotkey is enabled. Defaults to true optional eventType: "keydown" | "keyup"; ``` -Defined in: [hotkey-manager.ts:18](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L18) +Defined in: [hotkey-manager.ts:28](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L28) The event type to listen for. Defaults to 'keydown' @@ -45,7 +57,7 @@ The event type to listen for. Defaults to 'keydown' optional ignoreInputs: boolean; ``` -Defined in: [hotkey-manager.ts:20](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L20) +Defined in: [hotkey-manager.ts:30](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L30) Whether to ignore hotkeys when keyboard events originate from input-like elements (input, textarea, select, contenteditable). Defaults to true @@ -57,7 +69,7 @@ Whether to ignore hotkeys when keyboard events originate from input-like element optional platform: "mac" | "windows" | "linux"; ``` -Defined in: [hotkey-manager.ts:22](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L22) +Defined in: [hotkey-manager.ts:32](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L32) The target platform for resolving 'Mod' @@ -69,7 +81,7 @@ The target platform for resolving 'Mod' optional preventDefault: boolean; ``` -Defined in: [hotkey-manager.ts:24](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L24) +Defined in: [hotkey-manager.ts:34](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L34) Prevent the default browser action when the hotkey matches @@ -81,7 +93,7 @@ Prevent the default browser action when the hotkey matches optional requireReset: boolean; ``` -Defined in: [hotkey-manager.ts:26](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L26) +Defined in: [hotkey-manager.ts:36](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L36) If true, only trigger once until all keys are released. Default: false @@ -93,7 +105,7 @@ If true, only trigger once until all keys are released. Default: false optional stopPropagation: boolean; ``` -Defined in: [hotkey-manager.ts:28](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L28) +Defined in: [hotkey-manager.ts:38](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L38) Stop event propagation when the hotkey matches @@ -105,6 +117,6 @@ Stop event propagation when the hotkey matches optional target: Document | Window | HTMLElement | null; ``` -Defined in: [hotkey-manager.ts:30](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L30) +Defined in: [hotkey-manager.ts:40](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L40) The DOM element to attach the event listener to. Defaults to document. diff --git a/docs/reference/interfaces/HotkeyRegistration.md b/docs/reference/interfaces/HotkeyRegistration.md index 5c664cf..5a5a80a 100644 --- a/docs/reference/interfaces/HotkeyRegistration.md +++ b/docs/reference/interfaces/HotkeyRegistration.md @@ -5,7 +5,7 @@ title: HotkeyRegistration # Interface: HotkeyRegistration -Defined in: [hotkey-manager.ts:36](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L36) +Defined in: [hotkey-manager.ts:48](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L48) A registered hotkey handler in the HotkeyManager. @@ -17,7 +17,7 @@ A registered hotkey handler in the HotkeyManager. callback: HotkeyCallback; ``` -Defined in: [hotkey-manager.ts:44](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L44) +Defined in: [hotkey-manager.ts:56](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L56) The callback to invoke @@ -29,7 +29,7 @@ The callback to invoke hasFired: boolean; ``` -Defined in: [hotkey-manager.ts:48](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L48) +Defined in: [hotkey-manager.ts:60](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L60) Whether this registration has fired and needs reset (for requireReset) @@ -41,7 +41,7 @@ Whether this registration has fired and needs reset (for requireReset) hotkey: Hotkey; ``` -Defined in: [hotkey-manager.ts:40](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L40) +Defined in: [hotkey-manager.ts:52](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L52) The original hotkey string @@ -53,7 +53,7 @@ The original hotkey string id: string; ``` -Defined in: [hotkey-manager.ts:38](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L38) +Defined in: [hotkey-manager.ts:50](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L50) Unique identifier for this registration @@ -65,7 +65,7 @@ Unique identifier for this registration options: HotkeyOptions; ``` -Defined in: [hotkey-manager.ts:46](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L46) +Defined in: [hotkey-manager.ts:58](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L58) Options for this registration @@ -77,7 +77,7 @@ Options for this registration parsedHotkey: ParsedHotkey; ``` -Defined in: [hotkey-manager.ts:42](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L42) +Defined in: [hotkey-manager.ts:54](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L54) The parsed hotkey @@ -89,6 +89,6 @@ The parsed hotkey target: Document | Window | HTMLElement; ``` -Defined in: [hotkey-manager.ts:50](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L50) +Defined in: [hotkey-manager.ts:62](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L62) The resolved target element for this registration diff --git a/docs/reference/interfaces/HotkeyRegistrationHandle.md b/docs/reference/interfaces/HotkeyRegistrationHandle.md index 7903b84..30be008 100644 --- a/docs/reference/interfaces/HotkeyRegistrationHandle.md +++ b/docs/reference/interfaces/HotkeyRegistrationHandle.md @@ -5,7 +5,7 @@ title: HotkeyRegistrationHandle # Interface: HotkeyRegistrationHandle -Defined in: [hotkey-manager.ts:79](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L79) +Defined in: [hotkey-manager.ts:91](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L91) A handle returned from HotkeyManager.register() that allows updating the callback and options without re-registering the hotkey. @@ -41,7 +41,7 @@ handle.unregister() callback: HotkeyCallback; ``` -Defined in: [hotkey-manager.ts:90](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L90) +Defined in: [hotkey-manager.ts:102](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L102) The callback function. Can be set directly to update without re-registering. This avoids stale closures when the callback references React state. @@ -54,7 +54,7 @@ This avoids stale closures when the callback references React state. readonly id: string; ``` -Defined in: [hotkey-manager.ts:81](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L81) +Defined in: [hotkey-manager.ts:93](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L93) Unique identifier for this registration @@ -66,7 +66,7 @@ Unique identifier for this registration readonly isActive: boolean; ``` -Defined in: [hotkey-manager.ts:99](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L99) +Defined in: [hotkey-manager.ts:111](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L111) Check if this registration is still active (not unregistered) @@ -78,7 +78,7 @@ Check if this registration is still active (not unregistered) setOptions: (options) => void; ``` -Defined in: [hotkey-manager.ts:96](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L96) +Defined in: [hotkey-manager.ts:108](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L108) Update options (merged with existing options). Useful for updating `enabled`, `preventDefault`, etc. without re-registering. @@ -101,7 +101,7 @@ Useful for updating `enabled`, `preventDefault`, etc. without re-registering. unregister: () => void; ``` -Defined in: [hotkey-manager.ts:84](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L84) +Defined in: [hotkey-manager.ts:96](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L96) Unregister this hotkey diff --git a/docs/reference/interfaces/SequenceOptions.md b/docs/reference/interfaces/SequenceOptions.md index 845a6b9..93e9e52 100644 --- a/docs/reference/interfaces/SequenceOptions.md +++ b/docs/reference/interfaces/SequenceOptions.md @@ -15,13 +15,29 @@ Options for hotkey sequence matching. ## Properties +### conflictBehavior? + +```ts +optional conflictBehavior: ConflictBehavior; +``` + +Defined in: [hotkey-manager.ts:42](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L42) + +Behavior when this hotkey conflicts with an existing registration on the same target. Defaults to 'warn' + +#### Inherited from + +[`HotkeyOptions`](HotkeyOptions.md).[`conflictBehavior`](HotkeyOptions.md#conflictbehavior) + +*** + ### enabled? ```ts optional enabled: boolean; ``` -Defined in: [hotkey-manager.ts:16](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L16) +Defined in: [hotkey-manager.ts:26](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L26) Whether the hotkey is enabled. Defaults to true @@ -37,7 +53,7 @@ Whether the hotkey is enabled. Defaults to true optional eventType: "keydown" | "keyup"; ``` -Defined in: [hotkey-manager.ts:18](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L18) +Defined in: [hotkey-manager.ts:28](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L28) The event type to listen for. Defaults to 'keydown' @@ -53,7 +69,7 @@ The event type to listen for. Defaults to 'keydown' optional ignoreInputs: boolean; ``` -Defined in: [hotkey-manager.ts:20](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L20) +Defined in: [hotkey-manager.ts:30](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L30) Whether to ignore hotkeys when keyboard events originate from input-like elements (input, textarea, select, contenteditable). Defaults to true @@ -69,7 +85,7 @@ Whether to ignore hotkeys when keyboard events originate from input-like element optional platform: "mac" | "windows" | "linux"; ``` -Defined in: [hotkey-manager.ts:22](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L22) +Defined in: [hotkey-manager.ts:32](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L32) The target platform for resolving 'Mod' @@ -85,7 +101,7 @@ The target platform for resolving 'Mod' optional preventDefault: boolean; ``` -Defined in: [hotkey-manager.ts:24](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L24) +Defined in: [hotkey-manager.ts:34](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L34) Prevent the default browser action when the hotkey matches @@ -101,7 +117,7 @@ Prevent the default browser action when the hotkey matches optional requireReset: boolean; ``` -Defined in: [hotkey-manager.ts:26](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L26) +Defined in: [hotkey-manager.ts:36](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L36) If true, only trigger once until all keys are released. Default: false @@ -117,7 +133,7 @@ If true, only trigger once until all keys are released. Default: false optional stopPropagation: boolean; ``` -Defined in: [hotkey-manager.ts:28](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L28) +Defined in: [hotkey-manager.ts:38](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L38) Stop event propagation when the hotkey matches @@ -133,7 +149,7 @@ Stop event propagation when the hotkey matches optional target: Document | Window | HTMLElement | null; ``` -Defined in: [hotkey-manager.ts:30](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L30) +Defined in: [hotkey-manager.ts:40](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L40) The DOM element to attach the event listener to. Defaults to document. diff --git a/docs/reference/type-aliases/ConflictBehavior.md b/docs/reference/type-aliases/ConflictBehavior.md new file mode 100644 index 0000000..b7254ae --- /dev/null +++ b/docs/reference/type-aliases/ConflictBehavior.md @@ -0,0 +1,19 @@ +--- +id: ConflictBehavior +title: ConflictBehavior +--- + +# Type Alias: ConflictBehavior + +```ts +type ConflictBehavior = "warn" | "error" | "replace" | "allow"; +``` + +Defined in: [hotkey-manager.ts:19](https://github.com/TanStack/keys/blob/main/packages/keys/src/hotkey-manager.ts#L19) + +Behavior when registering a hotkey that conflicts with an existing registration. + +- `'warn'` - Log a warning to the console but allow both registrations (default) +- `'error'` - Throw an error and prevent the new registration +- `'replace'` - Unregister the existing hotkey and register the new one +- `'allow'` - Allow multiple registrations of the same hotkey without warning diff --git a/packages/keys/tests/match.test.ts b/packages/keys/tests/match.test.ts index e561634..be4ec32 100644 --- a/packages/keys/tests/match.test.ts +++ b/packages/keys/tests/match.test.ts @@ -259,7 +259,9 @@ describe('matchesKeyboardEvent', () => { metaKey: true, code: 'KeyT', // uppercase }) - expect(matchesKeyboardEvent(event2, 'Mod+Alt+t' as Hotkey, 'mac')).toBe(true) + expect(matchesKeyboardEvent(event2, 'Mod+Alt+t' as Hotkey, 'mac')).toBe( + true, + ) }) })