Files
linsa-linsa-io/web/hooks/use-key-down.ts
Aslam 867478d55c fix: Link, Pages, Topic, Hook and Others (#178)
* chore: remove useKeyDownListener

* chore: remove react-use, update jazz version and add query string

* chore: update jazz version

* chore: use simple mac or win utils code

* feat(util): add isTextInput

* feat(hooks): all needed hooks

* fix: link bunch stuff

* fix: page bunch stuff

* chore: bunch update for custom component

* chore: use throttle from internal hook

* chore: topic bunch stuff

* chore: update layout

* fix: truncate content header of topic detail
2024-09-23 23:16:02 +07:00

80 lines
1.9 KiB
TypeScript

import { isModKey, isTextInput } from "@/lib/utils"
import * as React from "react"
type Callback = (event: KeyboardEvent) => void
export type KeyFilter = ((event: KeyboardEvent) => boolean) | string
export type Options = {
allowInInput?: boolean
}
type RegisteredCallback = {
callback: Callback
options?: Options
}
// Registered keyboard event callbacks
let callbacks: RegisteredCallback[] = []
// Track if IME input suggestions are open so we can ignore keydown shortcuts
// in this case, they should never be triggered from mobile keyboards.
let imeOpen = false
// Based on implementation in react-use
// https://github.com/streamich/react-use/blob/master/src/useKey.ts#L15-L22
const createKeyPredicate = (keyFilter: KeyFilter) =>
typeof keyFilter === "function"
? keyFilter
: typeof keyFilter === "string"
? (event: KeyboardEvent) => event.key === keyFilter
: keyFilter
? (_event: KeyboardEvent) => true
: (_event: KeyboardEvent) => false
export function useKeyDown(key: KeyFilter, fn: Callback, options?: Options): void {
const predicate = createKeyPredicate(key)
React.useEffect(() => {
const handler = (event: KeyboardEvent) => {
if (predicate(event)) {
fn(event)
}
}
callbacks.push({
callback: handler,
options
})
return () => {
callbacks = callbacks.filter(cb => cb.callback !== handler)
}
}, [fn, predicate, options])
}
window.addEventListener("keydown", event => {
if (imeOpen) {
return
}
// reverse so that the last registered callbacks get executed first
for (const registered of callbacks.reverse()) {
if (event.defaultPrevented === true) {
break
}
if (!isTextInput(event.target as HTMLElement) || registered.options?.allowInInput || isModKey(event)) {
registered.callback(event)
}
}
})
window.addEventListener("compositionstart", () => {
imeOpen = true
})
window.addEventListener("compositionend", () => {
imeOpen = false
})