Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(onKeyStroke): support capture all handler #2197

Merged
merged 1 commit into from Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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