Skip to content

Commit

Permalink
feat(onKeyStroke): support capture all handler (#2197)
Browse files Browse the repository at this point in the history
  • Loading branch information
azaleta committed Sep 16, 2022
1 parent a3742d7 commit 86c12b9
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 11 deletions.
2 changes: 1 addition & 1 deletion packages/core/onKeyStroke/directive.ts
Expand Up @@ -12,7 +12,7 @@ HTMLElement,
BindingValueFunction | BindingValueArray
> = {
[directiveHooks.mounted](el, binding) {
const keys = binding.arg?.split(',') ?? []
const keys = binding.arg?.split(',') ?? true
if (typeof binding.value === 'function') {
onKeyStroke(keys, binding.value, {
target: el,
Expand Down
9 changes: 9 additions & 0 deletions packages/core/onKeyStroke/index.md
Expand Up @@ -26,6 +26,15 @@ import { onKeyStroke } from '@vueuse/core'
onKeyStroke(['s', 'S', 'ArrowDown'], (e) => {
e.preventDefault()
})

// listen to all keys by [true / skip the keyDefine]
onKeyStroke(true, (e) => {
e.preventDefault()
})
onKeyStroke((e) => {
e.preventDefault()
})

```

### Custom Event Target
Expand Down
76 changes: 76 additions & 0 deletions packages/core/onKeyStroke/index.test.ts
@@ -0,0 +1,76 @@
import { vi } from 'vitest'
import type { Ref } from 'vue-demi'
import { ref } from 'vue-demi'
import type { KeyStrokeEventName } from '.'
import { onKeyStroke } from '.'

describe('onKeyStroke', () => {
let element: Ref<HTMLElement>
let callBackFn: any

beforeEach(() => {
element = ref(document.createElement('div'))
callBackFn = vi.fn()
})

function createKeyEvent(key: string, type: KeyStrokeEventName) {
const ev = new KeyboardEvent(type, { key })
element.value.dispatchEvent(ev)
}

it('listen to single key', () => {
onKeyStroke('A', callBackFn, { target: element })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
expect(callBackFn).toBeCalledTimes(1)
})

it('listen to multi keys', () => {
onKeyStroke(['A', 'B', 'C'], callBackFn, { target: element })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
createKeyEvent('C', 'keydown')
createKeyEvent('D', 'keydown')
expect(callBackFn).toBeCalledTimes(3)
})

it('use function filter', () => {
const filter = (event: KeyboardEvent) => {
return event.key === 'A'
}
onKeyStroke(filter, callBackFn, { target: element })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
createKeyEvent('C', 'keydown')
expect(callBackFn).toBeCalledTimes(1)
})

it('listen to all keys by boolean', () => {
onKeyStroke(true, callBackFn, { target: element })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
createKeyEvent('C', 'keydown')
createKeyEvent('D', 'keydown')
createKeyEvent('E', 'keydown')
expect(callBackFn).toBeCalledTimes(5)
})

it('listen to all keys by constructor', () => {
onKeyStroke(callBackFn, { target: element })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
createKeyEvent('C', 'keydown')
createKeyEvent('D', 'keydown')
createKeyEvent('E', 'keydown')
expect(callBackFn).toBeCalledTimes(5)
})

it('listen to keypress', () => {
onKeyStroke('A', callBackFn, { target: element, eventName: 'keypress' })
createKeyEvent('A', 'keydown')
createKeyEvent('B', 'keydown')
createKeyEvent('A', 'keypress')
createKeyEvent('B', 'keypress')
expect(callBackFn).toBeCalledTimes(1)
})
})
43 changes: 33 additions & 10 deletions packages/core/onKeyStroke/index.ts
Expand Up @@ -3,7 +3,7 @@ import { useEventListener } from '../useEventListener'
import { defaultWindow } from '../_configurable'

export type KeyPredicate = (event: KeyboardEvent) => boolean
export type KeyFilter = null | undefined | string | string[] | KeyPredicate
export type KeyFilter = true | string | string[] | KeyPredicate
export type KeyStrokeEventName = 'keydown' | 'keypress' | 'keyup'
export interface OnKeyStrokeOptions {
eventName?: KeyStrokeEventName
Expand All @@ -21,22 +21,45 @@ const createKeyPredicate = (keyFilter: KeyFilter): KeyPredicate => {
else if (Array.isArray(keyFilter))
return (event: KeyboardEvent) => keyFilter.includes(event.key)

else if (keyFilter)
return () => true

else
return () => false
return () => true
}

export function onKeyStroke(key: KeyFilter, handler: (event: KeyboardEvent) => void, options?: OnKeyStrokeOptions): () => void
export function onKeyStroke(handler: (event: KeyboardEvent) => void, options?: OnKeyStrokeOptions): () => void

/**
* Listen for keyboard keys being stroked.
*
* @see https://vueuse.org/onKeyStroke
* @param key
* @param handler
* @param options
*/
export function onKeyStroke(key: KeyFilter, handler: (event: KeyboardEvent) => void, options: OnKeyStrokeOptions = {}) {
export function onKeyStroke(key: KeyFilter, handler: (event: KeyboardEvent) => void, options?: OnKeyStrokeOptions): () => void
export function onKeyStroke(handler: (event: KeyboardEvent) => void, options?: OnKeyStrokeOptions): () => void
export function onKeyStroke(...args: any[]) {
let key: KeyFilter
let handler: (event: KeyboardEvent) => void
let options: OnKeyStrokeOptions = {}

if (args.length === 3) {
key = args[0]
handler = args[1]
options = args[2]
}
else if (args.length === 2) {
if (typeof args[1] === 'object') {
key = true
handler = args[0]
options = args[1]
}
else {
key = args[0]
handler = args[1]
}
}
else {
key = true
handler = args[0]
}

const { target = defaultWindow, eventName = 'keydown', passive = false } = options
const predicate = createKeyPredicate(key)
const listener = (e: KeyboardEvent) => {
Expand Down

0 comments on commit 86c12b9

Please sign in to comment.