diff --git a/.changeset/vast-jokes-serve.md b/.changeset/vast-jokes-serve.md new file mode 100644 index 0000000..ffc5952 --- /dev/null +++ b/.changeset/vast-jokes-serve.md @@ -0,0 +1,6 @@ +--- +'@tanstack/hotkeys-devtools': patch +'@tanstack/hotkeys': patch +--- + +feat: callback variant of conflictBehavior diff --git a/docs/reference/type-aliases/ConflictBehavior.md b/docs/reference/type-aliases/ConflictBehavior.md index 50f06ca..c473ba7 100644 --- a/docs/reference/type-aliases/ConflictBehavior.md +++ b/docs/reference/type-aliases/ConflictBehavior.md @@ -6,10 +6,10 @@ title: ConflictBehavior # Type Alias: ConflictBehavior ```ts -type ConflictBehavior = "warn" | "error" | "replace" | "allow"; +type ConflictBehavior = "warn" | "error" | "replace" | "allow" | CustomConflictHandler; ``` -Defined in: [manager.utils.ts:11](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/manager.utils.ts#L11) +Defined in: [manager.utils.ts:16](https://github.com/TanStack/hotkeys/blob/main/packages/hotkeys/src/manager.utils.ts#L16) Behavior when registering a hotkey/sequence that conflicts with an existing registration. diff --git a/packages/hotkeys-devtools/src/components/DetailsPanel.tsx b/packages/hotkeys-devtools/src/components/DetailsPanel.tsx index 38411e3..e1e1d9f 100644 --- a/packages/hotkeys-devtools/src/components/DetailsPanel.tsx +++ b/packages/hotkeys-devtools/src/components/DetailsPanel.tsx @@ -119,7 +119,16 @@ function getConflictLabel( if (behavior === 'allow') return 'allowed' if (behavior === 'error') return 'error' if (behavior === 'replace') return 'replaced' - return 'warning' + if (behavior === 'warn') return 'warning' + return 'callback handled conflict with' +} + +function serializeConflictBehavior(behavior: ConflictBehavior): string { + if (typeof behavior === 'string') { + return behavior + } + + return '[Function function]' } function HotkeyDetails(props: { @@ -285,7 +294,9 @@ function HotkeyDetails(props: {
conflictBehavior - {conflictBehavior()} + + {serializeConflictBehavior(conflictBehavior())} +
hasFired @@ -498,7 +509,9 @@ function SequenceDetails(props: {
conflictBehavior - {conflictBehavior()} + + {serializeConflictBehavior(conflictBehavior())} +
diff --git a/packages/hotkeys/src/manager.utils.ts b/packages/hotkeys/src/manager.utils.ts index dc084fc..27ef095 100644 --- a/packages/hotkeys/src/manager.utils.ts +++ b/packages/hotkeys/src/manager.utils.ts @@ -1,5 +1,10 @@ import type { ParsedHotkey } from './hotkey' +type CustomConflictHandler = ( + keyDisplay: string, + unregisterAnotherConflictingId: () => void, +) => void + /** * Behavior when registering a hotkey/sequence that conflicts with an existing registration. * @@ -8,7 +13,12 @@ import type { ParsedHotkey } from './hotkey' * - `'replace'` - Unregister the existing registration and register the new one * - `'allow'` - Allow multiple registrations without warning */ -export type ConflictBehavior = 'warn' | 'error' | 'replace' | 'allow' +export type ConflictBehavior = + | 'warn' + | 'error' + | 'replace' + | 'allow' + | CustomConflictHandler /** * Default options for hotkey/sequence registration. @@ -164,6 +174,11 @@ export function handleConflict( ) } + if (typeof conflictBehavior === 'function') { + conflictBehavior(keyDisplay, () => unregister(conflictingId)) + return + } + // At this point, conflictBehavior must be 'replace' unregister(conflictingId) } diff --git a/packages/hotkeys/tests/manager.utils.test.ts b/packages/hotkeys/tests/manager.utils.test.ts index b929367..e17df03 100644 --- a/packages/hotkeys/tests/manager.utils.test.ts +++ b/packages/hotkeys/tests/manager.utils.test.ts @@ -286,5 +286,20 @@ describe('manager.utils', () => { expect(unregister).toHaveBeenCalledWith('id-1') }) + + it('should call custom callback if passed as conflictBehaviour', () => { + const unregister = vi.fn() + const handleConflictCallback = vi.fn() + + handleConflict('id-1', 'Mod+S', handleConflictCallback, unregister) + + expect(unregister).not.toHaveBeenCalledWith() + expect(handleConflictCallback).toHaveBeenCalledWith( + 'Mod+S', + expect.any(Function), + ) + handleConflictCallback.mock.calls[0]?.[1]() + expect(unregister).toHaveBeenCalledWith('id-1') + }) }) })