Files
archived-linsa/web/app/hooks/use-key-down.ts
Aslam a440828f8c chore: Enhancement + New Feature (#185)
* wip

* wip page

* chore: style

* wip pages

* wip pages

* chore: toggle

* chore: link

* feat: topic search

* chore: page section

* refactor: apply tailwind class ordering

* fix: handle loggedIn user for guest route

* feat: folder & image schema

* chore: move utils to shared

* refactor: tailwind class ordering

* feat: img ext for editor

* refactor: remove qa

* fix: tanstack start

* fix: wrong import

* chore: use toast

* chore: schema
2024-10-18 21:18:20 +07:00

79 lines
1.8 KiB
TypeScript

import * as React from "react"
import { isTextInput } from "@/lib/utils"
import { isModKey, isServer } from "@shared/utils"
export type KeyFilter = ((event: KeyboardEvent) => boolean) | string
export type Options = { allowInInput?: boolean }
type RegisteredCallback = {
callback: (event: KeyboardEvent) => void
options?: Options
}
let callbacks: RegisteredCallback[] = []
let isInitialized = false
const initializeKeyboardListeners = () => {
if (isServer() || isInitialized) return
let imeOpen = false
window.addEventListener("keydown", (event) => {
if (imeOpen) return
for (const registered of [...callbacks].reverse()) {
if (event.defaultPrevented) 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
})
isInitialized = true
}
const createKeyPredicate = (keyFilter: KeyFilter) =>
typeof keyFilter === "function"
? keyFilter
: typeof keyFilter === "string"
? (event: KeyboardEvent) => event.key === keyFilter
: keyFilter
? () => true
: () => false
export function useKeyDown(
key: KeyFilter,
fn: (event: KeyboardEvent) => void,
options?: Options,
): void {
const predicate = React.useMemo(() => createKeyPredicate(key), [key])
React.useEffect(() => {
initializeKeyboardListeners()
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])
}