diff --git a/packages/core/onKeyStroke/demo.vue b/packages/core/onKeyStroke/demo.vue index 88e1d1d9b01..61be6301b5e 100644 --- a/packages/core/onKeyStroke/demo.vue +++ b/packages/core/onKeyStroke/demo.vue @@ -5,21 +5,21 @@ import { onKeyStroke } from '@vueuse/core' const translateX = ref(0) const translateY = ref(0) -onKeyStroke(['w', 'W', 'ArrowUp'], (e: KeyboardEvent) => { +onKeyStroke(['w', 'W', 'ArrowUp'], () => { translateY.value -= 10 }) -onKeyStroke(['s', 'S', 'ArrowDown'], (e: KeyboardEvent) => { +onKeyStroke(['s', 'S', 'ArrowDown'], () => { translateY.value += 10 }) -onKeyStroke(['a', 'A', 'ArrowLeft'], (e: KeyboardEvent) => { +onKeyStroke(['a', 'A', 'ArrowLeft'], () => { translateX.value -= 10 }) -onKeyStroke(['d', 'D', 'ArrowRight'], (e: KeyboardEvent) => { +onKeyStroke(['d', 'D', 'ArrowRight'], () => { translateX.value += 10 -}) +}, { dedupe: true }) diff --git a/packages/core/onKeyStroke/index.md b/packages/core/onKeyStroke/index.md index ef4b7371c13..6f1c7a6a369 100644 --- a/packages/core/onKeyStroke/index.md +++ b/packages/core/onKeyStroke/index.md @@ -44,6 +44,21 @@ onKeyStroke('A', (e) => { }, { target: document }) ``` +### Ignore Repeated Events + +The callback will trigger only once when pressing `A` and **hold down**. + +```js +import { onKeyStroke, onKeyStrokeOnce } from '@vueuse/core' + +// use `autoRepeat` option +onKeyStroke('A', (e) => { + console.log('Key A pressed') +}, { autoRepeat: false }) +``` + +Reference: [KeyboardEvent.repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) + ## Directive Usage ```html diff --git a/packages/core/onKeyStroke/index.test.ts b/packages/core/onKeyStroke/index.test.ts index 35392f118ae..e11ba8efe7e 100644 --- a/packages/core/onKeyStroke/index.test.ts +++ b/packages/core/onKeyStroke/index.test.ts @@ -13,8 +13,8 @@ describe('onKeyStroke', () => { callBackFn = vi.fn() }) - function createKeyEvent(key: string, type: KeyStrokeEventName) { - const ev = new KeyboardEvent(type, { key }) + function createKeyEvent(key: string, type: KeyStrokeEventName, repeat = false) { + const ev = new KeyboardEvent(type, { key, repeat }) element.value.dispatchEvent(ev) } @@ -73,4 +73,13 @@ describe('onKeyStroke', () => { createKeyEvent('B', 'keypress') expect(callBackFn).toBeCalledTimes(1) }) + + it('ignore repeated events', () => { + onKeyStroke('A', callBackFn, { target: element, dedupe: true }) + createKeyEvent('A', 'keydown', false) + createKeyEvent('A', 'keydown', true) + createKeyEvent('A', 'keydown', true) + createKeyEvent('A', 'keydown', true) + expect(callBackFn).toBeCalledTimes(1) + }) }) diff --git a/packages/core/onKeyStroke/index.ts b/packages/core/onKeyStroke/index.ts index 58319fd556d..a4b71bb33c9 100644 --- a/packages/core/onKeyStroke/index.ts +++ b/packages/core/onKeyStroke/index.ts @@ -1,4 +1,5 @@ import type { MaybeComputedRef } from '@vueuse/shared' +import { resolveUnref } from '@vueuse/shared' import { useEventListener } from '../useEventListener' import { defaultWindow } from '../_configurable' @@ -9,6 +10,12 @@ export interface OnKeyStrokeOptions { eventName?: KeyStrokeEventName target?: MaybeComputedRef passive?: boolean + /** + * Set to `true` to ignore repeated events when the key is being held down. + * + * @default false + */ + dedupe?: MaybeComputedRef } const createKeyPredicate = (keyFilter: KeyFilter): KeyPredicate => { @@ -60,9 +67,17 @@ export function onKeyStroke(...args: any[]) { handler = args[0] } - const { target = defaultWindow, eventName = 'keydown', passive = false } = options + const { + target = defaultWindow, + eventName = 'keydown', + passive = false, + dedupe = false, + } = options const predicate = createKeyPredicate(key) const listener = (e: KeyboardEvent) => { + if (e.repeat && resolveUnref(dedupe)) + return + if (predicate(e)) handler(e) }